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.

11563 lines
346 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. ssiapi.c
  5. Abstract:
  6. Authentication and replication API routines (server side).
  7. Author:
  8. Cliff Van Dyke (cliffv) 28-Jun-1991
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 02-Jan-1992 (madana)
  15. added support for builtin/multidomain replication.
  16. --*/
  17. //
  18. // Common include files.
  19. //
  20. #include "logonsrv.h" // Include files common to entire service
  21. #pragma hdrstop
  22. //
  23. // Include files specific to this .c file
  24. //
  25. #include "lsarepl.h" // PackLsa ..
  26. #include <ssiapi.h>
  27. #include <netlogp.h>
  28. #include <kerbcon.h>
  29. #include <winldap.h>
  30. #include <loghours.h>
  31. #include <dnssrv.h> // NetpSrv...
  32. #include <ntldap.h> // LDAP_SERVER_PERMISSIVE_MODIFY_OID_W
  33. //
  34. // Define the maximum number of deltas returned on any single call
  35. //
  36. // Theoretically, MaxNumDeltas should be some function of
  37. // PreferredMaximumLength. However, by the time you allow for
  38. // the large swing in PreferredMaximumLength allowed by the BDC replication
  39. // Governor and then not wanting this buffer to be ridiculously large
  40. // when the full 128K is asked for, we find that 1000 entries is always
  41. // a reasonable compromise.
  42. //
  43. #define MAX_DELTA_COUNT 1000
  44. //
  45. // Maximum number of deltas that can be generated by a single change log entry.
  46. //
  47. #define MAX_DELTAS_PER_CHANGELOG 4
  48. //
  49. // Host prefix for SPNs
  50. //
  51. #define NL_HOST_PREFIX L"HOST/"
  52. //
  53. // work record for SPN update:
  54. //
  55. typedef struct _NL_SPN_UPDATE {
  56. BOOLEAN SetSpn;
  57. BOOLEAN SetDnsHostName;
  58. BOOLEAN WriteEventLogOnFailure;
  59. LPWSTR DnsHostName;
  60. LPWSTR NetbiosComputerName;
  61. LPWSTR UncDcName;
  62. LPWSTR NetbiosDomainName;
  63. LPWSTR DnsDomainName;
  64. } NL_SPN_UPDATE, * PNL_SPN_UPDATE ;
  65. //
  66. // Challenge data struct and relevant defines
  67. //
  68. typedef struct _NL_CHALLENGE {
  69. //
  70. // Link to next challenge entry in NlGlobalChallengeList
  71. // (Serialized by NlGlobalChallengeCritSect)
  72. //
  73. LIST_ENTRY ChNext;
  74. //
  75. // Challenge sent by the client
  76. //
  77. NETLOGON_CREDENTIAL ChClientChallenge;
  78. //
  79. // Challenge returned by the server (us)
  80. //
  81. NETLOGON_CREDENTIAL ChServerChallenge;
  82. //
  83. // Time stampt when the challenge entry got created
  84. //
  85. ULONG ChSetTime;
  86. //
  87. // Name(s) of the account that the client used to
  88. // unsuccessfully authenticate on this challenge.
  89. // May be NULL.
  90. //
  91. LPWSTR ChFailedAccountName;
  92. //
  93. // The name of the client (must be the last field)
  94. //
  95. WCHAR ChClientName[ANYSIZE_ARRAY];
  96. } NL_CHALLENGE, *PNL_CHALLENGE;
  97. // Lifetime of a challenge entry in the global list of
  98. // outstanding challenges
  99. #define NL_CHALLENGE_TIMEOUT 120000 // 2*60*1000 = 2 minutes
  100. // Maximum number of challenges we are going to keep
  101. // before we start throwing away existing ones at random
  102. #define NL_MAX_CHALLENGE_COUNT 100000
  103. // Number of challenges at which we reschedule the scavenger
  104. // to run soon (as given by NL_LARGE_CHALLENGE_TIMEOUT).
  105. // Running the scavenger soon will prevent us from commiting
  106. // a lot of memory for an extended period for challenges coming
  107. // from a malicious client making tons of challenge requests.
  108. //
  109. #define NL_LARGE_CHALLENGE_COUNT 5000
  110. // Timeout when the scavenger will be rescheduled to run
  111. // upon detection of current large number of outstanding
  112. // challenges (provided the scavenger isn't already sheduled
  113. // to run earlier).
  114. #define NL_LARGE_CHALLENGE_COUNT_TIMEOUT 120000 // 2*60*1000 = 2 minutes
  115. VOID
  116. NlRemoveChallenge(
  117. IN PNL_CHALLENGE Challenge
  118. )
  119. /*++
  120. Routine Description:
  121. This function delinks a challenge entry from the global
  122. list of challenges and frees this entry.
  123. Enter with NlGlobalChallengeCritSect locked
  124. Arguments:
  125. Challenge -- The entry to be delinked and freed
  126. Return Value:
  127. None
  128. --*/
  129. {
  130. if ( Challenge->ChFailedAccountName != NULL ) {
  131. LocalFree( Challenge->ChFailedAccountName );
  132. }
  133. RemoveEntryList(&Challenge->ChNext);
  134. LocalFree( Challenge );
  135. NlGlobalChallengeCount --;
  136. }
  137. VOID
  138. NlScavengeOldChallenges(
  139. VOID
  140. )
  141. /*++
  142. Routine Description:
  143. This function removes all expired challenge entries
  144. from the global list of outstanding challenges.
  145. Arguments:
  146. None
  147. Return Value:
  148. None
  149. --*/
  150. {
  151. NTSTATUS Status = STATUS_ACCESS_DENIED;
  152. PLIST_ENTRY ChallengeEntry = NULL;
  153. PNL_CHALLENGE Challenge = NULL;
  154. ULONG CurrentTime;
  155. ULONG ElapsedTime;
  156. LPWSTR MsgStrings[3];
  157. CurrentTime = GetTickCount();
  158. EnterCriticalSection( &NlGlobalChallengeCritSect );
  159. ChallengeEntry = NlGlobalChallengeList.Flink;
  160. while ( ChallengeEntry != &NlGlobalChallengeList ) {
  161. Challenge = CONTAINING_RECORD( ChallengeEntry, NL_CHALLENGE, ChNext );
  162. ChallengeEntry = ChallengeEntry->Flink;
  163. //
  164. // If time has wrapped, account for it
  165. //
  166. if ( CurrentTime >= Challenge->ChSetTime ) {
  167. ElapsedTime = CurrentTime - Challenge->ChSetTime;
  168. } else {
  169. ElapsedTime = (0xFFFFFFFF - Challenge->ChSetTime) + CurrentTime;
  170. }
  171. //
  172. // The list of challenges is sorted by the time stampt.
  173. // So if this entry is old, remove it. Otherwise, we have
  174. // removed all expired entries, so break from the loop.
  175. //
  176. if ( ElapsedTime >= NL_CHALLENGE_TIMEOUT ) {
  177. //
  178. // Write the event log stating that this client
  179. // failed to authenticate. Note that since we avoid
  180. // duplicate event logs, there will be only one
  181. // message (which is good) for multiple challenges
  182. // from the same client for the same account.
  183. //
  184. MsgStrings[0] = Challenge->ChClientName;
  185. //
  186. // If the account name is available, log it
  187. //
  188. if ( Challenge->ChFailedAccountName != NULL ) {
  189. MsgStrings[1] = Challenge->ChFailedAccountName;
  190. MsgStrings[2] = (LPWSTR) LongToPtr( Status );
  191. NlpWriteEventlog( NELOG_NetlogonServerAuthFailed,
  192. EVENTLOG_ERROR_TYPE,
  193. (LPBYTE) & Status,
  194. sizeof(Status),
  195. MsgStrings,
  196. 3 | NETP_LAST_MESSAGE_IS_NTSTATUS );
  197. } else {
  198. MsgStrings[1] = (LPWSTR) LongToPtr( Status );
  199. NlpWriteEventlog( NELOG_NetlogonServerAuthFailedNoAccount,
  200. EVENTLOG_ERROR_TYPE,
  201. (LPBYTE) & Status,
  202. sizeof(Status),
  203. MsgStrings,
  204. 2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
  205. }
  206. //
  207. // Delink and free this entry
  208. //
  209. NlRemoveChallenge( Challenge );
  210. } else {
  211. break;
  212. }
  213. }
  214. LeaveCriticalSection( &NlGlobalChallengeCritSect );
  215. }
  216. NTSTATUS
  217. NlInsertChallenge(
  218. IN LPWSTR ClientName,
  219. IN PNETLOGON_CREDENTIAL ClientChallenge,
  220. IN PNETLOGON_CREDENTIAL ServerChallenge
  221. )
  222. /*++
  223. Routine Description:
  224. This function inserts a pair of client/server challenges into the
  225. global list of outstanding challenges.
  226. Arguments:
  227. ClientName -- Name of the client supplying the client challenge.
  228. ClientCredential -- 64 bit challenge supplied by the client.
  229. ServerCredential -- Server challenge response returned by us
  230. Return Value:
  231. STATUS_SUCCESS -- A new challenge entry was successfully added
  232. STATUS_NO_MEMORY -- The was not enough memory
  233. --*/
  234. {
  235. PLIST_ENTRY ChallengeEntry = NULL;
  236. PNL_CHALLENGE NewChallenge = NULL;
  237. PNL_CHALLENGE Challenge = NULL;
  238. BOOL RescheduleScavenger = FALSE;
  239. //
  240. // Allocate a new challenge entry
  241. //
  242. NewChallenge = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_CHALLENGE) +
  243. (wcslen(ClientName) + 1) * sizeof(WCHAR) );
  244. if ( NewChallenge == NULL ) {
  245. return STATUS_NO_MEMORY;
  246. }
  247. //
  248. // Fill it in
  249. //
  250. NewChallenge->ChSetTime = GetTickCount();
  251. RtlCopyMemory( &NewChallenge->ChClientName, ClientName, (wcslen(ClientName) + 1) * sizeof(WCHAR) );
  252. NewChallenge->ChClientChallenge = *ClientChallenge;
  253. NewChallenge->ChServerChallenge = *ServerChallenge;
  254. EnterCriticalSection( &NlGlobalChallengeCritSect );
  255. //
  256. // First scavenge old entries
  257. //
  258. NlScavengeOldChallenges();
  259. //
  260. // Now determine if we have exceeded the limit
  261. // on the total number of outstanding challenges
  262. //
  263. if ( NlGlobalChallengeCount >= NL_MAX_CHALLENGE_COUNT ) {
  264. ULONG Index = 0;
  265. ULONG RemoveEntryNumber;
  266. //
  267. // Pick up an entry at random and free it
  268. //
  269. RemoveEntryNumber = rand() % NL_MAX_CHALLENGE_COUNT;
  270. //
  271. // Locate that entry in the list and remove it
  272. //
  273. for ( ChallengeEntry = NlGlobalChallengeList.Flink;
  274. ChallengeEntry != &NlGlobalChallengeList;
  275. ChallengeEntry = ChallengeEntry->Flink ) {
  276. if ( Index == RemoveEntryNumber ) {
  277. Challenge = CONTAINING_RECORD( ChallengeEntry, NL_CHALLENGE, ChNext );
  278. NlPrint(( NL_CRITICAL,
  279. "NlInsertChallenge: Removing challenge %ld for %ws\n",
  280. Index,
  281. Challenge->ChClientName ));
  282. NlRemoveChallenge( Challenge );
  283. break;
  284. }
  285. Index ++;
  286. }
  287. }
  288. //
  289. // Finally insert into the list at the tail to keep the list
  290. // sorted by the tick count.
  291. //
  292. // Note that the tick count can't be reset, so the list will
  293. // stay always sorted. The tick count can wrap, however,
  294. // but we will take care of it when we calculate the elapsed time.
  295. //
  296. InsertTailList( &NlGlobalChallengeList, &NewChallenge->ChNext );
  297. NlGlobalChallengeCount ++;
  298. //
  299. // If we have too many challenges,
  300. // reschedule the scavenger to run soon.
  301. // This way we don't commit large amount of
  302. // memory for an extended period of time for tons
  303. // of challenges comming from a malicious caller.
  304. //
  305. if ( NlGlobalChallengeCount >= NL_LARGE_CHALLENGE_COUNT ) {
  306. RescheduleScavenger = TRUE;
  307. NlPrint(( NL_CRITICAL,
  308. "NlInsertChallenge: Too many challenges: %lu. Will start scavenger in 2 mins\n",
  309. NlGlobalChallengeCount ));
  310. }
  311. LeaveCriticalSection( &NlGlobalChallengeCritSect );
  312. //
  313. // Reschedule the scavenger as needed
  314. //
  315. if ( RescheduleScavenger ) {
  316. LARGE_INTEGER TimeNow;
  317. DWORD Timeout = 0xFFFFFFFF;
  318. EnterCriticalSection( &NlGlobalScavengerCritSect );
  319. NlQuerySystemTime( &TimeNow );
  320. if ( !TimerExpired(&NlGlobalScavengerTimer, &TimeNow, &Timeout) ) {
  321. if ( Timeout > NL_LARGE_CHALLENGE_COUNT_TIMEOUT ) {
  322. NlGlobalScavengerTimer.Period -= (Timeout - NL_LARGE_CHALLENGE_COUNT_TIMEOUT);
  323. if ( !SetEvent( NlGlobalTimerEvent ) ) {
  324. NlPrint(( NL_CRITICAL,
  325. "NlInsertChallenge: SetEvent failed %ld\n",
  326. GetLastError() ));
  327. }
  328. }
  329. }
  330. LeaveCriticalSection( &NlGlobalScavengerCritSect );
  331. }
  332. return STATUS_SUCCESS;
  333. }
  334. VOID
  335. NlRemoveChallengeForClient(
  336. IN LPWSTR ClientName OPTIONAL,
  337. IN LPWSTR AccountName OPTIONAL,
  338. IN BOOL InterdomainTrustAccount
  339. )
  340. /*++
  341. Routine Description:
  342. This function removes challenge entries from the
  343. global list of outstanding challenges.
  344. Arguments:
  345. ClientName -- Name of the client whose associated
  346. challenges entries will be removed. If NULL, all
  347. entries in the list will be removed.
  348. AccountName -- Name of teh account used by the client
  349. to athenticate with this server. Used only if
  350. ClinetName is specified.
  351. InterdomainTrustAccount -- TRUE if the client used an
  352. interdomain trust account to set up a secure channel.
  353. Used only if ClientName is specified.
  354. Return Value:
  355. None
  356. --*/
  357. {
  358. NTSTATUS Status = STATUS_ACCESS_DENIED;
  359. PLIST_ENTRY ChallengeEntry = NULL;
  360. PNL_CHALLENGE Challenge = NULL;
  361. ULONG SameAccountChallengeCount = 0;
  362. BOOLEAN LogEvent = FALSE;
  363. LPWSTR MsgStrings[3];
  364. //
  365. // First scavenge old entries from the head of the list
  366. // Skip this step if we are removing all entries anyway
  367. //
  368. EnterCriticalSection( &NlGlobalChallengeCritSect );
  369. if ( ClientName != NULL ) {
  370. NlScavengeOldChallenges();
  371. }
  372. //
  373. // Next remove all entries in the list associated with client name
  374. //
  375. ChallengeEntry = NlGlobalChallengeList.Flink;
  376. while ( ChallengeEntry != &NlGlobalChallengeList ) {
  377. Challenge = CONTAINING_RECORD( ChallengeEntry, NL_CHALLENGE, ChNext );
  378. ChallengeEntry = ChallengeEntry->Flink;
  379. //
  380. // If the client name is NULL, we are shutting down,
  381. // so just delink and free all entries
  382. //
  383. if ( ClientName == NULL ) {
  384. NlRemoveChallenge( Challenge );
  385. //
  386. // If this entry is for the specified client,
  387. // process it
  388. //
  389. } else if ( NlNameCompare(ClientName,
  390. Challenge->ChClientName,
  391. NAMETYPE_COMPUTER) == 0 ) {
  392. MsgStrings[0] = Challenge->ChClientName;
  393. //
  394. // If this entry has an account different from
  395. // the specied one, log an event for it.
  396. // Note that since we avoid duplicate event logs,
  397. // there will be only one message (which is good)
  398. // for multiple challenges from the same client
  399. // for the same account.
  400. //
  401. if ( AccountName != NULL &&
  402. Challenge->ChFailedAccountName != NULL &&
  403. _wcsicmp(Challenge->ChFailedAccountName, AccountName) != 0 ) {
  404. MsgStrings[1] = Challenge->ChFailedAccountName;
  405. MsgStrings[2] = (LPWSTR) LongToPtr( Status );
  406. NlpWriteEventlog( NELOG_NetlogonServerAuthFailed,
  407. EVENTLOG_ERROR_TYPE,
  408. (LPBYTE) & Status,
  409. sizeof(Status),
  410. MsgStrings,
  411. 3 | NETP_LAST_MESSAGE_IS_NTSTATUS );
  412. //
  413. // Otherwise, count this entry in the number
  414. // of challenges with the specified or empty
  415. // account names (a challenge may have an empty
  416. // account name if we haven't reached it in the
  417. // authentication try loop).
  418. //
  419. } else {
  420. SameAccountChallengeCount ++;
  421. }
  422. //
  423. // Delink this entry and free it
  424. //
  425. NlRemoveChallenge( Challenge );
  426. }
  427. }
  428. LeaveCriticalSection( &NlGlobalChallengeCritSect );
  429. //
  430. // If there are more than a certain number of challenges
  431. // with the specified or emppty account, some other
  432. // (possibly malicious) client attempted to authenticate
  433. // using this account. Log an event for this. Don't specify
  434. // the account name as we don't know which account that
  435. // client would specify.
  436. //
  437. // For interdomain trust, the client may legitimately
  438. // try up to 3 times (passwords new, old, from PDC).
  439. // Otherwise, it gets 2 tries (new and old pwd).
  440. //
  441. if ( InterdomainTrustAccount ) {
  442. if ( SameAccountChallengeCount > 3 ) {
  443. LogEvent = TRUE;
  444. }
  445. } else {
  446. if ( SameAccountChallengeCount > 2 ) {
  447. LogEvent = TRUE;
  448. }
  449. }
  450. if ( LogEvent ) {
  451. MsgStrings[0] = ClientName;
  452. MsgStrings[1] = (LPWSTR) LongToPtr( Status );
  453. NlpWriteEventlog( NELOG_NetlogonServerAuthFailedNoAccount,
  454. EVENTLOG_ERROR_TYPE,
  455. (LPBYTE) & Status,
  456. sizeof(Status),
  457. MsgStrings,
  458. 2 | NETP_LAST_MESSAGE_IS_NTSTATUS );
  459. }
  460. }
  461. NTSTATUS
  462. NetrServerReqChallenge(
  463. IN LPWSTR PrimaryName OPTIONAL,
  464. IN LPWSTR ComputerName,
  465. IN PNETLOGON_CREDENTIAL ClientChallenge,
  466. OUT PNETLOGON_CREDENTIAL ServerChallenge
  467. )
  468. /*++
  469. Routine Description:
  470. This is the server side of I_NetServerReqChallenge.
  471. I_NetServerReqChallenge is the first of two functions used by a client
  472. Netlogon service to authenticate with another Netlogon service.
  473. (See I_NetServerAuthenticate below.)
  474. This function passes a challenge to the DC and the DC passes a challenge
  475. back to the caller.
  476. Arguments:
  477. PrimaryName -- Supplies the name of the DC we wish to authenticate with.
  478. ComputerName -- Name of the machine making the call.
  479. ClientCredential -- 64 bit challenge supplied by the BDC or member server.
  480. ServerCredential -- Receives 64 bit challenge from the PDC.
  481. Return Value:
  482. The status of the operation.
  483. --*/
  484. {
  485. #ifdef _WKSTA_NETLOGON
  486. return ERROR_NOT_SUPPORTED;
  487. UNREFERENCED_PARAMETER( PrimaryName );
  488. UNREFERENCED_PARAMETER( ComputerName );
  489. UNREFERENCED_PARAMETER( ClientChallenge );
  490. UNREFERENCED_PARAMETER( ServerChallenge );
  491. #endif // _WKSTA_NETLOGON
  492. #ifdef _DC_NETLOGON
  493. NTSTATUS Status;
  494. PDOMAIN_INFO DomainInfo = NULL;
  495. //
  496. // This API is not supported on workstations.
  497. //
  498. if ( NlGlobalMemberWorkstation ) {
  499. return STATUS_NOT_SUPPORTED;
  500. }
  501. //
  502. // Lookup which domain this call pertains to.
  503. //
  504. DomainInfo = NlFindDomainByServerName( PrimaryName );
  505. if ( DomainInfo == NULL ) {
  506. Status = STATUS_INVALID_COMPUTER_NAME;
  507. goto Cleanup;
  508. }
  509. NlPrint((NL_CHALLENGE_RES,
  510. "NetrServerReqChallenge: ClientChallenge = " ));
  511. NlpDumpBuffer(NL_CHALLENGE_RES, ClientChallenge, sizeof(*ClientChallenge) );
  512. //
  513. // Compute ServerChallenge to pass back to requestor
  514. //
  515. NlComputeChallenge(ServerChallenge);
  516. NlPrint((NL_CHALLENGE_RES,
  517. "NetrServerReqChallenge: ServerChallenge = " ));
  518. NlpDumpBuffer(NL_CHALLENGE_RES, ServerChallenge, sizeof(*ServerChallenge) );
  519. //
  520. // Add this entry into the challenge list.
  521. //
  522. // Remember both challenges until the corresponding I_NetAuthenticate call.
  523. // Notice that both challenges are not yet SessionKey-encrypted
  524. //
  525. Status = NlInsertChallenge( ComputerName,
  526. ClientChallenge,
  527. ServerChallenge );
  528. //
  529. // Common exit point
  530. //
  531. Cleanup:
  532. //
  533. // If the request failed, be carefull to not leak authentication
  534. // information.
  535. //
  536. if ( !NT_SUCCESS(Status) ) {
  537. RtlSecureZeroMemory( ServerChallenge, sizeof(*ServerChallenge) );
  538. }
  539. if ( DomainInfo != NULL ) {
  540. NlDereferenceDomain( DomainInfo );
  541. }
  542. return Status;
  543. #endif // _DC_NETLOGON
  544. }
  545. NTSTATUS
  546. NetrServerAuthenticate3(
  547. IN LPWSTR PrimaryName OPTIONAL,
  548. IN LPWSTR AccountName,
  549. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  550. IN LPWSTR ComputerName,
  551. IN PNETLOGON_CREDENTIAL ClientCredential,
  552. OUT PNETLOGON_CREDENTIAL ServerCredential,
  553. IN OUT PULONG NegotiatedFlags,
  554. OUT PULONG AccountRid
  555. )
  556. /*++
  557. Routine Description:
  558. This is the server side of I_NetServerAuthenticate
  559. I_NetServerAuthenticate is the second of two functions used by a client
  560. Netlogon service to authenticate with another Netlogon service.
  561. (See I_NetServerReqChallenge above.) Both a SAM or UAS server authenticates
  562. using this function.
  563. This function passes a credential to the DC and the DC passes a credential
  564. back to the caller.
  565. Arguments:
  566. PrimaryName -- Supplies the name of the DC we wish to authenticate with.
  567. AccountName -- Name of the Account to authenticate with.
  568. SecureChannelType -- The type of the account being accessed. This field must
  569. be set to UasServerSecureChannel to indicate a call from downlevel (LanMan
  570. 2.x and below) BDC or member server.
  571. ComputerName -- Name of the BDC or member server making the call.
  572. ClientCredential -- 64 bit credential supplied by the BDC or member server.
  573. ServerCredential -- Receives 64 bit credential from the PDC.
  574. NegotiatedFlags -- Specifies flags indicating what features the BDC supports.
  575. Returns a subset of those flags indicating what features the PDC supports.
  576. The PDC/BDC should ignore any bits that it doesn't understand.
  577. AccountRid -- Returns the RID of the account identified by AccountName
  578. Return Value:
  579. The status of the operation.
  580. --*/
  581. {
  582. #ifdef _WKSTA_NETLOGON
  583. return ERROR_NOT_SUPPORTED;
  584. UNREFERENCED_PARAMETER( PrimaryName );
  585. UNREFERENCED_PARAMETER( AccountName );
  586. UNREFERENCED_PARAMETER( SecureChannelType );
  587. UNREFERENCED_PARAMETER( ComputerName );
  588. UNREFERENCED_PARAMETER( ClientCredential );
  589. UNREFERENCED_PARAMETER( ServerCredential );
  590. UNREFERENCED_PARAMETER( NegotiatedFlags );
  591. #endif // _WKSTA_NETLOGON
  592. #ifdef _DC_NETLOGON
  593. NTSTATUS Status = STATUS_SUCCESS;
  594. PDOMAIN_INFO DomainInfo = NULL;
  595. ULONG LoopCount;
  596. NETLOGON_CREDENTIAL LocalClientCredential;
  597. NETLOGON_SESSION_KEY SessionKey;
  598. NT_OWF_PASSWORD OwfPassword;
  599. NT_OWF_PASSWORD OwfPreviousPassword;
  600. NT_OWF_PASSWORD LocalOwfPassword;
  601. NETLOGON_CREDENTIAL ServerChallenge;
  602. NETLOGON_CREDENTIAL ClientChallenge;
  603. BOOL IsInterdomainTrustAccount = FALSE;
  604. ULONG TrustAttributes;
  605. BOOL ClientAuthenticated = FALSE;
  606. PLIST_ENTRY ChallengeEntry;
  607. PNL_CHALLENGE Challenge;
  608. ULONG ChallengeCount = 0;
  609. //
  610. // This API is not supported on workstations.
  611. //
  612. if ( NlGlobalMemberWorkstation ) {
  613. return STATUS_NOT_SUPPORTED;
  614. }
  615. //
  616. // Start the WMI trace of server authentication
  617. //
  618. NlpTraceServerAuthEvent( EVENT_TRACE_TYPE_START,
  619. ComputerName,
  620. AccountName,
  621. SecureChannelType,
  622. NegotiatedFlags,
  623. STATUS_SUCCESS ); // Status isn't used at start
  624. //
  625. // Lookup which domain this call pertains to.
  626. //
  627. DomainInfo = NlFindDomainByServerName( PrimaryName );
  628. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  629. "NetrServerAuthenticate entered: %ws on account %ws (Negot: %lx)\n",
  630. ComputerName, AccountName, *NegotiatedFlags ));
  631. if ( DomainInfo == NULL ) {
  632. Status = STATUS_INVALID_COMPUTER_NAME;
  633. goto Cleanup;
  634. }
  635. //
  636. // Disallow this function for Lanman 2.X servers.
  637. //
  638. if ( SecureChannelType == UasServerSecureChannel ) {
  639. NlPrint((NL_CRITICAL,"NetrServerAuthenticate "
  640. "from LM 2.x (disallowed).\n"));
  641. Status = STATUS_ACCESS_DENIED;
  642. goto Cleanup;
  643. }
  644. //
  645. // Compute the NegotiatedFlags both sides support
  646. //
  647. *NegotiatedFlags &= NETLOGON_SUPPORTS_MASK |
  648. NETLOGON_SUPPORTS_DNS_DOMAIN_TRUST |
  649. NETLOGON_SUPPORTS_STRONG_KEY |
  650. NETLOGON_SUPPORTS_NT4EMULATOR_NEUTRALIZER |
  651. #ifdef ENABLE_AUTH_RPC
  652. (NlGlobalServerSupportsAuthRpc ? (NETLOGON_SUPPORTS_AUTH_RPC|NETLOGON_SUPPORTS_LSA_AUTH_RPC) : 0 ) |
  653. #endif // ENABLE_AUTH_RPC
  654. (NlGlobalParameters.AvoidSamRepl ? NETLOGON_SUPPORTS_AVOID_SAM_REPL : 0) |
  655. (NlGlobalParameters.AvoidLsaRepl ? NETLOGON_SUPPORTS_AVOID_LSA_REPL : 0);
  656. //
  657. // If we are emulating NT4.0 domain and the client
  658. // didn't indicate to neutralize the emulation,
  659. // treat the client as NT4.0 client. That way we
  660. // won't leak NT5.0 specific info to the client.
  661. // In fact, the client won't even ask for NT5.0
  662. // specific info after receiving such negotiated
  663. // from us.
  664. //
  665. if ( NlGlobalParameters.Nt4Emulator &&
  666. ((*NegotiatedFlags) & NETLOGON_SUPPORTS_NT4EMULATOR_NEUTRALIZER) == 0 ) {
  667. //
  668. // Pick up only those flags which existed in NT4.0
  669. //
  670. *NegotiatedFlags &= NETLOGON_SUPPORTS_NT4_MASK;
  671. }
  672. //
  673. // Get the password for the account. For interdomain trust
  674. // trust account, get both current and previous passwords
  675. //
  676. if ( IsDomainSecureChannelType( SecureChannelType ) ) {
  677. IsInterdomainTrustAccount = TRUE;
  678. }
  679. Status = NlGetIncomingPassword(
  680. DomainInfo,
  681. AccountName,
  682. SecureChannelType,
  683. 0, // Let routine figure out bits from SecureChannelType
  684. TRUE, // Fail for disabled accounts
  685. &OwfPassword,
  686. IsInterdomainTrustAccount ?
  687. &OwfPreviousPassword : // Get previous password for interdomain account
  688. NULL,
  689. AccountRid,
  690. &TrustAttributes,
  691. NULL ); // Don't need the account type
  692. if ( !NT_SUCCESS(Status) ) {
  693. NlPrintDom(( NL_CRITICAL, DomainInfo,
  694. "NetrServerAuthenticate: Can't NlGetIncomingPassword for %ws 0x%lx.\n",
  695. AccountName,
  696. Status ));
  697. goto Cleanup;
  698. }
  699. //
  700. // Output the passwords as needed
  701. //
  702. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: Password for account %ws = ", AccountName ));
  703. NlpDumpBuffer(NL_CHALLENGE_RES, &OwfPassword, sizeof(OwfPassword) );
  704. if ( IsInterdomainTrustAccount ) {
  705. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: Previous Password for account %ws = ", AccountName ));
  706. NlpDumpBuffer(NL_CHALLENGE_RES, &OwfPreviousPassword, sizeof(OwfPreviousPassword) );
  707. }
  708. //
  709. // Loop through all challenge entries for this client
  710. // and try to authenticate it against any one of the challenges.
  711. //
  712. EnterCriticalSection( &NlGlobalChallengeCritSect );
  713. //
  714. // First, take this opportunity to clean up expired challenge entries
  715. //
  716. NlScavengeOldChallenges();
  717. //
  718. // Now try all challenges for this client
  719. //
  720. for ( ChallengeEntry = NlGlobalChallengeList.Flink;
  721. ChallengeEntry != &NlGlobalChallengeList;
  722. ChallengeEntry = ChallengeEntry->Flink ) {
  723. Challenge = CONTAINING_RECORD( ChallengeEntry, NL_CHALLENGE, ChNext );
  724. //
  725. // Skip entries which are not for this client
  726. //
  727. if ( NlNameCompare(ComputerName,
  728. Challenge->ChClientName,
  729. NAMETYPE_COMPUTER) != 0 ) {
  730. continue;
  731. }
  732. ChallengeCount ++;
  733. //
  734. // Grab a copy of the Client and Server challenges.
  735. //
  736. RtlCopyMemory( &ServerChallenge,
  737. &Challenge->ChServerChallenge,
  738. sizeof(ServerChallenge) );
  739. RtlCopyMemory( &ClientChallenge,
  740. &Challenge->ChClientChallenge,
  741. sizeof(ClientChallenge) );
  742. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: ClientChallenge %lu = ", ChallengeCount ));
  743. NlpDumpBuffer(NL_CHALLENGE_RES, &ClientChallenge, sizeof(ClientChallenge) );
  744. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: ServerChallenge %lu = ", ChallengeCount ));
  745. NlpDumpBuffer(NL_CHALLENGE_RES, &ServerChallenge, sizeof(ServerChallenge) );
  746. //
  747. // Loop trying the local current password, then the local previous password
  748. // provided this is an interdomain trust account.
  749. //
  750. for ( LoopCount=0; LoopCount<2; LoopCount++ ) {
  751. //
  752. // On the first iteration, use the current password
  753. //
  754. if ( LoopCount == 0 ) {
  755. LocalOwfPassword = OwfPassword;
  756. //
  757. // On the second iteration, if this is an interdomain trust account,
  758. // use the previous password
  759. //
  760. } else if ( LoopCount == 1 && IsInterdomainTrustAccount ) {
  761. LocalOwfPassword = OwfPreviousPassword;
  762. //
  763. // Otherwise, try the next challenge, if any
  764. //
  765. } else {
  766. break;
  767. }
  768. //
  769. // Compute the session key given the two challenges and the
  770. // password.
  771. //
  772. Status = NlMakeSessionKey(
  773. *NegotiatedFlags,
  774. &LocalOwfPassword,
  775. &ClientChallenge,
  776. &ServerChallenge,
  777. &SessionKey );
  778. if (!NT_SUCCESS(Status)) {
  779. NlPrintDom((NL_CRITICAL, DomainInfo,
  780. "NetrServerAuthenticate: Can't NlMakeSessionKey for %ws 0x%lx.\n",
  781. AccountName,
  782. Status ));
  783. LeaveCriticalSection( &NlGlobalChallengeCritSect );
  784. goto Cleanup;
  785. }
  786. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: SessionKey %lu = ", LoopCount ));
  787. NlpDumpBuffer(NL_CHALLENGE_RES, &SessionKey, sizeof(SessionKey) );
  788. //
  789. // Compute ClientCredential to verify the one supplied by ComputerName
  790. //
  791. NlComputeCredentials( &ClientChallenge,
  792. &LocalClientCredential,
  793. &SessionKey);
  794. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: ClientCredential %lu GOT = ", LoopCount ));
  795. NlpDumpBuffer(NL_CHALLENGE_RES, ClientCredential, sizeof(*ClientCredential) );
  796. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: ClientCredential %lu MADE = ", LoopCount ));
  797. NlpDumpBuffer(NL_CHALLENGE_RES, &LocalClientCredential, sizeof(LocalClientCredential) );
  798. //
  799. // Verify the computed credentials with those supplied
  800. //
  801. if( RtlEqualMemory( ClientCredential,
  802. &LocalClientCredential,
  803. sizeof(LocalClientCredential)) ) {
  804. ClientAuthenticated = TRUE;
  805. break;
  806. }
  807. NlPrintDom((NL_CRITICAL, DomainInfo,
  808. "NetrServerAuthenticate: Bad password %lu for %ws on account %ws\n",
  809. LoopCount, ComputerName, AccountName ));
  810. }
  811. if ( ClientAuthenticated ) {
  812. break;
  813. }
  814. //
  815. // This challenge entry failed to authenticate.
  816. // Remember the account name as specified by the
  817. // client in this challenge entry if this account
  818. // isn't already on the entry.
  819. //
  820. if ( Challenge->ChFailedAccountName == NULL ||
  821. wcsstr(Challenge->ChFailedAccountName, AccountName) == NULL ) {
  822. ULONG OldLength = 0;
  823. LPWSTR TmpStorage = NULL;
  824. //
  825. // If there is already an account name,
  826. // allocate space for it
  827. //
  828. if ( Challenge->ChFailedAccountName != NULL ) {
  829. // add storage for a comma and a space
  830. OldLength = wcslen( Challenge->ChFailedAccountName ) + 2;
  831. }
  832. //
  833. // Allocate space for old (if any) and new account names
  834. //
  835. TmpStorage = LocalAlloc( LMEM_ZEROINIT,
  836. (OldLength+wcslen(AccountName)+1)*sizeof(WCHAR) );
  837. if ( TmpStorage != NULL ) {
  838. //
  839. // Copy old name(s), if any.
  840. // Separate names with a comma and a space.
  841. //
  842. if ( OldLength > 0 ) {
  843. RtlCopyMemory( TmpStorage,
  844. Challenge->ChFailedAccountName,
  845. (OldLength-2)*sizeof(WCHAR) );
  846. wcscat( TmpStorage, L", ");
  847. }
  848. //
  849. // Append the new account name
  850. //
  851. wcscat(TmpStorage, AccountName);
  852. //
  853. // Free the old name(s) and keep the new one
  854. //
  855. if ( Challenge->ChFailedAccountName != NULL ) {
  856. LocalFree( Challenge->ChFailedAccountName );
  857. }
  858. Challenge->ChFailedAccountName = TmpStorage;
  859. }
  860. }
  861. }
  862. LeaveCriticalSection( &NlGlobalChallengeCritSect );
  863. //
  864. // Error out if we didn't authenticate the client
  865. //
  866. if ( !ClientAuthenticated ) {
  867. NlPrintDom(( NL_CRITICAL, DomainInfo,
  868. "NetrServerAuthenticate: Failed to authenticate %ws on account %ws\n",
  869. ComputerName, AccountName ));
  870. Status = STATUS_ACCESS_DENIED;
  871. goto Cleanup;
  872. }
  873. //
  874. // Cleanup all challenges for this client
  875. //
  876. NlRemoveChallengeForClient( ComputerName, AccountName, IsInterdomainTrustAccount );
  877. //
  878. // Create the server session for this client
  879. //
  880. Status = NlInsertServerSession(
  881. DomainInfo,
  882. ComputerName,
  883. AccountName,
  884. SecureChannelType,
  885. // Only replicate those databases that negotiation says to replicate
  886. SS_AUTHENTICATED |
  887. NlMaxReplMask(*NegotiatedFlags) |
  888. ((TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) ?
  889. SS_FOREST_TRANSITIVE :
  890. 0 ),
  891. *AccountRid,
  892. *NegotiatedFlags,
  893. (SecureChannelType == ServerSecureChannel) ?
  894. NlTransportLookup( ComputerName ) :
  895. NULL,
  896. &SessionKey,
  897. &LocalClientCredential );
  898. if ( !NT_SUCCESS(Status) ) {
  899. NlPrintDom(( NL_CRITICAL, DomainInfo,
  900. "NetrServerAuthenticate: NlInsertServerSession failed for %ws on account %ws\n",
  901. ComputerName, AccountName ));
  902. goto Cleanup;
  903. }
  904. //
  905. // Compute ServerCredential from ServerChallenge to be returned to caller
  906. //
  907. NlComputeCredentials( &ServerChallenge,
  908. ServerCredential,
  909. &SessionKey );
  910. NlPrint((NL_CHALLENGE_RES,"NetrServerAuthenticate: ServerCredential SEND = " ));
  911. NlpDumpBuffer(NL_CHALLENGE_RES, ServerCredential, sizeof(*ServerCredential) );
  912. //
  913. // If the client is a pre NT 5 member workstation or BDC,
  914. // update the DS.
  915. //
  916. if ( !IsInterdomainTrustAccount &&
  917. ((*NegotiatedFlags) & ~NETLOGON_SUPPORTS_NT4_MASK) == 0 ) {
  918. OSVERSIONINFOEXW OsVersionInfoEx;
  919. //
  920. // Build the OsVersionInfo structure.
  921. //
  922. RtlZeroMemory( &OsVersionInfoEx, sizeof(OsVersionInfoEx) );
  923. OsVersionInfoEx.dwOSVersionInfoSize = sizeof(OsVersionInfoEx);
  924. //
  925. // Differentiate between NT 3 and NT 4.
  926. //
  927. if ( *NegotiatedFlags == 0 ) {
  928. OsVersionInfoEx.dwMajorVersion = 3;
  929. OsVersionInfoEx.dwMinorVersion = 1;
  930. } else if ( ((*NegotiatedFlags) & ~NETLOGON_SUPPORTS_NT351_MASK) == 0 ) {
  931. OsVersionInfoEx.dwMajorVersion = 3;
  932. OsVersionInfoEx.dwMinorVersion = 5;
  933. } else {
  934. OsVersionInfoEx.dwMajorVersion = 4;
  935. }
  936. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  937. "NetrServerAuthenticate: %ws is running NT %ld.%ld\n",
  938. ComputerName,
  939. OsVersionInfoEx.dwMajorVersion,
  940. OsVersionInfoEx.dwMinorVersion ));
  941. //
  942. // Set the DnsHostName on the computer object.
  943. //
  944. Status = LsaISetClientDnsHostName(
  945. ComputerName,
  946. NULL, // No DnsHostName
  947. &OsVersionInfoEx,
  948. L"Windows NT",
  949. NULL ); // Not interested in returning DnsHostName
  950. if ( !NT_SUCCESS(Status) ) {
  951. NlPrintDom((NL_CRITICAL, DomainInfo,
  952. "NetrServerAuthenticate: Cannot set client DNS host name %lx (ignoring)\n",
  953. Status ));
  954. // This isn't fatal
  955. }
  956. }
  957. //
  958. // Success!!!
  959. //
  960. Status = STATUS_SUCCESS;
  961. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  962. "NetrServerAuthenticate returns Success: %ws on account %ws (Negot: %lx)\n",
  963. ComputerName, AccountName, *NegotiatedFlags ));
  964. //
  965. // Common exit point
  966. //
  967. Cleanup:
  968. //
  969. // Return more appropriate error
  970. //
  971. if ( Status == STATUS_NO_SUCH_USER ) {
  972. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  973. }
  974. //
  975. // Handle failure
  976. //
  977. if ( !NT_SUCCESS( Status ) ) {
  978. LPWSTR MsgStrings[3];
  979. //
  980. // Be careful to not leak authentication information.
  981. //
  982. RtlSecureZeroMemory( ServerCredential, sizeof(*ServerCredential) );
  983. *AccountRid = 0;
  984. //
  985. // Write event log as appropriate
  986. //
  987. MsgStrings[0] = ComputerName;
  988. MsgStrings[1] = AccountName;
  989. if (Status == STATUS_NO_TRUST_SAM_ACCOUNT) {
  990. NlpWriteEventlog( NELOG_NetlogonServerAuthNoTrustSamAccount,
  991. EVENTLOG_ERROR_TYPE,
  992. (LPBYTE) & Status,
  993. sizeof(Status),
  994. MsgStrings,
  995. 2 );
  996. //
  997. // If this attempt failed with access denied and we tried challenges for
  998. // this client, the event log has already been output as appropriate
  999. //
  1000. } else if ( !(Status == STATUS_ACCESS_DENIED && ChallengeCount > 0) ) {
  1001. MsgStrings[2] = (LPWSTR) LongToPtr( Status );
  1002. NlpWriteEventlog( NELOG_NetlogonServerAuthFailed,
  1003. EVENTLOG_ERROR_TYPE,
  1004. (LPBYTE) & Status,
  1005. sizeof(Status),
  1006. MsgStrings,
  1007. 3 | NETP_LAST_MESSAGE_IS_NTSTATUS );
  1008. }
  1009. }
  1010. if ( DomainInfo != NULL ) {
  1011. NlDereferenceDomain( DomainInfo );
  1012. }
  1013. //
  1014. // End the WMI trace of server authentication
  1015. //
  1016. NlpTraceServerAuthEvent( EVENT_TRACE_TYPE_END,
  1017. ComputerName,
  1018. AccountName,
  1019. SecureChannelType,
  1020. NegotiatedFlags,
  1021. Status );
  1022. return Status;
  1023. #endif // _DC_NETLOGON
  1024. }
  1025. NTSTATUS
  1026. NetrServerAuthenticate2(
  1027. IN LPWSTR PrimaryName OPTIONAL,
  1028. IN LPWSTR AccountName,
  1029. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  1030. IN LPWSTR ComputerName,
  1031. IN PNETLOGON_CREDENTIAL ClientCredential,
  1032. OUT PNETLOGON_CREDENTIAL ServerCredential,
  1033. IN OUT PULONG NegotiatedFlags
  1034. )
  1035. /*++
  1036. Routine Description:
  1037. This is the NT 3.5x and NT 4.x version of I_NetServerAuthenicate3.
  1038. I_NetServerAuthenticate3 was introduced in NT 5.0 (December 1996).
  1039. Arguments:
  1040. Return Value:
  1041. The status of the operation.
  1042. --*/
  1043. {
  1044. ULONG AccountRid;
  1045. return NetrServerAuthenticate3( PrimaryName,
  1046. AccountName,
  1047. SecureChannelType,
  1048. ComputerName,
  1049. ClientCredential,
  1050. ServerCredential,
  1051. NegotiatedFlags,
  1052. &AccountRid );
  1053. }
  1054. NTSTATUS
  1055. NetrServerAuthenticate(
  1056. IN LPWSTR PrimaryName OPTIONAL,
  1057. IN LPWSTR AccountName,
  1058. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  1059. IN LPWSTR ComputerName,
  1060. IN PNETLOGON_CREDENTIAL ClientCredential,
  1061. OUT PNETLOGON_CREDENTIAL ServerCredential
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. This is the NT 3.1 version of I_NetServerAuthenicate2.
  1066. I_NetServerAuthenticate2 was introduced in NT 3.5 (December 1993).
  1067. Arguments:
  1068. Return Value:
  1069. The status of the operation.
  1070. --*/
  1071. {
  1072. ULONG NegotiatedFlags = 0;
  1073. return NetrServerAuthenticate2( PrimaryName,
  1074. AccountName,
  1075. SecureChannelType,
  1076. ComputerName,
  1077. ClientCredential,
  1078. ServerCredential,
  1079. &NegotiatedFlags );
  1080. }
  1081. NTSTATUS
  1082. NetpServerPasswordSet(
  1083. IN LPWSTR PrimaryName OPTIONAL,
  1084. IN LPWSTR AccountName,
  1085. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1086. IN LPWSTR ComputerName,
  1087. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1088. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1089. IN PENCRYPTED_LM_OWF_PASSWORD UasNewPassword OPTIONAL,
  1090. IN PNL_TRUST_PASSWORD ClearNewPassword OPTIONAL
  1091. )
  1092. /*++
  1093. Routine Description:
  1094. This function is used to change the password for the account being
  1095. used to maintain a secure channel. This function can only be called
  1096. by a server which has previously authenticated with a DC by calling
  1097. I_NetServerAuthenticate.
  1098. The call is made depending on the account type:
  1099. * A domain account password is changed from the PDC in the
  1100. trusting domain. The I_NetServerPasswordSet call is made to any
  1101. DC in the trusted domain.
  1102. * A server account password is changed from the specific server.
  1103. The I_NetServerPasswordSet call is made to the PDC in the domain
  1104. the server belongs to.
  1105. * A workstation account password is changed from the specific
  1106. workstation. The I_NetServerPasswordSet call is made to a DC in
  1107. the domain the server belongs to.
  1108. This function uses RPC to contact the DC named by PrimaryName.
  1109. Arguments:
  1110. PrimaryName -- Name of the PDC to change the servers password
  1111. with. NULL indicates this call is a local call being made on
  1112. behalf of a UAS server by the XACT server.
  1113. AccountName -- Name of the account to change the password for.
  1114. AccountType -- The type of account being accessed.
  1115. ComputerName -- Name of the BDC or member making the call.
  1116. Authenticator -- supplied by the server.
  1117. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  1118. UasNewPassword -- The new OWF password for the server.
  1119. ClearNewPassword -- The new cleartext password for the server.
  1120. Either, UasNewPassword or ClearNewPassword will be NULL.
  1121. Return Value:
  1122. NT status code.
  1123. STATUS_WRONG_PASSWORD - Indicates the server refuses to allow the password
  1124. to be changed. The client should continue to use the prior password.
  1125. --*/
  1126. {
  1127. #ifdef _WKSTA_NETLOGON
  1128. return ERROR_NOT_SUPPORTED;
  1129. UNREFERENCED_PARAMETER( PrimaryName );
  1130. UNREFERENCED_PARAMETER( AccountName );
  1131. UNREFERENCED_PARAMETER( AccountType );
  1132. UNREFERENCED_PARAMETER( ComputerName );
  1133. UNREFERENCED_PARAMETER( Authenticator );
  1134. UNREFERENCED_PARAMETER( ReturnAuthenticator );
  1135. UNREFERENCED_PARAMETER( UasNewPassword );
  1136. #endif // _WKSTA_NETLOGON
  1137. #ifdef _DC_NETLOGON
  1138. NTSTATUS Status;
  1139. PDOMAIN_INFO DomainInfo = NULL;
  1140. PSERVER_SESSION ServerSession;
  1141. SESSION_INFO SessionInfo;
  1142. LM_OWF_PASSWORD OwfPassword;
  1143. UNICODE_STRING NewPassword;
  1144. NT_OWF_PASSWORD CurrentOwfPassword;
  1145. DWORD ClearVersionNumber = 0;
  1146. //
  1147. // This API is not supported on workstations.
  1148. //
  1149. if ( NlGlobalMemberWorkstation ) {
  1150. return STATUS_NOT_SUPPORTED;
  1151. }
  1152. //
  1153. // If the DS is recovering from a backup,
  1154. // avoid changing the DS.
  1155. //
  1156. if ( NlGlobalDsPaused ) {
  1157. NlPrint((NL_CRITICAL,
  1158. "NetpServerPasswordSet: DsIsPaused.\n"));
  1159. Status = STATUS_DS_BUSY;
  1160. goto Cleanup;
  1161. }
  1162. //
  1163. // Lookup which domain this call pertains to.
  1164. //
  1165. DomainInfo = NlFindDomainByServerName( PrimaryName );
  1166. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1167. "NetpServerPasswordSet: Comp=%ws Acc=%ws Entered\n",
  1168. ComputerName,
  1169. AccountName ));
  1170. if ( DomainInfo == NULL ) {
  1171. Status = STATUS_INVALID_COMPUTER_NAME;
  1172. goto Cleanup;
  1173. }
  1174. //
  1175. // Get the old password
  1176. //
  1177. Status = NlGetIncomingPassword( DomainInfo,
  1178. AccountName,
  1179. AccountType,
  1180. 0,
  1181. FALSE,
  1182. &CurrentOwfPassword,
  1183. NULL,
  1184. NULL,
  1185. NULL,
  1186. NULL );
  1187. if( !NT_SUCCESS(Status) ) {
  1188. NlPrintDom((NL_CRITICAL, DomainInfo,
  1189. "NetpServerPasswordSet: Comp=%ws Acc=%ws failed because NlGetIncomingPassword failed with ntstatus 0x%x\n",
  1190. ComputerName,
  1191. AccountName,
  1192. Status));
  1193. Status = STATUS_ACCESS_DENIED;
  1194. goto Cleanup;
  1195. }
  1196. //
  1197. // Get the Session key for this session.
  1198. //
  1199. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  1200. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  1201. if (ServerSession == NULL) {
  1202. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1203. Status = STATUS_ACCESS_DENIED;
  1204. goto Cleanup;
  1205. }
  1206. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  1207. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  1208. //
  1209. // now verify the Authenticator and update seed if OK
  1210. //
  1211. Status = NlCheckAuthenticator( ServerSession,
  1212. Authenticator,
  1213. ReturnAuthenticator);
  1214. if ( !NT_SUCCESS(Status) ) {
  1215. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1216. goto Cleanup;
  1217. }
  1218. //
  1219. // Check if we're refusing password changes
  1220. //
  1221. // Only refuse password changes if the client is a workstation and the
  1222. // client supports password changing.
  1223. //
  1224. // If this is a PDC and the request was passed-through a BDC,
  1225. // we don't have access to the NETLOGON_SUPPORTS flag of the workstation.
  1226. // As such, we'll simply not check the NETLOGON_SUPPORTS flag in that
  1227. // case and assume the client can handle it.
  1228. //
  1229. if ( NlGlobalParameters.RefusePasswordChange &&
  1230. AccountType == WorkstationSecureChannel &&
  1231. (ServerSession->SsSecureChannelType == ServerSecureChannel ||
  1232. (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REFUSE_CHANGE_PWD) != 0 )){
  1233. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1234. Status = STATUS_WRONG_PASSWORD;
  1235. goto Cleanup;
  1236. }
  1237. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1238. //
  1239. // If the caller passed a cleartext password,
  1240. // decrypt it.
  1241. //
  1242. if ( ClearNewPassword != NULL ) {
  1243. NL_TRUST_PASSWORD LocalClearNewPassword;
  1244. NL_PASSWORD_VERSION PasswordVersion;
  1245. //
  1246. // Simply decrypt using the session key
  1247. //
  1248. LocalClearNewPassword = *ClearNewPassword;
  1249. NlDecryptRC4( &LocalClearNewPassword, sizeof(LocalClearNewPassword), &SessionInfo );
  1250. //
  1251. // Sanity check the length.
  1252. //
  1253. if ( IsDomainSecureChannelType( AccountType )) {
  1254. if ( (LocalClearNewPassword.Length >= sizeof(LocalClearNewPassword.Buffer)-sizeof(PasswordVersion)) ||
  1255. (LocalClearNewPassword.Length % sizeof(WCHAR)) != 0 ) {
  1256. NlPrintDom((NL_CRITICAL, DomainInfo,
  1257. "NetpServerPasswordSet: Decrypted interdomain password is too long %ld\n",
  1258. LocalClearNewPassword.Length ));
  1259. Status = STATUS_ACCESS_DENIED;
  1260. goto Cleanup;
  1261. }
  1262. } else {
  1263. if ( (LocalClearNewPassword.Length >= sizeof(LocalClearNewPassword.Buffer)) ||
  1264. (LocalClearNewPassword.Length % sizeof(WCHAR)) != 0 ) {
  1265. NlPrintDom((NL_CRITICAL, DomainInfo,
  1266. "NetpServerPasswordSet: Decrypted password is too long %ld\n",
  1267. LocalClearNewPassword.Length ));
  1268. Status = STATUS_ACCESS_DENIED;
  1269. goto Cleanup;
  1270. }
  1271. }
  1272. //
  1273. // Convert the new password into a unicode string.
  1274. //
  1275. NewPassword.Buffer = (LPWSTR)(((LPBYTE)LocalClearNewPassword.Buffer) +
  1276. NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
  1277. LocalClearNewPassword.Length);
  1278. ;
  1279. NewPassword.MaximumLength =
  1280. NewPassword.Length = (USHORT)LocalClearNewPassword.Length;
  1281. //
  1282. // Get the password version number for an interdomain trust
  1283. // account (may be absent)
  1284. //
  1285. if ( IsDomainSecureChannelType( AccountType ) ) {
  1286. RtlCopyMemory( &PasswordVersion,
  1287. ((LPBYTE)LocalClearNewPassword.Buffer) +
  1288. NL_MAX_PASSWORD_LENGTH * sizeof(WCHAR) -
  1289. LocalClearNewPassword.Length -
  1290. sizeof(PasswordVersion),
  1291. sizeof(PasswordVersion) );
  1292. if ( PasswordVersion.PasswordVersionPresent == PASSWORD_VERSION_NUMBER_PRESENT &&
  1293. PasswordVersion.PasswordVersionNumber > 0 ) {
  1294. ClearVersionNumber = PasswordVersion.PasswordVersionNumber;
  1295. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1296. "NetpServerPasswordSet: Got password version number 0x%lx\n",
  1297. ClearVersionNumber ));
  1298. } else {
  1299. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1300. "NetpServerPasswordSet: Got no password version number\n" ));
  1301. }
  1302. }
  1303. //
  1304. // We need to calculate the OWF version of the password so we
  1305. // can compare it to the current password and fail if they're
  1306. // equal.
  1307. //
  1308. Status = RtlCalculateNtOwfPassword( &NewPassword, &OwfPassword );
  1309. if( !NT_SUCCESS(Status) ) {
  1310. NlPrintDom((NL_CRITICAL, DomainInfo,
  1311. "NetpServerPasswordSet: Comp=%ws Acc=%ws failed because RtlCalculateNtOwfPassword failed with ntstatus 0x%x\n",
  1312. ComputerName,
  1313. AccountName,
  1314. Status));
  1315. Status = STATUS_ACCESS_DENIED;
  1316. goto Cleanup;
  1317. }
  1318. //
  1319. // If the caller passed an OWF password,
  1320. // decrypt it.
  1321. //
  1322. } else if ( UasNewPassword != NULL ) {
  1323. //
  1324. // decrypt the sessionkey from password
  1325. // i.e. OwfPassword = D2((E2(E1(STD_TXT, PW), SK)), SK)
  1326. // = E1(STD_TXT, PW)
  1327. // OwfPassword = One Way Function of the cleartext password.
  1328. //
  1329. if (Status = RtlDecryptLmOwfPwdWithLmOwfPwd(
  1330. UasNewPassword,
  1331. (PLM_OWF_PASSWORD) &SessionInfo.SessionKey,
  1332. &OwfPassword )) {
  1333. goto Cleanup;
  1334. }
  1335. //
  1336. // Internal error
  1337. } else {
  1338. NlPrintDom((NL_CRITICAL, DomainInfo,
  1339. "NetpServerPasswordSet: Neither clear nor OWF password.\n"));
  1340. Status = STATUS_ACCESS_DENIED;
  1341. goto Cleanup;
  1342. }
  1343. //
  1344. // We need to compare the new password with the current one and fail when
  1345. // equal to avoid a broken passoword history. Scenario:
  1346. //
  1347. // Interdomain trust. Domain A, with PDC PDC_A, has trust with domain B
  1348. // which has two DCs, DC_B1 and DC_B2. Current password history for
  1349. // domain A's password on all three machines is (P2, P1). PDC_A changes
  1350. // its password on DC_B1, resulting in this state:
  1351. //
  1352. // PDC_A: (P2,P1)->(P3,P2) DC_B1: (P2,P1)->(P3,P2) DC_B2: (P2,P1)
  1353. //
  1354. // Now suppose that PDC_A resets his secure channel to DC_B2. He will
  1355. // detect that DC_B2 does not know his new password, and will schedule
  1356. // a password set for his next scavenging (by default as long as 15 min).
  1357. // But, *before* the scavenging takes place, the new password replicates
  1358. // from DC_B1 to DC_B2. The scavenging will proceed as planned, resulting
  1359. // in a messed up password history on DC_B2:
  1360. //
  1361. // DC_B2: (P2,P1)->(P3,P2)->(P3,P3)
  1362. //
  1363. // This can result in authentication failures if there are any DCs in A to
  1364. // which the new password has not yet replicated.
  1365. //
  1366. if( ((ClearNewPassword != NULL) && RtlEqualNtOwfPassword( &CurrentOwfPassword, &OwfPassword )) ||
  1367. ((UasNewPassword != NULL) && RtlEqualLmOwfPassword( &CurrentOwfPassword, &OwfPassword )) ) {
  1368. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1369. "NetpServerPasswordSet: Comp=%ws Ac=%ws Not changing password -- already changed\n",
  1370. ComputerName,
  1371. AccountName ));
  1372. Status = STATUS_SUCCESS; // silently fail
  1373. goto Cleanup;
  1374. }
  1375. //
  1376. // Do the request locally.
  1377. //
  1378. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1379. "NetpServerPasswordSet: Comp=%ws Acc=%ws Changing password locally\n",
  1380. ComputerName,
  1381. AccountName ));
  1382. //
  1383. // Set the password on the account.
  1384. //
  1385. Status = NlSetIncomingPassword(
  1386. DomainInfo,
  1387. AccountName,
  1388. AccountType,
  1389. ClearNewPassword == NULL ? NULL : &NewPassword,
  1390. ClearVersionNumber,
  1391. ClearNewPassword == NULL ? &OwfPassword : NULL );
  1392. if ( !NT_SUCCESS(Status) ) {
  1393. goto Cleanup;
  1394. }
  1395. Status = STATUS_SUCCESS;
  1396. //
  1397. // Common exit point
  1398. //
  1399. Cleanup:
  1400. //
  1401. // If the request failed, be careful to not leak authentication
  1402. // information.
  1403. //
  1404. if ( Status == STATUS_ACCESS_DENIED ) {
  1405. RtlSecureZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  1406. }
  1407. //
  1408. // Also zero out automatic variables which store passwords to avoid
  1409. // having these on the stack for indefinite time.
  1410. //
  1411. RtlSecureZeroMemory( &OwfPassword, sizeof(OwfPassword) );
  1412. RtlSecureZeroMemory( &NewPassword, sizeof(NewPassword) );
  1413. RtlSecureZeroMemory( &CurrentOwfPassword, sizeof(CurrentOwfPassword) );
  1414. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1415. "NetpServerPasswordSet: Comp=%ws Acc=%ws returns 0x%lX\n",
  1416. ComputerName,
  1417. AccountName,
  1418. Status ));
  1419. if ( DomainInfo != NULL ) {
  1420. NlDereferenceDomain( DomainInfo );
  1421. }
  1422. return Status;
  1423. #endif // _DC_NETLOGON
  1424. }
  1425. NTSTATUS
  1426. NetrServerPasswordGet(
  1427. IN LPWSTR PrimaryName,
  1428. IN LPWSTR AccountName,
  1429. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1430. IN LPWSTR ComputerName,
  1431. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1432. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1433. OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword
  1434. )
  1435. /*++
  1436. Routine Description:
  1437. This function is used to by a BDC to get a machine account password
  1438. from the PDC in the doamin.
  1439. This function can only be called by a server which has previously
  1440. authenticated with a DC by calling I_NetServerAuthenticate.
  1441. This function uses RPC to contact the DC named by PrimaryName.
  1442. Arguments:
  1443. PrimaryName -- Computer name of the PDC to remote the call to.
  1444. AccountName -- Name of the account to get the password for.
  1445. AccountType -- The type of account being accessed.
  1446. ComputerName -- Name of the BDC making the call.
  1447. Authenticator -- supplied by the server.
  1448. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  1449. EncryptedNtOwfPassword -- Returns the OWF password of the account.
  1450. Return Value:
  1451. NT status code.
  1452. --*/
  1453. {
  1454. NTSTATUS Status;
  1455. PDOMAIN_INFO DomainInfo = NULL;
  1456. PSERVER_SESSION ServerSession;
  1457. SESSION_INFO SessionInfo;
  1458. NT_OWF_PASSWORD OwfPassword;
  1459. //
  1460. // This API is not supported on workstations.
  1461. //
  1462. if ( NlGlobalMemberWorkstation ) {
  1463. return STATUS_NOT_SUPPORTED;
  1464. }
  1465. //
  1466. // Lookup which domain this call pertains to.
  1467. //
  1468. DomainInfo = NlFindDomainByServerName( PrimaryName );
  1469. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1470. "NetrServerPasswordGet: Comp=%ws Acc=%ws Entered\n",
  1471. ComputerName,
  1472. AccountName ));
  1473. if ( DomainInfo == NULL ) {
  1474. Status = STATUS_INVALID_COMPUTER_NAME;
  1475. goto Cleanup;
  1476. }
  1477. //
  1478. // This call is only allowed to a PDC.
  1479. //
  1480. if ( DomainInfo->DomRole != RolePrimary ) {
  1481. NlPrintDom((NL_CRITICAL, DomainInfo,
  1482. "NetrServerPasswordGet: Call only valid to a PDC.\n" ));
  1483. Status = STATUS_ACCESS_DENIED;
  1484. goto Cleanup;
  1485. }
  1486. //
  1487. // Get the Session key for this session.
  1488. //
  1489. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  1490. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  1491. if (ServerSession == NULL) {
  1492. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1493. Status = STATUS_ACCESS_DENIED;
  1494. goto Cleanup;
  1495. }
  1496. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  1497. // SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  1498. //
  1499. // now verify the Authenticator and update seed if OK
  1500. //
  1501. Status = NlCheckAuthenticator( ServerSession,
  1502. Authenticator,
  1503. ReturnAuthenticator);
  1504. if ( !NT_SUCCESS(Status) ) {
  1505. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1506. goto Cleanup;
  1507. }
  1508. //
  1509. // Call is only allowed from a BDC.
  1510. //
  1511. if ( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  1512. NlPrintDom((NL_CRITICAL, DomainInfo,
  1513. "NetrServerPasswordGet: Call only valid from a BDC.\n" ));
  1514. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1515. Status = STATUS_ACCESS_DENIED;
  1516. goto Cleanup;
  1517. }
  1518. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1519. //
  1520. // Get the password for the account.
  1521. //
  1522. Status = NlGetIncomingPassword(
  1523. DomainInfo,
  1524. AccountName,
  1525. AccountType,
  1526. 0, // Let routine compute from AccountType
  1527. TRUE, // Fail if account is disabled
  1528. &OwfPassword,
  1529. NULL, // Don't return the previous password
  1530. NULL, // Don't return the account RID
  1531. NULL, // Don't return the trust attributes
  1532. NULL ); // Don't need the account type
  1533. if ( !NT_SUCCESS(Status) ) {
  1534. goto Cleanup;
  1535. }
  1536. //
  1537. // Encrypt the password again with the session key.
  1538. // The BDC will decrypt it on the other side.
  1539. //
  1540. Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
  1541. &OwfPassword,
  1542. (PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
  1543. EncryptedNtOwfPassword) ;
  1544. if ( !NT_SUCCESS( Status )) {
  1545. NlPrintDom((NL_CRITICAL, DomainInfo,
  1546. "NetrServerPasswordGet: Cannot RtlEncryptNtOwfPwdWithNtOwfPwd %lX\n",
  1547. Status));
  1548. goto Cleanup;
  1549. }
  1550. Status = STATUS_SUCCESS;
  1551. //
  1552. // Common exit point
  1553. //
  1554. Cleanup:
  1555. //
  1556. // If the request failed, be carefull to not leak authentication
  1557. // information.
  1558. //
  1559. if ( Status == STATUS_ACCESS_DENIED ) {
  1560. RtlSecureZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  1561. RtlSecureZeroMemory( EncryptedNtOwfPassword, sizeof(*EncryptedNtOwfPassword) );
  1562. }
  1563. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1564. "NetrServerPasswordGet: Comp=%ws Acc=%ws returns 0x%lX\n",
  1565. ComputerName,
  1566. AccountName,
  1567. Status ));
  1568. if ( DomainInfo != NULL ) {
  1569. NlDereferenceDomain( DomainInfo );
  1570. }
  1571. return Status;
  1572. }
  1573. NTSTATUS
  1574. NetrServerGetTrustInfo(
  1575. IN LPWSTR TrustedDcName,
  1576. IN LPWSTR AccountName,
  1577. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1578. IN LPWSTR ComputerName,
  1579. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1580. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1581. OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNewOwfPassword,
  1582. OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedOldOwfPassword,
  1583. OUT PNL_GENERIC_RPC_DATA *TrustInfo
  1584. )
  1585. /*++
  1586. Routine Description:
  1587. This function is used by a trusting side DC/workstation to get the
  1588. trust info (new and old passwords and trust attributes) from the
  1589. trusted side. The account name requested must match the account
  1590. name used at the secure channel setup time unless the call is made
  1591. by a BDC to its PDC; the BDC has full access to the entire trust info.
  1592. This function can only be called by a server which has previously
  1593. authenticated with a DC by calling I_NetServerAuthenticate.
  1594. This function uses RPC to contact the DC named by TrustedDcName.
  1595. Arguments:
  1596. TrustedDcName -- Computer name of the DC to remote the call to.
  1597. AccountName -- Name of the account to get the password for.
  1598. AccountType -- The type of account being accessed.
  1599. ComputerName -- Name of the DC making the call.
  1600. Authenticator -- supplied by this server.
  1601. ReturnAuthenticator -- Receives an authenticator returned by the
  1602. trusted side DC.
  1603. EncryptedNewOwfPassword -- Returns the new OWF password of the account.
  1604. EncryptedOldOwfPassword -- Returns the old OWF password of the account.
  1605. TrustInfo -- Returns trust info data (currently trust attributes).
  1606. Must be freed by calling NetApiBufferFree.
  1607. Return Value:
  1608. NT status code.
  1609. --*/
  1610. {
  1611. NTSTATUS Status;
  1612. PDOMAIN_INFO DomainInfo = NULL;
  1613. PSERVER_SESSION ServerSession;
  1614. SESSION_INFO SessionInfo;
  1615. NT_OWF_PASSWORD NewOwfPassword;
  1616. NT_OWF_PASSWORD OldOwfPassword;
  1617. ULONG AccountRid;
  1618. ULONG TrustAttributes = 0;
  1619. ULONG ServerSessionAccountRid;
  1620. BOOLEAN VerifyAccountMatch = FALSE;
  1621. BOOLEAN GetBothPasswords = FALSE;
  1622. PNL_GENERIC_RPC_DATA LocalTrustInfo = NULL;
  1623. //
  1624. // This API is not supported on workstations.
  1625. //
  1626. if ( NlGlobalMemberWorkstation ) {
  1627. return STATUS_NOT_SUPPORTED;
  1628. }
  1629. //
  1630. // Lookup which domain this call pertains to.
  1631. //
  1632. DomainInfo = NlFindDomainByServerName( TrustedDcName );
  1633. if ( DomainInfo == NULL ) {
  1634. Status = STATUS_INVALID_COMPUTER_NAME;
  1635. goto Cleanup;
  1636. }
  1637. //
  1638. // Get the Session key for this session.
  1639. //
  1640. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  1641. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  1642. if (ServerSession == NULL) {
  1643. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1644. Status = STATUS_ACCESS_DENIED;
  1645. goto Cleanup;
  1646. }
  1647. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  1648. // SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  1649. //
  1650. // now verify the Authenticator and update seed if OK
  1651. //
  1652. Status = NlCheckAuthenticator( ServerSession,
  1653. Authenticator,
  1654. ReturnAuthenticator);
  1655. if ( !NT_SUCCESS(Status) ) {
  1656. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1657. goto Cleanup;
  1658. }
  1659. //
  1660. // Check if we need to verify whether the trusted side
  1661. // is allowed to get passwords for this particular account.
  1662. // For our BDC, we allow full access to trust info.
  1663. //
  1664. if ( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  1665. ServerSessionAccountRid = ServerSession->SsAccountRid;
  1666. VerifyAccountMatch = TRUE;
  1667. }
  1668. //
  1669. // See if we need to get both new and previous passwords
  1670. //
  1671. if ( IsDomainSecureChannelType( AccountType ) ) {
  1672. GetBothPasswords = TRUE;
  1673. }
  1674. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1675. //
  1676. // Get the password for the account.
  1677. //
  1678. Status = NlGetIncomingPassword(
  1679. DomainInfo,
  1680. AccountName,
  1681. AccountType,
  1682. 0, // Let routine compute from AccountType
  1683. TRUE, // Fail if account is disabled
  1684. &NewOwfPassword,
  1685. GetBothPasswords ?
  1686. &OldOwfPassword :
  1687. NULL,
  1688. &AccountRid,
  1689. &TrustAttributes, // Get trust attributes
  1690. NULL ); // Don't need the account type
  1691. if ( !NT_SUCCESS(Status) ) {
  1692. goto Cleanup;
  1693. }
  1694. //
  1695. // See if we need to verify that the account requested is
  1696. // the one for which this server session was created.
  1697. //
  1698. if ( VerifyAccountMatch && ServerSessionAccountRid != AccountRid ) {
  1699. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1700. "NetrServerTrustPasswordsGet: %ws with AccountRid %lu asked for wrong account %ws and Rid %lu.\n",
  1701. ComputerName,
  1702. ServerSessionAccountRid,
  1703. AccountName,
  1704. AccountRid ));
  1705. Status = STATUS_ACCESS_DENIED;
  1706. goto Cleanup;
  1707. }
  1708. //
  1709. // Encrypt the passwords again with the session key.
  1710. // The trusting side DC will decrypt it on the other side.
  1711. //
  1712. Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
  1713. &NewOwfPassword,
  1714. (PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
  1715. EncryptedNewOwfPassword) ;
  1716. if ( !NT_SUCCESS( Status )) {
  1717. NlPrintDom((NL_CRITICAL, DomainInfo,
  1718. "NetrServerTrustPasswordsGet: Cannot RtlEncryptNtOwfPwdWithNtOwfPwd 0x%lx\n",
  1719. Status));
  1720. goto Cleanup;
  1721. }
  1722. //
  1723. // If no password exists on the account,
  1724. // return a blank password.
  1725. //
  1726. if ( !GetBothPasswords ) {
  1727. UNICODE_STRING TempUnicodeString;
  1728. RtlInitUnicodeString( &TempUnicodeString, NULL );
  1729. Status = RtlCalculateNtOwfPassword( &TempUnicodeString,
  1730. &OldOwfPassword );
  1731. if ( !NT_SUCCESS(Status) ) {
  1732. NlPrintDom((NL_CRITICAL, DomainInfo,
  1733. "NetrServerTrustPasswordsGet: %ws: cannot RtlCalculateNtOwfPassword (NULL) 0x%lx\n",
  1734. AccountName,
  1735. Status ));
  1736. goto Cleanup;
  1737. }
  1738. }
  1739. Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
  1740. &OldOwfPassword,
  1741. (PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
  1742. EncryptedOldOwfPassword) ;
  1743. if ( !NT_SUCCESS( Status )) {
  1744. NlPrintDom((NL_CRITICAL, DomainInfo,
  1745. "NetrServerTrustPasswordsGet: Cannot RtlEncryptNtOwfPwdWithNtOwfPwd 0x%lx\n",
  1746. Status));
  1747. goto Cleanup;
  1748. }
  1749. //
  1750. // Return the trust attributes if requested.
  1751. // Must be the first item on the list of
  1752. // ULONGs returned.
  1753. //
  1754. if ( TrustInfo != NULL ) {
  1755. NET_API_STATUS NetStatus;
  1756. NetStatus = NetApiBufferAllocate( sizeof(NL_GENERIC_RPC_DATA)+sizeof(ULONG),
  1757. &LocalTrustInfo );
  1758. if ( NetStatus != NO_ERROR ) {
  1759. Status = STATUS_NO_MEMORY;
  1760. goto Cleanup;
  1761. }
  1762. RtlZeroMemory( LocalTrustInfo, sizeof(NL_GENERIC_RPC_DATA)+sizeof(ULONG) );
  1763. LocalTrustInfo->UlongEntryCount = 1;
  1764. LocalTrustInfo->UlongData = (PULONG)(LocalTrustInfo+1);
  1765. *( (PULONG)(LocalTrustInfo+1) ) = TrustAttributes;
  1766. *TrustInfo = LocalTrustInfo;
  1767. }
  1768. Status = STATUS_SUCCESS;
  1769. //
  1770. // Common exit point
  1771. //
  1772. Cleanup:
  1773. //
  1774. // If the request failed, be carefull to not leak authentication
  1775. // information.
  1776. //
  1777. if ( !NT_SUCCESS( Status ) ) {
  1778. RtlSecureZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  1779. RtlSecureZeroMemory( EncryptedNewOwfPassword, sizeof(*EncryptedNewOwfPassword) );
  1780. RtlSecureZeroMemory( EncryptedOldOwfPassword, sizeof(*EncryptedOldOwfPassword) );
  1781. if ( LocalTrustInfo != NULL ) {
  1782. NetApiBufferFree( LocalTrustInfo );
  1783. }
  1784. }
  1785. NlPrintDom((NL_MISC, DomainInfo,
  1786. "NetrServerPasswordGet: Comp=%ws Acc=%ws returns 0x%lX\n",
  1787. ComputerName,
  1788. AccountName,
  1789. Status ));
  1790. if ( DomainInfo != NULL ) {
  1791. NlDereferenceDomain( DomainInfo );
  1792. }
  1793. return Status;
  1794. }
  1795. NTSTATUS
  1796. NetrServerTrustPasswordsGet(
  1797. IN LPWSTR TrustedDcName,
  1798. IN LPWSTR AccountName,
  1799. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1800. IN LPWSTR ComputerName,
  1801. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1802. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1803. OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNewOwfPassword,
  1804. OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedOldOwfPassword
  1805. )
  1806. /*++
  1807. Routine Description:
  1808. This function is used by a trusting side DC/workstation to get the
  1809. new and old passwords from the trusted side. The account name
  1810. requested must match the account name used at the secure channel
  1811. setup time unless the call is made by a BDC to its PDC; the BDC
  1812. has full access to the entire trust info.
  1813. This function can only be called by a server which has previously
  1814. authenticated with a DC by calling I_NetServerAuthenticate.
  1815. This function uses RPC to contact the DC named by TrustedDcName.
  1816. Arguments:
  1817. TrustedDcName -- Computer name of the DC to remote the call to.
  1818. AccountName -- Name of the account to get the password for.
  1819. AccountType -- The type of account being accessed.
  1820. ComputerName -- Name of the machine making the call.
  1821. Authenticator -- supplied by the server making the call.
  1822. ReturnAuthenticator -- Receives an authenticator returned by the
  1823. trusted side DC.
  1824. EncryptedNewOwfPassword -- Returns the new OWF password of the account.
  1825. EncryptedOldOwfPassword -- Returns the old OWF password of the account.
  1826. Return Value:
  1827. NT status code.
  1828. --*/
  1829. {
  1830. return NetrServerGetTrustInfo(
  1831. TrustedDcName,
  1832. AccountName,
  1833. AccountType,
  1834. ComputerName,
  1835. Authenticator,
  1836. ReturnAuthenticator,
  1837. EncryptedNewOwfPassword,
  1838. EncryptedOldOwfPassword,
  1839. NULL ); // no trust attributes
  1840. }
  1841. NTSTATUS
  1842. NetrServerPasswordSet(
  1843. IN LPWSTR PrimaryName OPTIONAL,
  1844. IN LPWSTR AccountName,
  1845. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1846. IN LPWSTR ComputerName,
  1847. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1848. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1849. IN PENCRYPTED_LM_OWF_PASSWORD UasNewPassword
  1850. )
  1851. /*++
  1852. Routine Description:
  1853. See NetpServerPasswordSet.
  1854. Arguments:
  1855. See NetpServerPasswordSet.
  1856. Return Value:
  1857. See NetpServerPasswordSet.
  1858. --*/
  1859. {
  1860. return NetpServerPasswordSet( PrimaryName,
  1861. AccountName,
  1862. AccountType,
  1863. ComputerName,
  1864. Authenticator,
  1865. ReturnAuthenticator,
  1866. UasNewPassword,
  1867. NULL ); // No clear password
  1868. }
  1869. NTSTATUS
  1870. NetrServerPasswordSet2(
  1871. IN LPWSTR PrimaryName OPTIONAL,
  1872. IN LPWSTR AccountName,
  1873. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  1874. IN LPWSTR ComputerName,
  1875. IN PNETLOGON_AUTHENTICATOR Authenticator,
  1876. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  1877. IN PNL_TRUST_PASSWORD ClearNewPassword
  1878. )
  1879. /*++
  1880. Routine Description:
  1881. See NetpServerPasswordSet.
  1882. Arguments:
  1883. See NetpServerPasswordSet.
  1884. Return Value:
  1885. See NetpServerPasswordSet.
  1886. --*/
  1887. {
  1888. return NetpServerPasswordSet( PrimaryName,
  1889. AccountName,
  1890. AccountType,
  1891. ComputerName,
  1892. Authenticator,
  1893. ReturnAuthenticator,
  1894. NULL, // No OWF password
  1895. ClearNewPassword );
  1896. }
  1897. NTSTATUS
  1898. NlPackSerialNumber (
  1899. IN PLARGE_INTEGER SerialNumber,
  1900. IN OUT PNETLOGON_DELTA_ENUM Delta,
  1901. IN LPDWORD BufferSize,
  1902. IN PSESSION_INFO SessionInfo
  1903. )
  1904. /*++
  1905. Routine Description:
  1906. Pack the specified serial number as a delta.
  1907. Arguments:
  1908. SerialNumber - The serial number to pack.
  1909. Delta: pointer to the delta structure where the new delta will
  1910. be returned.
  1911. DBInfo: pointer to the database info structure.
  1912. BufferSize: size of MIDL buffer that is consumed for this delta is
  1913. returned here.
  1914. SessionInfo: Info describing BDC that's calling us
  1915. Return Value:
  1916. NT status code.
  1917. --*/
  1918. {
  1919. PNLPR_MODIFIED_COUNT DeltaSerialNumberSkip;
  1920. PSAMPR_USER_INFO_BUFFER UserAll = NULL;
  1921. //
  1922. // Only pack this delta if the BDC expects it.
  1923. //
  1924. NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG);
  1925. UNREFERENCED_PARAMETER(SessionInfo);
  1926. NlPrint(( NL_SYNC_MORE,
  1927. "Packing skip to serial number delta: %lx %lx\n",
  1928. SerialNumber->HighPart,
  1929. SerialNumber->LowPart ));
  1930. *BufferSize = 0;
  1931. Delta->DeltaType = SerialNumberSkip;
  1932. Delta->DeltaID.Rid = 0;
  1933. Delta->DeltaUnion.DeltaSerialNumberSkip = NULL;
  1934. //
  1935. // Allocate a buffer to return to the caller.
  1936. //
  1937. DeltaSerialNumberSkip = (PNLPR_MODIFIED_COUNT)
  1938. MIDL_user_allocate( sizeof(*DeltaSerialNumberSkip) );
  1939. if (DeltaSerialNumberSkip == NULL) {
  1940. return STATUS_NO_MEMORY;
  1941. }
  1942. *BufferSize += sizeof(*DeltaSerialNumberSkip);
  1943. //
  1944. // Copy the serial number into the buffer.
  1945. //
  1946. RtlCopyMemory( &DeltaSerialNumberSkip->ModifiedCount,
  1947. SerialNumber,
  1948. sizeof( DeltaSerialNumberSkip->ModifiedCount ) );
  1949. Delta->DeltaUnion.DeltaSerialNumberSkip = DeltaSerialNumberSkip;
  1950. //
  1951. // All Done
  1952. //
  1953. return STATUS_SUCCESS;
  1954. }
  1955. NTSTATUS
  1956. NlPackSingleDelta (
  1957. IN PCHANGELOG_ENTRY ChangeLogEntry,
  1958. IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
  1959. OUT LPDWORD BufferConsumed,
  1960. IN PSESSION_INFO SessionInfo,
  1961. IN BOOLEAN ReturnSerialNumberDeltas
  1962. )
  1963. /*++
  1964. Routine Description:
  1965. Pack the deltas for a single change log entry.
  1966. Arguments:
  1967. ChangeLogEntry - The Change Log Entry describing the account to pack.
  1968. DeltaArray - Describes the array of deltas. The appropriate deltas will
  1969. be added to the end of this array. The caller has guaranteed that
  1970. that is room for at least MAX_DELTAS_PER_CHANGELOG - 1
  1971. deltas to be added to the array.
  1972. BufferConsumed - returns the size of MIDL buffer that is consumed for the
  1973. returned deltas
  1974. SessionInfo: Info describing BDC that's calling us
  1975. ReturnSerialNumberDeltas -- True if serial number deltas should be returned
  1976. when needed.
  1977. Return Value:
  1978. STATUS_SUCCESS -- The function completed successfully.
  1979. --*/
  1980. {
  1981. NTSTATUS Status = STATUS_SUCCESS;
  1982. PDB_INFO DBInfo;
  1983. DWORD BufferSize;
  1984. UNICODE_STRING UnicodeSecretName;
  1985. LPWSTR AccountName;
  1986. PSID Sid;
  1987. //
  1988. // Initialization
  1989. //
  1990. DBInfo = &NlGlobalDBInfoArray[ChangeLogEntry->DBIndex];
  1991. *BufferConsumed = 0;
  1992. //
  1993. // Macro to account for another delta array entry being consumed/returned
  1994. //
  1995. # define MoveToNextDeltaArrayEntry( _BufferSize ) \
  1996. *BufferConsumed += (sizeof(NETLOGON_DELTA_ENUM) + _BufferSize); \
  1997. (DeltaArray->CountReturned)++;
  1998. //
  1999. // Put the data for the changelog entry into the user's buffer.
  2000. //
  2001. switch ( ChangeLogEntry->DeltaType ) {
  2002. case AddOrChangeDomain:
  2003. Status = NlPackSamDomain(
  2004. &((DeltaArray->Deltas)
  2005. [DeltaArray->CountReturned]),
  2006. DBInfo,
  2007. &BufferSize );
  2008. break;
  2009. //
  2010. // The DS can't distinguish between a membership change and a property change.
  2011. // always replicate all aspects of the group.
  2012. //
  2013. case AddOrChangeGroup:
  2014. case ChangeGroupMembership:
  2015. case RenameGroup:
  2016. //
  2017. // we treat the rename as three deltas.
  2018. // 1. AddorChangeGroup delta.
  2019. // Backup deletes the account with old name and creates
  2020. // an account with new name.
  2021. //
  2022. // 2. Delta to tell the BDC that delta (3) below is for the
  2023. // same serial number as delta (1) above.
  2024. //
  2025. // 3. ChangeGroupMembership delta.
  2026. // Backup readds all members to new group.
  2027. //
  2028. Status = NlPackSamGroup( ChangeLogEntry->ObjectRid,
  2029. &((DeltaArray->Deltas)
  2030. [DeltaArray->CountReturned]),
  2031. DBInfo,
  2032. &BufferSize );
  2033. if( !NT_SUCCESS( Status ) ) {
  2034. break;
  2035. }
  2036. MoveToNextDeltaArrayEntry( BufferSize );
  2037. if ( ReturnSerialNumberDeltas ) {
  2038. Status = NlPackSerialNumber(
  2039. &ChangeLogEntry->SerialNumber,
  2040. &((DeltaArray->Deltas)
  2041. [DeltaArray->CountReturned]),
  2042. &BufferSize,
  2043. SessionInfo );
  2044. if( !NT_SUCCESS( Status ) ) {
  2045. break;
  2046. }
  2047. MoveToNextDeltaArrayEntry( BufferSize );
  2048. }
  2049. Status = NlPackSamGroupMember( ChangeLogEntry->ObjectRid,
  2050. &((DeltaArray->Deltas)
  2051. [DeltaArray->CountReturned]),
  2052. DBInfo,
  2053. &BufferSize );
  2054. break;
  2055. case AddOrChangeUser:
  2056. case RenameUser:
  2057. Status = NlPackSamUser( ChangeLogEntry->ObjectRid,
  2058. &((DeltaArray->Deltas)
  2059. [DeltaArray->CountReturned]),
  2060. DBInfo,
  2061. &BufferSize,
  2062. SessionInfo );
  2063. break;
  2064. //
  2065. // The DS can't distinguish between a membership change and a property change.
  2066. // always replicate all aspects of the alias.
  2067. //
  2068. case AddOrChangeAlias:
  2069. case ChangeAliasMembership:
  2070. case RenameAlias:
  2071. //
  2072. // we treat the rename as two deltas.
  2073. // 1. AddorChangeAlias delta.
  2074. // Backup deletes the account with old name and creates
  2075. // an account with new name.
  2076. //
  2077. // 2. Delta to tell the BDC that delta (3) below is for the
  2078. // same serial number as delta (1) above.
  2079. //
  2080. // 3. ChangeAliasMembership delta.
  2081. // Backup readds all members to new alias.
  2082. //
  2083. Status = NlPackSamAlias( ChangeLogEntry->ObjectRid,
  2084. &((DeltaArray->Deltas)
  2085. [DeltaArray->CountReturned]),
  2086. DBInfo,
  2087. &BufferSize );
  2088. if( !NT_SUCCESS( Status ) ) {
  2089. break;
  2090. }
  2091. MoveToNextDeltaArrayEntry( BufferSize );
  2092. if ( ReturnSerialNumberDeltas ) {
  2093. Status = NlPackSerialNumber(
  2094. &ChangeLogEntry->SerialNumber,
  2095. &((DeltaArray->Deltas)
  2096. [DeltaArray->CountReturned]),
  2097. &BufferSize,
  2098. SessionInfo );
  2099. if( !NT_SUCCESS( Status ) ) {
  2100. break;
  2101. }
  2102. MoveToNextDeltaArrayEntry( BufferSize );
  2103. }
  2104. Status = NlPackSamAliasMember( ChangeLogEntry->ObjectRid,
  2105. &((DeltaArray->Deltas)
  2106. [DeltaArray->CountReturned]),
  2107. DBInfo,
  2108. &BufferSize );
  2109. break;
  2110. case AddOrChangeLsaPolicy:
  2111. Status = NlPackLsaPolicy(
  2112. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  2113. DBInfo,
  2114. &BufferSize );
  2115. break;
  2116. case AddOrChangeLsaTDomain:
  2117. NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
  2118. if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
  2119. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2120. break;
  2121. }
  2122. Status = NlPackLsaTDomain(
  2123. (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)),
  2124. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  2125. DBInfo,
  2126. &BufferSize );
  2127. break;
  2128. case AddOrChangeLsaAccount:
  2129. NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
  2130. if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
  2131. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2132. break;
  2133. }
  2134. Status = NlPackLsaAccount(
  2135. (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)),
  2136. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  2137. DBInfo,
  2138. &BufferSize,
  2139. SessionInfo );
  2140. break;
  2141. case AddOrChangeLsaSecret:
  2142. NlAssert( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED );
  2143. if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
  2144. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2145. break;
  2146. }
  2147. RtlInitUnicodeString(
  2148. &UnicodeSecretName,
  2149. (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)) );
  2150. Status = NlPackLsaSecret(
  2151. &UnicodeSecretName,
  2152. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  2153. DBInfo,
  2154. &BufferSize,
  2155. SessionInfo );
  2156. break;
  2157. case DeleteGroup:
  2158. case DeleteGroupByName:
  2159. case DeleteUser:
  2160. case DeleteUserByName:
  2161. //
  2162. // If this is an NT 3.5 BDC,
  2163. // send the account name upon account deletion.
  2164. if ( ReturnSerialNumberDeltas ) {
  2165. //
  2166. // Send the NT 3.5 BDC a special delta type indicating the
  2167. // Name is attached.
  2168. //
  2169. if ( ChangeLogEntry->DeltaType == DeleteGroup ) {
  2170. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2171. DeleteGroupByName;
  2172. } else if ( ChangeLogEntry->DeltaType == DeleteUser ) {
  2173. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2174. DeleteUserByName;
  2175. } else {
  2176. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2177. ChangeLogEntry->DeltaType;
  2178. }
  2179. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Rid =
  2180. ChangeLogEntry->ObjectRid;
  2181. //
  2182. // Add the account name to the entry.
  2183. //
  2184. NlAssert(ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED);
  2185. if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
  2186. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2187. break;
  2188. }
  2189. BufferSize = (wcslen(
  2190. (LPWSTR) ((LPBYTE)ChangeLogEntry +
  2191. sizeof(CHANGELOG_ENTRY))) + 1 ) *
  2192. sizeof(WCHAR);
  2193. AccountName = (LPWSTR) MIDL_user_allocate( BufferSize );
  2194. if (AccountName == NULL) {
  2195. Status = STATUS_NO_MEMORY;
  2196. break;
  2197. }
  2198. wcscpy( AccountName,
  2199. (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
  2200. (DeltaArray->Deltas)[DeltaArray->CountReturned].
  2201. DeltaUnion.DeltaDeleteGroup =
  2202. MIDL_user_allocate(sizeof(struct _NETLOGON_DELTA_DELETE));
  2203. if ((DeltaArray->Deltas)[DeltaArray->CountReturned].
  2204. DeltaUnion.DeltaDeleteGroup == NULL ) {
  2205. MIDL_user_free(AccountName);
  2206. Status = STATUS_NO_MEMORY;
  2207. break;
  2208. }
  2209. INIT_PLACE_HOLDER( (DeltaArray->Deltas)[DeltaArray->CountReturned].
  2210. DeltaUnion.DeltaDeleteGroup );
  2211. (DeltaArray->Deltas)[DeltaArray->CountReturned].
  2212. DeltaUnion.DeltaDeleteGroup->AccountName = AccountName;
  2213. break; // out of switch
  2214. }
  2215. /* Drop through to handle NT 3.1 case. */
  2216. case DeleteAlias:
  2217. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2218. ChangeLogEntry->DeltaType;
  2219. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Rid =
  2220. ChangeLogEntry->ObjectRid;
  2221. BufferSize = 0;
  2222. break;
  2223. case DeleteLsaTDomain:
  2224. case DeleteLsaAccount:
  2225. NlAssert( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED );
  2226. if( (ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED) == 0 ) {
  2227. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2228. break;
  2229. }
  2230. BufferSize =
  2231. RtlLengthSid( (PSID)((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
  2232. Sid = (PSID) MIDL_user_allocate( BufferSize );
  2233. if( Sid == NULL ) {
  2234. Status = STATUS_NO_MEMORY;
  2235. break;
  2236. }
  2237. Status = RtlCopySid (
  2238. BufferSize,
  2239. Sid,
  2240. (PSID) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
  2241. if( !NT_SUCCESS( Status ) ) {
  2242. MIDL_user_free( Sid );
  2243. break;
  2244. }
  2245. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2246. ChangeLogEntry->DeltaType;
  2247. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Sid =
  2248. Sid;
  2249. break;
  2250. case DeleteLsaSecret:
  2251. NlAssert(ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED);
  2252. if( (ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
  2253. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2254. break;
  2255. }
  2256. BufferSize = (wcslen(
  2257. (LPWSTR) ((LPBYTE)ChangeLogEntry +
  2258. sizeof(CHANGELOG_ENTRY))) + 1 ) *
  2259. sizeof(WCHAR);
  2260. AccountName = (LPWSTR) MIDL_user_allocate( BufferSize );
  2261. if (AccountName == NULL) {
  2262. Status = STATUS_NO_MEMORY;
  2263. break;
  2264. }
  2265. wcscpy( AccountName,
  2266. (LPWSTR) ((LPBYTE)ChangeLogEntry + sizeof(CHANGELOG_ENTRY)));
  2267. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaType =
  2268. ChangeLogEntry->DeltaType;
  2269. (DeltaArray->Deltas)[DeltaArray->CountReturned].DeltaID.Name =
  2270. AccountName;
  2271. break;
  2272. default:
  2273. NlPrint((NL_CRITICAL, "NlPackSingleDelta: Invalid delta type in change log\n"));
  2274. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2275. break;
  2276. }
  2277. if ( NT_SUCCESS(Status) ) {
  2278. MoveToNextDeltaArrayEntry( BufferSize );
  2279. }
  2280. return Status;
  2281. #undef MoveToNextDeltaArrayEntry
  2282. }
  2283. NTSTATUS
  2284. NetrDatabaseDeltas (
  2285. IN LPWSTR PrimaryName,
  2286. IN LPWSTR ComputerName,
  2287. IN PNETLOGON_AUTHENTICATOR Authenticator,
  2288. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  2289. IN DWORD DatabaseID,
  2290. IN OUT PNLPR_MODIFIED_COUNT NlDomainModifiedCount,
  2291. OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
  2292. IN DWORD PreferredMaximumLength
  2293. )
  2294. /*++
  2295. Routine Description:
  2296. This function is used by a SAM BDC to
  2297. request SAM-style account delta information from a SAM PDC. This
  2298. function can only be called by a server which has previously
  2299. authenticated with the PDC by calling I_NetServerAuthenticate. This
  2300. function uses RPC to contact the Netlogon service on the PDC.
  2301. This function returns a list of deltas. A delta describes an
  2302. individual domain, user or group and all of the field values for that
  2303. object. The PDC maintains a list of deltas not including all of the
  2304. field values for that object. Rather, the PDC retrieves the field
  2305. values from SAM and returns those values from this call. The PDC
  2306. optimizes the data returned on this call by only returning the field
  2307. values for a particular object once on a single invocation of this
  2308. function. This optimizes the typical case where multiple deltas
  2309. exist for a single object (e.g., an application modified many fields
  2310. of the same user during a short period of time using different calls
  2311. to the SAM service).
  2312. Arguments:
  2313. PrimaryName -- Name of the PDC to retrieve the deltas from.
  2314. ComputerName -- Name of the BDC or member server making the call.
  2315. Authenticator -- supplied by the server.
  2316. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  2317. DatabaseID -- Identifies the databse for which the deltas are requested.
  2318. For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
  2319. databases may be defined later.
  2320. NlDomainModifiedCount -- Specifies the DomainModifiedCount of the
  2321. last delta retrieved by the server. Returns the
  2322. DomainModifiedCount of the last delta returned from the PDC
  2323. on this call.
  2324. Deltas -- Receives a pointer to a buffer where the information is
  2325. placed. The information returned is an array of
  2326. NETLOGON_DELTA_ENUM structures.
  2327. PreferredMaximumLength - Preferred maximum length of returned
  2328. data (in 8-bit bytes). This is not a hard upper limit, but
  2329. serves as a guide to the server. Due to data conversion
  2330. between systems with different natural data sizes, the actual
  2331. amount of data returned may be greater than this value.
  2332. Return Value:
  2333. STATUS_SUCCESS -- The function completed successfully.
  2334. STATUS_SYNCHRONIZATION_REQUIRED -- The replicant is totally out of sync and
  2335. should call I_NetDataSync to do a full synchronization with
  2336. the PDC.
  2337. STATUS_MORE_ENTRIES -- The replicant should call again to get more
  2338. data.
  2339. STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
  2340. the PDC.
  2341. --*/
  2342. {
  2343. NTSTATUS Status;
  2344. PDOMAIN_INFO DomainInfo = NULL;
  2345. PSERVER_SESSION ServerSession = NULL;
  2346. PCHANGELOG_ENTRY ChangeLogEntry = NULL;
  2347. BOOLEAN PackThisEntry = TRUE;
  2348. BOOL ChangelogLocked = FALSE;
  2349. PDB_INFO DBInfo;
  2350. LARGE_INTEGER RunningSerialNumber;
  2351. LARGE_INTEGER PackedSerialNumber;
  2352. LARGE_INTEGER OriginalSerialNumber;
  2353. DWORD BufferConsumed = 0;
  2354. DWORD BufferSize = 0;
  2355. PNETLOGON_DELTA_ENUM_ARRAY DeltaArray;
  2356. SESSION_INFO SessionInfo;
  2357. DEFSSIAPITIMER;
  2358. INITSSIAPITIMER;
  2359. STARTSSIAPITIMER;
  2360. //
  2361. // This API is not supported on workstations.
  2362. //
  2363. if ( NlGlobalMemberWorkstation || !NlGlobalPdcDoReplication ) {
  2364. NlPrint((NL_CRITICAL,
  2365. "NetrDatabaseDeltas: called from %ws. This machine doesn't support replication.\n",
  2366. ComputerName ));
  2367. return STATUS_NOT_SUPPORTED;
  2368. }
  2369. //
  2370. // If the DS is recovering from a backup,
  2371. // avoid changing the DS.
  2372. //
  2373. if ( NlGlobalDsPaused ) {
  2374. NlPrint((NL_CRITICAL,
  2375. "NetrDatabaseDeltas: DsIsPaused.\n"));
  2376. // Don't return a new status code since NT 4 DC would do a full sync
  2377. return STATUS_ACCESS_DENIED;
  2378. }
  2379. //
  2380. // Gross hack because of RPC implementation.
  2381. //
  2382. // Rpc executes API calls in an I/O completion port thread. If this thread
  2383. // goes CPU bound, then no other RPC will be allowed to start. Even worse,
  2384. // there is only one outstanding listen, so the 'second' coming RPC call
  2385. // gets RPC_S_SERVER_TOO_BUSY.
  2386. //
  2387. // By sleeping here (even for a short period) the I/O completion port releases
  2388. // another thread since it thinks this thread went I/O bound.
  2389. //
  2390. // We've seen this thread go CPU bound doing a full sync of a database with
  2391. // 1000's of LSA account objects.
  2392. //
  2393. RpcServerYield();
  2394. //
  2395. // Initialization
  2396. //
  2397. if ( DatabaseID >= NUM_DBS ) {
  2398. return STATUS_INVALID_LEVEL;
  2399. }
  2400. *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
  2401. MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
  2402. if( DeltaArray == NULL ) {
  2403. return STATUS_NO_MEMORY;
  2404. }
  2405. DeltaArray->CountReturned = 0;
  2406. DeltaArray->Deltas = NULL;
  2407. SessionInfo.NegotiatedFlags = 0;
  2408. DBInfo = &NlGlobalDBInfoArray[DatabaseID];
  2409. RtlCopyMemory( &RunningSerialNumber,
  2410. &NlDomainModifiedCount->ModifiedCount,
  2411. sizeof(RunningSerialNumber));
  2412. OriginalSerialNumber.QuadPart = RunningSerialNumber.QuadPart;
  2413. PackedSerialNumber.QuadPart = RunningSerialNumber.QuadPart;
  2414. //
  2415. // Find the domain this API was made to.
  2416. //
  2417. DomainInfo = NlFindDomainByServerName( PrimaryName );
  2418. NlPrintDom((NL_SYNC, DomainInfo,
  2419. "NetrDatabaseDeltas: " FORMAT_LPWSTR " partial sync called by " FORMAT_LPWSTR
  2420. " SerialNumber:%lx %lx.\n",
  2421. DBInfo->DBName,
  2422. ComputerName,
  2423. RunningSerialNumber.HighPart,
  2424. RunningSerialNumber.LowPart ));
  2425. if ( DomainInfo == NULL ) {
  2426. Status = STATUS_INVALID_COMPUTER_NAME;
  2427. goto Cleanup;
  2428. }
  2429. if ( !IsPrimaryDomain( DomainInfo )) {
  2430. Status = STATUS_NOT_SUPPORTED;
  2431. goto Cleanup;
  2432. }
  2433. //
  2434. // Retrieve the requestor's entry to get sessionkey
  2435. //
  2436. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  2437. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  2438. if (ServerSession == NULL) {
  2439. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2440. Status = STATUS_ACCESS_DENIED;
  2441. NlPrint((NL_CRITICAL,
  2442. "NetrDatabaseDeltas: No server session.\n"));
  2443. // Don't log this event since it happens in nature after a reboot
  2444. // or after we scavenge the server session.
  2445. goto CleanupNoEventlog;
  2446. }
  2447. //
  2448. // Allow this call only on ServerSecureChannel.
  2449. //
  2450. if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  2451. //
  2452. // If the only preblem is that this BDC hasn't authenticated,
  2453. // silently ask it to authenticate.
  2454. //
  2455. if ( ServerSession->SsSecureChannelType == NullSecureChannel ) {
  2456. NlPrint((NL_CRITICAL,
  2457. "NetrDatabaseDeltas: No authenticated server session.\n"));
  2458. ServerSession = NULL;
  2459. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2460. // Don't log this event since it happens in nature after a reboot
  2461. // or after we scavenge the server session.
  2462. Status = STATUS_ACCESS_DENIED;
  2463. goto CleanupNoEventlog;
  2464. } else {
  2465. NlPrint((NL_CRITICAL,
  2466. "NetrDatabaseDeltas: SecureChannel type isn't BDC. %ld\n",
  2467. ServerSession->SsSecureChannelType ));
  2468. ServerSession = NULL;
  2469. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2470. Status = STATUS_ACCESS_DENIED;
  2471. goto Cleanup;
  2472. }
  2473. }
  2474. //
  2475. // Verify the Authenticator and update seed if OK
  2476. //
  2477. Status = NlCheckAuthenticator( ServerSession,
  2478. Authenticator,
  2479. ReturnAuthenticator);
  2480. if ( !NT_SUCCESS(Status) ) {
  2481. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2482. NlPrint((NL_CRITICAL, "NetrDatabaseDeltas: authentication failed.\n" ));
  2483. ServerSession = NULL;
  2484. goto Cleanup;
  2485. }
  2486. //
  2487. // Prevent entry from being deleted, but drop the global lock.
  2488. //
  2489. // Beware of server with two concurrent calls outstanding
  2490. // (must have rebooted.)
  2491. //
  2492. if (ServerSession->SsFlags & SS_LOCKED ) {
  2493. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2494. NlPrint((NL_CRITICAL, "NetrDatabaseDeltas: Concurrent call detected.\n" ));
  2495. Status = STATUS_ACCESS_DENIED;
  2496. ServerSession = NULL;
  2497. goto Cleanup;
  2498. }
  2499. ServerSession->SsFlags |= SS_LOCKED;
  2500. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  2501. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  2502. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2503. //
  2504. // If the BDC is in sync,
  2505. // simply return.
  2506. //
  2507. LOCK_CHANGELOG();
  2508. ChangelogLocked = TRUE;
  2509. if ( RunningSerialNumber.QuadPart ==
  2510. NlGlobalChangeLogDesc.SerialNumber[DatabaseID].QuadPart ) {
  2511. Status = STATUS_SUCCESS;
  2512. goto Cleanup;
  2513. }
  2514. //
  2515. // Get a copy of the appropriate entry in the change_log.
  2516. // Note that the record_id contains last record received by client.
  2517. //
  2518. if ((ChangeLogEntry = NlGetNextUniqueChangeLogEntry(
  2519. &NlGlobalChangeLogDesc,
  2520. RunningSerialNumber,
  2521. DBInfo->DBIndex,
  2522. NULL ))== NULL) {
  2523. //
  2524. // Handle the case where the BDC has more recent changes than we do.
  2525. //
  2526. // Just return our newest change log entry with the same promotion count.
  2527. // The BDC will realize what's going on and un-do its newer changes.
  2528. //
  2529. // Only do this if our PromotionCount is greater than the BDCs. If
  2530. // our promotion count is equal to that of the BDC, either our change log
  2531. // has wrapped, or the BDC is royally confused.
  2532. //
  2533. // Don't be tempted to return a change log entry with an
  2534. // older promotion count. We'd have no way of knowing which delta
  2535. // to actually return to the caller.
  2536. //
  2537. if ( ((NlGlobalChangeLogDesc.SerialNumber[DatabaseID].HighPart &
  2538. NlGlobalChangeLogPromotionMask) >
  2539. (RunningSerialNumber.HighPart & NlGlobalChangeLogPromotionMask)) &&
  2540. (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_REDO) ) {
  2541. ChangeLogEntry = NlFindPromotionChangeLogEntry(
  2542. &NlGlobalChangeLogDesc,
  2543. RunningSerialNumber,
  2544. DBInfo->DBIndex );
  2545. //
  2546. // Don't actually pack this change log entry. We've found it
  2547. // so we can pack a "serial number" delta. But the BDC already
  2548. // has this particular change.
  2549. //
  2550. PackThisEntry = FALSE;
  2551. }
  2552. if ( ChangeLogEntry == NULL ) {
  2553. NlPrint((NL_CRITICAL,
  2554. "NetrDatabaseDeltas: "
  2555. "delta not found in cache, returning full required.\n" ));
  2556. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2557. goto Cleanup;
  2558. } else {
  2559. NlPrint((NL_SYNC, "NetrDatabaseDeltas: BDC more recent than PDC (recovering).\n" ));
  2560. }
  2561. }
  2562. UNLOCK_CHANGELOG();
  2563. ChangelogLocked = FALSE;
  2564. //
  2565. // Allocate memory for delta buffer.
  2566. //
  2567. DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
  2568. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  2569. if( DeltaArray->Deltas == NULL ) {
  2570. Status = STATUS_NO_MEMORY;
  2571. goto Cleanup;
  2572. }
  2573. //
  2574. // wipe off the buffer so that cleanup will not be in fault.
  2575. //
  2576. RtlZeroMemory( DeltaArray->Deltas,
  2577. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  2578. //
  2579. // Loop packing deltas as long as there is room for more deltas
  2580. //
  2581. // In some cases we pack multiple deltas on the wire for one entry in the
  2582. // change log, we want to ensure that all of these deltas are sent to
  2583. // the BDC on a single call.
  2584. //
  2585. while ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG <= MAX_DELTA_COUNT ) {
  2586. //
  2587. // If the serial number of the delta being packed isn't the one
  2588. // expected by the BDC, tell the BDC what the serial number is.
  2589. //
  2590. if ( ChangeLogEntry->SerialNumber.QuadPart !=
  2591. PackedSerialNumber.QuadPart + 1 ) {
  2592. if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG){
  2593. Status = NlPackSerialNumber(
  2594. &ChangeLogEntry->SerialNumber,
  2595. &((DeltaArray->Deltas)
  2596. [DeltaArray->CountReturned]),
  2597. &BufferSize,
  2598. &SessionInfo );
  2599. if( !NT_SUCCESS( Status ) ) {
  2600. goto Cleanup;
  2601. }
  2602. BufferConsumed += BufferSize;
  2603. DeltaArray->CountReturned ++;
  2604. //
  2605. // If we're not really going to pack the entry,
  2606. // pretend that we already have.
  2607. //
  2608. if ( !PackThisEntry) {
  2609. PackedSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
  2610. }
  2611. }
  2612. }
  2613. if ( PackThisEntry ) {
  2614. //
  2615. // Put the data for the changelog entry into the user's buffer.
  2616. //
  2617. Status = NlPackSingleDelta( ChangeLogEntry,
  2618. DeltaArray,
  2619. &BufferSize,
  2620. &SessionInfo,
  2621. (BOOLEAN)((SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_BDC_CHANGELOG) != 0) );
  2622. //
  2623. // If we successfully put the delta into the delta array,
  2624. // do the bookwork
  2625. //
  2626. if ( NT_SUCCESS( Status ) ) {
  2627. BufferConsumed += BufferSize;
  2628. PackedSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
  2629. NlPrint((NL_SYNC_MORE,
  2630. "NetrDatabaseDeltas: Modified count of the "
  2631. "packed record: %lx %lx\n",
  2632. ChangeLogEntry->SerialNumber.HighPart,
  2633. ChangeLogEntry->SerialNumber.LowPart ));
  2634. //
  2635. // In the case where an user/group/alias record was
  2636. // added and deleted before the delta was made we will
  2637. // trace the change log and see there is correpondance
  2638. // delete log. If we found one then ignore this delta
  2639. // and proceed to the next delta. If we couldn't find
  2640. // one then return error STATUS_SYNCHRONIZATION_REQUIRED.
  2641. //
  2642. } else if ( IsObjectNotFoundStatus( ChangeLogEntry->DeltaType, Status ) ) {
  2643. if( !NlRecoverChangeLog(ChangeLogEntry) ) {
  2644. NlPrint((NL_CRITICAL,
  2645. "NetrDatabaseDeltas: object not found in database, and no delete delta found (%lx).\n",
  2646. Status ));
  2647. #ifdef notdef
  2648. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  2649. IF_NL_DEBUG( BREAKPOINT ) {
  2650. NlAssert( FALSE );
  2651. }
  2652. goto Cleanup;
  2653. #else // notdef
  2654. //
  2655. // NT 5.0 SAM doesn't hold the write lock while determining if
  2656. // the object exists. So, the object might have been deleted and
  2657. // the but the delete delta hasn't been written to the change log yet.
  2658. // So, assume that the delete delta will appear sooner or later.
  2659. //
  2660. // REVIEW: I could just pack a delete delta.
  2661. //
  2662. Status = STATUS_SUCCESS;
  2663. #endif // notdef
  2664. } else {
  2665. //
  2666. // We found a delete delta, so ignore the original delta.
  2667. //
  2668. Status = STATUS_SUCCESS;
  2669. }
  2670. //
  2671. // All other errors are fatal
  2672. //
  2673. } else {
  2674. goto Cleanup;
  2675. }
  2676. }
  2677. PackThisEntry = TRUE;
  2678. //
  2679. // Free up used temp. record
  2680. //
  2681. RunningSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
  2682. NetpMemoryFree(ChangeLogEntry);
  2683. ChangeLogEntry = NULL;
  2684. //
  2685. // If we've returned all the entries, we're all done.
  2686. //
  2687. LOCK_CHANGELOG();
  2688. ChangelogLocked = TRUE;
  2689. if ((ChangeLogEntry = NlGetNextUniqueChangeLogEntry(
  2690. &NlGlobalChangeLogDesc,
  2691. RunningSerialNumber,
  2692. DBInfo->DBIndex,
  2693. NULL )) == NULL) {
  2694. Status = STATUS_SUCCESS;
  2695. goto Cleanup;
  2696. }
  2697. UNLOCK_CHANGELOG();
  2698. ChangelogLocked = FALSE;
  2699. //
  2700. // Don't return more data to the caller than he wants.
  2701. //
  2702. if( BufferConsumed >= PreferredMaximumLength) {
  2703. Status = STATUS_MORE_ENTRIES;
  2704. goto Cleanup;
  2705. }
  2706. //
  2707. // If we're debugging replication, return only one change to the caller.
  2708. //
  2709. #if NETLOGONDBG
  2710. if ( NlGlobalParameters.DbFlag & NL_ONECHANGE_REPL ) {
  2711. Status = STATUS_MORE_ENTRIES;
  2712. goto Cleanup;
  2713. }
  2714. #endif // NETLOGONDBG
  2715. //
  2716. // If the service is going down, stop packing deltas and
  2717. // return to the caller.
  2718. //
  2719. if( NlGlobalTerminate ) {
  2720. NlPrint((NL_CRITICAL, "NetrDatabaseDeltas is asked to return "
  2721. "when the service is going down.\n"));
  2722. Status = STATUS_MORE_ENTRIES;
  2723. goto Cleanup;
  2724. }
  2725. }
  2726. Status = STATUS_MORE_ENTRIES;
  2727. Cleanup:
  2728. //
  2729. // write event log
  2730. //
  2731. if ( !NT_SUCCESS( Status ) ) {
  2732. LPWSTR MsgStrings[2];
  2733. MsgStrings[0] = ComputerName;
  2734. MsgStrings[1] = (LPWSTR) LongToPtr( Status );
  2735. NlpWriteEventlog(
  2736. NELOG_NetlogonPartialSyncCallFailed,
  2737. EVENTLOG_WARNING_TYPE,
  2738. (LPBYTE)&Status,
  2739. sizeof(Status),
  2740. MsgStrings,
  2741. 2 | NETP_LAST_MESSAGE_IS_NTSTATUS | NETP_ALLOW_DUPLICATE_EVENTS );
  2742. } else {
  2743. //
  2744. // Log the successful replication only if deltas have been returned
  2745. // to the caller.
  2746. //
  2747. if ( DeltaArray->CountReturned != 0 ) {
  2748. LPWSTR MsgStrings[2];
  2749. WCHAR CountBuffer[20]; // random size
  2750. MsgStrings[0] = ComputerName;
  2751. ultow( DeltaArray->CountReturned, CountBuffer, 10);
  2752. MsgStrings[1] = CountBuffer;
  2753. NlpWriteEventlog(
  2754. NELOG_NetlogonPartialSyncCallSuccess,
  2755. EVENTLOG_INFORMATION_TYPE,
  2756. NULL,
  2757. 0,
  2758. MsgStrings,
  2759. 2 | NETP_ALLOW_DUPLICATE_EVENTS );
  2760. }
  2761. }
  2762. //
  2763. // Free up locally allocated resources.
  2764. //
  2765. CleanupNoEventlog:
  2766. //
  2767. // Copy the serial number back to the caller
  2768. //
  2769. if ( NT_SUCCESS(Status)) {
  2770. RtlCopyMemory( &NlDomainModifiedCount->ModifiedCount,
  2771. &PackedSerialNumber,
  2772. sizeof(PackedSerialNumber));
  2773. //
  2774. // If this is an NT 3.1 BDC,
  2775. // Only remember the latest Serial Number it asked for, AND
  2776. // force it the call back once it has updated the SerialNumber
  2777. // so we know what that serial number is.
  2778. //
  2779. // NT 3.5 BDCs "persistently" try to update their database to the
  2780. // PDCs version once they get a pulse indicating their database is
  2781. // out of date.
  2782. //
  2783. if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_PERSISTENT_BDC) == 0 ) {
  2784. //
  2785. // Use the SerialNumber the BDC originally passed us.
  2786. //
  2787. PackedSerialNumber.QuadPart = OriginalSerialNumber.QuadPart;
  2788. //
  2789. // If we're returning any deltas at all,
  2790. // force the BDC to call us back.
  2791. //
  2792. if ( Status == STATUS_SUCCESS && DeltaArray->CountReturned != 0 ) {
  2793. Status = STATUS_MORE_ENTRIES;
  2794. }
  2795. }
  2796. //
  2797. // If we weren't successful,
  2798. // Don't return any deltas.
  2799. //
  2800. } else {
  2801. if ( DeltaArray->Deltas != NULL ) {
  2802. NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
  2803. DeltaArray->Deltas = NULL;
  2804. }
  2805. DeltaArray->CountReturned = 0;
  2806. }
  2807. if ( ChangelogLocked ) {
  2808. UNLOCK_CHANGELOG();
  2809. }
  2810. if( ChangeLogEntry != NULL) {
  2811. NetpMemoryFree( ChangeLogEntry );
  2812. }
  2813. //
  2814. // Unlock the server session entry if we've locked it.
  2815. //
  2816. if ( ServerSession != NULL ) {
  2817. //
  2818. // If we are successfully returning these deltas to the BDC,
  2819. // update our tables to reflect the changes.
  2820. //
  2821. if ( Status == STATUS_SUCCESS ) {
  2822. NlPrimaryAnnouncementFinish( ServerSession,
  2823. DatabaseID,
  2824. &PackedSerialNumber );
  2825. }
  2826. NlUnlockServerSession( ServerSession );
  2827. }
  2828. //
  2829. // If the BDC called us just as SAM was shutting down,
  2830. // map the status to prevent the BDC from full syncing.
  2831. //
  2832. if ( Status == STATUS_INVALID_SERVER_STATE ) {
  2833. Status = STATUS_ACCESS_DENIED;
  2834. }
  2835. NlPrint((NL_SYNC,
  2836. "NetrDatabaseDeltas: " FORMAT_LPWSTR " returning (0x%lx) to "
  2837. FORMAT_LPWSTR "\n",
  2838. DBInfo->DBName,
  2839. Status,
  2840. ComputerName ));
  2841. STOPSSIAPITIMER;
  2842. if ( DomainInfo != NULL ) {
  2843. NlDereferenceDomain( DomainInfo );
  2844. }
  2845. NlPrint((NL_REPL_TIME,"NetrDatabaseDeltas Time:\n"));
  2846. PRINTSSIAPITIMER;
  2847. return Status;
  2848. }
  2849. NTSTATUS
  2850. NlSyncSamDatabase(
  2851. IN PSERVER_SESSION ServerSession,
  2852. IN DWORD DatabaseID,
  2853. IN SYNC_STATE RestartState,
  2854. IN OUT PULONG SyncContext,
  2855. IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
  2856. IN DWORD PreferredMaximumLength,
  2857. IN PSESSION_INFO SessionInfo
  2858. )
  2859. /*++
  2860. Routine Description:
  2861. This function is a real worker for the NetrDatabaseSync function and
  2862. retrieves a SAM database in the delta buffer.
  2863. This function uses the find-first find-next model to return portions
  2864. of the SAM database at a time. The SAM database is returned as a
  2865. list of deltas like those returned from I_NetDatabaseDeltas. The
  2866. following deltas are returned for each domain:
  2867. * One AddOrChangeDomain delta, followed by
  2868. * One AddOrChangeGroup delta for each group, followed by,
  2869. * One AddOrChangeUser delta for each user, followed by
  2870. * One ChangeGroupMembership delta for each group followed by,
  2871. * One AddOrChangeAlias delta for each alias, followed by,
  2872. * One ChangeAliasMembership delta for each alias.
  2873. Arguments:
  2874. ServerSession -- pointer to connection context.
  2875. DatabaseID -- Identifies the databse for which the deltas are requested.
  2876. For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
  2877. databases may be defined later.
  2878. RestartState -- Specifies whether this is a restart of the full sync and how
  2879. to interpret SyncContext. This value should be NormalState unless this
  2880. is the restart of a full sync.
  2881. However, if the caller is continuing a full sync after a reboot,
  2882. the following values are used:
  2883. GroupState - SyncContext is the global group rid to continue with.
  2884. UserState - SyncContext is the user rid to continue with
  2885. GroupMemberState - SyncContext is the global group rid to continue with
  2886. AliasState - SyncContext should be zero to restart at first alias
  2887. AliasMemberState - SyncContext should be zero to restart at first alias
  2888. One cannot continue the LSA database in this way.
  2889. SyncContext -- Specifies context needed to continue the
  2890. operation. The caller should treat this as an opaque
  2891. value. The value should be zero before the first call.
  2892. DeltaArray -- Pointer to a buffer where the information
  2893. is placed. The information returned is an array of
  2894. NETLOGON_DELTA_ENUM structures.
  2895. PreferredMaximumLength - Preferred maximum length of returned
  2896. data (in 8-bit bytes). This is not a hard upper limit, but
  2897. serves as a guide to the server. Due to data conversion
  2898. between systems with different natural data sizes, the actual
  2899. amount of data returned may be greater than this value.
  2900. SessionInfo - Information shared between PDC and BDC.
  2901. Return Value:
  2902. STATUS_SUCCESS -- The function completed successfully.
  2903. STATUS_MORE_ENTRIES -- The replicant should call again to get more
  2904. data.
  2905. --*/
  2906. {
  2907. NTSTATUS Status;
  2908. PSAM_SYNC_CONTEXT SamDBContext;
  2909. PDB_INFO DBInfo;
  2910. DWORD BufferConsumed = 0;
  2911. DWORD BufferSize;
  2912. DBInfo = &NlGlobalDBInfoArray[DatabaseID];
  2913. //
  2914. // Allocate memory for delta buffer.
  2915. //
  2916. DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
  2917. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  2918. if( DeltaArray->Deltas == NULL ) {
  2919. NlPrint((NL_CRITICAL,
  2920. "NlSyncSamDatabase: Can't allocate %d bytes\n",
  2921. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) ));
  2922. return( STATUS_NO_MEMORY );
  2923. }
  2924. //
  2925. // wipe off the buffer so that cleanup will not be in fault.
  2926. //
  2927. RtlZeroMemory( DeltaArray->Deltas,
  2928. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  2929. //
  2930. // If this is the first call or an explicit restart call,
  2931. // allocate and initialize the sync context.
  2932. //
  2933. if ( *SyncContext == 0 || RestartState != NormalState ) {
  2934. //
  2935. // If there already is a sync context,
  2936. // delete it.
  2937. //
  2938. if ( ServerSession->SsSync != NULL ) {
  2939. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  2940. } else {
  2941. ServerSession->SsSync = NetpMemoryAllocate( sizeof(SYNC_CONTEXT) );
  2942. if ( ServerSession->SsSync == NULL ) {
  2943. Status = STATUS_NO_MEMORY;
  2944. goto Cleanup;
  2945. }
  2946. }
  2947. //
  2948. // Initialize all the fields in the newly allocated resume handle
  2949. // to indicate that SAM has never yet been called.
  2950. //
  2951. INIT_SYNC_CONTEXT( ServerSession->SsSync, SamDBContextType );
  2952. SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
  2953. SamDBContext->SyncSerial = 1;
  2954. //
  2955. // Compute the continuation state based on the input parameters
  2956. //
  2957. switch ( RestartState ) {
  2958. case NormalState:
  2959. //
  2960. // Put the description of the Domain at the front of the buffer for the
  2961. // first call.
  2962. //
  2963. Status = NlPackSamDomain( &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  2964. DBInfo,
  2965. &BufferSize );
  2966. (DeltaArray->CountReturned)++;
  2967. BufferConsumed += BufferSize;
  2968. if ( !NT_SUCCESS(Status) ) {
  2969. goto Cleanup;
  2970. }
  2971. SamDBContext->SyncState = GroupState;
  2972. SamDBContext->SamEnumHandle = 0;
  2973. break;
  2974. case AliasState:
  2975. case AliasMemberState:
  2976. if ( *SyncContext != 0 ) {
  2977. NlPrint(( NL_CRITICAL,
  2978. "NlSyncSamDatabase: Cannot restart alias enumeration.\n" ));
  2979. Status = STATUS_INVALID_PARAMETER;
  2980. goto Cleanup;
  2981. }
  2982. /* Drop Through */
  2983. case GroupState:
  2984. case UserState:
  2985. case GroupMemberState:
  2986. SamDBContext->SyncState = RestartState;
  2987. SamDBContext->SamEnumHandle = *SyncContext;
  2988. break;
  2989. default:
  2990. NlPrint(( NL_CRITICAL,
  2991. "NlSyncSamDatabase: Invalid RestartState passed %ld.\n",
  2992. RestartState ));
  2993. Status = STATUS_INVALID_PARAMETER;
  2994. goto Cleanup;
  2995. }
  2996. } else {
  2997. // NlAssert( ServerSession->SsSync != NULL);
  2998. if( ServerSession->SsSync == NULL) {
  2999. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3000. goto Cleanup;
  3001. }
  3002. NlAssert( ServerSession->SsSync->DBContextType ==
  3003. SamDBContextType);
  3004. if( ServerSession->SsSync->DBContextType !=
  3005. SamDBContextType ) {
  3006. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3007. goto Cleanup;
  3008. }
  3009. SamDBContext = &(ServerSession->SsSync->DBContext.Sam);
  3010. NlAssert( SamDBContext->SyncSerial == *SyncContext );
  3011. if( SamDBContext->SyncSerial != *SyncContext ) {
  3012. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3013. goto Cleanup;
  3014. }
  3015. SamDBContext->SyncSerial++;
  3016. }
  3017. //
  3018. // Loop for each entry placed in the output buffer
  3019. //
  3020. // Each iteration of the loop below puts one more entry into the array
  3021. // returned to the caller. The algorithm is split into 2 parts. The
  3022. // first part checks to see if we need to retrieve more information from
  3023. // SAM and gets the description of several users or group from SAM in a
  3024. // single call. The second part puts a single entry into the buffer
  3025. // returned to the caller.
  3026. //
  3027. while ( SamDBContext->SyncState != SamDoneState ) {
  3028. //
  3029. // If we've filled out pre-allocated array,
  3030. // return now.
  3031. //
  3032. if ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG > MAX_DELTA_COUNT ) {
  3033. Status = STATUS_MORE_ENTRIES;
  3034. goto Cleanup;
  3035. }
  3036. //
  3037. // Get more information from SAM
  3038. //
  3039. // Handle when we've not yet called SAM or we've already consumed
  3040. // all of the information returned on a previous call to SAM.
  3041. //
  3042. // This is a 'while' rather than an 'if' to handle the case
  3043. // where SAM returns zero entries.
  3044. //
  3045. while ( SamDBContext->Index >= SamDBContext->Count ) {
  3046. //
  3047. // Free any previous buffer returned from SAM.
  3048. //
  3049. if ( ServerSession->SsSync != NULL ) {
  3050. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  3051. }
  3052. //
  3053. // If we've already gotten everything from SAM,
  3054. // we've finished all of the groups,
  3055. //
  3056. // If we've just done the groups,
  3057. // go on to do the users.
  3058. //
  3059. // If we've just done the users,
  3060. // go on to do the group memberships.
  3061. //
  3062. // If we've just done the group memberships,
  3063. // go on to do the alias.
  3064. //
  3065. // If we've just done the alias,
  3066. // go on to do the alias membership.
  3067. //
  3068. // If we've just done the alias memberships,
  3069. // we're all done.
  3070. //
  3071. if ( SamDBContext->SamAllDone ) {
  3072. SamDBContext->SamEnumHandle = 0;
  3073. SamDBContext->Index = 0;
  3074. SamDBContext->Count = 0;
  3075. SamDBContext->SamAllDone = FALSE;
  3076. if (SamDBContext->SyncState == GroupState ) {
  3077. NlPrint((NL_SYNC,
  3078. "NlSyncSamDatabase: packing user records.\n"));
  3079. SamDBContext->SyncState = UserState;
  3080. } else if (SamDBContext->SyncState == UserState ) {
  3081. NlPrint((NL_SYNC,
  3082. "NlSyncSamDatabase: "
  3083. "packing groupmember records.\n"));
  3084. SamDBContext->SyncState = GroupMemberState;
  3085. } else if (SamDBContext->SyncState == GroupMemberState ){
  3086. NlPrint((NL_SYNC,
  3087. "NlSyncSamDatabase: packing alias records.\n"));
  3088. SamDBContext->SyncState = AliasState;
  3089. } else if (SamDBContext->SyncState == AliasState ){
  3090. NlPrint((NL_SYNC,
  3091. "NlSyncSamDatabase: "
  3092. " packing aliasmember records.\n"));
  3093. SamDBContext->SyncState = AliasMemberState ;
  3094. } else if (SamDBContext->SyncState == AliasMemberState ){
  3095. NlPrint((NL_SYNC,
  3096. "NlSyncSamDatabase: packing done.\n"));
  3097. SamDBContext->SyncState = SamDoneState;
  3098. Status = STATUS_SUCCESS;
  3099. }
  3100. break;
  3101. }
  3102. //
  3103. // Do the actual enumeration
  3104. //
  3105. if (SamDBContext->SyncState == GroupState ||
  3106. SamDBContext->SyncState == GroupMemberState ) {
  3107. Status = SamIEnumerateAccountRids(
  3108. DBInfo->DBHandle,
  3109. SAM_GLOBAL_GROUP_ACCOUNT,
  3110. SamDBContext->SamEnumHandle, // Return RIDs greater than this
  3111. SAM_SYNC_PREF_MAX,
  3112. &SamDBContext->Count,
  3113. &SamDBContext->RidArray );
  3114. if ( !NT_SUCCESS( Status ) ) {
  3115. SamDBContext->RidArray = NULL;
  3116. goto Cleanup;
  3117. }
  3118. if ( SamDBContext->Count != 0 ) {
  3119. SamDBContext->SamEnumHandle =
  3120. SamDBContext->RidArray[SamDBContext->Count-1];
  3121. }
  3122. } else if (SamDBContext->SyncState == UserState ) {
  3123. Status = SamIEnumerateAccountRids(
  3124. DBInfo->DBHandle,
  3125. SAM_USER_ACCOUNT,
  3126. SamDBContext->SamEnumHandle, // Return RIDs greater than this
  3127. SAM_SYNC_PREF_MAX,
  3128. &SamDBContext->Count,
  3129. &SamDBContext->RidArray );
  3130. if ( !NT_SUCCESS( Status ) ) {
  3131. SamDBContext->RidArray = NULL;
  3132. goto Cleanup;
  3133. }
  3134. if ( SamDBContext->Count != 0 ) {
  3135. SamDBContext->SamEnumHandle =
  3136. SamDBContext->RidArray[SamDBContext->Count-1];
  3137. }
  3138. } else if (SamDBContext->SyncState == AliasState ||
  3139. SamDBContext->SyncState == AliasMemberState ) {
  3140. Status = SamrEnumerateAliasesInDomain(
  3141. DBInfo->DBHandle,
  3142. &SamDBContext->SamEnumHandle,
  3143. &SamDBContext->SamEnum,
  3144. SAM_SYNC_PREF_MAX,
  3145. &SamDBContext->Count );
  3146. if ( !NT_SUCCESS( Status ) ) {
  3147. SamDBContext->SamEnum = NULL;
  3148. goto Cleanup;
  3149. }
  3150. NlAssert( SamDBContext->Count ==
  3151. SamDBContext->SamEnum->EntriesRead );
  3152. }
  3153. //
  3154. // If SAM says there is more information,
  3155. // just ensure he returned something to us on this call.
  3156. //
  3157. if ( Status == STATUS_MORE_ENTRIES ) {
  3158. // NlAssert( SamDBContext->Count != 0 );
  3159. //
  3160. // If SAM says he's returned all of the information,
  3161. // remember not to ask SAM for more.
  3162. //
  3163. } else {
  3164. SamDBContext->SamAllDone = TRUE;
  3165. }
  3166. SamDBContext->Index = 0;
  3167. }
  3168. //
  3169. // Place this entry into the return buffer.
  3170. //
  3171. if ( SamDBContext->Count > 0 ) {
  3172. if (SamDBContext->SyncState == GroupState ) {
  3173. Status = NlPackSamGroup(
  3174. SamDBContext->RidArray[SamDBContext->Index],
  3175. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3176. DBInfo,
  3177. &BufferSize );
  3178. } else if (SamDBContext->SyncState == UserState ) {
  3179. Status = NlPackSamUser(
  3180. SamDBContext->RidArray[SamDBContext->Index],
  3181. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3182. DBInfo,
  3183. &BufferSize,
  3184. SessionInfo );
  3185. } else if (SamDBContext->SyncState == GroupMemberState ) {
  3186. Status = NlPackSamGroupMember(
  3187. SamDBContext->RidArray[SamDBContext->Index],
  3188. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3189. DBInfo,
  3190. &BufferSize );
  3191. } else if (SamDBContext->SyncState == AliasState ) {
  3192. Status = NlPackSamAlias(
  3193. SamDBContext->SamEnum->
  3194. Buffer[SamDBContext->Index].RelativeId,
  3195. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3196. DBInfo,
  3197. &BufferSize );
  3198. } else if (SamDBContext->SyncState == AliasMemberState ) {
  3199. Status = NlPackSamAliasMember(
  3200. SamDBContext->SamEnum->
  3201. Buffer[SamDBContext->Index].RelativeId,
  3202. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3203. DBInfo,
  3204. &BufferSize );
  3205. }
  3206. //
  3207. // If there was a real error or this group didn't fit,
  3208. // return to the caller.
  3209. //
  3210. if ( Status != STATUS_SUCCESS ) {
  3211. goto Cleanup;
  3212. }
  3213. SamDBContext->Index ++;
  3214. (DeltaArray->CountReturned)++;
  3215. BufferConsumed +=
  3216. (sizeof(NETLOGON_DELTA_ENUM) + BufferSize);
  3217. if( BufferConsumed >= PreferredMaximumLength) {
  3218. Status = STATUS_MORE_ENTRIES;
  3219. goto Cleanup;
  3220. }
  3221. //
  3222. // If we're debugging replication, return only one change to the caller.
  3223. //
  3224. #if NETLOGONDBG
  3225. if ( NlGlobalParameters.DbFlag & NL_ONECHANGE_REPL ) {
  3226. Status = STATUS_MORE_ENTRIES;
  3227. goto Cleanup;
  3228. }
  3229. #endif // NETLOGONDBG
  3230. //
  3231. // if the service is going down, stop packing records and
  3232. // return to the caller.
  3233. //
  3234. // Don't alarm the caller with the status code. He'll find out
  3235. // on the next call that we're no longer here.
  3236. //
  3237. if( NlGlobalTerminate ) {
  3238. NlPrint((NL_CRITICAL, "NetrDatabaseSync is asked to return "
  3239. "when the service is going down.\n"));
  3240. Status = STATUS_MORE_ENTRIES;
  3241. goto Cleanup;
  3242. }
  3243. }
  3244. }
  3245. Cleanup:
  3246. //
  3247. // Set the return parameters to the proper values.
  3248. //
  3249. if ( NT_SUCCESS( Status ) ) {
  3250. *SyncContext = SamDBContext->SyncSerial;
  3251. } else {
  3252. if ( DeltaArray->Deltas != NULL ) {
  3253. NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
  3254. DeltaArray->Deltas = NULL;
  3255. }
  3256. DeltaArray->CountReturned = 0;
  3257. *SyncContext = 0;
  3258. NlPrint((NL_CRITICAL,
  3259. "NlSyncSamDatabase: returning unsuccessful (%lx).\n",
  3260. Status));
  3261. }
  3262. return Status;
  3263. }
  3264. NTSTATUS
  3265. NlSyncLsaDatabase(
  3266. IN PSERVER_SESSION ServerSession,
  3267. IN OUT PULONG SyncContext,
  3268. IN OUT PNETLOGON_DELTA_ENUM_ARRAY DeltaArray,
  3269. IN DWORD PreferredMaximumLength,
  3270. IN PSESSION_INFO SessionInfo
  3271. )
  3272. /*++
  3273. Routine Description:
  3274. This function is a real worker for the NetrDatabaseSync function and
  3275. retrieves the LSA database in the delta buffer.
  3276. This function uses the find-first find-next model to return portions
  3277. of the SAM database at a time. The SAM database is returned as a
  3278. list of deltas like those returned from I_NetDatabaseDeltas. The
  3279. following deltas are returned for each domain:
  3280. * One AddOrChangeLsaPolicy delta, followed by,
  3281. * One AddOrChangeLsaAccounts delta for each lsa account, followed by,
  3282. * One AddOrChangeLsaTDomain delta for each trusted domain, followed by,
  3283. * One AddOrChangeLsaSecret delta for each lsa secret.
  3284. Arguments:
  3285. ServerSession -- pointer to connection context.
  3286. SyncContext -- Specifies context needed to continue the
  3287. operation. The caller should treat this as an opaque
  3288. value. The value should be zero before the first call.
  3289. DeltaArray -- Pointer to a buffer where the information
  3290. is placed. The information returned is an array of
  3291. NETLOGON_DELTA_ENUM structures.
  3292. PreferredMaximumLength - Preferred maximum length of returned
  3293. data (in 8-bit bytes). This is not a hard upper limit, but
  3294. serves as a guide to the server. Due to data conversion
  3295. between systems with different natural data sizes, the actual
  3296. amount of data returned may be greater than this value.
  3297. SessionInfo - Information shared between PDC and BDC.
  3298. Return Value:
  3299. STATUS_SUCCESS -- The function completed successfully.
  3300. STATUS_MORE_ENTRIES -- The replicant should call again to get more
  3301. data.
  3302. --*/
  3303. {
  3304. NTSTATUS Status;
  3305. PLSA_SYNC_CONTEXT LsaDBContext;
  3306. PDB_INFO DBInfo;
  3307. DWORD BufferConsumed = 0;
  3308. DWORD BufferSize;
  3309. BOOL IgnoreDeltaObject = FALSE;
  3310. DBInfo = &NlGlobalDBInfoArray[LSA_DB];
  3311. //
  3312. // Allocate memory for delta buffer.
  3313. //
  3314. DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
  3315. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  3316. if( DeltaArray->Deltas == NULL ) {
  3317. NlPrint((NL_CRITICAL,
  3318. "NlSyncLsaDatabase: Can't allocate %d bytes\n",
  3319. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) ));
  3320. return( STATUS_NO_MEMORY );
  3321. }
  3322. //
  3323. // wipe off the buffer so that cleanup will not be in fault.
  3324. //
  3325. RtlZeroMemory( DeltaArray->Deltas,
  3326. MAX_DELTA_COUNT * sizeof(NETLOGON_DELTA_ENUM) );
  3327. //
  3328. // If this is the first call, allocate and initialize the sync context.
  3329. //
  3330. if ( *SyncContext == 0 ) {
  3331. //
  3332. // If there already is a sync context,
  3333. // delete it.
  3334. //
  3335. if ( ServerSession->SsSync != NULL ) {
  3336. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  3337. } else {
  3338. ServerSession->SsSync = NetpMemoryAllocate( sizeof(SYNC_CONTEXT) );
  3339. if ( ServerSession->SsSync == NULL ) {
  3340. Status = STATUS_NO_MEMORY;
  3341. goto Cleanup;
  3342. }
  3343. }
  3344. //
  3345. // Initialize all the fields in the newly allocated resume handle
  3346. // to indicate that SAM has never yet been called.
  3347. //
  3348. INIT_SYNC_CONTEXT( ServerSession->SsSync, LsaDBContextType );
  3349. LsaDBContext = &(ServerSession->SsSync->DBContext.Lsa);
  3350. LsaDBContext->SyncState = AccountState;
  3351. LsaDBContext->SyncSerial = 1;
  3352. LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
  3353. NlPrint((NL_SYNC,
  3354. "NlSyncLsaDatabase: "
  3355. "Starting full sync, packing lsa account records\n"));
  3356. //
  3357. // Put the description of the Policy at the front of the buffer for the
  3358. // first call.
  3359. //
  3360. Status = NlPackLsaPolicy(
  3361. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3362. DBInfo,
  3363. &BufferSize );
  3364. (DeltaArray->CountReturned)++;
  3365. BufferConsumed += BufferSize;
  3366. if ( !NT_SUCCESS(Status) ) {
  3367. goto Cleanup;
  3368. }
  3369. } else {
  3370. if( ServerSession->SsSync == NULL ) {
  3371. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3372. goto Cleanup;
  3373. }
  3374. NlAssert( ServerSession->SsSync->DBContextType == LsaDBContextType);
  3375. if( ServerSession->SsSync->DBContextType != LsaDBContextType) {
  3376. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3377. goto Cleanup;
  3378. }
  3379. LsaDBContext = &(ServerSession->SsSync->DBContext.Lsa);
  3380. NlAssert( LsaDBContext->SyncSerial == *SyncContext );
  3381. if( LsaDBContext->SyncSerial != *SyncContext ) {
  3382. Status = STATUS_SYNCHRONIZATION_REQUIRED;
  3383. goto Cleanup;
  3384. }
  3385. LsaDBContext->SyncSerial++;
  3386. }
  3387. //
  3388. // Loop for each entry placed in the output buffer
  3389. //
  3390. // Each iteration of the loop below puts one more entry into the array
  3391. // returned to the caller. The algorithm is split into 2 parts.
  3392. // The first part checks to see if we need to retrieve more information
  3393. // from LSA and gets the description of several accounts, TDomain or
  3394. // Secret from LSA in a single call. The second part puts a single
  3395. // entry into the buffer returned to the caller.
  3396. //
  3397. while ( LsaDBContext->SyncState != LsaDoneState ) {
  3398. //
  3399. // If we've filled out pre-allocated array,
  3400. // return now.
  3401. //
  3402. if ( DeltaArray->CountReturned + MAX_DELTAS_PER_CHANGELOG > MAX_DELTA_COUNT ) {
  3403. Status = STATUS_MORE_ENTRIES;
  3404. goto Cleanup;
  3405. }
  3406. //
  3407. // Get more information from LSA
  3408. //
  3409. // Handle when we've not yet called LSA or we've already consumed
  3410. // all of the information returned on a previous call to SAM.
  3411. //
  3412. // This is a 'while' rather than an 'if' to handle the case
  3413. // where LSA returns zero entries.
  3414. //
  3415. while ( LsaDBContext->Index >= LsaDBContext->Count ) {
  3416. //
  3417. // Free any previous buffer returned from SAM.
  3418. //
  3419. if ( ServerSession->SsSync != NULL ) {
  3420. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  3421. }
  3422. //
  3423. // If we've already gotten everything from LSA,
  3424. // we've finished all of the accounts,
  3425. //
  3426. // If we've just done the accounts,
  3427. // go on to do the TDomains.
  3428. //
  3429. // If we've just done the TDomains,
  3430. // go on to do the Secrets
  3431. //
  3432. // If we've just done the Secret,
  3433. // we're all done.
  3434. //
  3435. if ( LsaDBContext->LsaAllDone ) {
  3436. LsaDBContext->LsaEnumHandle = 0;
  3437. LsaDBContext->Index = 0;
  3438. LsaDBContext->Count = 0;
  3439. LsaDBContext->LsaAllDone = FALSE;
  3440. if (LsaDBContext->SyncState == AccountState ) {
  3441. NlPrint((NL_SYNC,
  3442. "NlSyncLsaDatabase: "
  3443. " packing TDomain records.\n"));
  3444. LsaDBContext->SyncState = TDomainState;
  3445. } else if (LsaDBContext->SyncState == TDomainState ) {
  3446. NlPrint((NL_SYNC,
  3447. "NlSyncLsaDatabase: packing secret records.\n"));
  3448. LsaDBContext->SyncState = SecretState;
  3449. } else if (LsaDBContext->SyncState == SecretState ) {
  3450. NlPrint((NL_SYNC,
  3451. "NlSyncLsaDatabase: packing done.\n"));
  3452. LsaDBContext->SyncState = LsaDoneState;
  3453. LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
  3454. Status = STATUS_SUCCESS;
  3455. }
  3456. break;
  3457. }
  3458. if (LsaDBContext->SyncState == AccountState ) {
  3459. LsaDBContext->LsaEnumBufferType = AccountEnumBuffer;
  3460. Status = LsarEnumerateAccounts(
  3461. DBInfo->DBHandle,
  3462. &LsaDBContext->LsaEnumHandle,
  3463. &LsaDBContext->LsaEnum.Account,
  3464. SAM_SYNC_PREF_MAX);
  3465. if (Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
  3466. LsaDBContext->Count =
  3467. LsaDBContext->LsaEnum.Account.EntriesRead;
  3468. }
  3469. } else if (LsaDBContext->SyncState == TDomainState ) {
  3470. LsaDBContext->LsaEnumBufferType = TDomainEnumBuffer;
  3471. Status = LsarEnumerateTrustedDomains(
  3472. DBInfo->DBHandle,
  3473. &LsaDBContext->LsaEnumHandle,
  3474. &LsaDBContext->LsaEnum.TDomain,
  3475. SAM_SYNC_PREF_MAX);
  3476. if (Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
  3477. LsaDBContext->Count =
  3478. LsaDBContext->LsaEnum.TDomain.EntriesRead;
  3479. }
  3480. } else if (LsaDBContext->SyncState == SecretState ) {
  3481. LsaDBContext->LsaEnumBufferType = SecretEnumBuffer;
  3482. Status = LsaIEnumerateSecrets(
  3483. DBInfo->DBHandle,
  3484. &LsaDBContext->LsaEnumHandle,
  3485. &LsaDBContext->LsaEnum.Secret,
  3486. SAM_SYNC_PREF_MAX,
  3487. &LsaDBContext->Count );
  3488. }
  3489. //
  3490. // If LSA says there is more information,
  3491. // just ensure he returned something to us on this call.
  3492. //
  3493. if ( Status == STATUS_SUCCESS || Status == STATUS_MORE_ENTRIES ) {
  3494. NlAssert( LsaDBContext->Count != 0 );
  3495. //
  3496. // If LSA says he's returned all of the information,
  3497. // remember not to ask it for more.
  3498. //
  3499. } else if ( Status == STATUS_NO_MORE_ENTRIES ) {
  3500. LsaDBContext->LsaAllDone = TRUE;
  3501. LsaDBContext->Count = 0;
  3502. //
  3503. // Any other error is fatal
  3504. //
  3505. } else {
  3506. LsaDBContext->LsaEnumBufferType = EmptyEnumBuffer;
  3507. LsaDBContext->Count = 0;
  3508. goto Cleanup;
  3509. }
  3510. LsaDBContext->Index = 0;
  3511. }
  3512. //
  3513. // Place this entry into the return buffer.
  3514. //
  3515. if ( LsaDBContext->Count > 0 ) {
  3516. if (LsaDBContext->SyncState == AccountState ) {
  3517. Status = NlPackLsaAccount(
  3518. LsaDBContext->LsaEnum.Account.
  3519. Information[LsaDBContext->Index].Sid,
  3520. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3521. DBInfo,
  3522. &BufferSize,
  3523. SessionInfo );
  3524. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  3525. Status = STATUS_SUCCESS;
  3526. IgnoreDeltaObject = TRUE;
  3527. BufferSize = 0;
  3528. }
  3529. } else if (LsaDBContext->SyncState == TDomainState ) {
  3530. Status = NlPackLsaTDomain(
  3531. LsaDBContext->LsaEnum.TDomain.
  3532. Information[LsaDBContext->Index].Sid,
  3533. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3534. DBInfo,
  3535. &BufferSize );
  3536. } else if (LsaDBContext->SyncState == SecretState ) {
  3537. PUNICODE_STRING SecretName;
  3538. SecretName =
  3539. &((PUNICODE_STRING)LsaDBContext->LsaEnum.Secret)
  3540. [LsaDBContext->Index];
  3541. //
  3542. // ignore local secret objects.
  3543. //
  3544. if( (SecretName->Length / sizeof(WCHAR) >
  3545. LSA_GLOBAL_SECRET_PREFIX_LENGTH ) &&
  3546. (_wcsnicmp( SecretName->Buffer,
  3547. LSA_GLOBAL_SECRET_PREFIX,
  3548. LSA_GLOBAL_SECRET_PREFIX_LENGTH ) == 0)) {
  3549. Status = NlPackLsaSecret(
  3550. SecretName,
  3551. &((DeltaArray->Deltas)[DeltaArray->CountReturned]),
  3552. DBInfo,
  3553. &BufferSize,
  3554. SessionInfo );
  3555. } else {
  3556. Status = STATUS_SUCCESS;
  3557. IgnoreDeltaObject = TRUE;
  3558. BufferSize = 0;
  3559. }
  3560. }
  3561. //
  3562. // If there was a real error or this group didn't fit,
  3563. // return to the caller.
  3564. //
  3565. if ( Status != STATUS_SUCCESS ) {
  3566. goto Cleanup;
  3567. }
  3568. LsaDBContext->Index ++;
  3569. //
  3570. // if this object is ignored, don't modify return values.
  3571. //
  3572. if ( !IgnoreDeltaObject ) {
  3573. (DeltaArray->CountReturned)++;
  3574. BufferConsumed +=
  3575. (sizeof(NETLOGON_DELTA_ENUM) + BufferSize);
  3576. if( BufferConsumed >= PreferredMaximumLength) {
  3577. Status = STATUS_MORE_ENTRIES;
  3578. goto Cleanup;
  3579. }
  3580. //
  3581. // If we're debugging replication, return only one change to the caller.
  3582. //
  3583. #if NETLOGONDBG
  3584. if ( NlGlobalParameters.DbFlag & NL_ONECHANGE_REPL ) {
  3585. Status = STATUS_MORE_ENTRIES;
  3586. goto Cleanup;
  3587. }
  3588. #endif // NETLOGONDBG
  3589. } else {
  3590. IgnoreDeltaObject = FALSE;
  3591. }
  3592. }
  3593. }
  3594. Cleanup:
  3595. //
  3596. // Set the return parameters to the proper values.
  3597. //
  3598. if ( NT_SUCCESS( Status ) ) {
  3599. *SyncContext = LsaDBContext->SyncSerial;
  3600. } else {
  3601. if ( DeltaArray->Deltas != NULL ) {
  3602. NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
  3603. DeltaArray->Deltas = NULL;
  3604. }
  3605. DeltaArray->CountReturned = 0;
  3606. *SyncContext = 0;
  3607. }
  3608. if (!NT_SUCCESS(Status)) {
  3609. NlPrint((NL_CRITICAL,
  3610. "NlSyncLsaDatabase: returning unsuccessful (%lx).\n",
  3611. Status));
  3612. }
  3613. return Status;
  3614. }
  3615. NTSTATUS
  3616. NetrDatabaseSync (
  3617. IN LPWSTR PrimaryName,
  3618. IN LPWSTR ComputerName,
  3619. IN PNETLOGON_AUTHENTICATOR Authenticator,
  3620. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  3621. IN DWORD DatabaseID,
  3622. IN OUT PULONG SyncContext,
  3623. OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
  3624. IN DWORD PreferredMaximumLength
  3625. )
  3626. /*++
  3627. Routine Description:
  3628. NT 3.1 version of NetrDatabaseSync2. Don't pass the RestartState parameter.
  3629. Sync Context is all that is needed to identify the state.
  3630. Arguments:
  3631. Same as NetrDatabaseSync2 (with the exception mentioned above).
  3632. Return Value:
  3633. Save as NetrDatabaseSync2.
  3634. --*/
  3635. {
  3636. return NetrDatabaseSync2(
  3637. PrimaryName,
  3638. ComputerName,
  3639. Authenticator,
  3640. ReturnAuthenticator,
  3641. DatabaseID,
  3642. NormalState,
  3643. SyncContext,
  3644. DeltaArrayRet,
  3645. PreferredMaximumLength );
  3646. }
  3647. NTSTATUS
  3648. NetrDatabaseSync2 (
  3649. IN LPWSTR PrimaryName,
  3650. IN LPWSTR ComputerName,
  3651. IN PNETLOGON_AUTHENTICATOR Authenticator,
  3652. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  3653. IN DWORD DatabaseID,
  3654. IN SYNC_STATE RestartState,
  3655. IN OUT PULONG SyncContext,
  3656. OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet,
  3657. IN DWORD PreferredMaximumLength
  3658. )
  3659. /*++
  3660. Routine Description:
  3661. This function is used by an NT BDC to request
  3662. the entire SAM/LSA database from a PDC in NTLANMAN-style format.
  3663. This function can only be called by a server which has previously
  3664. authenticated with the PDC by calling I_NetServerAuthenticate. This
  3665. function uses RPC to contact the Netlogon service on the PDC.
  3666. Arguments:
  3667. PrimaryName -- Name of the PDC to retrieve the deltas from.
  3668. ComputerName -- Name of the BDC or member server making the call.
  3669. Authenticator -- supplied by the server.
  3670. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  3671. DatabaseID -- Identifies the databse for which the deltas are requested.
  3672. For SAM database the ID is 0, for Builtin Domain the ID is 1. Other
  3673. databases may be defined later.
  3674. RestartState -- Specifies whether this is a restart of the full sync and how
  3675. to interpret SyncContext. This value should be NormalState unless this
  3676. is the restart of a full sync.
  3677. However, if the caller is continuing a full sync after a reboot,
  3678. the following values are used:
  3679. GroupState - SyncContext is the global group rid to continue with.
  3680. UserState - SyncContext is the user rid to continue with
  3681. GroupMemberState - SyncContext is the global group rid to continue with
  3682. AliasState - SyncContext should be zero to restart at first alias
  3683. AliasMemberState - SyncContext should be zero to restart at first alias
  3684. One cannot continue the LSA database in this way.
  3685. SyncContext -- Specifies context needed to continue the
  3686. operation. The caller should treat this as an opaque
  3687. value. The value should be zero before the first call.
  3688. DeltaArray -- Receives a pointer to a buffer where the information
  3689. is placed. The information returned is an array of
  3690. NETLOGON_DELTA_ENUM structures.
  3691. PreferredMaximumLength - Preferred maximum length of returned
  3692. data (in 8-bit bytes). This is not a hard upper limit, but
  3693. serves as a guide to the server. Due to data conversion
  3694. between systems with different natural data sizes, the actual
  3695. amount of data returned may be greater than this value.
  3696. Return Value:
  3697. STATUS_SUCCESS -- The function completed successfully.
  3698. STATUS_MORE_ENTRIES -- The replicant should call again to get more
  3699. data.
  3700. STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
  3701. the PDC.
  3702. --*/
  3703. {
  3704. NTSTATUS Status;
  3705. PDOMAIN_INFO DomainInfo = NULL;
  3706. PSERVER_SESSION ServerSession = NULL;
  3707. PNETLOGON_DELTA_ENUM_ARRAY DeltaArray;
  3708. SESSION_INFO SessionInfo;
  3709. PDB_INFO DBInfo;
  3710. DEFSSIAPITIMER;
  3711. INITSSIAPITIMER;
  3712. STARTSSIAPITIMER;
  3713. //
  3714. // This API is not supported on workstations.
  3715. //
  3716. if ( NlGlobalMemberWorkstation || !NlGlobalPdcDoReplication ) {
  3717. NlPrint((NL_CRITICAL,
  3718. "NetrDatabaseSync2: called from %ws. This machine doesn't support replication.\n",
  3719. ComputerName ));
  3720. return STATUS_NOT_SUPPORTED;
  3721. }
  3722. //
  3723. // Gross hack because of RPC implementation.
  3724. //
  3725. // Rpc executes API calls in an I/O completion port thread. If this thread
  3726. // goes CPU bound, then no other RPC will be allowed to start. Even worse,
  3727. // there is only one outstanding listen, so the 'second' coming RPC call
  3728. // gets RPC_S_SERVER_TOO_BUSY.
  3729. //
  3730. // By sleeping here (even for a short period) the I/O completion port releases
  3731. // another thread since it thinks this thread went I/O bound.
  3732. //
  3733. // We've seen this thread go CPU bound doing a full sync of a database with
  3734. // 1000's of LSA account objects.
  3735. //
  3736. RpcServerYield();
  3737. //
  3738. // If the DS is recovering from a backup,
  3739. // avoid changing the DS.
  3740. //
  3741. if ( NlGlobalDsPaused ) {
  3742. NlPrint((NL_CRITICAL,
  3743. "NetrDatabaseSync2: DsIsPaused.\n"));
  3744. // Don't return a new status code since NT 4 DC would do a full sync
  3745. return STATUS_ACCESS_DENIED;
  3746. }
  3747. if ( DatabaseID >= NUM_DBS ) {
  3748. return STATUS_INVALID_LEVEL;
  3749. }
  3750. DBInfo = &NlGlobalDBInfoArray[DatabaseID];
  3751. //
  3752. // Initialization
  3753. //
  3754. *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
  3755. MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
  3756. if( DeltaArray == NULL ) {
  3757. return(STATUS_NO_MEMORY);
  3758. }
  3759. DeltaArray->Deltas = NULL;
  3760. DeltaArray->CountReturned = 0;
  3761. //
  3762. // Lookup which domain this call pertains to.
  3763. //
  3764. DomainInfo = NlFindDomainByServerName( PrimaryName );
  3765. NlPrintDom((NL_SYNC, DomainInfo,
  3766. "NetrDatabaseSync: " FORMAT_LPWSTR " full sync called by " FORMAT_LPWSTR " State: %ld Context: 0x%lx.\n",
  3767. DBInfo->DBName,
  3768. ComputerName,
  3769. RestartState,
  3770. *SyncContext ));
  3771. if ( DomainInfo == NULL ) {
  3772. Status = STATUS_INVALID_COMPUTER_NAME;
  3773. goto Cleanup;
  3774. }
  3775. if ( !IsPrimaryDomain( DomainInfo )) {
  3776. Status = STATUS_NOT_SUPPORTED;
  3777. goto Cleanup;
  3778. }
  3779. //
  3780. // Retrieve the requestor's entry to get sessionkey
  3781. //
  3782. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  3783. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  3784. if (ServerSession == NULL) {
  3785. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3786. Status = STATUS_ACCESS_DENIED;
  3787. // Don't log this event since it happens in nature after a reboot
  3788. // or after we scavenge the server session.
  3789. NlPrint((NL_CRITICAL,
  3790. "NetrDatabaseSync: No server session.\n"));
  3791. goto CleanupNoEventlog;
  3792. }
  3793. //
  3794. // Allow this call only on ServerSecureChannel.
  3795. //
  3796. if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  3797. //
  3798. // If the only preblem is that this BDC hasn't authenticated,
  3799. // silently ask it to authenticate.
  3800. //
  3801. if ( ServerSession->SsSecureChannelType == NullSecureChannel ) {
  3802. NlPrint((NL_CRITICAL,
  3803. "NetrDatabaseSync: No authenticated server session.\n"));
  3804. ServerSession = NULL;
  3805. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3806. // Don't log this event since it happens in nature after a reboot
  3807. // or after we scavenge the server session.
  3808. Status = STATUS_ACCESS_DENIED;
  3809. goto CleanupNoEventlog;
  3810. } else {
  3811. NlPrint((NL_CRITICAL,
  3812. "NetrDatabaseSync: SecureChannel type isn't BDC. %ld\n",
  3813. ServerSession->SsSecureChannelType ));
  3814. ServerSession = NULL;
  3815. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3816. Status = STATUS_ACCESS_DENIED;
  3817. goto Cleanup;
  3818. }
  3819. }
  3820. //
  3821. // Verify the Authenticator and update seed if OK
  3822. //
  3823. Status = NlCheckAuthenticator( ServerSession,
  3824. Authenticator,
  3825. ReturnAuthenticator);
  3826. if ( !NT_SUCCESS(Status) ) {
  3827. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3828. NlPrint((NL_CRITICAL,
  3829. "NetrDatabaseSync: authentication failed.\n" ));
  3830. ServerSession = NULL;
  3831. goto Cleanup;
  3832. }
  3833. //
  3834. // Prevent entry from being deleted, but drop the global lock.
  3835. //
  3836. // Beware of server with two concurrent calls outstanding
  3837. // (must have rebooted.)
  3838. //
  3839. if (ServerSession->SsFlags & SS_LOCKED ) {
  3840. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3841. NlPrint((NL_CRITICAL, "NetrDatabaseSync: Concurrent call detected.\n" ));
  3842. Status = STATUS_ACCESS_DENIED;
  3843. ServerSession = NULL;
  3844. goto Cleanup;
  3845. }
  3846. ServerSession->SsFlags |= SS_LOCKED;
  3847. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  3848. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  3849. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3850. if( DatabaseID == LSA_DB ) {
  3851. NlAssert( RestartState == NormalState );
  3852. Status = NlSyncLsaDatabase( ServerSession,
  3853. SyncContext,
  3854. DeltaArray,
  3855. PreferredMaximumLength,
  3856. &SessionInfo );
  3857. } else {
  3858. Status = NlSyncSamDatabase( ServerSession,
  3859. DatabaseID,
  3860. RestartState,
  3861. SyncContext,
  3862. DeltaArray,
  3863. PreferredMaximumLength,
  3864. &SessionInfo );
  3865. }
  3866. Cleanup:
  3867. //
  3868. // write event log
  3869. //
  3870. if ( !NT_SUCCESS( Status ) ) {
  3871. LPWSTR MsgStrings[2];
  3872. MsgStrings[0] = ComputerName;
  3873. MsgStrings[1] = (LPWSTR) LongToPtr( Status );
  3874. NlpWriteEventlog(
  3875. NELOG_NetlogonFullSyncCallFailed,
  3876. EVENTLOG_WARNING_TYPE,
  3877. (LPBYTE)&Status,
  3878. sizeof(Status),
  3879. MsgStrings,
  3880. 2 | NETP_LAST_MESSAGE_IS_NTSTATUS | NETP_ALLOW_DUPLICATE_EVENTS );
  3881. } else {
  3882. LPWSTR MsgStrings[2];
  3883. WCHAR CountBuffer[20]; // random size
  3884. MsgStrings[0] = ComputerName;
  3885. ultow( DeltaArray->CountReturned, CountBuffer, 10);
  3886. MsgStrings[1] = CountBuffer;
  3887. NlpWriteEventlog(
  3888. NELOG_NetlogonFullSyncCallSuccess,
  3889. EVENTLOG_INFORMATION_TYPE,
  3890. NULL,
  3891. 0,
  3892. MsgStrings,
  3893. 2 | NETP_ALLOW_DUPLICATE_EVENTS );
  3894. }
  3895. //
  3896. // Unlock the server session entry if we've locked it.
  3897. //
  3898. CleanupNoEventlog:
  3899. if ( ServerSession != NULL ) {
  3900. //
  3901. // If we're done, free up the context structure,
  3902. //
  3903. if ( Status != STATUS_MORE_ENTRIES && ServerSession->SsSync != NULL ) {
  3904. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  3905. NetpMemoryFree( ServerSession->SsSync );
  3906. ServerSession->SsSync = NULL;
  3907. }
  3908. //
  3909. // If we are successfully returning these deltas to the BDC,
  3910. // update our tables to reflect the changes.
  3911. //
  3912. if ( Status == STATUS_SUCCESS ) {
  3913. NlPrimaryAnnouncementFinish( ServerSession,
  3914. DatabaseID,
  3915. NULL );
  3916. }
  3917. NlUnlockServerSession( ServerSession );
  3918. }
  3919. //
  3920. // If the BDC called us just as SAM was shutting down,
  3921. // map the status to prevent the BDC from full syncing.
  3922. //
  3923. if ( Status == STATUS_INVALID_SERVER_STATE ) {
  3924. Status = STATUS_ACCESS_DENIED;
  3925. }
  3926. NlPrint((NL_SYNC,
  3927. "NetrDatabaseSync: " FORMAT_LPWSTR " returning (0x%lx) to " FORMAT_LPWSTR " Context: 0x%lx.\n",
  3928. DBInfo->DBName,
  3929. Status,
  3930. ComputerName,
  3931. *SyncContext ));
  3932. STOPSSIAPITIMER;
  3933. NlPrint((NL_REPL_TIME,"NetrDatabaseSync Time:\n"));
  3934. PRINTSSIAPITIMER;
  3935. if ( DomainInfo != NULL ) {
  3936. NlDereferenceDomain( DomainInfo );
  3937. }
  3938. return Status;
  3939. }
  3940. NTSTATUS
  3941. NetrDatabaseRedo(
  3942. IN LPWSTR PrimaryName,
  3943. IN LPWSTR ComputerName,
  3944. IN PNETLOGON_AUTHENTICATOR Authenticator,
  3945. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  3946. IN LPBYTE OrigChangeLogEntry,
  3947. IN DWORD ChangeLogEntrySize,
  3948. OUT PNETLOGON_DELTA_ENUM_ARRAY *DeltaArrayRet
  3949. )
  3950. /*++
  3951. Routine Description:
  3952. This function is used by a SAM BDC to request infomation about a single
  3953. account. This function can only be called by a server which has previously
  3954. authenticated with the PDC by calling I_NetServerAuthenticate. This
  3955. function uses RPC to contact the Netlogon service on the PDC.
  3956. Arguments:
  3957. PrimaryName -- Name of the PDC to retrieve the delta from.
  3958. ComputerName -- Name of the BDC making the call.
  3959. Authenticator -- supplied by the server.
  3960. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  3961. ChangeLogEntry -- A description of the account to be queried.
  3962. ChangeLogEntrySize -- Size (in bytes) of the ChangeLogEntry.
  3963. DeltaArrayRet -- Receives a pointer to a buffer where the information is
  3964. placed. The information returned is an array of
  3965. NETLOGON_DELTA_ENUM structures.
  3966. Return Value:
  3967. STATUS_SUCCESS -- The function completed successfully.
  3968. STATUS_ACCESS_DENIED -- The replicant should re-authenticate with
  3969. the PDC.
  3970. --*/
  3971. {
  3972. PCHANGELOG_ENTRY ChangeLogEntry;
  3973. NTSTATUS Status;
  3974. PDOMAIN_INFO DomainInfo = NULL;
  3975. PSERVER_SESSION ServerSession = NULL;
  3976. LPWSTR MsgStrings[2];
  3977. DWORD BufferSize;
  3978. PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL;
  3979. SESSION_INFO SessionInfo;
  3980. DEFSSIAPITIMER;
  3981. INITSSIAPITIMER;
  3982. STARTSSIAPITIMER;
  3983. //
  3984. // This API is not supported on workstations.
  3985. //
  3986. if ( NlGlobalMemberWorkstation || !NlGlobalPdcDoReplication ) {
  3987. NlPrint((NL_CRITICAL,
  3988. "NetrDatabaseRedo: called from %ws. This machine doesn't support replication.\n",
  3989. ComputerName ));
  3990. return STATUS_NOT_SUPPORTED;
  3991. }
  3992. //
  3993. // If the DS is recovering from a backup,
  3994. // avoid changing the DS.
  3995. //
  3996. if ( NlGlobalDsPaused ) {
  3997. NlPrint((NL_CRITICAL,
  3998. "NetrDatabaseRedo: DsIsPaused.\n"));
  3999. // Don't return a new status code since NT 4 DC would do a full sync
  4000. return STATUS_ACCESS_DENIED;
  4001. }
  4002. //
  4003. // Initialization
  4004. //
  4005. ChangeLogEntry = (PCHANGELOG_ENTRY) OrigChangeLogEntry;
  4006. if ( !NlValidateChangeLogEntry( ChangeLogEntry, ChangeLogEntrySize ) ||
  4007. ChangeLogEntry->DBIndex >= NUM_DBS ) {
  4008. Status = STATUS_INVALID_PARAMETER;
  4009. goto Cleanup;
  4010. }
  4011. //
  4012. // Find the domain this API was made to.
  4013. //
  4014. DomainInfo = NlFindDomainByServerName( PrimaryName );
  4015. NlPrintDom((NL_SYNC, DomainInfo,
  4016. "NetrDatabaseRedo: " FORMAT_LPWSTR " redo sync called by " FORMAT_LPWSTR
  4017. " with this change log entry:\n",
  4018. NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
  4019. ComputerName ));
  4020. if ( DomainInfo == NULL ) {
  4021. Status = STATUS_INVALID_COMPUTER_NAME;
  4022. goto Cleanup;
  4023. }
  4024. if ( !IsPrimaryDomain( DomainInfo )) {
  4025. Status = STATUS_NOT_SUPPORTED;
  4026. goto Cleanup;
  4027. }
  4028. #if NETLOGONDBG
  4029. PrintChangeLogEntry( ChangeLogEntry );
  4030. #endif // NETLOGONDBG
  4031. //
  4032. // The change log entry really represents an object and not an operation.
  4033. // Therefore, convert the delta type from whatever was passed to an
  4034. // "AddOrChange" operation. Then NlPackSingleDelta will return everything
  4035. // we know about the object.
  4036. //
  4037. ChangeLogEntry->DeltaType = (UCHAR)NlGlobalAddDeltaType[ChangeLogEntry->DeltaType];
  4038. *DeltaArrayRet = DeltaArray = (PNETLOGON_DELTA_ENUM_ARRAY)
  4039. MIDL_user_allocate( sizeof(NETLOGON_DELTA_ENUM_ARRAY) );
  4040. if( DeltaArray == NULL ) {
  4041. return STATUS_NO_MEMORY;
  4042. }
  4043. DeltaArray->CountReturned = 0;
  4044. DeltaArray->Deltas = NULL;
  4045. SessionInfo.NegotiatedFlags = 0;
  4046. //
  4047. // Retrieve the requestor's entry to get sessionkey
  4048. //
  4049. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  4050. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  4051. if (ServerSession == NULL) {
  4052. NlPrint((NL_CRITICAL,
  4053. "NetrDatabaseRedo: No server session.\n"));
  4054. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4055. Status = STATUS_ACCESS_DENIED;
  4056. // Don't log this event since it happens in nature after a reboot
  4057. // or after we scavenge the server session.
  4058. goto CleanupNoEventlog;
  4059. }
  4060. //
  4061. // Allow this call only on ServerSecureChannel.
  4062. //
  4063. if( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  4064. //
  4065. // If the only preblem is that this BDC hasn't authenticated,
  4066. // silently ask it to authenticate.
  4067. //
  4068. if ( ServerSession->SsSecureChannelType == NullSecureChannel ) {
  4069. NlPrint((NL_CRITICAL,
  4070. "NetrDatabaseRedo: No authenticated server session.\n"));
  4071. ServerSession = NULL;
  4072. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4073. // Don't log this event since it happens in nature after a reboot
  4074. // or after we scavenge the server session.
  4075. Status = STATUS_ACCESS_DENIED;
  4076. goto CleanupNoEventlog;
  4077. } else {
  4078. NlPrint((NL_CRITICAL,
  4079. "NetrDatabaseRedo: SecureChannel type isn't BDC. %ld\n",
  4080. ServerSession->SsSecureChannelType ));
  4081. ServerSession = NULL;
  4082. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4083. Status = STATUS_ACCESS_DENIED;
  4084. goto Cleanup;
  4085. }
  4086. }
  4087. //
  4088. // Verify the Authenticator and update seed if OK
  4089. //
  4090. Status = NlCheckAuthenticator( ServerSession,
  4091. Authenticator,
  4092. ReturnAuthenticator);
  4093. if ( !NT_SUCCESS(Status) ) {
  4094. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4095. NlPrint((NL_CRITICAL, "NetrDatabaseRedo: authentication failed.\n" ));
  4096. ServerSession = NULL;
  4097. goto Cleanup;
  4098. }
  4099. //
  4100. // Prevent entry from being deleted, but drop the global lock.
  4101. //
  4102. // Beware of server with two concurrent calls outstanding
  4103. // (must have rebooted.)
  4104. //
  4105. if (ServerSession->SsFlags & SS_LOCKED ) {
  4106. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4107. NlPrint((NL_CRITICAL, "NetrDatabaseRedo: Concurrent call detected.\n" ));
  4108. Status = STATUS_ACCESS_DENIED;
  4109. ServerSession = NULL;
  4110. goto Cleanup;
  4111. }
  4112. ServerSession->SsFlags |= SS_LOCKED;
  4113. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  4114. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  4115. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4116. //
  4117. // Allocate memory for delta buffer.
  4118. //
  4119. DeltaArray->Deltas = (PNETLOGON_DELTA_ENUM) MIDL_user_allocate(
  4120. MAX_DELTAS_PER_CHANGELOG * sizeof(NETLOGON_DELTA_ENUM) );
  4121. if( DeltaArray->Deltas == NULL ) {
  4122. Status = STATUS_NO_MEMORY;
  4123. goto Cleanup;
  4124. }
  4125. //
  4126. // wipe off the buffer so that cleanup will not be in fault.
  4127. //
  4128. RtlZeroMemory( DeltaArray->Deltas,
  4129. MAX_DELTAS_PER_CHANGELOG * sizeof(NETLOGON_DELTA_ENUM) );
  4130. //
  4131. // Put the data for the changelog entry into the user's buffer.
  4132. //
  4133. Status = NlPackSingleDelta( ChangeLogEntry,
  4134. DeltaArray,
  4135. &BufferSize,
  4136. &SessionInfo,
  4137. FALSE );
  4138. //
  4139. // If the only problem is that the object no longer exists,
  4140. // return a delta asking the BDC to delete the object.
  4141. //
  4142. if ( !NT_SUCCESS(Status) &&
  4143. IsObjectNotFoundStatus( ChangeLogEntry->DeltaType, Status ) ) {
  4144. NlPrint((NL_SYNC,
  4145. "NetrDatabaseRedo: " FORMAT_LPWSTR " object no longer exists (0x%lx) "
  4146. FORMAT_LPWSTR "\n",
  4147. NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
  4148. Status,
  4149. ComputerName ));
  4150. //
  4151. // Convert the change log entry into an appropriate delete delta type and
  4152. // try again.
  4153. //
  4154. ChangeLogEntry->DeltaType = (UCHAR)NlGlobalDeleteDeltaType[ChangeLogEntry->DeltaType];
  4155. Status = NlPackSingleDelta( ChangeLogEntry,
  4156. DeltaArray,
  4157. &BufferSize,
  4158. &SessionInfo,
  4159. FALSE );
  4160. }
  4161. Cleanup:
  4162. //
  4163. // write event log
  4164. //
  4165. if ( !NT_SUCCESS( Status ) ) {
  4166. MsgStrings[0] = ComputerName;
  4167. MsgStrings[1] = (LPWSTR) LongToPtr( Status );
  4168. NlpWriteEventlog(
  4169. NELOG_NetlogonPartialSyncCallFailed,
  4170. EVENTLOG_WARNING_TYPE,
  4171. (LPBYTE)&Status,
  4172. sizeof(Status),
  4173. MsgStrings,
  4174. 2 | NETP_LAST_MESSAGE_IS_NTSTATUS | NETP_ALLOW_DUPLICATE_EVENTS );
  4175. } else {
  4176. //
  4177. // Log the successful replication only if deltas have been returned
  4178. // to the caller.
  4179. //
  4180. if ( DeltaArray->CountReturned != 0 ) {
  4181. LPWSTR MsgStrings[2];
  4182. WCHAR CountBuffer[20]; // random size
  4183. MsgStrings[0] = ComputerName;
  4184. ultow( DeltaArray->CountReturned, CountBuffer, 10);
  4185. MsgStrings[1] = CountBuffer;
  4186. NlpWriteEventlog(
  4187. NELOG_NetlogonPartialSyncCallSuccess,
  4188. EVENTLOG_INFORMATION_TYPE,
  4189. NULL,
  4190. 0,
  4191. MsgStrings,
  4192. 2 | NETP_ALLOW_DUPLICATE_EVENTS );
  4193. }
  4194. }
  4195. //
  4196. // Free up locally allocated resources.
  4197. //
  4198. CleanupNoEventlog:
  4199. //
  4200. // If we weren't successful,
  4201. // Don't return any deltas.
  4202. //
  4203. if ( !NT_SUCCESS(Status)) {
  4204. if ( DeltaArray != NULL ) {
  4205. if ( DeltaArray->Deltas != NULL ) {
  4206. NlFreeDBDeltaArray( DeltaArray->Deltas, DeltaArray->CountReturned );
  4207. DeltaArray->Deltas = NULL;
  4208. }
  4209. DeltaArray->CountReturned = 0;
  4210. }
  4211. }
  4212. //
  4213. // Unlock the server session entry if we've locked it.
  4214. //
  4215. if ( ServerSession != NULL ) {
  4216. NlUnlockServerSession( ServerSession );
  4217. }
  4218. //
  4219. // If the BDC called us just as SAM was shutting down,
  4220. // map the status to prevent the BDC from full syncing.
  4221. //
  4222. if ( Status == STATUS_INVALID_SERVER_STATE ) {
  4223. Status = STATUS_ACCESS_DENIED;
  4224. }
  4225. NlPrint((NL_SYNC,
  4226. "NetrDatabaseRedo: " FORMAT_LPWSTR " returning (0x%lx) to "
  4227. FORMAT_LPWSTR "\n",
  4228. NlGlobalDBInfoArray[ChangeLogEntry->DBIndex].DBName,
  4229. Status,
  4230. ComputerName ));
  4231. STOPSSIAPITIMER;
  4232. NlPrint((NL_REPL_TIME,"NetrDatabaseRedo Time:\n"));
  4233. PRINTSSIAPITIMER;
  4234. if ( DomainInfo != NULL ) {
  4235. NlDereferenceDomain( DomainInfo );
  4236. }
  4237. return Status;
  4238. }
  4239. NTSTATUS
  4240. NetrAccountDeltas (
  4241. IN LPWSTR PrimaryName,
  4242. IN LPWSTR ComputerName,
  4243. IN PNETLOGON_AUTHENTICATOR Authenticator,
  4244. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  4245. IN PUAS_INFO_0 RecordId,
  4246. IN DWORD Count,
  4247. IN DWORD Level,
  4248. OUT LPBYTE Buffer,
  4249. IN DWORD BufferSize,
  4250. OUT PULONG CountReturned,
  4251. OUT PULONG TotalEntries,
  4252. OUT PUAS_INFO_0 NextRecordId
  4253. )
  4254. /*++
  4255. Routine Description:
  4256. This function is used by a UAS BDC or UAS member server to request
  4257. UAS-style account change information. This function can only be
  4258. called by a server which has previously authenticated with the PDC by
  4259. calling I_NetServerAuthenticate.
  4260. This function is only called by the XACT server upon receipt of a
  4261. I_NetAccountDeltas XACT SMB from a UAS BDC or a UAS member server.
  4262. As such, many of the parameters are opaque since the XACT server
  4263. doesn't need to interpret any of that data. This function uses RPC
  4264. to contact the Netlogon service.
  4265. The LanMan 3.0 SSI Functional Specification describes the operation
  4266. of this function.
  4267. Arguments:
  4268. PrimaryName -- Must be NULL to indicate this call is a local call
  4269. being made on behalf of a UAS server by the XACT server.
  4270. ComputerName -- Name of the BDC or member making the call.
  4271. Authenticator -- supplied by the server.
  4272. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  4273. RecordId -- Supplies an opaque buffer indicating the last record
  4274. received from a previous call to this function.
  4275. Count -- Supplies the number of Delta records requested.
  4276. Level -- Reserved. Must be zero.
  4277. Buffer -- Returns opaque data representing the information to be
  4278. returned.
  4279. BufferSize -- Size of buffer in bytes.
  4280. CountReturned -- Returns the number of records returned in buffer.
  4281. TotalEntries -- Returns the total number of records available.
  4282. NextRecordId -- Returns an opaque buffer identifying the last
  4283. record received by this function.
  4284. Return Value:
  4285. NT status code.
  4286. --*/
  4287. {
  4288. NlAssert(!"NetrAccountDeltas called");
  4289. UNREFERENCED_PARAMETER( PrimaryName );
  4290. UNREFERENCED_PARAMETER( ComputerName );
  4291. UNREFERENCED_PARAMETER( Authenticator );
  4292. UNREFERENCED_PARAMETER( ReturnAuthenticator );
  4293. UNREFERENCED_PARAMETER( RecordId );
  4294. UNREFERENCED_PARAMETER( Count );
  4295. UNREFERENCED_PARAMETER( Level );
  4296. UNREFERENCED_PARAMETER( Buffer );
  4297. UNREFERENCED_PARAMETER( BufferSize );
  4298. UNREFERENCED_PARAMETER( CountReturned );
  4299. UNREFERENCED_PARAMETER( TotalEntries );
  4300. UNREFERENCED_PARAMETER( NextRecordId );
  4301. return(STATUS_NOT_IMPLEMENTED);
  4302. }
  4303. NTSTATUS
  4304. NetrAccountSync (
  4305. IN LPWSTR PrimaryName,
  4306. IN LPWSTR ComputerName,
  4307. IN PNETLOGON_AUTHENTICATOR Authenticator,
  4308. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  4309. IN DWORD Reference,
  4310. IN DWORD Level,
  4311. OUT LPBYTE Buffer,
  4312. IN DWORD BufferSize,
  4313. OUT PULONG CountReturned,
  4314. OUT PULONG TotalEntries,
  4315. OUT PULONG NextReference,
  4316. OUT PUAS_INFO_0 LastRecordId
  4317. )
  4318. /*++
  4319. Routine Description:
  4320. This function is used by a UAS BDC or UAS member server to request
  4321. the entire user accounts database. This function can only be called
  4322. by a server which has previously authenticated with the PDC by
  4323. calling I_NetServerAuthenticate.
  4324. This function is only called by the XACT server upon receipt of a
  4325. I_NetAccountSync XACT SMB from a UAS BDC or a UAS member server. As
  4326. such, many of the parameters are opaque since the XACT server doesn't
  4327. need to interpret any of that data. This function uses RPC to
  4328. contact the Netlogon service.
  4329. The LanMan 3.0 SSI Functional Specification describes the operation
  4330. of this function.
  4331. "reference" and "next_reference" are treated as below.
  4332. 1. "reference" should hold either 0 or value of "next_reference"
  4333. from previous call to this API.
  4334. 2. Send the modals and ALL group records in the first call. The API
  4335. expects the buffer to be large enough to hold this info (worst
  4336. case size would be
  4337. MAXGROUP * (sizeof(struct group_info_1) + MAXCOMMENTSZ)
  4338. + sizeof(struct user_modals_info_0)
  4339. which, for now, will be 256 * (26 + 49) + 16 = 19216 bytes
  4340. Arguments:
  4341. PrimaryName -- Must be NULL to indicate this call is a local call
  4342. being made on behalf of a UAS server by the XACT server.
  4343. ComputerName -- Name of the BDC or member making the call.
  4344. Authenticator -- supplied by the server.
  4345. ReturnAuthenticator -- Receives an authenticator returned by the PDC.
  4346. Reference -- Supplies find-first find-next handle returned by the
  4347. previous call to this function or 0 if it is the first call.
  4348. Level -- Reserved. Must be zero.
  4349. Buffer -- Returns opaque data representing the information to be
  4350. returned.
  4351. BufferLen -- Length of buffer in bytes.
  4352. CountReturned -- Returns the number of records returned in buffer.
  4353. TotalEntries -- Returns the total number of records available.
  4354. NextReference -- Returns a find-first find-next handle to be
  4355. provided on the next call.
  4356. LastRecordId -- Returns an opaque buffer identifying the last
  4357. record received by this function.
  4358. Return Value:
  4359. NT status code.
  4360. --*/
  4361. {
  4362. NlAssert(!"NetrAccountDeltas called");
  4363. UNREFERENCED_PARAMETER( PrimaryName );
  4364. UNREFERENCED_PARAMETER( ComputerName );
  4365. UNREFERENCED_PARAMETER( Authenticator );
  4366. UNREFERENCED_PARAMETER( ReturnAuthenticator );
  4367. UNREFERENCED_PARAMETER( Reference );
  4368. UNREFERENCED_PARAMETER( Level );
  4369. UNREFERENCED_PARAMETER( Buffer );
  4370. UNREFERENCED_PARAMETER( BufferSize );
  4371. UNREFERENCED_PARAMETER( CountReturned );
  4372. UNREFERENCED_PARAMETER( TotalEntries );
  4373. UNREFERENCED_PARAMETER( NextReference );
  4374. UNREFERENCED_PARAMETER( LastRecordId );
  4375. return(STATUS_NOT_IMPLEMENTED);
  4376. }
  4377. NTSTATUS
  4378. NlGetTrustedSideInfo(
  4379. IN PCLIENT_SESSION ClientSession,
  4380. IN LPWSTR AccountName OPTIONAL,
  4381. IN NETLOGON_SECURE_CHANNEL_TYPE AccountType,
  4382. OUT PNT_OWF_PASSWORD NewOwfPassword,
  4383. OUT PNT_OWF_PASSWORD OldOwfPassword,
  4384. OUT PNL_GENERIC_RPC_DATA *TrustInfo
  4385. )
  4386. /*++
  4387. Routine Description:
  4388. This function is used by a trusting side DC to get the new and old
  4389. passwords from the trusted side.
  4390. The caller must be the writer of the client session.
  4391. Arguments:
  4392. ClientSession - Identifies a session to the trusted side.
  4393. The caller must be the writer of this client session.
  4394. AccountName -- Name of the account to get the password for. If NULL,
  4395. the account name from the ClientSession is used.
  4396. AccountType -- The type of account being accessed. Ignored if
  4397. AccountName is NULL in which case the account type specified
  4398. in teh ClientSession is used.
  4399. NewOwfPassword -- Returns the new OWF password of the account.
  4400. OldOwfPassword -- Returns the old OWF password of the account.
  4401. TrustInfo -- Returns the trusted domain info
  4402. Return Value:
  4403. NT status code.
  4404. --*/
  4405. {
  4406. NTSTATUS Status;
  4407. NETLOGON_AUTHENTICATOR OurAuthenticator;
  4408. NETLOGON_AUTHENTICATOR ReturnAuthenticator;
  4409. SESSION_INFO SessionInfo;
  4410. BOOLEAN FirstTry = TRUE;
  4411. BOOLEAN OldServer = FALSE;
  4412. ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrNewPassword;
  4413. ENCRYPTED_LM_OWF_PASSWORD SessKeyEncrOldPassword;
  4414. NETLOGON_CREDENTIAL CurrentAuthenticationSeed;
  4415. PNL_GENERIC_RPC_DATA LocalTrustInfo = NULL;
  4416. //
  4417. // If the server doesn't support this functionality,
  4418. // there is nothing for us to do here
  4419. //
  4420. if ( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_NO_PWD_ATTR_MONITOR ) {
  4421. return STATUS_NOT_SUPPORTED;
  4422. }
  4423. //
  4424. // If the session isn't authenticated,
  4425. // do so now.
  4426. //
  4427. FirstTryFailed:
  4428. Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
  4429. if ( !NT_SUCCESS(Status) ) {
  4430. goto Cleanup;
  4431. }
  4432. SessionInfo.SessionKey = ClientSession->CsSessionKey;
  4433. SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
  4434. //
  4435. // Remember current authentication seed. We may need to reset it
  4436. // if the trusted side server is the old one that doesn't have
  4437. // the passwords monitoring API.
  4438. //
  4439. CurrentAuthenticationSeed = ClientSession->CsAuthenticationSeed;
  4440. //
  4441. // Build the Authenticator for this request to the server
  4442. //
  4443. NlBuildAuthenticator(
  4444. &ClientSession->CsAuthenticationSeed,
  4445. &ClientSession->CsSessionKey,
  4446. &OurAuthenticator);
  4447. //
  4448. // Get the passwords (and perhaps attributes) from the server
  4449. //
  4450. NL_API_START( Status, ClientSession, TRUE ) {
  4451. NlAssert( ClientSession->CsUncServerName != NULL );
  4452. Status = I_NetServerGetTrustInfo(
  4453. ClientSession->CsUncServerName,
  4454. (AccountName != NULL) ?
  4455. AccountName :
  4456. ClientSession->CsAccountName,
  4457. (AccountName != NULL) ?
  4458. AccountType :
  4459. ClientSession->CsSecureChannelType,
  4460. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  4461. &OurAuthenticator,
  4462. &ReturnAuthenticator,
  4463. &SessKeyEncrNewPassword,
  4464. &SessKeyEncrOldPassword,
  4465. &LocalTrustInfo );
  4466. //
  4467. // If the server is old that doesn't support this functionality,
  4468. // remember to never ask it about attributes again
  4469. //
  4470. if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
  4471. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4472. ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_NO_PWD_ATTR_MONITOR;
  4473. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4474. //
  4475. // Avoid dropping the secure channel in the NL_API_ELSE logic
  4476. // by making it think all was fine
  4477. //
  4478. OldServer = TRUE;
  4479. Status = STATUS_SUCCESS;
  4480. } else if ( !NT_SUCCESS(Status) ) {
  4481. NlPrintRpcDebug( "I_NetServerGetTrustInfo", Status );
  4482. }
  4483. // NOTE: This call may drop the secure channel behind our back
  4484. } NL_API_ELSE( Status, ClientSession, TRUE ) {
  4485. } NL_API_END;
  4486. if ( OldServer ) {
  4487. goto Cleanup;
  4488. }
  4489. //
  4490. // Now verify primary's authenticator and update our seed
  4491. //
  4492. if ( Status == STATUS_ACCESS_DENIED ||
  4493. !NlUpdateSeed( &ClientSession->CsAuthenticationSeed,
  4494. &ReturnAuthenticator.Credential,
  4495. &ClientSession->CsSessionKey) ) {
  4496. NlPrintCs(( NL_CRITICAL, ClientSession,
  4497. "NlGetTrustedSideInfo: denying access after status: 0x%lx\n",
  4498. Status ));
  4499. //
  4500. // Preserve any status indicating a communication error.
  4501. //
  4502. if ( NT_SUCCESS(Status) ) {
  4503. Status = STATUS_ACCESS_DENIED;
  4504. }
  4505. NlSetStatusClientSession( ClientSession, Status );
  4506. //
  4507. // Perhaps the netlogon service on the server has just restarted.
  4508. // Try just once to set up a session to the server again.
  4509. //
  4510. if ( FirstTry ) {
  4511. FirstTry = FALSE;
  4512. goto FirstTryFailed;
  4513. }
  4514. }
  4515. if ( !NT_SUCCESS(Status) ) {
  4516. goto Cleanup;
  4517. }
  4518. //
  4519. // Decrypt the password returned from the server.
  4520. //
  4521. Status = RtlDecryptNtOwfPwdWithNtOwfPwd(
  4522. &SessKeyEncrNewPassword,
  4523. (PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
  4524. NewOwfPassword );
  4525. NlAssert( NT_SUCCESS(Status) );
  4526. Status = RtlDecryptNtOwfPwdWithNtOwfPwd(
  4527. &SessKeyEncrOldPassword,
  4528. (PNT_OWF_PASSWORD) &SessionInfo.SessionKey,
  4529. OldOwfPassword );
  4530. NlAssert( NT_SUCCESS(Status) );
  4531. //
  4532. // Common exit
  4533. //
  4534. Cleanup:
  4535. //
  4536. // Remember to not try this call to this server in future.
  4537. // Reset authentication seed to have the secure channel
  4538. // working next time we use it.
  4539. //
  4540. if ( OldServer ) {
  4541. ClientSession->CsAuthenticationSeed = CurrentAuthenticationSeed;
  4542. Status = STATUS_NOT_SUPPORTED;
  4543. }
  4544. //
  4545. // On success, return the trust info
  4546. //
  4547. if ( !NT_SUCCESS(Status) ) {
  4548. NlPrintCs(( NL_CRITICAL, ClientSession,
  4549. "NlGetTrustedSideInfo: %ws: failed %lX\n",
  4550. AccountName,
  4551. Status ));
  4552. if ( LocalTrustInfo != NULL ) {
  4553. NetApiBufferFree( LocalTrustInfo );
  4554. }
  4555. } else {
  4556. *TrustInfo = LocalTrustInfo;
  4557. }
  4558. return Status;
  4559. }
  4560. NTSTATUS
  4561. NlVerifyTrust(
  4562. IN PCLIENT_SESSION ClientSession,
  4563. OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
  4564. )
  4565. /*++
  4566. Routine Description:
  4567. This function is used by the trusting side to verify the status
  4568. of the secure channel to the trusted side DC.
  4569. It first tries to use an API that goes over the secure channel and
  4570. returns the passwords used for the given trust relationship from the
  4571. trusted side. The trusting side checks if there is match between the
  4572. passwords returned from the trusted side and those it has locally. If
  4573. they match, the API returns success to the caller. If the trusted side
  4574. lacks this functionality, the trusting side verifies the trust by
  4575. performing an authentication call over the secure channel for a bogus
  4576. domain\user. If the secure channel works, this is bound to fail with
  4577. STATUS_NO_SUCH_USER in which case success is returned to the caller.
  4578. Arguments:
  4579. ClientSession - Identifies a session to the trusted side.
  4580. QueryInformation - Returns a pointer to a NETLOGON_INFO_2 buffer
  4581. which contains the requested information. The buffer must be
  4582. freed using NetApiBufferFree.
  4583. Return Value:
  4584. NT status code.
  4585. --*/
  4586. {
  4587. NTSTATUS Status = STATUS_SUCCESS; // Status of operation
  4588. NTSTATUS SecureChannelStatus = STATUS_SUCCESS; // Status of secure channel
  4589. NTSTATUS VerificationStatus = STATUS_SUCCESS; // Status of trust verification
  4590. NT_OWF_PASSWORD NewOwfPassword;
  4591. NT_OWF_PASSWORD OldOwfPassword;
  4592. NT_OWF_PASSWORD OurNewOwfPassword;
  4593. NT_OWF_PASSWORD OurOldOwfPassword;
  4594. PUNICODE_STRING OurNewPassword = NULL;
  4595. PUNICODE_STRING OurOldPassword = NULL;
  4596. ULONG DummyPasswordVersionNumber;
  4597. LPBYTE ValidationInformation = NULL;
  4598. LPWSTR ServerName = NULL;
  4599. ULONG ServerDiscoveryFlags = 0;
  4600. PNL_GENERIC_RPC_DATA TrustInfo = NULL;
  4601. BOOL AmWriter = FALSE;
  4602. BOOL TrustAttribVerified = FALSE;
  4603. //
  4604. // Become a Writer of the ClientSession.
  4605. //
  4606. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4607. NlPrintCs((NL_CRITICAL, ClientSession,
  4608. "NlVerifyTrust: Can't become writer of client session.\n"));
  4609. Status = STATUS_NO_LOGON_SERVERS;
  4610. goto Cleanup;
  4611. }
  4612. AmWriter = TRUE;
  4613. //
  4614. // Get the trust passwords from the trusted side
  4615. //
  4616. Status = NlGetTrustedSideInfo( ClientSession,
  4617. NULL, // Use the account specified in the client session
  4618. NullSecureChannel, // Let the routine get the account type
  4619. &NewOwfPassword,
  4620. &OldOwfPassword,
  4621. &TrustInfo );
  4622. //
  4623. // If this call is not supported on the trusted side DC,
  4624. // we can only check if the secure chanel is currently healthy.
  4625. //
  4626. if ( Status == STATUS_NOT_SUPPORTED ) {
  4627. NETLOGON_INTERACTIVE_INFO LogonInformation;
  4628. PNETLOGON_LOGON_IDENTITY_INFO Identity = (PNETLOGON_LOGON_IDENTITY_INFO) &LogonInformation;
  4629. BOOLEAN Authoritative;
  4630. WCHAR BogusName[2];
  4631. ULONG ExtraFlags = 0;
  4632. BogusName[0] = (WCHAR) 0xFFFF;
  4633. BogusName[1] = UNICODE_NULL;
  4634. //
  4635. // Reset the status
  4636. //
  4637. Status = STATUS_SUCCESS;
  4638. //
  4639. // Initialize the structure with bogus names
  4640. //
  4641. RtlZeroMemory( &LogonInformation, sizeof(LogonInformation) );
  4642. RtlInitUnicodeString( &Identity->LogonDomainName, BogusName );
  4643. RtlInitUnicodeString( &Identity->UserName, BogusName );
  4644. RtlInitUnicodeString( &Identity->Workstation, BogusName );
  4645. //
  4646. // Release the writer lock as it is used
  4647. // in the following secure channel call
  4648. //
  4649. NlResetWriterClientSession( ClientSession );
  4650. AmWriter = FALSE;
  4651. //
  4652. // Force a call over the secure channel
  4653. //
  4654. Status = NlpUserValidateHigher( ClientSession,
  4655. FALSE, // not doing indirect trust
  4656. NetlogonInteractiveInformation,
  4657. (LPBYTE) &LogonInformation,
  4658. NetlogonValidationSamInfo,
  4659. &ValidationInformation,
  4660. &Authoritative,
  4661. &ExtraFlags );
  4662. //
  4663. // This is bound to fail. Ignore the failure.
  4664. //
  4665. NlAssert( !NT_SUCCESS(Status) );
  4666. Status = STATUS_SUCCESS;
  4667. //
  4668. // Get the secure channel status after
  4669. // we made a call over it
  4670. //
  4671. SecureChannelStatus = NlCaptureServerClientSession(
  4672. ClientSession,
  4673. &ServerName,
  4674. &ServerDiscoveryFlags );
  4675. //
  4676. // The above is our best we can do to
  4677. // verify the trust for an old server
  4678. //
  4679. VerificationStatus = SecureChannelStatus;
  4680. //
  4681. // Otherwise, this is the new server.
  4682. // Check the secure channel state. If it's successful,
  4683. // verify the trust status by checking whether local
  4684. // trust attributes and passwords match with those
  4685. // received from the trusted side.
  4686. //
  4687. } else {
  4688. //
  4689. // Get the secure channel status and the server
  4690. // name. Do this while holding the writer lock
  4691. // to ensure we return the name of the server
  4692. // used to verify the trust.
  4693. //
  4694. SecureChannelStatus = NlCaptureServerClientSession(
  4695. ClientSession,
  4696. &ServerName,
  4697. &ServerDiscoveryFlags );
  4698. //
  4699. // Release the writer lock. We don't need it anymore.
  4700. //
  4701. NlResetWriterClientSession( ClientSession );
  4702. AmWriter = FALSE;
  4703. //
  4704. // If secure channel is down, there is nothing
  4705. // to verify
  4706. //
  4707. if ( !NT_SUCCESS(SecureChannelStatus) ) {
  4708. VerificationStatus = SecureChannelStatus;
  4709. Status = STATUS_SUCCESS;
  4710. goto Cleanup;
  4711. }
  4712. //
  4713. // OK, secure channel is up. However, if we couldn't
  4714. // get the trust info for some reason, set the
  4715. // verification status to the error we got while
  4716. // getting the trust info and bail out.
  4717. //
  4718. if ( !NT_SUCCESS(Status) ) {
  4719. VerificationStatus = Status;
  4720. Status = STATUS_SUCCESS;
  4721. goto Cleanup;
  4722. }
  4723. //
  4724. // If the trusted side returned trust attributes,
  4725. // check if trust attributes match.
  4726. //
  4727. // The first ULONG in the trust info is the trust attributes
  4728. //
  4729. if ( TrustInfo != NULL && TrustInfo->UlongEntryCount > NL_GENERIC_RPC_TRUST_ATTRIB_INDEX ) {
  4730. //
  4731. // We are only interested in the forest transitive bit
  4732. //
  4733. if ( (ClientSession->CsTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0 ) {
  4734. if ( (TrustInfo->UlongData[NL_GENERIC_RPC_TRUST_ATTRIB_INDEX] &
  4735. TRUST_ATTRIBUTE_FOREST_TRANSITIVE) == 0 ) {
  4736. NlPrintCs(( NL_CRITICAL, ClientSession,
  4737. "NlVerifyTrust: F bit is set locally but not on trusted side\n" ));
  4738. VerificationStatus = STATUS_DOMAIN_TRUST_INCONSISTENT;
  4739. goto Cleanup;
  4740. }
  4741. } else {
  4742. if ( (TrustInfo->UlongData[NL_GENERIC_RPC_TRUST_ATTRIB_INDEX] &
  4743. TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0 ) {
  4744. NlPrintCs(( NL_CRITICAL, ClientSession,
  4745. "NlVerifyTrust: F bit is set on trusted side but not locally\n" ));
  4746. VerificationStatus = STATUS_DOMAIN_TRUST_INCONSISTENT;
  4747. goto Cleanup;
  4748. }
  4749. }
  4750. TrustAttribVerified = TRUE;
  4751. }
  4752. //
  4753. // OK, the trust attributes check succeeded.
  4754. // Proceed to check the password match
  4755. //
  4756. // Get our local passwords
  4757. //
  4758. Status = NlGetOutgoingPassword( ClientSession,
  4759. &OurNewPassword,
  4760. &OurOldPassword,
  4761. &DummyPasswordVersionNumber,
  4762. NULL ); // No need to return password set time
  4763. if ( !NT_SUCCESS(Status) ) {
  4764. goto Cleanup;
  4765. }
  4766. //
  4767. // Check if our new password matches
  4768. // either one returned from the trusted side
  4769. //
  4770. if ( OurNewPassword != NULL ) {
  4771. Status = RtlCalculateNtOwfPassword( OurNewPassword,
  4772. &OurNewOwfPassword );
  4773. if ( !NT_SUCCESS( Status ) ) {
  4774. //
  4775. // return more appropriate error.
  4776. //
  4777. if ( !NlpIsNtStatusResourceError( Status )) {
  4778. Status = STATUS_NO_TRUST_LSA_SECRET;
  4779. }
  4780. goto Cleanup;
  4781. }
  4782. //
  4783. // Check if this password is the same as the new one from trusted side
  4784. //
  4785. if ( RtlEqualNtOwfPassword(&OurNewOwfPassword, &NewOwfPassword) ) {
  4786. NlPrintCs(( NL_MISC, ClientSession,
  4787. "NlVerifyTrust: new-new password match (%s trust attributes)\n",
  4788. (TrustAttribVerified ? "with" : "without") ));
  4789. VerificationStatus = STATUS_SUCCESS;
  4790. goto Cleanup;
  4791. }
  4792. //
  4793. // Check if this password is the same as the old one from trusted side
  4794. //
  4795. if ( RtlEqualNtOwfPassword(&OurNewOwfPassword, &OldOwfPassword) ) {
  4796. NlPrintCs(( NL_MISC, ClientSession,
  4797. "NlVerifyTrust: new-old password match (%s trust attributes)\n",
  4798. (TrustAttribVerified ? "with" : "without") ));
  4799. VerificationStatus = STATUS_SUCCESS;
  4800. goto Cleanup;
  4801. }
  4802. }
  4803. //
  4804. // Check if our old password matches
  4805. // either one returned from the trusted side
  4806. //
  4807. if ( OurOldPassword != NULL ) {
  4808. Status = RtlCalculateNtOwfPassword( OurOldPassword,
  4809. &OurOldOwfPassword );
  4810. if ( !NT_SUCCESS( Status ) ) {
  4811. //
  4812. // return more appropriate error.
  4813. //
  4814. if ( !NlpIsNtStatusResourceError( Status )) {
  4815. Status = STATUS_NO_TRUST_LSA_SECRET;
  4816. }
  4817. goto Cleanup;
  4818. }
  4819. //
  4820. // Check if this password is the same as the new one from trusted side
  4821. //
  4822. if ( RtlEqualNtOwfPassword(&OurOldOwfPassword, &NewOwfPassword) ) {
  4823. NlPrintCs(( NL_MISC, ClientSession,
  4824. "NlVerifyTrust: old-new password match (%s trust attributes)\n",
  4825. (TrustAttribVerified ? "with" : "without") ));
  4826. VerificationStatus = STATUS_SUCCESS;
  4827. goto Cleanup;
  4828. }
  4829. //
  4830. // Check if this password is the same as the old one from trusted side
  4831. //
  4832. if ( RtlEqualNtOwfPassword(&OurOldOwfPassword, &OldOwfPassword) ) {
  4833. NlPrintCs(( NL_MISC, ClientSession,
  4834. "NlVerifyTrust: old-old password match (%s trust attributes)\n",
  4835. (TrustAttribVerified ? "with" : "without") ));
  4836. VerificationStatus = STATUS_SUCCESS;
  4837. goto Cleanup;
  4838. }
  4839. }
  4840. //
  4841. // If we are here, passwords didn't match
  4842. //
  4843. VerificationStatus = STATUS_WRONG_PASSWORD;
  4844. NlPrintCs(( NL_CRITICAL, ClientSession,
  4845. "NlVerifyTrust: passwords don't match\n" ));
  4846. }
  4847. Cleanup:
  4848. if ( AmWriter ) {
  4849. NlResetWriterClientSession( ClientSession );
  4850. }
  4851. //
  4852. // On success, return the results of verification
  4853. //
  4854. if ( Status == STATUS_SUCCESS ) {
  4855. //
  4856. // If we don't know the server name,
  4857. // set it to blank name
  4858. //
  4859. if ( ServerName == NULL ) {
  4860. ServerName = NetpAllocWStrFromWStr( L"" );
  4861. if ( ServerName == NULL ) {
  4862. Status = STATUS_NO_MEMORY;
  4863. }
  4864. }
  4865. //
  4866. // Allocate the memory for returned structure
  4867. //
  4868. if ( Status == STATUS_SUCCESS ) {
  4869. QueryInformation->NetlogonInfo2 = MIDL_user_allocate( sizeof(NETLOGON_INFO_2) );
  4870. if ( QueryInformation->NetlogonInfo2 == NULL ) {
  4871. Status = STATUS_NO_MEMORY;
  4872. }
  4873. }
  4874. //
  4875. // If allocations succeeded,
  4876. // return the data
  4877. //
  4878. if ( Status == STATUS_SUCCESS ) {
  4879. QueryInformation->NetlogonInfo2->netlog2_flags = 0;
  4880. //
  4881. // Indicate that we are returing the verification status
  4882. // in netlog2_pdc_connection_status
  4883. //
  4884. QueryInformation->NetlogonInfo2->netlog2_flags |= NETLOGON_VERIFY_STATUS_RETURNED;
  4885. QueryInformation->NetlogonInfo2->netlog2_pdc_connection_status =
  4886. NetpNtStatusToApiStatus( VerificationStatus );
  4887. //
  4888. // Return the server discovery flags
  4889. //
  4890. if ( ServerDiscoveryFlags & CS_DISCOVERY_HAS_TIMESERV ) {
  4891. QueryInformation->NetlogonInfo2->netlog2_flags |= NETLOGON_HAS_TIMESERV;
  4892. }
  4893. if ( ServerDiscoveryFlags & CS_DISCOVERY_HAS_IP ) {
  4894. QueryInformation->NetlogonInfo2->netlog2_flags |= NETLOGON_HAS_IP;
  4895. }
  4896. //
  4897. // Return the current secure channel status
  4898. // and the server name
  4899. //
  4900. QueryInformation->NetlogonInfo2->netlog2_tc_connection_status =
  4901. NetpNtStatusToApiStatus( SecureChannelStatus );
  4902. QueryInformation->NetlogonInfo2->netlog2_trusted_dc_name = ServerName;
  4903. ServerName = NULL; // don't free this name below
  4904. }
  4905. }
  4906. //
  4907. // Free locally used resources
  4908. //
  4909. if ( OurNewPassword != NULL ) {
  4910. LocalFree( OurNewPassword );
  4911. }
  4912. if ( OurOldPassword != NULL ) {
  4913. LocalFree( OurOldPassword );
  4914. }
  4915. if ( ServerName != NULL ) {
  4916. NetApiBufferFree( ServerName );
  4917. }
  4918. if ( TrustInfo != NULL ) {
  4919. NetApiBufferFree( TrustInfo );
  4920. }
  4921. if ( ValidationInformation != NULL ) {
  4922. MIDL_user_free( ValidationInformation );
  4923. }
  4924. return Status;
  4925. }
  4926. NET_API_STATUS
  4927. NetrLogonControl(
  4928. IN LPWSTR ServerName OPTIONAL,
  4929. IN DWORD FunctionCode,
  4930. IN DWORD QueryLevel,
  4931. OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
  4932. )
  4933. /*++
  4934. Routine Description:
  4935. This function controls various aspects of the Netlogon service. It
  4936. can be used to request that a BDC ensure that its copy of the SAM
  4937. database is brought up to date. It can, also, be used to determine
  4938. if a BDC currently has a secure channel open to the PDC.
  4939. Only an Admin, Account Operator or Server Operator may call this
  4940. function.
  4941. Arguments:
  4942. ServerName - The name of the remote server.
  4943. FunctionCode - Defines the operation to be performed. The valid
  4944. values are:
  4945. FunctionCode Values
  4946. NETLOGON_CONTROL_QUERY - No operation. Merely returns the
  4947. information requested.
  4948. NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
  4949. to be brought in sync with the copy on the PDC. This
  4950. operation does NOT imply a full synchronize. The
  4951. Netlogon service will merely replicate any outstanding
  4952. differences if possible.
  4953. NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
  4954. completely new copy of the SAM database from the PDC.
  4955. This operation will perform a full synchronize.
  4956. NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
  4957. to replicate now.
  4958. QueryLevel - Indicates what information should be returned from
  4959. the Netlogon Service. Must be 1.
  4960. QueryInformation - Returns a pointer to a buffer which contains the
  4961. requested information. The buffer must be freed using
  4962. NetApiBufferFree.
  4963. Return Value:
  4964. NERR_Success: the operation was successful
  4965. ERROR_NOT_SUPPORTED: Function code is not valid on the specified
  4966. server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
  4967. --*/
  4968. {
  4969. NET_API_STATUS NetStatus;
  4970. QueryInformation->NetlogonInfo1 = NULL;
  4971. switch( QueryLevel ) {
  4972. case (1):
  4973. break;
  4974. case (2):
  4975. NetStatus = ERROR_NOT_SUPPORTED;
  4976. goto Cleanup;
  4977. default:
  4978. NetStatus = ERROR_INVALID_LEVEL;
  4979. goto Cleanup;
  4980. }
  4981. //
  4982. // ensure the input data is valid.
  4983. //
  4984. switch( FunctionCode ) {
  4985. case NETLOGON_CONTROL_QUERY:
  4986. case NETLOGON_CONTROL_REPLICATE:
  4987. case NETLOGON_CONTROL_SYNCHRONIZE:
  4988. case NETLOGON_CONTROL_PDC_REPLICATE:
  4989. #if NETLOGONDBG
  4990. case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
  4991. case NETLOGON_CONTROL_TRUNCATE_LOG:
  4992. case NETLOGON_CONTROL_BREAKPOINT:
  4993. #endif // NETLOGONDBG
  4994. break;
  4995. default:
  4996. NetStatus = ERROR_NOT_SUPPORTED;
  4997. goto Cleanup;
  4998. }
  4999. NetStatus = NetrLogonControl2Ex(
  5000. ServerName,
  5001. FunctionCode,
  5002. QueryLevel,
  5003. NULL,
  5004. QueryInformation );
  5005. Cleanup:
  5006. return( NetStatus );
  5007. }
  5008. NET_API_STATUS
  5009. NetrLogonControl2(
  5010. IN LPWSTR ServerName OPTIONAL,
  5011. IN DWORD FunctionCode,
  5012. IN DWORD QueryLevel,
  5013. IN PNETLOGON_CONTROL_DATA_INFORMATION InputData,
  5014. OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
  5015. )
  5016. /*++
  5017. Routine Description:
  5018. Same as NetrLogonControl2Ex.
  5019. A client should never pass a QueryLevel of 4 to this procedure. We don't check since, if
  5020. they did, it's too late now. The client will access violate upon return.
  5021. Arguments:
  5022. Same as NetrLogonControl2Ex.
  5023. Return Value:
  5024. --*/
  5025. {
  5026. NET_API_STATUS NetStatus;
  5027. NetStatus = NetrLogonControl2Ex(
  5028. ServerName,
  5029. FunctionCode,
  5030. QueryLevel,
  5031. InputData,
  5032. QueryInformation );
  5033. return NetStatus;
  5034. }
  5035. NET_API_STATUS
  5036. NetrLogonControl2Ex(
  5037. IN LPWSTR ServerName OPTIONAL,
  5038. IN DWORD FunctionCode,
  5039. IN DWORD QueryLevel,
  5040. IN PNETLOGON_CONTROL_DATA_INFORMATION InputData,
  5041. OUT PNETLOGON_CONTROL_QUERY_INFORMATION QueryInformation
  5042. )
  5043. /*++
  5044. Routine Description:
  5045. This function controls various aspects of the Netlogon service. It
  5046. can be used to request that a BDC ensure that its copy of the SAM
  5047. database is brought up to date. It can, also, be used to determine
  5048. if a BDC currently has a secure channel open to the PDC.
  5049. Only an Admin, Account Operator or Server Operator may call this
  5050. function.
  5051. Arguments:
  5052. ServerName - The name of the remote server.
  5053. FunctionCode - Defines the operation to be performed. The valid
  5054. values are:
  5055. FunctionCode Values
  5056. NETLOGON_CONTROL_QUERY - No operation. Merely returns the
  5057. information requested.
  5058. NETLOGON_CONTROL_REPLICATE: Forces the SAM database on a BDC
  5059. to be brought in sync with the copy on the PDC. This
  5060. operation does NOT imply a full synchronize. The
  5061. Netlogon service will merely replicate any outstanding
  5062. differences if possible.
  5063. NETLOGON_CONTROL_SYNCHRONIZE: Forces a BDC to get a
  5064. completely new copy of the SAM database from the PDC.
  5065. This operation will perform a full synchronize.
  5066. NETLOGON_CONTROL_PDC_REPLICATE: Forces a PDC to ask each BDC
  5067. to replicate now.
  5068. NETLOGON_CONTROL_REDISCOVER: Forces a DC to rediscover the
  5069. specified trusted domain DC.
  5070. NETLOGON_CONTROL_TC_QUERY: Query the status of the specified
  5071. trusted domain secure channel.
  5072. NETLOGON_CONTROL_TC_VERIFY: Verify the status of the specified
  5073. trusted domain secure channel. If the current status is
  5074. success (which means that the last operation performed
  5075. over the secure channel was successful), ping the DC. If the
  5076. current status is not success or the ping fails, rediscover
  5077. a new DC.
  5078. NETLOGON_CONTROL_TRANSPORT_NOTIFY: Notifies netlogon that a new transport
  5079. has been added. Currently, it merely resets discovery timeouts allowing
  5080. all secure channel discoveries to be retried immediately. However, the
  5081. intention is to later add support for anything similar. The intention is that
  5082. a client can call this function after a new transport has been added (e.g., it
  5083. dialed a RAS link) and immediately before calling Netlogon (e.g., indirectly
  5084. by doing an LsaLogonUser).
  5085. NETLOGON_CONTROL_FORCE_DNS_REG: Forces the DC to re-register all of its
  5086. DNS records. QueryLevel parameter must be 1.
  5087. NETLOGON_CONTROL_QUERY_DNS_REG: Query the status of DNS updates
  5088. performed by netlogon. If there was any DNS registration or
  5089. deregistration error for any of the records as they were
  5090. updated last time, the query result will be negative;
  5091. otherwise the query result will be positive.
  5092. QueryLevel parameter must be 1.
  5093. QueryLevel - Indicates what information should be returned from
  5094. the Netlogon Service.
  5095. InputData - According to the function code specified this parameter
  5096. will carry input data. NETLOGON_CONTROL_REDISCOVER,
  5097. NETLOGON_CONTROL_TC_QUERY, and NETLOGON_CONTROL_TC_VERIFY
  5098. function code specify the trusted domain name (LPWSTR type) here.
  5099. NETLOGON_CONTROL_FIND_USER function code specifies the user name
  5100. (LPWSTR type) here.
  5101. QueryInformation - Returns a pointer to a buffer which contains the
  5102. requested information. The buffer must be freed using
  5103. NetApiBufferFree.
  5104. Return Value:
  5105. NERR_Success: the operation was successful
  5106. ERROR_NOT_SUPPORTED: Function code is not valid on the specified
  5107. server. (e.g. NETLOGON_CONTROL_REPLICATE was passed to a PDC).
  5108. --*/
  5109. {
  5110. NET_API_STATUS NetStatus;
  5111. NTSTATUS Status;
  5112. DWORD i;
  5113. DWORD InfoSize;
  5114. BOOL DnsLastStatusCheck = TRUE;
  5115. ACCESS_MASK DesiredAccess;
  5116. UNICODE_STRING DomainName;
  5117. PDOMAIN_INFO DomainInfo = NULL;
  5118. PCLIENT_SESSION ClientSession = NULL;
  5119. LPWSTR TDCName = NULL;
  5120. LPWSTR TrustedDomainName = NULL;
  5121. LPWSTR SamAccountName = NULL;
  5122. LPWSTR SamDomainName = NULL;
  5123. DWORD SamExtraFlags;
  5124. DWORD TcServerDiscoveryFlags = 0;
  5125. PNL_DC_CACHE_ENTRY DcCacheEntry = NULL;
  5126. //
  5127. // Lookup which domain this call pertains to.
  5128. //
  5129. DomainInfo = NlFindDomainByServerName( ServerName );
  5130. if ( DomainInfo == NULL ) {
  5131. NetStatus = ERROR_INVALID_COMPUTERNAME;
  5132. goto Cleanup;
  5133. }
  5134. //
  5135. // Ensure the QueryLevel is valid
  5136. //
  5137. QueryInformation->NetlogonInfo1 = NULL;
  5138. switch( QueryLevel ) {
  5139. case (1):
  5140. case (2):
  5141. case (3):
  5142. case (4):
  5143. break;
  5144. default:
  5145. NetStatus = ERROR_INVALID_LEVEL;
  5146. goto Cleanup;
  5147. }
  5148. //
  5149. // ensure the input data is valid.
  5150. //
  5151. switch( FunctionCode ) {
  5152. case NETLOGON_CONTROL_REDISCOVER:
  5153. case NETLOGON_CONTROL_TC_QUERY:
  5154. case NETLOGON_CONTROL_TC_VERIFY:
  5155. case NETLOGON_CONTROL_FIND_USER:
  5156. case NETLOGON_CONTROL_CHANGE_PASSWORD:
  5157. #if NETLOGONDBG
  5158. case NETLOGON_CONTROL_SET_DBFLAG:
  5159. #endif // NETLOGONDBG
  5160. NlAssert( InputData != NULL );
  5161. if( InputData == NULL ) {
  5162. NetStatus = ERROR_INVALID_PARAMETER;
  5163. goto Cleanup;
  5164. }
  5165. break;
  5166. default:
  5167. break;
  5168. }
  5169. //
  5170. // compute access mask.
  5171. //
  5172. switch ( FunctionCode ) {
  5173. case NETLOGON_CONTROL_QUERY:
  5174. case NETLOGON_CONTROL_TC_QUERY:
  5175. case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
  5176. case NETLOGON_CONTROL_QUERY_DNS_REG:
  5177. DesiredAccess = NETLOGON_QUERY_ACCESS;
  5178. break;
  5179. case NETLOGON_CONTROL_REPLICATE:
  5180. case NETLOGON_CONTROL_SYNCHRONIZE:
  5181. case NETLOGON_CONTROL_PDC_REPLICATE:
  5182. case NETLOGON_CONTROL_REDISCOVER:
  5183. case NETLOGON_CONTROL_TC_VERIFY:
  5184. case NETLOGON_CONTROL_FIND_USER:
  5185. case NETLOGON_CONTROL_CHANGE_PASSWORD:
  5186. case NETLOGON_CONTROL_FORCE_DNS_REG:
  5187. #if NETLOGONDBG
  5188. case NETLOGON_CONTROL_BREAKPOINT:
  5189. case NETLOGON_CONTROL_SET_DBFLAG:
  5190. case NETLOGON_CONTROL_TRUNCATE_LOG:
  5191. case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
  5192. #endif // NETLOGONDBG
  5193. default:
  5194. DesiredAccess = NETLOGON_CONTROL_ACCESS;
  5195. break;
  5196. }
  5197. //
  5198. // Perform access validation on the caller.
  5199. //
  5200. NetStatus = NetpAccessCheck(
  5201. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  5202. DesiredAccess, // Desired access
  5203. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  5204. if ( NetStatus != NERR_Success) {
  5205. NetStatus = ERROR_ACCESS_DENIED;
  5206. goto Cleanup;
  5207. }
  5208. //
  5209. // Handle the various FunctionCodes
  5210. //
  5211. switch ( FunctionCode ) {
  5212. //
  5213. // On a query, do nothing but return status.
  5214. //
  5215. case NETLOGON_CONTROL_QUERY:
  5216. NlPrintDom((NL_MISC, DomainInfo,
  5217. "QUERY function received.\n"));
  5218. break;
  5219. #ifdef _DC_NETLOGON
  5220. //
  5221. // Force a PDC to broadcast a database change record.
  5222. //
  5223. case NETLOGON_CONTROL_PDC_REPLICATE:
  5224. NlPrint((NL_SYNC, "PDC REPLICATE function received.\n" ));
  5225. #if 0
  5226. {
  5227. NlSitesUpdateSiteCoverage( DomainInfo, NULL );
  5228. NlPrintDom((NL_CRITICAL, DomainInfo,
  5229. "Cliffs test code *****************************.\n" ));
  5230. }
  5231. #endif
  5232. //
  5233. // This FunctionCode is only valid on a PDC
  5234. //
  5235. if ( !NlGlobalPdcDoReplication ) {
  5236. NlPrint((NL_CRITICAL, "PDC REPLICATE only supported in mixed mode.\n" ));
  5237. NetStatus = ERROR_NOT_SUPPORTED;
  5238. goto Cleanup;
  5239. }
  5240. //
  5241. // Simply send the announcement. Any BDC that is out of date
  5242. // will replicate any changes.
  5243. //
  5244. NlPrimaryAnnouncement( ANNOUNCE_FORCE );
  5245. break;
  5246. #endif // _DC_NETLOGON
  5247. //
  5248. // Force to rediscover trusted domain DCs.
  5249. //
  5250. case NETLOGON_CONTROL_REDISCOVER: {
  5251. LPWSTR DiscoveredDc;
  5252. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5253. "NETLOGON_CONTROL_REDISCOVER function received.\n"));
  5254. NlAssert( InputData->TrustedDomainName != NULL );
  5255. if( InputData->TrustedDomainName == NULL ) {
  5256. NlPrintDom((NL_CRITICAL, DomainInfo,
  5257. "NetrLogonControl called with function code NETLOGON_CONTROL_REDISCOVER "
  5258. "specified NULL trusted domain name. \n"));
  5259. NetStatus = ERROR_INVALID_PARAMETER;
  5260. goto Cleanup;
  5261. }
  5262. //
  5263. // Determine if there is a \ in the passed name.
  5264. // If so, truncate the string there and save a pointer to the
  5265. // DC name after the \.
  5266. //
  5267. DiscoveredDc = wcschr( InputData->TrustedDomainName, L'\\' );
  5268. if ( DiscoveredDc != NULL ) {
  5269. *DiscoveredDc = L'\0';
  5270. DiscoveredDc++;
  5271. }
  5272. RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
  5273. //
  5274. // get client structure for the specified domain.
  5275. //
  5276. ClientSession = NlFindNamedClientSession( DomainInfo,
  5277. &DomainName,
  5278. NL_DIRECT_TRUST_REQUIRED,
  5279. NULL );
  5280. if( ClientSession == NULL ) {
  5281. NlPrintDom((NL_CRITICAL, DomainInfo,
  5282. "NetrLogonControl can't find the client structure of the domain %wZ specified.\n",
  5283. &DomainName ));
  5284. NetStatus = ERROR_NO_SUCH_DOMAIN;
  5285. goto Cleanup;
  5286. }
  5287. //
  5288. // Ping the DC to figure out if it is available
  5289. //
  5290. if ( DiscoveredDc != NULL ) {
  5291. //
  5292. // We ensure that the DC has our account.
  5293. // Otherwise, if this DC doesn't have our
  5294. // account, the session setup may rediscover
  5295. // a different DC and we may end up setting
  5296. // up the secure channel to a DC other than
  5297. // the one passed to us.
  5298. //
  5299. NetStatus = NlPingDcName(
  5300. ClientSession,
  5301. 0, // Try both ping mechanisms
  5302. TRUE, // Cache this DC
  5303. FALSE, // Do not require IP
  5304. TRUE, // Ensure the DC has our account
  5305. FALSE, // Do not refresh the session
  5306. DiscoveredDc, // Ping this DC
  5307. &DcCacheEntry );
  5308. if ( NetStatus != NO_ERROR ) {
  5309. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  5310. "NetrLogonControl: Unsuccessful response from DC %ws 0x%lx\n",
  5311. DiscoveredDc, NetStatus ));
  5312. //
  5313. // If the service is paused on the server, return the
  5314. // appropriate status. Otherwise, map the status
  5315. // to a generic error code.
  5316. //
  5317. if ( NetStatus != ERROR_SERVICE_NOT_ACTIVE ) {
  5318. NetStatus = ERROR_NO_LOGON_SERVERS;
  5319. }
  5320. goto Cleanup;
  5321. } else {
  5322. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  5323. "NetrLogonControl: Successful response from DC %ws\n",
  5324. DiscoveredDc ));
  5325. }
  5326. }
  5327. //
  5328. // Force Discovery of a DC
  5329. //
  5330. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  5331. NlPrintDom((NL_CRITICAL, DomainInfo,
  5332. "NetrLogonControl2: Can't become writer of client session.\n"));
  5333. NetStatus = ERROR_NO_LOGON_SERVERS;
  5334. goto Cleanup;
  5335. } else {
  5336. //
  5337. // Reset the current DC.
  5338. //
  5339. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  5340. //
  5341. // If the caller specified a DC,
  5342. // set it in the client sesion structure.
  5343. //
  5344. if ( DcCacheEntry != NULL ) {
  5345. NetStatus = NlSetServerClientSession(
  5346. ClientSession,
  5347. DcCacheEntry,
  5348. TRUE, // was discovery with account
  5349. FALSE ); // not the session refresh
  5350. if ( NetStatus != NO_ERROR ) {
  5351. NlPrintCs(( NL_CRITICAL, ClientSession,
  5352. "NetrLogonControl: NlSetServerClientSession failed 0x%lx\n",
  5353. NetStatus ));
  5354. NlResetWriterClientSession( ClientSession );
  5355. goto Cleanup;
  5356. }
  5357. }
  5358. //
  5359. // Setup a session to the DC (Discover one if needed)
  5360. //
  5361. Status = NlSessionSetup( ClientSession );
  5362. NlResetWriterClientSession( ClientSession );
  5363. if ( !NT_SUCCESS(Status) ) {
  5364. NlPrintCs((NL_CRITICAL, ClientSession,
  5365. "NetrLogonControl: Discovery failed %lx\n",
  5366. Status ));
  5367. NetStatus = NetpNtStatusToApiStatus( Status );
  5368. goto Cleanup;
  5369. }
  5370. }
  5371. break;
  5372. }
  5373. case NETLOGON_CONTROL_TC_QUERY:
  5374. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5375. "NETLOGON_CONTROL_TC_QUERY function received.\n"));
  5376. NlAssert( InputData->TrustedDomainName != NULL );
  5377. if( InputData->TrustedDomainName == NULL ) {
  5378. NlPrintDom((NL_CRITICAL, DomainInfo,
  5379. "NetrLogonControl called with NETLOGON_CONTROL_TC_QUERY "
  5380. "and specified NULL trusted domain name. \n"));
  5381. NetStatus = ERROR_INVALID_PARAMETER;
  5382. goto Cleanup;
  5383. }
  5384. RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
  5385. //
  5386. // get client structure for the specified domain.
  5387. //
  5388. ClientSession = NlFindNamedClientSession( DomainInfo,
  5389. &DomainName,
  5390. NL_DIRECT_TRUST_REQUIRED,
  5391. NULL );
  5392. if( ClientSession == NULL ) {
  5393. NlPrintDom((NL_CRITICAL, DomainInfo,
  5394. "NetrLogonControl can't find the client structure of the domain %wZ specified.\n",
  5395. &DomainName ));
  5396. NetStatus = ERROR_NO_SUCH_DOMAIN;
  5397. goto Cleanup;
  5398. }
  5399. break;
  5400. case NETLOGON_CONTROL_TC_VERIFY:
  5401. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5402. "NETLOGON_CONTROL_TC_VERIFY function received.\n"));
  5403. NlAssert( InputData->TrustedDomainName != NULL );
  5404. if( InputData->TrustedDomainName == NULL ) {
  5405. NlPrintDom((NL_CRITICAL, DomainInfo,
  5406. "NetrLogonControl called with NETLOGON_CONTROL_TC_VERIFY "
  5407. "and specified NULL trusted domain name. \n"));
  5408. NetStatus = ERROR_INVALID_PARAMETER;
  5409. goto Cleanup;
  5410. }
  5411. //
  5412. // This requires query level 2
  5413. //
  5414. if ( QueryLevel != 2 ) {
  5415. NetStatus = ERROR_INVALID_LEVEL;
  5416. goto Cleanup;
  5417. }
  5418. RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
  5419. //
  5420. // get client structure for the specified domain.
  5421. //
  5422. ClientSession = NlFindNamedClientSession( DomainInfo,
  5423. &DomainName,
  5424. NL_DIRECT_TRUST_REQUIRED,
  5425. NULL );
  5426. if( ClientSession == NULL ) {
  5427. NlPrintDom((NL_CRITICAL, DomainInfo,
  5428. "NetrLogonControl can't find the client structure of the domain %wZ specified.\n",
  5429. &DomainName ));
  5430. NetStatus = ERROR_NO_SUCH_DOMAIN;
  5431. goto Cleanup;
  5432. }
  5433. //
  5434. // Verify the trust
  5435. //
  5436. Status = NlVerifyTrust( ClientSession, QueryInformation );
  5437. //
  5438. // NlVerifyTrust built all the required info,
  5439. // so we are done
  5440. //
  5441. NetStatus = NetpNtStatusToApiStatus( Status );
  5442. goto Cleanup;
  5443. case NETLOGON_CONTROL_CHANGE_PASSWORD:
  5444. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5445. "NETLOGON_CONTROL_CHANGE_PASSWORD function received.\n"));
  5446. // NlAssert( InputData->TrustedDomainName != NULL );
  5447. if( InputData->TrustedDomainName == NULL ) {
  5448. NlPrintDom((NL_CRITICAL, DomainInfo,
  5449. "NetrLogonControl called with NETLOGON_CONTROL_CHANGE_PASSWORD "
  5450. "and specified NULL trusted domain name. \n"));
  5451. NetStatus = ERROR_INVALID_PARAMETER;
  5452. goto Cleanup;
  5453. }
  5454. RtlInitUnicodeString(&DomainName, InputData->TrustedDomainName );
  5455. //
  5456. // get client structure for the specified domain.
  5457. //
  5458. ClientSession = NlFindNamedClientSession( DomainInfo,
  5459. &DomainName,
  5460. NL_DIRECT_TRUST_REQUIRED | NL_ROLE_PRIMARY_OK,
  5461. NULL );
  5462. if( ClientSession == NULL ) {
  5463. NlPrintDom((NL_CRITICAL, DomainInfo,
  5464. "NetrLogonControl can't find the client structure of the domain %wZ specified.\n",
  5465. &DomainName ));
  5466. NetStatus = ERROR_NO_SUCH_DOMAIN;
  5467. goto Cleanup;
  5468. }
  5469. //
  5470. // Do not allow password change for an interdomain trust account on a BDC
  5471. //
  5472. if ( (DomainInfo->DomRole == RoleBackup) &&
  5473. ( IsDomainSecureChannelType(ClientSession->CsSecureChannelType )) ) {
  5474. NlPrintDom((NL_CRITICAL, DomainInfo,
  5475. "NetrLogonControl called with NETLOGON_CONTROL_CHANGE_PASSWORD "
  5476. "for an interdomain trust account on a BDC. \n"));
  5477. NetStatus = ERROR_INVALID_DOMAIN_ROLE;
  5478. goto Cleanup;
  5479. }
  5480. //
  5481. // Force the password on the client session found
  5482. //
  5483. Status = NlChangePassword( ClientSession, TRUE, NULL );
  5484. if ( !NT_SUCCESS(Status) ) {
  5485. NlPrintCs((NL_CRITICAL, ClientSession,
  5486. "NetrLogonControl: Password Change failed %lx\n",
  5487. Status ));
  5488. NetStatus = NetpNtStatusToApiStatus( Status );
  5489. goto Cleanup;
  5490. }
  5491. break;
  5492. //
  5493. // A client has added a new transport and needs us to use it.
  5494. // Mark all the client sessions that its OK to authentication NOW.
  5495. //
  5496. case NETLOGON_CONTROL_TRANSPORT_NOTIFY: {
  5497. NlPrint((NL_SESSION_SETUP , "NETLOGON_CONTROL_TRANSPORT_NOTIFY function received.\n" ));
  5498. //
  5499. // Flush any caches that aren't valid any more since there
  5500. // is now a new transport
  5501. //
  5502. NlFlushCacheOnPnp();
  5503. break;
  5504. }
  5505. //
  5506. // Find a user in one of the trusted domains.
  5507. //
  5508. case NETLOGON_CONTROL_FIND_USER: {
  5509. UNICODE_STRING UserNameString;
  5510. NlPrint((NL_MISC, "NETLOGON_CONTROL_FIND_USER function received for %ws.\n", InputData->UserName ));
  5511. if ( QueryLevel != 4 ) {
  5512. NetStatus = ERROR_INVALID_PARAMETER;
  5513. goto Cleanup;
  5514. }
  5515. //
  5516. // Don't allow on workstation since CrackSingleName isn't implemented on
  5517. // a workstation.
  5518. //
  5519. if ( NlGlobalMemberWorkstation ) {
  5520. NetStatus = ERROR_NOT_SUPPORTED;
  5521. goto Cleanup;
  5522. }
  5523. //
  5524. // Find a user in one of the trusted domains.
  5525. //
  5526. // Allow machine accounts just as a handy extension.
  5527. // Don't find "Local User" accounts since we can't pass through to them
  5528. //
  5529. RtlInitUnicodeString( &UserNameString, InputData->UserName );
  5530. Status = NlPickDomainWithAccount (
  5531. DomainInfo,
  5532. &UserNameString,
  5533. NULL, // No domain name
  5534. USER_NORMAL_ACCOUNT | USER_MACHINE_ACCOUNT_MASK,
  5535. NullSecureChannel, // No incoming secure channel
  5536. FALSE, // Call wasn't expedited to root
  5537. FALSE, // Call wasn't first hop across forest.
  5538. &SamAccountName,
  5539. &SamDomainName,
  5540. &SamExtraFlags );
  5541. if ( !NT_SUCCESS( Status )) {
  5542. if ( Status == STATUS_NO_SUCH_DOMAIN ) {
  5543. NetStatus = NERR_UserNotFound;
  5544. } else {
  5545. NetStatus = NetpNtStatusToApiStatus( Status );
  5546. }
  5547. goto Cleanup;
  5548. }
  5549. //
  5550. // If the account isn't in this forest,
  5551. // tell the caller.
  5552. //
  5553. if ( SamExtraFlags & (NL_EXFLAGS_EXPEDITE_TO_ROOT|NL_EXFLAGS_CROSS_FOREST_HOP) ) {
  5554. NlPrintDom((NL_CRITICAL, DomainInfo,
  5555. "NetrLogonControl: User %ws is in a trusted forest (%lx).\n",
  5556. InputData->UserName,
  5557. SamExtraFlags ));
  5558. NetStatus = NERR_UserNotFound;
  5559. goto Cleanup;
  5560. }
  5561. }
  5562. break;
  5563. //
  5564. // Force re-registration of all DNS records for this DC
  5565. //
  5566. case NETLOGON_CONTROL_FORCE_DNS_REG:
  5567. NlPrint(( NL_DNS, "NETLOGON_CONTROL_FORCE_DNS_REG function received.\n" ));
  5568. //
  5569. // This is not supported on workstations
  5570. //
  5571. if ( NlGlobalMemberWorkstation ) {
  5572. NetStatus = ERROR_NOT_SUPPORTED;
  5573. goto Cleanup;
  5574. }
  5575. //
  5576. // Re-register all records
  5577. //
  5578. NlDnsForceScavenge( TRUE, // refresh domain entries
  5579. TRUE ); // force re-registration
  5580. break;
  5581. //
  5582. // Query the status of last DNS updates
  5583. //
  5584. case NETLOGON_CONTROL_QUERY_DNS_REG:
  5585. //
  5586. // This is not supported on workstations
  5587. //
  5588. if ( NlGlobalMemberWorkstation ) {
  5589. NetStatus = ERROR_NOT_SUPPORTED;
  5590. goto Cleanup;
  5591. }
  5592. //
  5593. // This requires query level 1
  5594. //
  5595. if ( QueryLevel != 1 ) {
  5596. NetStatus = ERROR_INVALID_LEVEL;
  5597. goto Cleanup;
  5598. }
  5599. //
  5600. // Call the worker
  5601. //
  5602. DnsLastStatusCheck = NlDnsCheckLastStatus();
  5603. break;
  5604. #if NETLOGONDBG
  5605. //
  5606. // Force a breakpoint
  5607. //
  5608. case NETLOGON_CONTROL_BREAKPOINT:
  5609. KdPrint(( "I_NetLogonControl Break Point\n"));
  5610. #if DBG
  5611. DbgBreakPoint();
  5612. #else // DBG
  5613. NetStatus = ERROR_NOT_SUPPORTED;
  5614. goto Cleanup;
  5615. #endif // DBG
  5616. break;
  5617. //
  5618. // Change the debug flags
  5619. //
  5620. case NETLOGON_CONTROL_SET_DBFLAG:
  5621. NlGlobalParameters.DbFlag = InputData->DebugFlag;
  5622. NlPrint((NL_MISC,"DbFlag is set to %lx\n", NlGlobalParameters.DbFlag ));
  5623. break;
  5624. //
  5625. // Truncate the log file
  5626. //
  5627. case NETLOGON_CONTROL_TRUNCATE_LOG:
  5628. NlOpenDebugFile( TRUE );
  5629. NlPrint((NL_MISC, "TRUNCATE_LOG function received.\n" ));
  5630. break;
  5631. //
  5632. // Backup changelog file
  5633. //
  5634. case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
  5635. NlPrint((NL_MISC, "BACKUP_CHANGE_LOG function received, (%ld).\n", NetStatus ));
  5636. NetStatus = NlBackupChangeLogFile();
  5637. break;
  5638. #if DBG
  5639. //
  5640. // Unload Netlogon.dll
  5641. //
  5642. case NETLOGON_CONTROL_UNLOAD_NETLOGON_DLL:
  5643. //
  5644. // Don't unload the DLL now.
  5645. // RPC still needs the the DLL as a security provider throughout shutdown.
  5646. //
  5647. NlPrint((NL_MISC, "UNLOAD_NETLOGON_DLL function received.\n" ));
  5648. NlGlobalUnloadNetlogon = TRUE;
  5649. NetStatus = NO_ERROR;
  5650. break;
  5651. #endif // DBG
  5652. #endif // NETLOGONDBG
  5653. //
  5654. // All other function codes are invalid.
  5655. //
  5656. default:
  5657. NetStatus = ERROR_NOT_SUPPORTED;
  5658. goto Cleanup;
  5659. }
  5660. //
  5661. // allocate return info structure.
  5662. //
  5663. switch( QueryLevel ) {
  5664. case (1):
  5665. InfoSize = sizeof(NETLOGON_INFO_1);
  5666. break;
  5667. case (2):
  5668. InfoSize = sizeof(NETLOGON_INFO_2);
  5669. break;
  5670. case (3):
  5671. InfoSize = sizeof(NETLOGON_INFO_3);
  5672. break;
  5673. case (4):
  5674. InfoSize = sizeof(NETLOGON_INFO_4);
  5675. break;
  5676. }
  5677. QueryInformation->NetlogonInfo1 = MIDL_user_allocate( InfoSize );
  5678. if ( QueryInformation->NetlogonInfo1 == NULL ) {
  5679. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5680. goto Cleanup;
  5681. }
  5682. //
  5683. // Return DomainName and DC Name.
  5684. //
  5685. switch( QueryLevel ) {
  5686. case (4):
  5687. switch ( FunctionCode ) {
  5688. case NETLOGON_CONTROL_FIND_USER: {
  5689. UNICODE_STRING SamDomainNameString;
  5690. //
  5691. // If the account is in this Domain,
  5692. // return the name of this DC.
  5693. //
  5694. RtlInitUnicodeString( &SamDomainNameString, SamDomainName );
  5695. if ( RtlEqualDomainName( &SamDomainNameString,
  5696. &DomainInfo->DomUnicodeDomainNameString ) ||
  5697. NlEqualDnsNameU( &SamDomainNameString,
  5698. &DomainInfo->DomUnicodeDnsDomainNameString ) ) {
  5699. //
  5700. // Grab the name of this DC.
  5701. //
  5702. TDCName = NetpAllocWStrFromWStr( DomainInfo->DomUncUnicodeComputerName );
  5703. if ( TDCName == NULL ) {
  5704. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5705. goto Cleanup;
  5706. }
  5707. //
  5708. // Grab the name of this domain.
  5709. //
  5710. LOCK_TRUST_LIST( DomainInfo );
  5711. TrustedDomainName = NetpAllocWStrFromWStr( DomainInfo->DomUnicodeDomainName );
  5712. UNLOCK_TRUST_LIST( DomainInfo );
  5713. if ( TrustedDomainName == NULL ) {
  5714. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5715. goto Cleanup;
  5716. }
  5717. //
  5718. // If the account is not in this domain,
  5719. // find the client session and get the DC name from there.
  5720. //
  5721. } else {
  5722. //
  5723. // Find the client session of the Domain anywhere in the forest.
  5724. //
  5725. ClientSession = NlFindNamedClientSession(
  5726. DomainInfo,
  5727. &SamDomainNameString,
  5728. 0, // Indirect trust OK
  5729. NULL );
  5730. if ( ClientSession == NULL ) {
  5731. //
  5732. // Replication latency. The GC knows of a domain in the forest
  5733. // that we don't trust.
  5734. //
  5735. NlPrintDom((NL_CRITICAL, DomainInfo,
  5736. "NetrLogonControl: User %ws\\%ws apparently isn't in this forest.\n",
  5737. SamAccountName,
  5738. SamDomainName ));
  5739. NetStatus = NERR_UserNotFound;
  5740. goto Cleanup;
  5741. }
  5742. //
  5743. // If the account isn't on a directly trusted domain,
  5744. // indicate that we don't know the DC.
  5745. //
  5746. if ( (ClientSession->CsFlags & CS_DIRECT_TRUST) == 0 ) {
  5747. TDCName = NULL;
  5748. //
  5749. // If the account is on a directly trusted domain,
  5750. // return the name of a DC in the domain.
  5751. //
  5752. // Capture the name of the server
  5753. // (even if it is an empty string.)
  5754. //
  5755. } else {
  5756. // REVIEW: do discovery here.
  5757. Status = NlCaptureServerClientSession( ClientSession, &TDCName, NULL );
  5758. if ( !NT_SUCCESS( Status )) {
  5759. TDCName = NetpAllocWStrFromWStr( L"" );
  5760. }
  5761. if ( TDCName == NULL ) {
  5762. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5763. goto Cleanup;
  5764. }
  5765. }
  5766. //
  5767. // Capture the name of the domain.
  5768. //
  5769. if ( ClientSession->CsDebugDomainName != NULL ) {
  5770. TrustedDomainName = NetpAllocWStrFromWStr( ClientSession->CsDebugDomainName );
  5771. if ( TrustedDomainName == NULL ) {
  5772. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5773. goto Cleanup;
  5774. }
  5775. } else {
  5776. TrustedDomainName = NULL;
  5777. }
  5778. }
  5779. QueryInformation->NetlogonInfo4->netlog4_trusted_dc_name = TDCName;
  5780. QueryInformation->NetlogonInfo4->netlog4_trusted_domain_name = TrustedDomainName;
  5781. break;
  5782. }
  5783. default:
  5784. NetStatus = ERROR_INVALID_PARAMETER;
  5785. goto Cleanup;
  5786. }
  5787. break;
  5788. //
  5789. // Return queried profile information.
  5790. //
  5791. case (3):
  5792. QueryInformation->NetlogonInfo3->netlog3_flags = 0;
  5793. QueryInformation->NetlogonInfo3->netlog3_logon_attempts =
  5794. // ??: What about kerberos logons
  5795. MsvGetLogonAttemptCount();
  5796. QueryInformation->NetlogonInfo3->netlog3_reserved1 = 0;
  5797. QueryInformation->NetlogonInfo3->netlog3_reserved2 = 0;
  5798. QueryInformation->NetlogonInfo3->netlog3_reserved3 = 0;
  5799. QueryInformation->NetlogonInfo3->netlog3_reserved4 = 0;
  5800. QueryInformation->NetlogonInfo3->netlog3_reserved5 = 0;
  5801. break;
  5802. //
  5803. // Return secure channel specific information.
  5804. //
  5805. case (2):
  5806. switch ( FunctionCode ) {
  5807. case NETLOGON_CONTROL_REDISCOVER:
  5808. case NETLOGON_CONTROL_TC_QUERY:
  5809. case NETLOGON_CONTROL_TC_VERIFY:
  5810. if( ClientSession == NULL ) {
  5811. NetStatus = ERROR_NO_SUCH_DOMAIN;
  5812. goto Cleanup;
  5813. }
  5814. //
  5815. // Capture the name of the server
  5816. // (even if it is an empty string.)
  5817. //
  5818. Status = NlCaptureServerClientSession( ClientSession,
  5819. &TDCName,
  5820. &TcServerDiscoveryFlags );
  5821. QueryInformation->NetlogonInfo2->netlog2_tc_connection_status =
  5822. NetpNtStatusToApiStatus(Status);
  5823. if ( !NT_SUCCESS( Status )) {
  5824. TDCName = NetpAllocWStrFromWStr( L"" );
  5825. }
  5826. if ( TDCName == NULL ) {
  5827. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  5828. goto Cleanup;
  5829. }
  5830. QueryInformation->NetlogonInfo2->netlog2_trusted_dc_name = TDCName;
  5831. break;
  5832. default:
  5833. NetStatus = ERROR_INVALID_PARAMETER;
  5834. goto Cleanup;
  5835. }
  5836. //
  5837. // fall through to fill other fields of the info structure.
  5838. //
  5839. //
  5840. // Return status of secure channel to PDC.
  5841. //
  5842. case (1):
  5843. //
  5844. // Fill in the return buffer
  5845. //
  5846. QueryInformation->NetlogonInfo1->netlog1_flags = 0;
  5847. if ( TcServerDiscoveryFlags & CS_DISCOVERY_HAS_TIMESERV ) {
  5848. QueryInformation->NetlogonInfo1->netlog1_flags |= NETLOGON_HAS_TIMESERV;
  5849. }
  5850. if ( TcServerDiscoveryFlags & CS_DISCOVERY_HAS_IP ) {
  5851. QueryInformation->NetlogonInfo1->netlog1_flags |= NETLOGON_HAS_IP;
  5852. }
  5853. if ( !DnsLastStatusCheck ) {
  5854. QueryInformation->NetlogonInfo1->netlog1_flags |= NETLOGON_DNS_UPDATE_FAILURE;
  5855. }
  5856. if ( DomainInfo->DomRole == RolePrimary ) {
  5857. QueryInformation->NetlogonInfo1->netlog1_pdc_connection_status =
  5858. NERR_Success;
  5859. } else {
  5860. PCLIENT_SESSION LocalClientSession;
  5861. LocalClientSession = NlRefDomClientSession( DomainInfo );
  5862. if ( LocalClientSession != NULL ) {
  5863. QueryInformation->NetlogonInfo1->netlog1_pdc_connection_status =
  5864. NetpNtStatusToApiStatus( LocalClientSession->CsConnectionStatus);
  5865. NlUnrefClientSession( LocalClientSession );
  5866. } else {
  5867. QueryInformation->NetlogonInfo1->netlog1_pdc_connection_status =
  5868. ERROR_NOT_SUPPORTED;
  5869. }
  5870. }
  5871. break;
  5872. default:
  5873. break;
  5874. }
  5875. NetStatus = NERR_Success;
  5876. //
  5877. // Free up locally used resources.
  5878. //
  5879. Cleanup:
  5880. if( ClientSession != NULL ) {
  5881. NlUnrefClientSession( ClientSession );
  5882. }
  5883. if ( SamAccountName != NULL ) {
  5884. NetApiBufferFree( SamAccountName );
  5885. }
  5886. if ( SamDomainName != NULL ) {
  5887. NetApiBufferFree( SamDomainName );
  5888. }
  5889. if ( NetStatus != NERR_Success ) {
  5890. if ( QueryInformation->NetlogonInfo1 != NULL ) {
  5891. MIDL_user_free( QueryInformation->NetlogonInfo1 );
  5892. QueryInformation->NetlogonInfo1 = NULL;
  5893. }
  5894. if ( TDCName != NULL ) {
  5895. MIDL_user_free( TDCName );
  5896. }
  5897. if ( TrustedDomainName != NULL ) {
  5898. MIDL_user_free( TrustedDomainName );
  5899. }
  5900. }
  5901. if ( DomainInfo != NULL ) {
  5902. NlDereferenceDomain( DomainInfo );
  5903. }
  5904. if ( DcCacheEntry != NULL ) {
  5905. NetpDcDerefCacheEntry( DcCacheEntry );
  5906. }
  5907. return NetStatus;
  5908. }
  5909. VOID
  5910. NlFreePingContext(
  5911. IN PNL_GETDC_CONTEXT PingContext
  5912. )
  5913. /*++
  5914. Routine Description:
  5915. Free the context used to perform DC pings.
  5916. Arguments:
  5917. PingContext - Context used to perform the pings.
  5918. --*/
  5919. {
  5920. if ( PingContext != NULL ) {
  5921. NetpDcUninitializeContext( PingContext );
  5922. NetApiBufferFree( PingContext );
  5923. }
  5924. }
  5925. NET_API_STATUS
  5926. NlPingDcName (
  5927. IN PCLIENT_SESSION ClientSession,
  5928. IN ULONG DcNamePingFlags,
  5929. IN BOOL CachePingedDc,
  5930. IN BOOL RequireIp,
  5931. IN BOOL DoPingWithAccount,
  5932. IN BOOL RefreshClientSession,
  5933. IN LPWSTR DcName OPTIONAL,
  5934. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry OPTIONAL
  5935. )
  5936. /*++
  5937. Routine Description:
  5938. Ping the specified DC using the appropriate ping mechanism.
  5939. If RefreshClientSession is TRUE, the caller must be the writer
  5940. of the passed client session.
  5941. Arguments:
  5942. ClientSession - The client session to ping a DC for. If DcName is
  5943. NULL, the server from ClientSession is pinged. If DcName isn't
  5944. NULL, ClientSession is used to get the session info (other than
  5945. the server name) needed to perform the pings.
  5946. DcNamePingFlags - Specifies properties of DcName. Can be
  5947. DS_PING_NETBIOS_HOST or DS_PING_NETBIOS_HOST or zero. If other than
  5948. zero, only the specified ping mechanism will be used.
  5949. CachePingedDc - If TRUE, the successfully pinged DC will be cached
  5950. for future use by DsGetDcName.
  5951. RequireIp - TRUE if pinging the DC must be done using only IP enabled
  5952. transports.
  5953. DoPingWithAccount - If TRUE, the account name for this machine will be
  5954. specified in the pings.
  5955. RefreshClientSession - If TRUE, the client session will be refreshed
  5956. using the ping response info. If TRUE, the caller must be the
  5957. writer of the client session.
  5958. DcName - If set, that DC name will be pinged using info from ClientSession.
  5959. NlDcCacheEntry - Returns the data structure describing response received
  5960. from the DC. Should be freed by calling NetpDcDerefCacheEntry.
  5961. Return Value:
  5962. NO_ERROR - Success.
  5963. ERROR_NO_LOGON_SERVERS - No DC could be found
  5964. ERROR_NO_SUCH_USER - Returned if we do ping with account and the DC doesn't
  5965. have the account specified.
  5966. ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
  5967. domain controller of the specified domain.
  5968. ERROR_SERVICE_NOT_ACTIVE - The netlogon service is paused on the server.
  5969. --*/
  5970. {
  5971. NTSTATUS Status;
  5972. NET_API_STATUS NetStatus;
  5973. LPWSTR LocalDcName = NULL;
  5974. LPWSTR CapturedDnsForestName = NULL;
  5975. ULONG AllowableAccountControlBits;
  5976. NL_GETDC_CONTEXT Context;
  5977. BOOL TriedContextInitialization = FALSE;
  5978. ULONG Flags = 0;
  5979. ULONG InternalFlags = 0;
  5980. PDNS_RECORD DnsRecords = NULL;
  5981. LPSOCKET_ADDRESS SockAddresses = NULL;
  5982. LPSOCKET_ADDRESS AllocatedSockAddresses = NULL;
  5983. SOCKET_ADDRESS OneSockAddress;
  5984. SOCKADDR_IN OneSockAddressIn;
  5985. ULONG SockAddressCount = 0;
  5986. ULONG LoopIndex;
  5987. PNL_DC_CACHE_ENTRY LocalNlDcCacheEntry = NULL;
  5988. //
  5989. // If a DC name specified, try to figure out its properties
  5990. //
  5991. if ( DcName != NULL ) {
  5992. //
  5993. // Prove below that the name is valid
  5994. //
  5995. NetStatus = ERROR_INVALID_COMPUTERNAME;
  5996. //
  5997. // If the DC name is a syntactically valid DNS name,
  5998. // assume the server name is a DNS name.
  5999. //
  6000. // Skip this step if we are told that the name is Netbios.
  6001. //
  6002. if ( (DcNamePingFlags & DS_PING_NETBIOS_HOST) == 0 &&
  6003. NetpDcValidDnsDomain(DcName) ) {
  6004. NetStatus = NO_ERROR;
  6005. //
  6006. // Get the IP address of the server from DNS
  6007. //
  6008. NetStatus = DnsQuery_W( DcName,
  6009. DNS_TYPE_A,
  6010. 0,
  6011. NULL, // No list of DNS servers
  6012. &DnsRecords,
  6013. NULL );
  6014. //
  6015. // On success, set to use ldap pings. Otherwise, do not
  6016. // error out, rather try the mailslot mechanism
  6017. //
  6018. if ( NetStatus == NO_ERROR ) {
  6019. NetStatus = NetpSrvProcessARecords( DnsRecords,
  6020. NULL,
  6021. 0,
  6022. &SockAddressCount,
  6023. &AllocatedSockAddresses );
  6024. if ( NetStatus == NO_ERROR && SockAddressCount > 0 ) {
  6025. SockAddresses = AllocatedSockAddresses;
  6026. InternalFlags |= DS_PING_USING_LDAP;
  6027. InternalFlags |= DS_PING_DNS_HOST;
  6028. }
  6029. }
  6030. }
  6031. //
  6032. // If the DC name is syntactically valid Netbios name,
  6033. // assume you can use mailslot pings
  6034. //
  6035. // Skip this step if we are told that the name is DNS.
  6036. //
  6037. if ( (DcNamePingFlags & DS_PING_DNS_HOST) == 0 &&
  6038. NetpIsComputerNameValid(DcName) &&
  6039. wcslen(DcName) <= CNLEN ) { // NetpIsComputerNameValid doesn't require 15 chacter limit
  6040. NetStatus = NO_ERROR;
  6041. InternalFlags |= DS_PING_USING_MAILSLOT;
  6042. InternalFlags |= DS_PING_NETBIOS_HOST;
  6043. }
  6044. //
  6045. // If there is no ping mechanism to use, error out
  6046. //
  6047. if ( (InternalFlags & (DS_PING_USING_LDAP|DS_PING_USING_MAILSLOT)) == 0 ) {
  6048. NlPrintCs(( NL_CRITICAL, ClientSession,
  6049. "NlPingDcName: No ping mechanism for %ws 0x%lx\n",
  6050. DcName, NetStatus ));
  6051. NetStatus = ERROR_NO_LOGON_SERVERS;
  6052. goto Cleanup;
  6053. }
  6054. LocalDcName = DcName;
  6055. }
  6056. //
  6057. // If this is a client session to a domain in our
  6058. // forest (always the case on workstation),
  6059. // get our forest name.
  6060. //
  6061. if ( NlGlobalMemberWorkstation ||
  6062. (ClientSession->CsFlags & CS_DOMAIN_IN_FOREST) != 0 ) {
  6063. CapturedDnsForestName = LocalAlloc( LMEM_ZEROINIT,
  6064. (NL_MAX_DNS_LENGTH+1)*sizeof(WCHAR) );
  6065. if ( CapturedDnsForestName == NULL ) {
  6066. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6067. goto Cleanup;
  6068. }
  6069. NlCaptureDnsForestName( CapturedDnsForestName );
  6070. }
  6071. //
  6072. // Capture the needed info from the Client session
  6073. //
  6074. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6075. //
  6076. // Capture the server name if the one wasn't passed.
  6077. // If the client session is idle, this will fail.
  6078. //
  6079. if ( DcName == NULL ) {
  6080. ULONG DiscoveryFlags = 0;
  6081. Status = NlCaptureServerClientSession( ClientSession,
  6082. &LocalDcName,
  6083. &DiscoveryFlags );
  6084. if ( !NT_SUCCESS(Status) ) {
  6085. NlPrintCs(( NL_CRITICAL, ClientSession,
  6086. "NlPingDcName: Cannot NlCaptureServerClientSession %ld\n",
  6087. Status ));
  6088. if ( Status == STATUS_NO_MEMORY ) {
  6089. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  6090. } else {
  6091. NetStatus = ERROR_NO_LOGON_SERVERS;
  6092. }
  6093. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6094. goto Cleanup;
  6095. }
  6096. if ( DiscoveryFlags & CS_DISCOVERY_DNS_SERVER ) {
  6097. InternalFlags |= DS_PING_DNS_HOST;
  6098. } else {
  6099. InternalFlags |= DS_PING_NETBIOS_HOST;
  6100. }
  6101. if ( DiscoveryFlags & CS_DISCOVERY_USE_LDAP ) {
  6102. InternalFlags |= DS_PING_USING_LDAP;
  6103. //
  6104. // Capture the cached server socket address
  6105. //
  6106. NlAssert( ClientSession->CsServerSockAddr.iSockaddrLength != 0 );
  6107. OneSockAddress.iSockaddrLength = ClientSession->CsServerSockAddr.iSockaddrLength;
  6108. OneSockAddress.lpSockaddr = (LPSOCKADDR) &OneSockAddressIn;
  6109. RtlCopyMemory( OneSockAddress.lpSockaddr,
  6110. ClientSession->CsServerSockAddr.lpSockaddr,
  6111. ClientSession->CsServerSockAddr.iSockaddrLength );
  6112. SockAddresses = &OneSockAddress;
  6113. SockAddressCount = 1;
  6114. }
  6115. if ( DiscoveryFlags & CS_DISCOVERY_USE_MAILSLOT ) {
  6116. InternalFlags |= DS_PING_USING_MAILSLOT;
  6117. }
  6118. }
  6119. //
  6120. // If this is NT5 domain, its (non-NULL) DNS domain name is trusted to be correct.
  6121. // Otherwise, we don't trust the DNS domain name (NULL) because we may not know
  6122. // the correct DNS name of externaly trusted domain after the domain got upgraded
  6123. // (because we don't update TDOs on trusting side).
  6124. //
  6125. if ( ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST ) {
  6126. InternalFlags |= DS_IS_TRUSTED_DNS_DOMAIN;
  6127. }
  6128. //
  6129. // Determine the Account type we're looking for.
  6130. //
  6131. switch ( ClientSession->CsSecureChannelType ) {
  6132. case WorkstationSecureChannel:
  6133. AllowableAccountControlBits = USER_WORKSTATION_TRUST_ACCOUNT;
  6134. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  6135. break;
  6136. case TrustedDomainSecureChannel:
  6137. AllowableAccountControlBits = USER_INTERDOMAIN_TRUST_ACCOUNT;
  6138. break;
  6139. case TrustedDnsDomainSecureChannel:
  6140. AllowableAccountControlBits = USER_DNS_DOMAIN_TRUST_ACCOUNT;
  6141. break;
  6142. case ServerSecureChannel:
  6143. AllowableAccountControlBits = USER_SERVER_TRUST_ACCOUNT;
  6144. Flags |= DS_PDC_REQUIRED;
  6145. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  6146. break;
  6147. default:
  6148. NlPrintCs(( NL_CRITICAL, ClientSession,
  6149. "NlPingDcName: invalid SecureChannelType %ld\n",
  6150. ClientSession->CsSecureChannelType ));
  6151. NetStatus = ERROR_NO_LOGON_SERVERS;
  6152. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6153. goto Cleanup;
  6154. }
  6155. //
  6156. // Indicate whether IP transport is required
  6157. //
  6158. if ( RequireIp ) {
  6159. Flags |= DS_IP_REQUIRED;
  6160. }
  6161. //
  6162. // Initialize the ping context.
  6163. //
  6164. TriedContextInitialization = TRUE;
  6165. NetStatus = NetpDcInitializeContext(
  6166. ClientSession->CsDomainInfo, // SendDatagramContext
  6167. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  6168. #ifdef DONT_REQUIRE_MACHINE_ACCOUNT // useful for number of trust testing
  6169. NULL,
  6170. #else // DONT_REQUIRE_MACHINE_ACCOUNT
  6171. DoPingWithAccount ? // Specify the account name as directed
  6172. ClientSession->CsAccountName :
  6173. NULL,
  6174. #endif // DONT_REQUIRE_MACHINE_ACCOUNT
  6175. DoPingWithAccount ? // Specify the account control bits as directed
  6176. AllowableAccountControlBits :
  6177. 0,
  6178. ClientSession->CsNetbiosDomainName.Buffer,
  6179. ClientSession->CsDnsDomainName.Buffer,
  6180. CapturedDnsForestName,
  6181. ClientSession->CsDomainId,
  6182. ClientSession->CsDomainGuid,
  6183. NULL,
  6184. DcName == NULL ?
  6185. LocalDcName+2 : // Skip '\\' in the DC name
  6186. LocalDcName, // captured from the client session
  6187. SockAddresses,
  6188. SockAddressCount,
  6189. Flags,
  6190. InternalFlags,
  6191. NL_GETDC_CONTEXT_INITIALIZE_FLAGS | NL_GETDC_CONTEXT_INITIALIZE_PING,
  6192. &Context );
  6193. if ( NetStatus != NO_ERROR ) {
  6194. NlPrintCs(( NL_CRITICAL, ClientSession,
  6195. "NlPingDcName: Cannot NetpDcInitializeContext 0x%lx\n",
  6196. NetStatus ));
  6197. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6198. goto Cleanup;
  6199. }
  6200. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6201. //
  6202. // Ping the DC and get a response from it
  6203. //
  6204. // If we ping using a cached IP address, it's possible that server's
  6205. // IP address changed. For this case, if we fail to ping the server
  6206. // on the first try, we will refresh the address by querying DNS
  6207. // and then we will retry to ping the server.
  6208. //
  6209. for ( LoopIndex = 0; LoopIndex < 2; LoopIndex++ ) {
  6210. NET_API_STATUS TmpNetStatus;
  6211. ULONG TmpSockAddressCount = 0;
  6212. ULONG Index;
  6213. if ( LocalNlDcCacheEntry != NULL ) {
  6214. NetpDcDerefCacheEntry( LocalNlDcCacheEntry );
  6215. LocalNlDcCacheEntry = NULL;
  6216. }
  6217. NetStatus = NlPingDcNameWithContext(
  6218. &Context,
  6219. MAX_DC_RETRIES, // send 2 pings
  6220. TRUE, // wait for response
  6221. (NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000), // timeout
  6222. NULL, // Don't care which domain name matched
  6223. &LocalNlDcCacheEntry );
  6224. //
  6225. // If we pinged successfully or
  6226. // we got hard error or
  6227. // we didn't do LDAP pings or
  6228. // we already did DNS queries because DC name was passed or
  6229. // this is the second attempt to ping the DC,
  6230. //
  6231. // we are done
  6232. //
  6233. if ( NetStatus == NO_ERROR ||
  6234. NetStatus == ERROR_NOT_ENOUGH_MEMORY ||
  6235. SockAddresses == NULL ||
  6236. DcName != NULL ||
  6237. LoopIndex == 1 ) {
  6238. break;
  6239. }
  6240. //
  6241. // Attempt to get fresh DNS records
  6242. //
  6243. TmpNetStatus = DnsQuery_W( LocalDcName+2, // Skip '\\' in the DC name
  6244. DNS_TYPE_A,
  6245. 0,
  6246. NULL, // No list of DNS servers
  6247. &DnsRecords,
  6248. NULL );
  6249. //
  6250. // Bail on error
  6251. //
  6252. if ( TmpNetStatus != NO_ERROR ) {
  6253. break;
  6254. }
  6255. //
  6256. // Process fresh DNS records
  6257. //
  6258. TmpNetStatus = NetpSrvProcessARecords( DnsRecords,
  6259. NULL,
  6260. 0, // force port to be zero
  6261. &TmpSockAddressCount,
  6262. &AllocatedSockAddresses );
  6263. //
  6264. // Bail on error
  6265. //
  6266. if ( TmpNetStatus != NO_ERROR || TmpSockAddressCount == 0 ) {
  6267. break;
  6268. }
  6269. //
  6270. // Check if there are new addresses we didn't try
  6271. //
  6272. SockAddressCount = 0;
  6273. for ( Index = 0; Index < TmpSockAddressCount; Index++ ) {
  6274. //
  6275. // Keep this entry if it's not the one we had
  6276. //
  6277. if ( AllocatedSockAddresses[Index].iSockaddrLength != OneSockAddress.iSockaddrLength ||
  6278. !RtlEqualMemory(AllocatedSockAddresses[Index].lpSockaddr,
  6279. OneSockAddress.lpSockaddr,
  6280. OneSockAddress.iSockaddrLength) ) {
  6281. AllocatedSockAddresses[SockAddressCount] = AllocatedSockAddresses[Index];
  6282. SockAddressCount ++;
  6283. }
  6284. }
  6285. //
  6286. // Bail if we didn't get any new addresses
  6287. //
  6288. if ( SockAddressCount == 0 ) {
  6289. break;
  6290. }
  6291. //
  6292. // We have new addresses. Free the current
  6293. // address list and add the new addresses
  6294. //
  6295. NetpDcFreeAddressList( &Context );
  6296. TmpNetStatus = NetpDcProcessAddressList( &Context,
  6297. LocalDcName+2,
  6298. AllocatedSockAddresses,
  6299. SockAddressCount,
  6300. FALSE, // Don't know if site specific
  6301. NULL );
  6302. //
  6303. // Bail on error. Otherwise, retry pinging
  6304. // given the new addresses.
  6305. //
  6306. if ( TmpNetStatus != NO_ERROR ) {
  6307. break;
  6308. }
  6309. NlPrintCs(( NL_CRITICAL, ClientSession,
  6310. "NlPingDcName: Retry DC ping for %lu new addresses\n",
  6311. SockAddressCount ));
  6312. }
  6313. //
  6314. // Cache this DC if asked
  6315. //
  6316. if ( NetStatus == NO_ERROR && CachePingedDc ) {
  6317. //
  6318. // Set the FORCE flag so that the old entry (if any) gets replaced
  6319. //
  6320. Context.QueriedFlags |= DS_FORCE_REDISCOVERY;
  6321. NetpDcInsertCacheEntry( &Context, LocalNlDcCacheEntry );
  6322. NlPrintCs(( NL_MISC, ClientSession,
  6323. "NlPingDcName: %ws: %ws: Caching pinged DC info for %ws\n",
  6324. Context.NlDcDomainEntry->UnicodeNetbiosDomainName,
  6325. Context.NlDcDomainEntry->UnicodeDnsDomainName,
  6326. LocalDcName ));
  6327. }
  6328. //
  6329. // Refresh the client session, if asked
  6330. //
  6331. if ( NetStatus == NO_ERROR && RefreshClientSession ) {
  6332. NetStatus = NlSetServerClientSession(
  6333. ClientSession,
  6334. LocalNlDcCacheEntry,
  6335. DoPingWithAccount, // was discovery with account?
  6336. TRUE ); // session refresh
  6337. }
  6338. //
  6339. // Return the cache entry
  6340. //
  6341. if ( NetStatus == NO_ERROR && NlDcCacheEntry != NULL ) {
  6342. *NlDcCacheEntry = LocalNlDcCacheEntry;
  6343. LocalNlDcCacheEntry = NULL;
  6344. }
  6345. Cleanup:
  6346. if ( DnsRecords != NULL ) {
  6347. DnsRecordListFree( DnsRecords, DnsFreeRecordListDeep );
  6348. }
  6349. if ( AllocatedSockAddresses != NULL ) {
  6350. LocalFree( AllocatedSockAddresses );
  6351. }
  6352. if ( DcName == NULL && LocalDcName != NULL ) {
  6353. NetApiBufferFree( LocalDcName );
  6354. }
  6355. if ( TriedContextInitialization ) {
  6356. NetpDcUninitializeContext( &Context );
  6357. }
  6358. if ( LocalNlDcCacheEntry != NULL ) {
  6359. NetpDcDerefCacheEntry( LocalNlDcCacheEntry );
  6360. }
  6361. if ( CapturedDnsForestName != NULL ) {
  6362. LocalFree( CapturedDnsForestName );
  6363. }
  6364. return NetStatus;
  6365. }
  6366. NTSTATUS
  6367. NlGetAnyDCName (
  6368. IN PCLIENT_SESSION ClientSession,
  6369. IN BOOL RequireIp,
  6370. IN BOOL EnsureDcHasOurAccount,
  6371. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry,
  6372. OUT PBOOLEAN DcRediscovered
  6373. )
  6374. /*++
  6375. Routine Description:
  6376. Get the name of the any domain controller for a trusted domain.
  6377. The domain controller found in guaranteed to have be up at one point during
  6378. this API call. The machine is also guaranteed to be a DC in the domain
  6379. specified.
  6380. The caller of this routine should not have any locks held (it calls the
  6381. LSA back in several instances). This routine may take some time to execute.
  6382. The caller must be a writer of the ClientSession.
  6383. Arguments:
  6384. ClientSession - Structure describing the session to the domain whose
  6385. DC is to be returned.
  6386. RequireIp - TRUE if communication with the DC must be done using only
  6387. IP enabled transports.
  6388. EnsureDcHasOurAccount - If TRUE this routine will verify that the returned
  6389. DC has an account for this machine.
  6390. NlDcCacheEntry - Returns the data structure describing response received
  6391. from the server. Should be freed by calling NetpDcDerefCacheEntry
  6392. DcRediscovered - Returns whether a new DC has been discovered
  6393. Return Value:
  6394. STATUS_SUCCESS - Success. Buffer contains DC name prefixed by \\.
  6395. STATUS_NO_LOGON_SERVERS - No DC could be found
  6396. STATUS_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
  6397. STATUS_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  6398. broken.
  6399. STATUS_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  6400. broken or the password is broken.
  6401. STATUS_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
  6402. domain controller of the specified domain.
  6403. --*/
  6404. {
  6405. NTSTATUS Status;
  6406. NET_API_STATUS NetStatus;
  6407. BOOLEAN DiscoveryDone = FALSE;
  6408. PNL_DC_CACHE_ENTRY NlLocalDcCacheEntry = NULL;
  6409. //
  6410. // Don't give up unless we've done discovery.
  6411. //
  6412. do {
  6413. //
  6414. // If we don't currently know the name of the server,
  6415. // discover one.
  6416. //
  6417. if ( ClientSession->CsState == CS_IDLE ) {
  6418. //
  6419. // If we've tried to authenticate recently,
  6420. // don't bother trying again.
  6421. //
  6422. if ( !NlTimeToReauthenticate( ClientSession ) ) {
  6423. Status = ClientSession->CsConnectionStatus;
  6424. goto Cleanup;
  6425. }
  6426. Status = NlDiscoverDc( ClientSession,
  6427. DT_Synchronous,
  6428. FALSE,
  6429. EnsureDcHasOurAccount ?
  6430. TRUE :
  6431. FALSE );
  6432. if ( !NT_SUCCESS(Status) ) {
  6433. NlPrintCs((NL_CRITICAL, ClientSession,
  6434. "NlGetAnyDCName: Discovery failed %lx\n",
  6435. Status ));
  6436. goto Cleanup;
  6437. }
  6438. DiscoveryDone = TRUE;
  6439. }
  6440. //
  6441. // Try multiple times to get a response from the DC using the appropriate protocol.
  6442. //
  6443. if ( NlLocalDcCacheEntry != NULL ) {
  6444. NetpDcDerefCacheEntry( NlLocalDcCacheEntry );
  6445. NlLocalDcCacheEntry = NULL;
  6446. }
  6447. NetStatus = NlPingDcName( ClientSession,
  6448. 0, // Use ping mechanism specified in ClientSession
  6449. FALSE, // Don't cache this DC (it is already cached)
  6450. RequireIp, // Require IP as the caller said
  6451. EnsureDcHasOurAccount,
  6452. TRUE, // Refresh the session since we are the writer
  6453. NULL, // Ping the server specified in ClientSession
  6454. &NlLocalDcCacheEntry );
  6455. //
  6456. // If we couldn't ping the DC when IP was required, see if we can ping it at all.
  6457. // If we can't ping it at all, this DC is dead, so drop it. Otherwise, the DC is
  6458. // alive (so we won't drop it), but the caller is out of luck.
  6459. //
  6460. if ( NetStatus == ERROR_NO_LOGON_SERVERS && RequireIp ) {
  6461. NET_API_STATUS TmpNetStatus;
  6462. TmpNetStatus = NlPingDcName( ClientSession,
  6463. 0, // Use ping mechanism specified in ClientSession
  6464. FALSE, // Don't cache this DC (it is already cached)
  6465. FALSE, // Do not require IP
  6466. FALSE, // Don't specify account name
  6467. TRUE, // Refresh the session since we are the writer
  6468. NULL, // Ping the server specified in ClientSession
  6469. NULL ); // No cache entry needed
  6470. //
  6471. // Don't drop this DC if it's alive
  6472. //
  6473. if ( TmpNetStatus == NO_ERROR ) {
  6474. NlPrintCs(( NL_CRITICAL, ClientSession,
  6475. "NlGetAnyDCName: IP is required but only non-IP is available for %ws\n",
  6476. ClientSession->CsUncServerName ));
  6477. Status = STATUS_NO_LOGON_SERVERS;
  6478. goto Cleanup;
  6479. }
  6480. //
  6481. // Drop through and re-discover a new DC
  6482. //
  6483. }
  6484. if ( NetStatus == NO_ERROR ) {
  6485. Status = STATUS_SUCCESS;
  6486. goto Cleanup;
  6487. } else {
  6488. NlPrintCs(( NL_CRITICAL, ClientSession,
  6489. "NlGetAnyDCName: Can't ping the DC %ws 0x%lx.\n",
  6490. ClientSession->CsUncServerName, NetStatus ));
  6491. }
  6492. //
  6493. // Drop the secure channel to force the next iteration to discover
  6494. // a new dc
  6495. //
  6496. if ( !DiscoveryDone ) {
  6497. NlPrintCs((NL_CRITICAL, ClientSession,
  6498. "NlGetAnyDCName: Current DC '%ws' no longer available. (rediscover)\n",
  6499. ClientSession->CsUncServerName ));
  6500. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  6501. }
  6502. } while ( !DiscoveryDone );
  6503. Status = STATUS_NO_LOGON_SERVERS;
  6504. NlPrintCs(( NL_CRITICAL, ClientSession, "NlGetAnyDCName: Failed\n" ));
  6505. //
  6506. // Free any locally used resources.
  6507. //
  6508. Cleanup:
  6509. //
  6510. // Don't divulge too much to the caller.
  6511. //
  6512. if ( Status == STATUS_ACCESS_DENIED ) {
  6513. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  6514. }
  6515. //
  6516. // Return the DC info to the caller.
  6517. //
  6518. if ( Status == STATUS_SUCCESS ) {
  6519. *NlDcCacheEntry = NlLocalDcCacheEntry;
  6520. if ( DcRediscovered != NULL ) {
  6521. *DcRediscovered = DiscoveryDone;
  6522. }
  6523. } else if ( NlLocalDcCacheEntry != NULL ) {
  6524. NetpDcDerefCacheEntry( NlLocalDcCacheEntry );
  6525. }
  6526. return Status;
  6527. }
  6528. NET_API_STATUS
  6529. NetrGetAnyDCName (
  6530. IN LPWSTR ServerName OPTIONAL,
  6531. IN LPWSTR DomainName OPTIONAL,
  6532. OUT LPWSTR *Buffer
  6533. )
  6534. /*++
  6535. Routine Description:
  6536. Get the name of the any domain controller for a trusted domain.
  6537. The domain controller found in guaranteed to have be up at one point during
  6538. this API call.
  6539. Arguments:
  6540. ServerName - name of remote server (null for local)
  6541. DomainName - name of domain (null for primary domain)
  6542. Buffer - Returns a pointer to an allcated buffer containing the
  6543. servername of a DC of the domain. The server name is prefixed
  6544. by \\. The buffer should be deallocated using NetApiBufferFree.
  6545. Return Value:
  6546. ERROR_SUCCESS - Success. Buffer contains DC name prefixed by \\.
  6547. ERROR_NO_LOGON_SERVERS - No DC could be found
  6548. ERROR_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
  6549. ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  6550. broken.
  6551. ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  6552. broken or the password is broken.
  6553. ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
  6554. domain controller of the specified domain.
  6555. --*/
  6556. {
  6557. NTSTATUS Status;
  6558. NET_API_STATUS NetStatus;
  6559. LPWSTR TmpUncServerName = NULL;
  6560. UNICODE_STRING DomainNameString;
  6561. PDOMAIN_INFO DomainInfo = NULL;
  6562. PCLIENT_SESSION ClientSession = NULL;
  6563. PNL_DC_CACHE_ENTRY NlDcCacheEntry = NULL;
  6564. BOOL AmWriter = FALSE;
  6565. //
  6566. // Lookup which domain this call pertains to.
  6567. //
  6568. DomainInfo = NlFindDomainByServerName( ServerName );
  6569. if ( DomainInfo == NULL ) {
  6570. NetStatus = ERROR_INVALID_COMPUTERNAME;
  6571. goto Cleanup;
  6572. }
  6573. //
  6574. // Fill in the primary domain name if the caller didn't specify one.
  6575. //
  6576. if ( DomainName == NULL || *DomainName == L'\0' ) {
  6577. RtlInitUnicodeString( &DomainNameString, DomainInfo->DomUnicodeDomainName );
  6578. } else {
  6579. RtlInitUnicodeString( &DomainNameString, DomainName );
  6580. }
  6581. //
  6582. // On the PDC or BDC,
  6583. // find the Client session for the domain.
  6584. // On workstations,
  6585. // find the primary domain client session.
  6586. //
  6587. ClientSession = NlFindNamedClientSession( DomainInfo,
  6588. &DomainNameString,
  6589. NL_DIRECT_TRUST_REQUIRED,
  6590. NULL );
  6591. if ( ClientSession == NULL ) {
  6592. NlPrintDom(( NL_CRITICAL, DomainInfo,
  6593. "NetrGetAnyDCName: %ws: No such trusted domain\n",
  6594. DomainName ));
  6595. NetStatus = ERROR_NO_SUCH_DOMAIN;
  6596. goto Cleanup;
  6597. }
  6598. //
  6599. // Become a writer of the client session.
  6600. //
  6601. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  6602. NlPrintCs((NL_CRITICAL, ClientSession,
  6603. "NetrGetAnyDCName: Can't become writer of client session.\n"));
  6604. NetStatus = ERROR_NO_LOGON_SERVERS;
  6605. goto Cleanup;
  6606. }
  6607. AmWriter = TRUE;
  6608. //
  6609. // Call the internal routine to do the actual work.
  6610. //
  6611. // Ensure that the responding DC has our account as required by this API
  6612. //
  6613. Status = NlGetAnyDCName( ClientSession,
  6614. FALSE, // IP is not required
  6615. TRUE, // Do with-account discovery
  6616. &NlDcCacheEntry,
  6617. NULL ); // don't care if the DC was rediscovered
  6618. if ( !NT_SUCCESS(Status) ) {
  6619. NetStatus = NetpNtStatusToApiStatus(Status);
  6620. goto Cleanup;
  6621. }
  6622. //
  6623. // Prefer Netbios DC name for this old API
  6624. //
  6625. if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  6626. NetStatus = NetApiBufferAllocate(
  6627. (wcslen(NlDcCacheEntry->UnicodeNetbiosDcName) + 3) * sizeof(WCHAR),
  6628. &TmpUncServerName );
  6629. if ( NetStatus != NO_ERROR ) {
  6630. goto Cleanup;
  6631. }
  6632. wcscpy( TmpUncServerName, L"\\\\" );
  6633. wcscpy( TmpUncServerName+2, NlDcCacheEntry->UnicodeNetbiosDcName );
  6634. } else {
  6635. NetStatus = NetApiBufferAllocate(
  6636. (wcslen(NlDcCacheEntry->UnicodeDnsHostName) + 3) * sizeof(WCHAR),
  6637. &TmpUncServerName );
  6638. if ( NetStatus != NO_ERROR ) {
  6639. goto Cleanup;
  6640. }
  6641. wcscpy( TmpUncServerName, L"\\\\" );
  6642. wcscpy( TmpUncServerName+2, NlDcCacheEntry->UnicodeDnsHostName );
  6643. }
  6644. *Buffer = TmpUncServerName;
  6645. NetStatus = NERR_Success;
  6646. Cleanup:
  6647. if ( DomainInfo != NULL ) {
  6648. NlDereferenceDomain( DomainInfo );
  6649. }
  6650. if ( ClientSession != NULL ) {
  6651. if ( AmWriter ) {
  6652. NlResetWriterClientSession( ClientSession );
  6653. }
  6654. NlUnrefClientSession( ClientSession );
  6655. }
  6656. if ( NlDcCacheEntry != NULL ) {
  6657. NetpDcDerefCacheEntry( NlDcCacheEntry );
  6658. }
  6659. return NetStatus;
  6660. }
  6661. BOOLEAN
  6662. NlAllocOneDomainInfo(
  6663. IN LPWSTR DomainName OPTIONAL,
  6664. IN LPWSTR DnsDomainName OPTIONAL,
  6665. IN LPWSTR DnsForestName OPTIONAL,
  6666. IN GUID *DomainGuid OPTIONAL,
  6667. IN PSID DomainSid OPTIONAL,
  6668. OUT PNETLOGON_ONE_DOMAIN_INFO OneDomainInfo
  6669. )
  6670. /*++
  6671. Routine Description:
  6672. This function fill in a NETLOGON_ONE_DOMAIN_INFO structure suitable for
  6673. returning from an RPC server routine.
  6674. Arguments:
  6675. Sundry parameters to fill in.
  6676. OneDomainInfo - Pointer to an already allocated structure to fill in.
  6677. Return Value:
  6678. TRUE - success
  6679. FALSE - couldn't allocate memory
  6680. --*/
  6681. {
  6682. IN ULONG DomainSidSize;
  6683. // Copy Netbios Domainname
  6684. if ( !NlAllocStringFromWStr(
  6685. DomainName,
  6686. &OneDomainInfo->DomainName ) ) {
  6687. return FALSE;
  6688. }
  6689. // Copy DNS domain name
  6690. if ( !NlAllocStringFromWStr(
  6691. DnsDomainName,
  6692. &OneDomainInfo->DnsDomainName ) ) {
  6693. return FALSE;
  6694. }
  6695. // Copy DNS tree name
  6696. if ( !NlAllocStringFromWStr(
  6697. DnsForestName,
  6698. &OneDomainInfo->DnsForestName ) ) {
  6699. return FALSE;
  6700. }
  6701. // Copy Domain GUID
  6702. if ( DomainGuid != NULL ) {
  6703. OneDomainInfo->DomainGuid = *DomainGuid;
  6704. }
  6705. // Copy DomainSid
  6706. if ( DomainSid != NULL ) {
  6707. DomainSidSize = RtlLengthSid( DomainSid );
  6708. OneDomainInfo->DomainSid = MIDL_user_allocate( DomainSidSize );
  6709. if ( OneDomainInfo->DomainSid == NULL ) {
  6710. return FALSE;
  6711. }
  6712. RtlCopyMemory( OneDomainInfo->DomainSid, DomainSid, DomainSidSize );
  6713. }
  6714. return TRUE;
  6715. }
  6716. VOID
  6717. NlFreeOneDomainInfo(
  6718. IN PNETLOGON_ONE_DOMAIN_INFO OneDomainInfo
  6719. )
  6720. /*++
  6721. Routine Description:
  6722. This function free all buffers allocated from a NETLOGON_ONE_DOMAIN_INFO structure
  6723. Arguments:
  6724. OneDomainInfo - Pointer to an already allocated structure to fill in.
  6725. Return Value:
  6726. None.
  6727. --*/
  6728. {
  6729. if ( OneDomainInfo->DomainName.Buffer != NULL ) {
  6730. MIDL_user_free( OneDomainInfo->DomainName.Buffer );
  6731. }
  6732. if ( OneDomainInfo->DnsDomainName.Buffer != NULL ) {
  6733. MIDL_user_free( OneDomainInfo->DnsDomainName.Buffer );
  6734. }
  6735. if ( OneDomainInfo->DnsForestName.Buffer != NULL ) {
  6736. MIDL_user_free( OneDomainInfo->DnsForestName.Buffer );
  6737. }
  6738. if ( OneDomainInfo->TrustExtension.Buffer != NULL ) {
  6739. MIDL_user_free( OneDomainInfo->TrustExtension.Buffer );
  6740. }
  6741. if ( OneDomainInfo->DomainSid != NULL ) {
  6742. MIDL_user_free( OneDomainInfo->DomainSid );
  6743. }
  6744. return;
  6745. }
  6746. NTSTATUS
  6747. NetrLogonDummyRoutine1(
  6748. IN LPWSTR ServerName OPTIONAL,
  6749. IN LPWSTR ComputerName,
  6750. IN PNETLOGON_AUTHENTICATOR Authenticator,
  6751. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  6752. IN DWORD QueryLevel,
  6753. OUT PNETLOGON_DUMMY1 Buffer
  6754. )
  6755. /*++
  6756. Routine Description:
  6757. This function is never called.
  6758. Arguments:
  6759. Return Value:
  6760. --*/
  6761. {
  6762. return STATUS_NOT_IMPLEMENTED;
  6763. UNREFERENCED_PARAMETER( ServerName );
  6764. UNREFERENCED_PARAMETER( ComputerName );
  6765. UNREFERENCED_PARAMETER( Authenticator );
  6766. UNREFERENCED_PARAMETER( ReturnAuthenticator );
  6767. UNREFERENCED_PARAMETER( QueryLevel );
  6768. UNREFERENCED_PARAMETER( Buffer );
  6769. }
  6770. NTSTATUS
  6771. NetrLogonGetDomainInfo(
  6772. IN LPWSTR ServerName OPTIONAL,
  6773. IN LPWSTR ComputerName,
  6774. IN PNETLOGON_AUTHENTICATOR Authenticator,
  6775. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  6776. IN DWORD QueryLevel,
  6777. PNETLOGON_WORKSTATION_INFORMATION InBuffer,
  6778. OUT PNETLOGON_DOMAIN_INFORMATION OutBuffer
  6779. )
  6780. /*++
  6781. Routine Description:
  6782. This function is used by an NT workstation to query information about the
  6783. domain it is a member of.
  6784. Arguments:
  6785. ServerName -- Name of the DC to retrieve the data from.
  6786. ComputerName -- Name of the workstation making the call.
  6787. Authenticator -- supplied by the workstation.
  6788. ReturnAuthenticator -- Receives an authenticator returned by the DC.
  6789. QueryLevel - Level of information to return from the DC. Valid values are:
  6790. 1: Return NETLOGON_DOMAIN_INFO structure.
  6791. InBuffer - Buffer to pass to DC
  6792. OutBuffer - Returns a pointer to an allocated buffer containing the queried
  6793. information.
  6794. Return Value:
  6795. STATUS_SUCCESS -- The function completed successfully.
  6796. STATUS_ACCESS_DENIED -- The workstations should re-authenticate with
  6797. the DC.
  6798. --*/
  6799. {
  6800. NTSTATUS Status;
  6801. PDOMAIN_INFO DomainInfo = NULL;
  6802. PSERVER_SESSION ServerSession;
  6803. SESSION_INFO SessionInfo;
  6804. LM_OWF_PASSWORD OwfPassword;
  6805. PNETLOGON_DOMAIN_INFO NetlogonDomainInfo = NULL;
  6806. PNETLOGON_LSA_POLICY_INFO NetlogonLsaPolicyInfo = NULL;
  6807. PNETLOGON_LSA_POLICY_INFO OutLsaPolicy = NULL;
  6808. PNETLOGON_WORKSTATION_INFO InWorkstationInfo = NULL;
  6809. ULONG i;
  6810. ULONG ForestTrustListCount = 0;
  6811. PULONG IndexInReturnedList = NULL;
  6812. LPWSTR PreviousDnsHostName = NULL;
  6813. BOOLEAN DomainLocked = FALSE;
  6814. BOOLEAN ClientHandlesSpn = FALSE;
  6815. BOOLEAN NeedBidirectionalTrust = FALSE;
  6816. PLIST_ENTRY ListEntry;
  6817. LPBYTE Where;
  6818. //
  6819. // This API is not supported on workstations.
  6820. //
  6821. if ( NlGlobalMemberWorkstation ) {
  6822. return STATUS_NOT_SUPPORTED;
  6823. }
  6824. //
  6825. // Lookup which domain this call pertains to.
  6826. //
  6827. DomainInfo = NlFindDomainByServerName( ServerName );
  6828. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  6829. "NetrLogonGetDomainInfo: %ws %ld Entered\n",
  6830. ComputerName,
  6831. QueryLevel ));
  6832. if ( DomainInfo == NULL ) {
  6833. Status = STATUS_INVALID_COMPUTER_NAME;
  6834. goto Cleanup;
  6835. }
  6836. //
  6837. // Get the Session key for this session.
  6838. //
  6839. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  6840. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  6841. if (ServerSession == NULL) {
  6842. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  6843. Status = STATUS_ACCESS_DENIED;
  6844. goto Cleanup;
  6845. }
  6846. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  6847. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  6848. //
  6849. // now verify the Authenticator and update seed if OK
  6850. //
  6851. Status = NlCheckAuthenticator( ServerSession,
  6852. Authenticator,
  6853. ReturnAuthenticator);
  6854. if ( !NT_SUCCESS(Status) ) {
  6855. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  6856. goto Cleanup;
  6857. }
  6858. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  6859. //
  6860. // Ensure we support the query level specified.
  6861. //
  6862. switch ( QueryLevel ) {
  6863. case NETLOGON_QUERY_DOMAIN_INFO:
  6864. //
  6865. // Determine the size of the buffer to allocate.
  6866. //
  6867. EnterCriticalSection( &NlGlobalDomainCritSect );
  6868. LOCK_TRUST_LIST( DomainInfo );
  6869. DomainLocked = TRUE;
  6870. //
  6871. // Allocate the buffer to return.
  6872. //
  6873. NetlogonDomainInfo = MIDL_user_allocate( sizeof(*NetlogonDomainInfo) );
  6874. if ( NetlogonDomainInfo == NULL ) {
  6875. Status = STATUS_NO_MEMORY;
  6876. goto Cleanup;
  6877. }
  6878. RtlZeroMemory( NetlogonDomainInfo, sizeof(*NetlogonDomainInfo) );
  6879. //
  6880. // Tell the caller the common set of bits we support
  6881. //
  6882. NetlogonDomainInfo->WorkstationFlags =
  6883. InBuffer->WorkstationInfo->WorkstationFlags &
  6884. NL_GET_DOMAIN_INFO_SUPPORTED;
  6885. ClientHandlesSpn =
  6886. (NetlogonDomainInfo->WorkstationFlags & NL_CLIENT_HANDLES_SPN) != 0;
  6887. NeedBidirectionalTrust =
  6888. (NetlogonDomainInfo->WorkstationFlags & NL_NEED_BIDIRECTIONAL_TRUSTS) != 0;
  6889. //
  6890. // Copy the information into the buffer.
  6891. //
  6892. // Copy the description of the primary domain.
  6893. //
  6894. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  6895. if ( !NlAllocOneDomainInfo(
  6896. DomainInfo->DomUnicodeDomainNameString.Buffer,
  6897. DomainInfo->DomUnicodeDnsDomainNameString.Buffer,
  6898. NlGlobalUnicodeDnsForestName,
  6899. &DomainInfo->DomDomainGuidBuffer,
  6900. DomainInfo->DomAccountDomainId,
  6901. &NetlogonDomainInfo->PrimaryDomain ) ) {
  6902. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  6903. Status = STATUS_NO_MEMORY;
  6904. goto Cleanup;
  6905. }
  6906. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  6907. //
  6908. // Determine the length of the forest trust list to return
  6909. //
  6910. ForestTrustListCount = DomainInfo->DomForestTrustListCount;
  6911. //
  6912. // Check if need to exclude directly trusting domains
  6913. //
  6914. if ( !NeedBidirectionalTrust ) {
  6915. ULONG Index;
  6916. for ( Index=0; Index<DomainInfo->DomForestTrustListCount; Index++ ) {
  6917. if ( (DomainInfo->DomForestTrustList[Index].Flags &
  6918. (DS_DOMAIN_PRIMARY|DS_DOMAIN_IN_FOREST|DS_DOMAIN_DIRECT_OUTBOUND)) == 0 ) {
  6919. ForestTrustListCount--;
  6920. }
  6921. }
  6922. }
  6923. //
  6924. // Copy trusted domain info
  6925. //
  6926. if ( ForestTrustListCount != 0 ) {
  6927. PNETLOGON_ONE_DOMAIN_INFO TrustedDomainInfo;
  6928. ULONG Index;
  6929. ULONG ReturnedEntryIndex = 0;
  6930. //
  6931. // If need to exclude directly trusting domains,
  6932. // allocate an array of ULONGs that will be used to keep track of the
  6933. // index of a trust entry in the returned list. This is needed to
  6934. // corectly set ParentIndex for entries returned.
  6935. //
  6936. if ( !NeedBidirectionalTrust ) {
  6937. IndexInReturnedList = LocalAlloc( LMEM_ZEROINIT,
  6938. DomainInfo->DomForestTrustListCount * sizeof(ULONG) );
  6939. if ( IndexInReturnedList == NULL ) {
  6940. Status = STATUS_NO_MEMORY;
  6941. goto Cleanup;
  6942. }
  6943. }
  6944. //
  6945. // Allocate a buffer for the trusted domain info.
  6946. //
  6947. NetlogonDomainInfo->TrustedDomains =
  6948. MIDL_user_allocate( sizeof(NETLOGON_ONE_DOMAIN_INFO) * ForestTrustListCount );
  6949. if ( NetlogonDomainInfo->TrustedDomains == NULL ) {
  6950. Status = STATUS_NO_MEMORY;
  6951. goto Cleanup;
  6952. }
  6953. RtlZeroMemory( NetlogonDomainInfo->TrustedDomains,
  6954. sizeof(NETLOGON_ONE_DOMAIN_INFO) * ForestTrustListCount );
  6955. TrustedDomainInfo = NetlogonDomainInfo->TrustedDomains;
  6956. NetlogonDomainInfo->TrustedDomainCount = ForestTrustListCount;
  6957. for ( Index=0; Index<DomainInfo->DomForestTrustListCount; Index++ ) {
  6958. PNL_TRUST_EXTENSION TrustExtension;
  6959. //
  6960. // Skip this entry if need to exclude directly trusting domains.
  6961. // Otherwise, remember the index of this entry in the returned list.
  6962. //
  6963. if ( !NeedBidirectionalTrust ) {
  6964. if ( (DomainInfo->DomForestTrustList[Index].Flags &
  6965. (DS_DOMAIN_PRIMARY|DS_DOMAIN_IN_FOREST|DS_DOMAIN_DIRECT_OUTBOUND)) == 0 ) {
  6966. continue;
  6967. } else {
  6968. IndexInReturnedList[Index] = ReturnedEntryIndex;
  6969. }
  6970. }
  6971. //
  6972. // Fill in the fixed length data
  6973. //
  6974. TrustExtension = MIDL_user_allocate( sizeof(NL_TRUST_EXTENSION) );
  6975. if ( TrustExtension == NULL ) {
  6976. Status = STATUS_NO_MEMORY;
  6977. goto Cleanup;
  6978. }
  6979. TrustExtension->Flags = DomainInfo->DomForestTrustList[Index].Flags;
  6980. //
  6981. // If this is a primary domain entry, determine whether it runs
  6982. // in native or mixed mode
  6983. //
  6984. if ( (DomainInfo->DomForestTrustList[Index].Flags & DS_DOMAIN_PRIMARY) &&
  6985. !SamIMixedDomain( DomainInfo->DomSamServerHandle ) ) {
  6986. TrustExtension->Flags |= DS_DOMAIN_NATIVE_MODE;
  6987. }
  6988. //
  6989. // Do not leak the new DS_DOMAIN_DIRECT_INBOUND bit to an old client;
  6990. // it can get confused otherwise. The new DS_DOMAIN_DIRECT_OUTBOUND
  6991. // bit is just the renamed old DS_DOMAIN_DIRECT_TRUST, so leave it alone.
  6992. //
  6993. if ( !NeedBidirectionalTrust ) {
  6994. TrustExtension->Flags &= ~DS_DOMAIN_DIRECT_INBOUND;
  6995. }
  6996. TrustExtension->ParentIndex = DomainInfo->DomForestTrustList[Index].ParentIndex;
  6997. TrustExtension->TrustType = DomainInfo->DomForestTrustList[Index].TrustType;
  6998. TrustExtension->TrustAttributes = DomainInfo->DomForestTrustList[Index].TrustAttributes;
  6999. TrustedDomainInfo->TrustExtension.Buffer = (LPWSTR)TrustExtension;
  7000. TrustedDomainInfo->TrustExtension.MaximumLength =
  7001. TrustedDomainInfo->TrustExtension.Length = sizeof(NL_TRUST_EXTENSION);
  7002. // Copy the description of the primary domain.
  7003. if ( !NlAllocOneDomainInfo(
  7004. DomainInfo->DomForestTrustList[Index].NetbiosDomainName,
  7005. DomainInfo->DomForestTrustList[Index].DnsDomainName,
  7006. NULL, // DNS Tree name not meaningfull
  7007. &DomainInfo->DomForestTrustList[Index].DomainGuid,
  7008. DomainInfo->DomForestTrustList[Index].DomainSid,
  7009. TrustedDomainInfo ) ) {
  7010. Status = STATUS_NO_MEMORY;
  7011. goto Cleanup;
  7012. }
  7013. // Move on to the next trusted domain.
  7014. TrustedDomainInfo ++;
  7015. ReturnedEntryIndex ++;
  7016. }
  7017. //
  7018. // Fix ParentIndex. If need to exclude directly trusting domains,
  7019. // adjust the index to point to the appropriate entry in the
  7020. // returned list. Otherwise, leave it alone.
  7021. //
  7022. if ( !NeedBidirectionalTrust ) {
  7023. PNL_TRUST_EXTENSION TrustExtension;
  7024. TrustedDomainInfo = NetlogonDomainInfo->TrustedDomains;
  7025. for ( Index=0; Index<ForestTrustListCount; Index++ ) {
  7026. TrustExtension = (PNL_TRUST_EXTENSION)TrustedDomainInfo->TrustExtension.Buffer;
  7027. if ( (TrustExtension->Flags & DS_DOMAIN_IN_FOREST) != 0 &&
  7028. (TrustExtension->Flags & DS_DOMAIN_TREE_ROOT) == 0 ) {
  7029. TrustExtension->ParentIndex =
  7030. IndexInReturnedList[TrustExtension->ParentIndex];
  7031. }
  7032. TrustedDomainInfo ++;
  7033. }
  7034. }
  7035. }
  7036. //
  7037. // Indicate that the LSA policy should be handled.
  7038. //
  7039. InWorkstationInfo = InBuffer->WorkstationInfo;
  7040. OutLsaPolicy = &NetlogonDomainInfo->LsaPolicy;
  7041. break;
  7042. case NETLOGON_QUERY_LSA_POLICY_INFO:
  7043. //
  7044. // Allocate the buffer to return.
  7045. //
  7046. NetlogonLsaPolicyInfo = MIDL_user_allocate( sizeof(*NetlogonLsaPolicyInfo) );
  7047. if ( NetlogonLsaPolicyInfo == NULL ) {
  7048. Status = STATUS_NO_MEMORY;
  7049. goto Cleanup;
  7050. }
  7051. //
  7052. // Indicate that the LSA policy should be handled.
  7053. //
  7054. InWorkstationInfo = InBuffer->WorkstationInfo;
  7055. OutLsaPolicy = NetlogonLsaPolicyInfo;
  7056. break;
  7057. default:
  7058. Status = STATUS_INVALID_LEVEL;
  7059. goto Cleanup;
  7060. }
  7061. //
  7062. // If we're passing LSA policy back and forth between workstation/DC,
  7063. // handle the next leg.
  7064. //
  7065. if ( InWorkstationInfo != NULL ) {
  7066. OSVERSIONINFOEXW OsVersionInfoEx;
  7067. POSVERSIONINFOEXW OsVersionInfoExPtr = NULL;
  7068. LPWSTR OsName;
  7069. LPWSTR AllocatedOsName = NULL;
  7070. //
  7071. // See if the caller passed the OS version to us.
  7072. //
  7073. if ( InWorkstationInfo->OsVersion.Length >= sizeof(OsVersionInfoEx) ) {
  7074. //
  7075. // Copy the version to get the alignment right
  7076. // (since RPC thinks this is a WCHAR buffer).
  7077. //
  7078. RtlCopyMemory( &OsVersionInfoEx,
  7079. InWorkstationInfo->OsVersion.Buffer,
  7080. sizeof(OsVersionInfoEx) );
  7081. OsVersionInfoExPtr = &OsVersionInfoEx;
  7082. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  7083. "NetrLogonGetDomainInfo: %ws is running NT %ld.%ld build %ld (%ld)\n",
  7084. ComputerName,
  7085. OsVersionInfoEx.dwMajorVersion,
  7086. OsVersionInfoEx.dwMinorVersion,
  7087. OsVersionInfoEx.dwBuildNumber,
  7088. OsVersionInfoEx.wProductType ));
  7089. }
  7090. //
  7091. // See if the caller passed us an OsName.
  7092. //
  7093. if ( InWorkstationInfo->OsName.Length ) {
  7094. AllocatedOsName = LocalAlloc( 0, InWorkstationInfo->OsName.Length + sizeof(WCHAR));
  7095. if ( AllocatedOsName == NULL) {
  7096. OsName = L"Windows 2000";
  7097. } else {
  7098. RtlCopyMemory( AllocatedOsName,
  7099. InWorkstationInfo->OsName.Buffer,
  7100. InWorkstationInfo->OsName.Length );
  7101. AllocatedOsName[InWorkstationInfo->OsName.Length/sizeof(WCHAR)] = L'\0';
  7102. OsName = AllocatedOsName;
  7103. }
  7104. //
  7105. // If the caller didn't pass us its OsName,
  7106. // make one up.
  7107. // (Only pre RTM versions of WIN 2000 did this.)
  7108. //
  7109. } else {
  7110. if ( OsVersionInfoExPtr == NULL ) {
  7111. OsName = L"Windows 2000";
  7112. } else {
  7113. if ( OsVersionInfoExPtr->wProductType == VER_NT_WORKSTATION ) {
  7114. OsName = L"Windows 2000 Professional";
  7115. } else {
  7116. OsName = L"Windows 2000 Server";
  7117. }
  7118. }
  7119. }
  7120. //
  7121. // Set the DnsHostName on the computer object.
  7122. // If the client handles SPN setting, get the DnsHostName from the DS
  7123. // rather than setting it.
  7124. //
  7125. Status = LsaISetClientDnsHostName(
  7126. ComputerName,
  7127. ClientHandlesSpn ? NULL : InWorkstationInfo->DnsHostName,
  7128. OsVersionInfoExPtr,
  7129. OsName,
  7130. ClientHandlesSpn ? &PreviousDnsHostName : NULL );
  7131. if ( !NT_SUCCESS(Status) ) {
  7132. NlPrintDom((NL_CRITICAL, DomainInfo,
  7133. "NetrLogonGetDomainInfo: Cannot set client DNS host name %lx (ignoring)\n",
  7134. Status ));
  7135. PreviousDnsHostName = NULL;
  7136. // This isn't fatal
  7137. }
  7138. NlPrintDom((NL_MISC, DomainInfo,
  7139. "NetrLogonGetDomainInfo: DnsHostName of %ws is %ws\n",
  7140. ComputerName,
  7141. PreviousDnsHostName ));
  7142. if ( AllocatedOsName != NULL) {
  7143. LocalFree( AllocatedOsName );
  7144. }
  7145. //
  7146. // Set the HOST/name SPN on the object as well. This
  7147. // is handled mostly by the DS side of things.
  7148. //
  7149. if ( !ClientHandlesSpn ) {
  7150. NlSetDsSPN( FALSE, // Don't wait for this to complete
  7151. TRUE, // Set the SPN
  7152. FALSE, // We've already set the Dns host name
  7153. DomainInfo,
  7154. DomainInfo->DomUncUnicodeComputerName,
  7155. ComputerName,
  7156. InWorkstationInfo->DnsHostName );
  7157. }
  7158. //
  7159. // Return the Previous DNS Host Name to the client
  7160. //
  7161. if ( NetlogonDomainInfo != NULL ) {
  7162. RtlInitUnicodeString( &NetlogonDomainInfo->DnsHostNameInDs, PreviousDnsHostName );
  7163. PreviousDnsHostName = NULL;
  7164. }
  7165. //
  7166. // Tell the caller there is no policy to return
  7167. //
  7168. OutLsaPolicy->LsaPolicySize = 0;
  7169. OutLsaPolicy->LsaPolicy = NULL;
  7170. }
  7171. Status = STATUS_SUCCESS;
  7172. //
  7173. // Common exit point
  7174. //
  7175. Cleanup:
  7176. //
  7177. // If the request failed, be careful to not leak authentication
  7178. // information.
  7179. //
  7180. if ( Status == STATUS_ACCESS_DENIED ) {
  7181. RtlSecureZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  7182. }
  7183. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  7184. "NetrLogonGetDomainInfo: %ws %ld Returns 0x%lX\n",
  7185. ComputerName,
  7186. QueryLevel,
  7187. Status ));
  7188. if ( DomainInfo != NULL ) {
  7189. NlDereferenceDomain( DomainInfo );
  7190. }
  7191. if ( IndexInReturnedList != NULL ) {
  7192. LocalFree( IndexInReturnedList );
  7193. }
  7194. if ( PreviousDnsHostName != NULL ) {
  7195. MIDL_user_free( PreviousDnsHostName );
  7196. }
  7197. if ( NT_SUCCESS(Status)) {
  7198. if ( NetlogonDomainInfo != NULL ) {
  7199. OutBuffer->DomainInfo = NetlogonDomainInfo;
  7200. } else if ( NetlogonLsaPolicyInfo != NULL ) {
  7201. OutBuffer->LsaPolicyInfo = NetlogonLsaPolicyInfo;
  7202. }
  7203. } else {
  7204. if ( NetlogonDomainInfo != NULL ) {
  7205. NlFreeOneDomainInfo( &NetlogonDomainInfo->PrimaryDomain );
  7206. for ( i=0; i<NetlogonDomainInfo->TrustedDomainCount; i++ ) {
  7207. NlFreeOneDomainInfo( &NetlogonDomainInfo->TrustedDomains[i] );
  7208. }
  7209. if ( NetlogonDomainInfo->LsaPolicy.LsaPolicy != NULL ) {
  7210. MIDL_user_free( NetlogonDomainInfo->LsaPolicy.LsaPolicy );
  7211. }
  7212. MIDL_user_free( NetlogonDomainInfo );
  7213. }
  7214. if ( NetlogonLsaPolicyInfo != NULL ) {
  7215. if ( NetlogonLsaPolicyInfo->LsaPolicy != NULL ) {
  7216. MIDL_user_free( NetlogonLsaPolicyInfo->LsaPolicy );
  7217. }
  7218. MIDL_user_free( NetlogonLsaPolicyInfo );
  7219. }
  7220. }
  7221. if ( DomainLocked ) {
  7222. UNLOCK_TRUST_LIST( DomainInfo );
  7223. LeaveCriticalSection( &NlGlobalDomainCritSect );
  7224. }
  7225. return Status;
  7226. }
  7227. NTSTATUS
  7228. NetrLogonSetServiceBits(
  7229. IN LPWSTR ServerName,
  7230. IN DWORD ServiceBitsOfInterest,
  7231. IN DWORD ServiceBits
  7232. )
  7233. /*++
  7234. Routine Description:
  7235. Inidcates whether this DC is currently running the specified service.
  7236. For instance,
  7237. NetLogonSetServiceBits( DS_KDC_FLAG, DS_KDC_FLAG );
  7238. tells Netlogon the KDC is running. And
  7239. NetLogonSetServiceBits( DS_KDC_FLAG, 0 );
  7240. tells Netlogon the KDC is not running.
  7241. This out of proc API can set only a certain set of bits:
  7242. DS_TIMESERV_FLAG
  7243. DS_GOOD_TIMESERV_FLAG
  7244. If other bits are attempted to be set, access denied is returned.
  7245. Arguments:
  7246. ServerName -- Name of the DC to retrieve the data from.
  7247. ServiceBitsOfInterest - A mask of the service bits being changed, set,
  7248. or reset by this call. Only the following flags are valid:
  7249. DS_KDC_FLAG
  7250. DS_DS_FLAG
  7251. DS_TIMESERV_FLAG
  7252. DS_GOOD_TIMESERV_FLAG
  7253. ServiceBits - A mask indicating what the bits specified by ServiceBitsOfInterest
  7254. should be set to.
  7255. Return Value:
  7256. STATUS_SUCCESS - Success.
  7257. STATUS_ACCESS_DENIED - Caller does not have permission to call this API.
  7258. STATUS_INVALID_PARAMETER - The parameters have extaneous bits set.
  7259. --*/
  7260. {
  7261. NET_API_STATUS NetStatus;
  7262. //
  7263. // Out of proc callers can set only certain bits
  7264. //
  7265. if ( (ServiceBitsOfInterest & ~DS_OUTOFPROC_VALID_SERVICE_BITS) != 0 ) {
  7266. return STATUS_ACCESS_DENIED;
  7267. }
  7268. //
  7269. // Perform access validation on the caller.
  7270. //
  7271. NetStatus = NetpAccessCheck(
  7272. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  7273. NETLOGON_CONTROL_ACCESS, // Desired access
  7274. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  7275. if ( NetStatus != NERR_Success) {
  7276. return STATUS_ACCESS_DENIED;
  7277. }
  7278. return I_NetLogonSetServiceBits( ServiceBitsOfInterest, ServiceBits );
  7279. UNREFERENCED_PARAMETER( ServerName );
  7280. }
  7281. NET_API_STATUS
  7282. NlComputeMd5Digest(
  7283. IN LPBYTE Message,
  7284. IN ULONG MessageSize,
  7285. IN PNT_OWF_PASSWORD OwfPassword,
  7286. OUT CHAR MessageDigest[NL_DIGEST_SIZE]
  7287. )
  7288. /*++
  7289. Routine Description:
  7290. Compute the message digest for Message.
  7291. Arguments:
  7292. Message - The message to compute the digest for.
  7293. MessageSize - The size of Message in bytes.
  7294. OwfPassword - Password of the account to used salt the digest
  7295. MessageDigest - Returns the 128-bit digest of the message.
  7296. Return Value:
  7297. NERR_Success: the operation was successful
  7298. ERROR_NOT_SUPPORTED: MD5 is not supported on this machine.
  7299. --*/
  7300. {
  7301. NET_API_STATUS NetStatus;
  7302. NTSTATUS Status;
  7303. PCHECKSUM_FUNCTION Check;
  7304. PCHECKSUM_BUFFER CheckBuffer = NULL;
  7305. BOOL Initialized = FALSE;
  7306. //
  7307. // Locate the checksum routine for the context, loading it if necessary from the
  7308. // the crypto support DLL
  7309. //
  7310. Status = CDLocateCheckSum(KERB_CHECKSUM_MD5, &Check);
  7311. if (!NT_SUCCESS(Status)) {
  7312. NlPrint((NL_CRITICAL,
  7313. "NlComputeMd5Digest: MD5 is not supported\n",
  7314. DomainName ));
  7315. NetStatus = ERROR_NOT_SUPPORTED;
  7316. goto Cleanup;
  7317. }
  7318. //
  7319. // Initialize
  7320. //
  7321. Status = Check->Initialize(0, &CheckBuffer);
  7322. if (!NT_SUCCESS(Status)) {
  7323. NlPrint((NL_CRITICAL,
  7324. "NlComputeMd5Digest: cannot initialize MD5 0x%lx\n",
  7325. Status ));
  7326. NetStatus = NetpNtStatusToApiStatus(Status);
  7327. goto Cleanup;
  7328. }
  7329. Initialized = TRUE;
  7330. //
  7331. // First compute the digest of the OWF
  7332. //
  7333. Status = Check->Sum( CheckBuffer, sizeof(*OwfPassword), (PUCHAR) OwfPassword );
  7334. if (!NT_SUCCESS(Status)) {
  7335. NlPrint((NL_CRITICAL,
  7336. "NlComputeMd5Digest: cannot checksum OWF password 0x%lx\n",
  7337. Status ));
  7338. NetStatus = NetpNtStatusToApiStatus(Status);
  7339. goto Cleanup;
  7340. }
  7341. //
  7342. // Then compute the digest of the message itself
  7343. //
  7344. Status = Check->Sum( CheckBuffer, MessageSize, Message );
  7345. if (!NT_SUCCESS(Status)) {
  7346. NlPrint((NL_CRITICAL,
  7347. "NlComputeMd5Digest: cannot checksum message 0x%lx\n",
  7348. Status ));
  7349. NetStatus = NetpNtStatusToApiStatus(Status);
  7350. goto Cleanup;
  7351. }
  7352. //
  7353. // Grab the digest.
  7354. //
  7355. if ( Check->CheckSumSize != NL_DIGEST_SIZE ) {
  7356. NlPrint((NL_CRITICAL,
  7357. "NlComputeMd5Digest: digest is the wrong size.\n" ));
  7358. NetStatus = ERROR_INTERNAL_ERROR;
  7359. goto Cleanup;
  7360. }
  7361. Status = Check->Finalize(CheckBuffer, MessageDigest);
  7362. if (!NT_SUCCESS(Status)) {
  7363. NlPrint((NL_CRITICAL,
  7364. "NlComputeMd5Digest: cannot checksum message 0x%lx\n",
  7365. Status ));
  7366. NetStatus = NetpNtStatusToApiStatus(Status);
  7367. goto Cleanup;
  7368. }
  7369. //
  7370. // Done.
  7371. //
  7372. NetStatus = NO_ERROR;
  7373. Cleanup:
  7374. if ( Initialized ) {
  7375. Status = Check->Finish(&CheckBuffer);
  7376. if (!NT_SUCCESS(Status)) {
  7377. NlPrint((NL_CRITICAL,
  7378. "NlComputeMd5Digest: cannot finish 0x%lx\n",
  7379. Status ));
  7380. }
  7381. }
  7382. return NetStatus;
  7383. }
  7384. NET_API_STATUS
  7385. NetrLogonGetTrustRid(
  7386. IN LPWSTR ServerName OPTIONAL,
  7387. IN LPWSTR DomainName OPTIONAL,
  7388. OUT PULONG Rid
  7389. )
  7390. /*++
  7391. Routine Description:
  7392. Returns the Rid of the account that ServerName uses in its secure channel to DomainName.
  7393. This routine performs an access check to determine if the caller can access
  7394. the requested RID. If the caller wants RID for the local machine account
  7395. (in which case the caller should specify by passing NULL for both ServerName
  7396. and DomainName), this routine requires Authenticated Users access. Otherwise,
  7397. if the caller wants a RID for a trust account on a DC, the admin or local
  7398. system access right is required.
  7399. Arguments:
  7400. ServerName - The name of the remote server.
  7401. DomainName - The name (DNS or Netbios) of the domain the trust is to.
  7402. NULL implies the domain the machine is a member of.
  7403. Rid - Rid is the RID of the account in the specified domain that represents the
  7404. trust relationship between the ServerName and DomainName.
  7405. Return Value:
  7406. NERR_Success: the operation was successful
  7407. ERROR_NO_SUCH_DOMAIN: The specified domain does not exist.
  7408. ERROR_NO_LOGON_SERVERS: There are currently no logon server available for the domain or
  7409. there is some problem with the secure channel.
  7410. ERROR_NOT_SUPPORTED: The specified trusted domain does not support digesting.
  7411. --*/
  7412. {
  7413. NET_API_STATUS NetStatus = NO_ERROR;
  7414. NTSTATUS Status = STATUS_SUCCESS;
  7415. PCLIENT_SESSION ClientSession = NULL;
  7416. PDOMAIN_INFO DomainInfo = NULL;
  7417. UNICODE_STRING DomainNameString;
  7418. BOOL AmWriter = FALSE;
  7419. ULONG LocalRid = 0;
  7420. //
  7421. // Perform access validation on the caller.
  7422. //
  7423. // If the caller wants RID for the local machine account
  7424. // (which the caller should specify by passing NULL for
  7425. // both ServerName and DomainName), require Authenticated
  7426. // Users access. Otherwise, require admin or local system
  7427. // access right.
  7428. //
  7429. NetStatus = NetpAccessCheck(
  7430. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  7431. (ServerName == NULL && DomainName == NULL) ? // Desired access:
  7432. NETLOGON_FTINFO_ACCESS : // Authenticated user
  7433. NETLOGON_CONTROL_ACCESS, // Admin or local system
  7434. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  7435. if ( NetStatus != NERR_Success) {
  7436. NetStatus = ERROR_ACCESS_DENIED;
  7437. goto Cleanup;
  7438. }
  7439. //
  7440. // Lookup which domain this call pertains to.
  7441. //
  7442. DomainInfo = NlFindDomainByServerName( ServerName );
  7443. if ( DomainInfo == NULL ) {
  7444. NetStatus = ERROR_INVALID_COMPUTERNAME;
  7445. goto Cleanup;
  7446. }
  7447. //
  7448. // On the PDC or BDC,
  7449. // find the Client session for the domain.
  7450. // On workstations,
  7451. // find the primary domain client session.
  7452. //
  7453. if ( DomainName == NULL ) {
  7454. DomainName = DomainInfo->DomUnicodeDomainName;
  7455. }
  7456. RtlInitUnicodeString( &DomainNameString, DomainName );
  7457. ClientSession = NlFindNamedClientSession( DomainInfo,
  7458. &DomainNameString,
  7459. NL_DIRECT_TRUST_REQUIRED | NL_ROLE_PRIMARY_OK,
  7460. NULL );
  7461. if ( ClientSession == NULL ) {
  7462. NlPrintDom((NL_CRITICAL, DomainInfo,
  7463. "NetrLogonGetTrustRid: %ws: No such trusted domain\n",
  7464. DomainName ));
  7465. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7466. goto Cleanup;
  7467. }
  7468. //
  7469. // If we have the RID cached, use it
  7470. //
  7471. LocalRid = ClientSession->CsAccountRid;
  7472. if ( LocalRid != 0 ) {
  7473. *Rid = LocalRid;
  7474. NetStatus = NO_ERROR;
  7475. goto Cleanup;
  7476. }
  7477. //
  7478. // We don't have the RID cached, so go get it.
  7479. //
  7480. // Become a writer of the ClientSession.
  7481. //
  7482. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  7483. NlPrintCs((NL_CRITICAL, ClientSession,
  7484. "NetrLogonGetTrustRid: Can't become writer of client session.\n" ));
  7485. NetStatus = ERROR_NO_LOGON_SERVERS;
  7486. goto Cleanup;
  7487. }
  7488. AmWriter = TRUE;
  7489. //
  7490. // If this is a server secure channel (i.e. we are a DC and
  7491. // this is our domain) we can get the RID from local SAM
  7492. //
  7493. if ( ClientSession->CsSecureChannelType == ServerSecureChannel ) {
  7494. ULONG AccountRid = 0;
  7495. Status = NlSamOpenNamedUser( DomainInfo,
  7496. ClientSession->CsAccountName,
  7497. NULL,
  7498. &AccountRid,
  7499. NULL );
  7500. //
  7501. // Just stash it into the client session.
  7502. //
  7503. // Note that if we are a BDC, we also set RID during
  7504. // secure channel setup to our PDC, so whoever writes
  7505. // last is the winner. But hopefully the same value
  7506. // will be written in both cases.
  7507. //
  7508. if ( NT_SUCCESS(Status) ) {
  7509. ClientSession->CsAccountRid = AccountRid;
  7510. } else {
  7511. NlPrintDom(( NL_CRITICAL, DomainInfo,
  7512. "NlUpdateRole: NlSamOpenNamedUser failed 0x%lx\n",
  7513. Status ));
  7514. }
  7515. //
  7516. // For all other secure channel types,
  7517. // we have to get the RID as a side
  7518. // effect of setting up the secure channel.
  7519. //
  7520. } else if ( ClientSession->CsState != CS_AUTHENTICATED &&
  7521. NlTimeToReauthenticate(ClientSession) ) {
  7522. //
  7523. // Try to set up the channel
  7524. //
  7525. NlSessionSetup( ClientSession);
  7526. }
  7527. //
  7528. // Ensure that we return non-zero RID on success
  7529. //
  7530. LocalRid = ClientSession->CsAccountRid;
  7531. if ( LocalRid != 0 ) {
  7532. *Rid = LocalRid;
  7533. NetStatus = NO_ERROR;
  7534. } else {
  7535. //
  7536. // If the trust is not NT5, this call is not supported
  7537. //
  7538. if ( (ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST) == 0 ) {
  7539. NetStatus = ERROR_NOT_SUPPORTED;
  7540. } else {
  7541. NetStatus = ERROR_TRUSTED_RELATIONSHIP_FAILURE;
  7542. }
  7543. }
  7544. //
  7545. // Free any locally used resources.
  7546. //
  7547. Cleanup:
  7548. if ( AmWriter ) {
  7549. NlResetWriterClientSession( ClientSession );
  7550. }
  7551. if ( ClientSession != NULL ) {
  7552. NlUnrefClientSession( ClientSession );
  7553. }
  7554. if ( DomainInfo != NULL ) {
  7555. NlDereferenceDomain( DomainInfo );
  7556. }
  7557. return NetStatus;
  7558. }
  7559. NET_API_STATUS
  7560. NetrLogonComputeServerDigest(
  7561. IN LPWSTR ServerName OPTIONAL,
  7562. IN ULONG Rid,
  7563. IN LPBYTE Message,
  7564. IN ULONG MessageSize,
  7565. OUT CHAR NewMessageDigest[NL_DIGEST_SIZE],
  7566. OUT CHAR OldMessageDigest[NL_DIGEST_SIZE]
  7567. )
  7568. /*++
  7569. Routine Description:
  7570. Compute the message digest for Message on the server.
  7571. A digest is computed given the message and the password used on
  7572. the account identified by teh account RID. Since there may be up
  7573. to 2 passwords on the account (for interdomain trust), this routine
  7574. returns 2 digets corresponding to the 2 passwords. If the account
  7575. has just one password on the server side (true for any account other
  7576. than the intedomain trust account) or the two passwords are the same
  7577. the 2 digests returned will be identical.
  7578. Only an Admin or LocalSystem or LocalService may call this function.
  7579. Arguments:
  7580. ServerName - The name of the remote server.
  7581. Rid - The RID of the account to create the digest for.
  7582. The RID must be the RID of a machine account or the API returns an error.
  7583. Message - The message to compute the digest for.
  7584. MessageSize - The size of Message in bytes.
  7585. NewMessageDigest - Returns the 128-bit digest of the message corresponding to
  7586. the new account password.
  7587. OldMessageDigest - Returns the 128-bit digest of the message corresponding to
  7588. the old account password.
  7589. Return Value:
  7590. NERR_Success: the operation was successful
  7591. ERROR_NOT_SUPPORTED: The specified trusted domain does not support digesting.
  7592. --*/
  7593. {
  7594. NTSTATUS Status;
  7595. NET_API_STATUS NetStatus;
  7596. PDOMAIN_INFO DomainInfo = NULL;
  7597. PSID UserSid = NULL;
  7598. UNICODE_STRING UserSidString;
  7599. PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
  7600. SID_AND_ATTRIBUTES_LIST ReverseMembership;
  7601. LPWSTR LocalUserName = NULL;
  7602. NT_OWF_PASSWORD NewOwfPassword;
  7603. NT_OWF_PASSWORD OldOwfPassword;
  7604. ULONG AccountRid;
  7605. ULONG LocalUserAccountControl;
  7606. //
  7607. // Perform access validation on the caller.
  7608. //
  7609. NetStatus = NetpAccessCheck(
  7610. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  7611. NETLOGON_CONTROL_ACCESS, // Desired access
  7612. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  7613. if ( NetStatus != NERR_Success) {
  7614. NlPrint((NL_CRITICAL,
  7615. "NetrLogonComputeServerDigest: Account %ld failed access check.\n",
  7616. Rid ));
  7617. NetStatus = ERROR_ACCESS_DENIED;
  7618. goto Cleanup;
  7619. }
  7620. //
  7621. // Lookup which domain this call pertains to.
  7622. //
  7623. DomainInfo = NlFindDomainByServerName( ServerName );
  7624. if ( DomainInfo == NULL ) {
  7625. NlPrint((NL_CRITICAL,
  7626. "NetrLogonComputeServerDigest: Account %ld: cannot find domain for %ws\n",
  7627. Rid,
  7628. ServerName ));
  7629. NetStatus = ERROR_INVALID_COMPUTERNAME;
  7630. goto Cleanup;
  7631. }
  7632. //
  7633. // Convert the account RID to an account SID
  7634. //
  7635. NetStatus = NetpDomainIdToSid( DomainInfo->DomAccountDomainId,
  7636. Rid,
  7637. &UserSid );
  7638. if ( NetStatus != NO_ERROR ) {
  7639. NlPrintDom((NL_CRITICAL, DomainInfo,
  7640. "NetrLogonComputeServerDigest: Account %ld: cannot convert domain ID to sid.: %ld\n",
  7641. Rid,
  7642. NetStatus ));
  7643. goto Cleanup;
  7644. }
  7645. //
  7646. // Get the info about the user.
  7647. //
  7648. // Use SamIGetUserLogonInformation instead of SamrOpenUser.
  7649. // The former is more efficient (since it only does one
  7650. // DirSearch and doesn't lock the global SAM lock) and more powerful
  7651. // (since it returns UserAllInformation).
  7652. //
  7653. UserSidString.Buffer = UserSid;
  7654. UserSidString.MaximumLength =
  7655. UserSidString.Length = (USHORT) RtlLengthSid( UserSid );
  7656. Status = SamIGetUserLogonInformation(
  7657. DomainInfo->DomSamAccountDomainHandle,
  7658. SAM_NO_MEMBERSHIPS | // Don't need group memberships
  7659. SAM_OPEN_BY_SID, // Next parameter is the SID of the account
  7660. &UserSidString,
  7661. &UserAllInfo,
  7662. &ReverseMembership,
  7663. NULL );
  7664. if ( !NT_SUCCESS(Status) ) {
  7665. NlPrintDom(( NL_CRITICAL, DomainInfo,
  7666. "NetrLogonComputeServerDigest: Account %ld: Cannot SamIGetUserLogonInfo 0x%lx\n",
  7667. Rid,
  7668. Status ));
  7669. if ( Status == STATUS_NOT_FOUND ||
  7670. Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  7671. NetStatus = ERROR_NO_SUCH_USER;
  7672. } else {
  7673. NetStatus = NetpNtStatusToApiStatus(Status);
  7674. }
  7675. goto Cleanup;
  7676. }
  7677. NlPrint((NL_ENCRYPT,
  7678. "NetrLogonComputeServerDigest: %ld: %wZ: Message: ",
  7679. Rid,
  7680. &UserAllInfo->All.UserName ));
  7681. NlpDumpBuffer(NL_ENCRYPT, Message, MessageSize );
  7682. //
  7683. // Ensure the account is a machine account.
  7684. //
  7685. if ( (UserAllInfo->All.UserAccountControl & USER_MACHINE_ACCOUNT_MASK) == 0 ) {
  7686. NlPrintDom((NL_CRITICAL, DomainInfo,
  7687. "NetrLogonComputeServerDigest: Account %ld isn't a machine account\n",
  7688. Rid ));
  7689. NetStatus = ERROR_NO_SUCH_USER;
  7690. goto Cleanup;
  7691. }
  7692. if ( UserAllInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED ) {
  7693. NlPrintDom((NL_CRITICAL, DomainInfo,
  7694. "NetrLogonComputeServerDigest: Account %ld is disabled\n",
  7695. Rid ));
  7696. NetStatus = ERROR_NO_SUCH_USER;
  7697. goto Cleanup;
  7698. }
  7699. //
  7700. // Get the password(s) for the account. For interdomain trust
  7701. // trust account, get both current and previous passwords
  7702. //
  7703. LocalUserName = LocalAlloc( 0, UserAllInfo->All.UserName.Length + sizeof(WCHAR) );
  7704. if ( LocalUserName == NULL ) {
  7705. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  7706. goto Cleanup;
  7707. }
  7708. RtlCopyMemory( LocalUserName,
  7709. UserAllInfo->All.UserName.Buffer,
  7710. UserAllInfo->All.UserName.Length );
  7711. LocalUserName[ (UserAllInfo->All.UserName.Length)/sizeof(WCHAR) ] = UNICODE_NULL;
  7712. //
  7713. // NlGetIncomingPassword checks for the exact equality of the user account control
  7714. // to the trust account flags. Therefore pass only these flags if they are set in
  7715. // the data retuned from SAM.
  7716. //
  7717. LocalUserAccountControl = UserAllInfo->All.UserAccountControl;
  7718. if ( UserAllInfo->All.UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT ) {
  7719. LocalUserAccountControl = USER_INTERDOMAIN_TRUST_ACCOUNT;
  7720. }
  7721. if ( UserAllInfo->All.UserAccountControl & USER_DNS_DOMAIN_TRUST_ACCOUNT ) {
  7722. LocalUserAccountControl = USER_DNS_DOMAIN_TRUST_ACCOUNT;
  7723. }
  7724. Status = NlGetIncomingPassword(
  7725. DomainInfo,
  7726. LocalUserName,
  7727. NullSecureChannel, // The account control bits are passed next
  7728. LocalUserAccountControl,
  7729. TRUE, // Fail for disabled accounts
  7730. &NewOwfPassword,
  7731. (UserAllInfo->All.UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) ?
  7732. &OldOwfPassword : // Get previous password for interdomain account
  7733. NULL,
  7734. &AccountRid,
  7735. NULL, // Don't need trust attributes
  7736. NULL ); // Don't need the account type
  7737. if ( !NT_SUCCESS(Status) ) {
  7738. NlPrintDom(( NL_CRITICAL, DomainInfo,
  7739. "NetrLogonComputeServerDigest: Can't NlGetIncomingPassword for %wZ 0x%lx.\n",
  7740. &UserAllInfo->All.UserName,
  7741. Status ));
  7742. NetStatus = NetpNtStatusToApiStatus(Status);
  7743. goto Cleanup;
  7744. }
  7745. NlAssert( Rid == AccountRid );
  7746. //
  7747. // If there is no old password on the account,
  7748. // use the new one
  7749. //
  7750. if ( (UserAllInfo->All.UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0 ) {
  7751. RtlCopyMemory( &OldOwfPassword,
  7752. &NewOwfPassword,
  7753. sizeof(OldOwfPassword) );
  7754. }
  7755. //
  7756. // Compute the new message digest.
  7757. //
  7758. NetStatus = NlComputeMd5Digest( Message, MessageSize, &NewOwfPassword, NewMessageDigest );
  7759. if ( NetStatus != NO_ERROR ) {
  7760. NlPrint(( NL_CRITICAL,
  7761. "NetrLogonComputeServerDigest: %ld: NlComputeMd5Digest failed (1): 0x%lx\n",
  7762. Rid, NetStatus ));
  7763. goto Cleanup;
  7764. }
  7765. NlPrint((NL_ENCRYPT,
  7766. "NetrLogonComputeServerDigest: %ld: New Password: ",
  7767. Rid ));
  7768. NlpDumpBuffer(NL_ENCRYPT, &NewOwfPassword, sizeof(NewOwfPassword) );
  7769. NlPrint((NL_ENCRYPT,
  7770. "NetrLogonComputeServerDigest: %ld: New Digest: ",
  7771. Rid ));
  7772. NlpDumpBuffer(NL_ENCRYPT, NewMessageDigest, sizeof(NewMessageDigest) );
  7773. //
  7774. // Compute the old message digest.
  7775. //
  7776. NetStatus = NlComputeMd5Digest( Message, MessageSize, &OldOwfPassword, OldMessageDigest );
  7777. if ( NetStatus != NO_ERROR ) {
  7778. NlPrint(( NL_CRITICAL,
  7779. "NetrLogonComputeServerDigest: %ld: NlComputeMd5Digest failed (2): 0x%lx\n",
  7780. Rid, NetStatus ));
  7781. goto Cleanup;
  7782. }
  7783. NlPrint((NL_ENCRYPT,
  7784. "NetrLogonComputeServerDigest: %ld: Old Password: ",
  7785. Rid ));
  7786. NlpDumpBuffer(NL_ENCRYPT, &OldOwfPassword, sizeof(OldOwfPassword) );
  7787. NlPrint((NL_ENCRYPT,
  7788. "NetrLogonComputeServerDigest: %ld: Old Digest: ",
  7789. Rid ));
  7790. NlpDumpBuffer(NL_ENCRYPT, OldMessageDigest, sizeof(OldMessageDigest) );
  7791. //
  7792. // Free any locally used resources.
  7793. //
  7794. Cleanup:
  7795. if ( DomainInfo != NULL ) {
  7796. NlDereferenceDomain( DomainInfo );
  7797. }
  7798. if ( UserSid != NULL ) {
  7799. NetpMemoryFree( UserSid );
  7800. }
  7801. if ( LocalUserName != NULL ) {
  7802. LocalFree( LocalUserName );
  7803. }
  7804. if ( UserAllInfo != NULL ) {
  7805. SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
  7806. }
  7807. return NetStatus;
  7808. }
  7809. NET_API_STATUS
  7810. NetrLogonComputeClientDigest(
  7811. IN LPWSTR ServerName OPTIONAL,
  7812. IN LPWSTR DomainName OPTIONAL,
  7813. IN LPBYTE Message,
  7814. IN ULONG MessageSize,
  7815. OUT CHAR NewMessageDigest[NL_DIGEST_SIZE],
  7816. OUT CHAR OldMessageDigest[NL_DIGEST_SIZE]
  7817. )
  7818. /*++
  7819. Routine Description:
  7820. Compute the message digest for Message on the client.
  7821. A digest is computed given the message and the password used on
  7822. the account identified by the domain name. Since there are two
  7823. passwords on the account on the client side, this routine
  7824. returns 2 digests corresponding to the 2 passwords. If the two
  7825. passwords are the same the 2 digests returned will be identical.
  7826. Only an Admin or LocalSystem or LocalService may call this function.
  7827. Arguments:
  7828. ServerName - The name of the remote server.
  7829. DomainName - The name (DNS or Netbios) of the domain the trust is to.
  7830. NULL implies the domain the machine is a member of.
  7831. Message - The message to compute the digest for.
  7832. MessageSize - The size of Message in bytes.
  7833. NewMessageDigest - Returns the 128-bit digest of the message corresponding
  7834. to the new password
  7835. NewMessageDigest - Returns the 128-bit digest of the message corresponding
  7836. to the new password
  7837. Return Value:
  7838. NERR_Success: the operation was successful
  7839. ERROR_NOT_SUPPORTED: The specified trusted domain does not support digesting.
  7840. --*/
  7841. {
  7842. NET_API_STATUS NetStatus = NO_ERROR;
  7843. NTSTATUS Status = STATUS_SUCCESS;
  7844. PCLIENT_SESSION ClientSession = NULL;
  7845. PDOMAIN_INFO DomainInfo = NULL;
  7846. UNICODE_STRING DomainNameString;
  7847. PUNICODE_STRING NewPassword = NULL;
  7848. PUNICODE_STRING OldPassword = NULL;
  7849. ULONG DummyPasswordVersionNumber;
  7850. NT_OWF_PASSWORD NewOwfPassword;
  7851. NT_OWF_PASSWORD OldOwfPassword;
  7852. NlPrint((NL_ENCRYPT,
  7853. "NetrLogonComputeClientDigest: %ws: Message: ",
  7854. DomainName ));
  7855. NlpDumpBuffer(NL_ENCRYPT, Message, MessageSize );
  7856. //
  7857. // Perform access validation on the caller.
  7858. //
  7859. NetStatus = NetpAccessCheck(
  7860. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  7861. NETLOGON_CONTROL_ACCESS, // Desired access
  7862. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  7863. if ( NetStatus != NERR_Success) {
  7864. NetStatus = ERROR_ACCESS_DENIED;
  7865. goto Cleanup;
  7866. }
  7867. //
  7868. // Lookup which domain this call pertains to.
  7869. //
  7870. DomainInfo = NlFindDomainByServerName( ServerName );
  7871. if ( DomainInfo == NULL ) {
  7872. NetStatus = ERROR_INVALID_COMPUTERNAME;
  7873. goto Cleanup;
  7874. }
  7875. //
  7876. // On the PDC or BDC,
  7877. // find the Client session for the domain.
  7878. // On workstations,
  7879. // find the primary domain client session.
  7880. //
  7881. if ( DomainName == NULL ) {
  7882. DomainName = DomainInfo->DomUnicodeDomainName;
  7883. }
  7884. RtlInitUnicodeString( &DomainNameString, DomainName );
  7885. ClientSession = NlFindNamedClientSession( DomainInfo,
  7886. &DomainNameString,
  7887. NL_DIRECT_TRUST_REQUIRED | NL_ROLE_PRIMARY_OK,
  7888. NULL );
  7889. if ( ClientSession == NULL ) {
  7890. NlPrintDom((NL_CRITICAL, DomainInfo,
  7891. "NetrLogonComputeClientDigest: %ws: No such trusted domain\n",
  7892. DomainName ));
  7893. NetStatus = ERROR_NO_SUCH_DOMAIN;
  7894. goto Cleanup;
  7895. }
  7896. //
  7897. // Get the two passwords on the account in clear
  7898. //
  7899. Status = NlGetOutgoingPassword( ClientSession,
  7900. &NewPassword,
  7901. &OldPassword,
  7902. &DummyPasswordVersionNumber,
  7903. NULL ); // No need to return password set time
  7904. if ( !NT_SUCCESS(Status) ) {
  7905. NlPrintCs(( NL_CRITICAL, ClientSession,
  7906. "NetrLogonComputeClientDigest: cannot NlGetOutgoingPassword 0x%lx\n",
  7907. Status ));
  7908. //
  7909. // Return more appropriate error.
  7910. //
  7911. if ( !NlpIsNtStatusResourceError( Status )) {
  7912. Status = STATUS_NO_TRUST_LSA_SECRET;
  7913. }
  7914. NetStatus = NetpNtStatusToApiStatus(Status);
  7915. goto Cleanup;
  7916. }
  7917. //
  7918. // Compute the new OWF password
  7919. //
  7920. if ( NewPassword != NULL ) {
  7921. Status = RtlCalculateNtOwfPassword( NewPassword,
  7922. &NewOwfPassword );
  7923. if ( !NT_SUCCESS( Status ) ) {
  7924. //
  7925. // return more appropriate error.
  7926. //
  7927. if ( !NlpIsNtStatusResourceError( Status )) {
  7928. Status = STATUS_NO_TRUST_LSA_SECRET;
  7929. }
  7930. NetStatus = NetpNtStatusToApiStatus(Status);
  7931. goto Cleanup;
  7932. }
  7933. //
  7934. // If no new password exists on the account,
  7935. // use a blank password
  7936. //
  7937. } else {
  7938. UNICODE_STRING TempUnicodeString;
  7939. RtlInitUnicodeString(&TempUnicodeString, NULL);
  7940. Status = RtlCalculateNtOwfPassword( &TempUnicodeString,
  7941. &NewOwfPassword );
  7942. if ( !NT_SUCCESS(Status) ) {
  7943. NlPrint(( NL_CRITICAL,
  7944. "NetrLogonComputeClientDigest: %ws Cannot RtlCalculateNtOwfPassword (NULL) 0x%lx\n",
  7945. DomainName,
  7946. Status ));
  7947. NetStatus = NetpNtStatusToApiStatus(Status);
  7948. goto Cleanup;
  7949. }
  7950. }
  7951. //
  7952. // Compute the old OWF password
  7953. //
  7954. if ( OldPassword != NULL ) {
  7955. Status = RtlCalculateNtOwfPassword( OldPassword,
  7956. &OldOwfPassword );
  7957. if ( !NT_SUCCESS( Status ) ) {
  7958. //
  7959. // return more appropriate error.
  7960. //
  7961. if ( !NlpIsNtStatusResourceError( Status )) {
  7962. Status = STATUS_NO_TRUST_LSA_SECRET;
  7963. }
  7964. NetStatus = NetpNtStatusToApiStatus(Status);
  7965. goto Cleanup;
  7966. }
  7967. //
  7968. // If no old password exists on the account,
  7969. // use the new password in place of the old one
  7970. //
  7971. } else {
  7972. RtlCopyMemory( &OldOwfPassword,
  7973. &NewOwfPassword,
  7974. sizeof(OldOwfPassword) );
  7975. }
  7976. //
  7977. // Compute the new message digest.
  7978. //
  7979. NetStatus = NlComputeMd5Digest( Message, MessageSize, &NewOwfPassword, NewMessageDigest );
  7980. if ( NetStatus != NO_ERROR ) {
  7981. NlPrintCs(( NL_CRITICAL, ClientSession,
  7982. "NetrLogonComputeClientDigest: cannot NlComputeMd5Digest (1) 0x%lx\n",
  7983. NetStatus ));
  7984. goto Cleanup;
  7985. }
  7986. NlPrint((NL_ENCRYPT,
  7987. "NetrLogonComputeClientDigest: %ws: New Password: ",
  7988. DomainName ));
  7989. NlpDumpBuffer(NL_ENCRYPT, &NewOwfPassword, sizeof(NewOwfPassword) );
  7990. NlPrint((NL_ENCRYPT,
  7991. "NetrLogonComputeClientDigest: %ws: New Digest: ",
  7992. DomainName ));
  7993. NlpDumpBuffer(NL_ENCRYPT, NewMessageDigest, sizeof(NewMessageDigest) );
  7994. //
  7995. // Compute the old message digest.
  7996. //
  7997. NetStatus = NlComputeMd5Digest( Message, MessageSize, &OldOwfPassword, OldMessageDigest );
  7998. if ( NetStatus != NO_ERROR ) {
  7999. NlPrintCs(( NL_CRITICAL, ClientSession,
  8000. "NetrLogonComputeClientDigest: cannot NlComputeMd5Digest (2) 0x%lx\n",
  8001. NetStatus ));
  8002. goto Cleanup;
  8003. }
  8004. NlPrint((NL_ENCRYPT,
  8005. "NetrLogonComputeClientDigest: %ws: Old Password: ",
  8006. DomainName ));
  8007. NlpDumpBuffer(NL_ENCRYPT, &OldOwfPassword, sizeof(OldOwfPassword) );
  8008. NlPrint((NL_ENCRYPT,
  8009. "NetrLogonComputeClientDigest: %ws: Old Digest: ",
  8010. DomainName ));
  8011. NlpDumpBuffer(NL_ENCRYPT, OldMessageDigest, sizeof(OldMessageDigest) );
  8012. //
  8013. // Free any locally used resources.
  8014. //
  8015. Cleanup:
  8016. if ( ClientSession != NULL ) {
  8017. NlUnrefClientSession( ClientSession );
  8018. }
  8019. if ( DomainInfo != NULL ) {
  8020. NlDereferenceDomain( DomainInfo );
  8021. }
  8022. if ( NewPassword != NULL ) {
  8023. LocalFree( NewPassword );
  8024. }
  8025. if ( OldPassword != NULL ) {
  8026. LocalFree( OldPassword );
  8027. }
  8028. return NetStatus;
  8029. }
  8030. NET_API_STATUS
  8031. NetrLogonGetTimeServiceParentDomain(
  8032. IN LPWSTR ServerName OPTIONAL,
  8033. OUT LPWSTR *DomainName,
  8034. OUT PBOOL PdcSameSite
  8035. )
  8036. /*++
  8037. Routine Description:
  8038. Returns the domain name of the domain that is logically the "parent" of this
  8039. domain. The returned domain name is suitable for passing into the
  8040. NetLogonGetTrustRid and NetLogonComputeClientDigest API.
  8041. On a workstation or member server, the returned domain name is that of the
  8042. domain that ServerName is a member of.
  8043. On a DC that is at the root of the forest, ERROR_NO_SUCH_DOMAIN is returned.
  8044. On a DC that is at the root of a tree in the forest, the name of a trusted
  8045. domain that is also at the root of a tree in the forest is returned.
  8046. On any other DC, the name of the domain that is directly the parent domain
  8047. is returned.
  8048. (See the notes on multiple hosted domains in the code below.)
  8049. Only an Admin or LocalSystem may call this function.
  8050. Arguments:
  8051. ServerName - The name of the remote server.
  8052. DomainName - Returns the name of the parent domain.
  8053. The returned buffer should be freed using NetApiBufferFree
  8054. PdcSameSite - Return TRUE if the PDC of ServerName's domain is in the same
  8055. site as ServerName.
  8056. (This value should be ignored if ServerName is not a DC.)
  8057. Return Value:
  8058. NERR_Success: the operation was successful
  8059. ERROR_NO_SUCH_DOMAIN: This server is a DC in the domain that is at the
  8060. root of the forest.
  8061. --*/
  8062. {
  8063. NET_API_STATUS NetStatus;
  8064. NTSTATUS Status;
  8065. PCLIENT_SESSION ClientSession = NULL;
  8066. PDOMAIN_INFO DomainInfo = NULL;
  8067. BOOLEAN IsSameSite;
  8068. //
  8069. // Perform access validation on the caller.
  8070. //
  8071. NetStatus = NetpAccessCheck(
  8072. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  8073. NETLOGON_CONTROL_ACCESS, // Desired access
  8074. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  8075. if ( NetStatus != NERR_Success) {
  8076. NetStatus = ERROR_ACCESS_DENIED;
  8077. goto Cleanup;
  8078. }
  8079. //
  8080. // Lookup which domain this call pertains to.
  8081. //
  8082. // MULTIHOST: This API doesn't take the hosted domain name on purpose.
  8083. // When I do multiple hosted domains, this API should find the
  8084. // DomainInfo structure for the hosted domain that's closest to the root.
  8085. // We'll return the parent of that domain.
  8086. //
  8087. // Since there is only one physical clock on this machine, we'll only run
  8088. // one copy of the time service. It should sync from as high up the tree
  8089. // as we have trust to.
  8090. //
  8091. UNREFERENCED_PARAMETER( ServerName );
  8092. DomainInfo = NlFindDomainByServerName( NULL );
  8093. if ( DomainInfo == NULL ) {
  8094. NetStatus = ERROR_INVALID_COMPUTERNAME;
  8095. goto Cleanup;
  8096. }
  8097. //
  8098. // On a workstation,
  8099. // Use the session for the domain we're a member of.
  8100. //
  8101. if ( NlGlobalMemberWorkstation ) {
  8102. ClientSession = NlRefDomClientSession( DomainInfo );
  8103. IsSameSite = TRUE;
  8104. //
  8105. // On a DC,
  8106. // Use the session for the domain representing our parent domain
  8107. //
  8108. } else {
  8109. //
  8110. // Determine whether the PDC is in the same site
  8111. //
  8112. Status = SamISameSite( &IsSameSite );
  8113. if ( !NT_SUCCESS(Status) ) {
  8114. NlPrintDom(( NL_CRITICAL, DomainInfo,
  8115. "NetrLogonGetTimeServiceParentDomain: Cannot SamISameSite.\n" ));
  8116. NetStatus = NetpNtStatusToApiStatus(Status);
  8117. goto Cleanup;
  8118. }
  8119. ClientSession = NlRefDomParentClientSession( DomainInfo );
  8120. }
  8121. if ( ClientSession == NULL ) {
  8122. NlPrintDom(( NL_CRITICAL, DomainInfo,
  8123. "NetrLogonGetTimeServiceParentDomain: Cannot find trust to my parent domain.\n" ));
  8124. NetStatus = ERROR_NO_SUCH_DOMAIN;
  8125. goto Cleanup;
  8126. }
  8127. //
  8128. // Return the name of the trusted parent domain to the caller.
  8129. //
  8130. LOCK_TRUST_LIST( DomainInfo );
  8131. if ( ClientSession->CsDnsDomainName.Length == 0 ) {
  8132. *DomainName = NetpAllocWStrFromWStr( ClientSession->CsNetbiosDomainName.Buffer );
  8133. } else {
  8134. *DomainName = NetpAllocWStrFromWStr( ClientSession->CsDnsDomainName.Buffer );
  8135. }
  8136. UNLOCK_TRUST_LIST( DomainInfo );
  8137. if ( *DomainName == NULL ) {
  8138. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8139. goto Cleanup;
  8140. }
  8141. *PdcSameSite = IsSameSite;
  8142. NlPrintDom(( NL_SESSION_SETUP, DomainInfo,
  8143. "NetrLogonGetTimeServiceParentDomain: %ws is the parent domain. (PdcSameSite: %ld)\n",
  8144. *DomainName,
  8145. IsSameSite ));
  8146. //
  8147. // Free any locally used resources.
  8148. //
  8149. Cleanup:
  8150. if ( ClientSession != NULL ) {
  8151. NlUnrefClientSession( ClientSession );
  8152. }
  8153. if ( DomainInfo != NULL ) {
  8154. NlDereferenceDomain( DomainInfo );
  8155. }
  8156. return NetStatus;
  8157. }
  8158. DWORD
  8159. NlSetDsSPNWorker(
  8160. PNL_SPN_UPDATE Update
  8161. )
  8162. /*++
  8163. Routine Description:
  8164. Updates the SPN of the computer object described in the
  8165. NL_SPN_UPDATE structure. The SPN is updated, but the rules
  8166. about SPN update are left to the DS.
  8167. Arguments:
  8168. Update - Update record describing the name of the computer
  8169. object and the SPN to use.
  8170. Return Value:
  8171. ignored - this is a thread pool worker function.
  8172. --*/
  8173. {
  8174. NET_API_STATUS NetStatus = NO_ERROR;
  8175. ULONG CrackStatus = DS_NAME_NO_ERROR;
  8176. LPWSTR DnsHostNameValues[2];
  8177. LPWSTR SpnArray[3];
  8178. LPWSTR DnsSpn = NULL;
  8179. LPWSTR NetbiosSpn = NULL;
  8180. LDAPModW DnsHostNameAttr;
  8181. LDAPModW SpnAttr;
  8182. LDAPModW *Mods[3] = {NULL};
  8183. HANDLE hDs = NULL;
  8184. LDAP *LdapHandle = NULL;
  8185. LDAPMessage *LdapMessage = NULL;
  8186. PDS_NAME_RESULTW CrackedName = NULL;
  8187. LPWSTR DnOfAccount = NULL;
  8188. LPWSTR NameToCrack;
  8189. DWORD SamNameSize;
  8190. WCHAR SamName[ DNLEN + 1 + CNLEN + 1 + 1];
  8191. ULONG LdapStatus;
  8192. LONG LdapOption;
  8193. LDAP_TIMEVAL LdapTimeout;
  8194. ULONG MessageNumber;
  8195. //
  8196. // Ldap modify control needed to indicate that the
  8197. // existing values of the modified attributes should
  8198. // be left intact and the missing ones should be added.
  8199. // Without this control, a modification of an attribute
  8200. // that results in an addition of a value that already
  8201. // exists will fail.
  8202. //
  8203. LDAPControl ModifyControl =
  8204. {
  8205. LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
  8206. {
  8207. 0, NULL
  8208. },
  8209. FALSE
  8210. };
  8211. PLDAPControl ModifyControlArray[2] =
  8212. {
  8213. &ModifyControl,
  8214. NULL
  8215. };
  8216. //
  8217. // Sanity check computer name
  8218. //
  8219. if ( wcslen( Update->NetbiosComputerName ) > CNLEN ) {
  8220. NetStatus = ERROR_INVALID_COMPUTERNAME;
  8221. goto Cleanup;
  8222. }
  8223. //
  8224. // Prepare DnsHostName modification entry
  8225. //
  8226. if ( Update->SetDnsHostName ) {
  8227. DnsHostNameValues[0] = Update->DnsHostName;
  8228. DnsHostNameValues[1] = NULL;
  8229. NlPrint(( NL_MISC, "SPN: Setting DnsHostName %ws\n",
  8230. DnsHostNameValues[0] ));
  8231. //
  8232. // If we set both DnsHostName and SPN, then DnsHostName is
  8233. // missing, so add it. If we set DnsHostName only, then
  8234. // DnsHostName already exists (but incorrect), so replace it.
  8235. //
  8236. if ( Update->SetSpn ) {
  8237. DnsHostNameAttr.mod_op = LDAP_MOD_ADD;
  8238. } else {
  8239. DnsHostNameAttr.mod_op = LDAP_MOD_REPLACE;
  8240. }
  8241. DnsHostNameAttr.mod_type = L"DnsHostName";
  8242. DnsHostNameAttr.mod_values = DnsHostNameValues;
  8243. Mods[0] = &DnsHostNameAttr;
  8244. Mods[1] = NULL;
  8245. }
  8246. //
  8247. // Prepare SPN modification entries
  8248. //
  8249. if ( Update->SetSpn ) {
  8250. LPBYTE Where;
  8251. DWORD SpnSize;
  8252. //
  8253. // Build the DNS SPN
  8254. //
  8255. SpnSize = (wcslen( Update->DnsHostName ) + 1) * sizeof( WCHAR );
  8256. SpnSize += sizeof( NL_HOST_PREFIX ) ;
  8257. DnsSpn = (LPWSTR) LocalAlloc( 0, SpnSize );
  8258. if ( DnsSpn == NULL ) {
  8259. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8260. goto Cleanup;
  8261. }
  8262. wcscpy( DnsSpn, NL_HOST_PREFIX );
  8263. wcscpy( DnsSpn + (sizeof( NL_HOST_PREFIX ) / sizeof(WCHAR) ) - 1,
  8264. Update->DnsHostName );
  8265. //
  8266. // Build the Netbios SPN
  8267. //
  8268. SpnSize = (wcslen( Update->NetbiosComputerName ) + 1) * sizeof( WCHAR );
  8269. SpnSize += sizeof( NL_HOST_PREFIX ) ;
  8270. NetbiosSpn = (LPWSTR) LocalAlloc( 0, SpnSize );
  8271. if ( NetbiosSpn == NULL ) {
  8272. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8273. goto Cleanup;
  8274. }
  8275. wcscpy( NetbiosSpn, NL_HOST_PREFIX );
  8276. wcscpy( NetbiosSpn + (sizeof( NL_HOST_PREFIX ) / sizeof(WCHAR) ) - 1,
  8277. Update->NetbiosComputerName );
  8278. NlPrint(( NL_MISC,
  8279. "SPN: Setting SPN %ws and %ws\n",
  8280. DnsSpn,
  8281. NetbiosSpn ));
  8282. SpnArray[0] = DnsSpn;
  8283. SpnArray[1] = NetbiosSpn;
  8284. SpnArray[2] = NULL;
  8285. SpnAttr.mod_op = LDAP_MOD_ADD;
  8286. SpnAttr.mod_type = L"ServicePrincipalName";
  8287. SpnAttr.mod_values = SpnArray;
  8288. //
  8289. // Use the first modification entry slot available (actually,
  8290. // when we set SPNs, we always set DnsHostName first, but
  8291. // let's be general and check it here).
  8292. //
  8293. if ( Mods[0] == NULL ) {
  8294. Mods[0] = &SpnAttr;
  8295. Mods[1] = NULL;
  8296. } else {
  8297. Mods[1] = &SpnAttr;
  8298. Mods[2] = NULL;
  8299. }
  8300. }
  8301. //
  8302. // The name of the computer object is
  8303. // <NetbiosDomainName>\<NetbiosComputerName>$
  8304. //
  8305. wcscpy( SamName, Update->NetbiosDomainName );
  8306. wcscat( SamName, L"\\" );
  8307. wcscat( SamName, Update->NetbiosComputerName );
  8308. wcscat( SamName, L"$" );
  8309. //
  8310. // Bind to the DS on the DC.
  8311. //
  8312. NetStatus = DsBindW( Update->UncDcName,
  8313. NULL,
  8314. &hDs );
  8315. if ( NetStatus != NO_ERROR ) {
  8316. NlPrint(( NL_CRITICAL,
  8317. "SPN: Cannot bind to DS on %ws: %ld\n",
  8318. Update->UncDcName,
  8319. NetStatus ));
  8320. goto Cleanup ;
  8321. }
  8322. //
  8323. // Crack the sam account name into a DN:
  8324. //
  8325. NameToCrack = SamName;
  8326. NetStatus = DsCrackNamesW(
  8327. hDs,
  8328. 0,
  8329. DS_NT4_ACCOUNT_NAME,
  8330. DS_FQDN_1779_NAME,
  8331. 1,
  8332. &NameToCrack,
  8333. &CrackedName );
  8334. if ( NetStatus != NO_ERROR ) {
  8335. NlPrint(( NL_CRITICAL,
  8336. "SPN: CrackNames failed on %ws for %ws: %ld\n",
  8337. Update->UncDcName,
  8338. SamName,
  8339. NetStatus ));
  8340. goto Cleanup ;
  8341. }
  8342. if ( CrackedName->cItems != 1 ) {
  8343. CrackStatus = DS_NAME_ERROR_NOT_UNIQUE;
  8344. NlPrint(( NL_CRITICAL,
  8345. "SPN: Cracked Name is not unique on %ws for %ws: %ld\n",
  8346. Update->UncDcName,
  8347. SamName,
  8348. NetStatus ));
  8349. goto Cleanup ;
  8350. }
  8351. if ( CrackedName->rItems[ 0 ].status != DS_NAME_NO_ERROR ) {
  8352. NlPrint(( NL_CRITICAL,
  8353. "SPN: CrackNames failed on %ws for %ws: substatus %ld\n",
  8354. Update->UncDcName,
  8355. SamName,
  8356. CrackedName->rItems[ 0 ].status ));
  8357. CrackStatus = CrackedName->rItems[ 0 ].status;
  8358. goto Cleanup ;
  8359. }
  8360. DnOfAccount = CrackedName->rItems[0].pName;
  8361. //
  8362. // Open an LDAP connection to the DC and set useful options
  8363. //
  8364. LdapHandle = ldap_init( Update->UncDcName+2, LDAP_PORT );
  8365. if ( LdapHandle == NULL ) {
  8366. NetStatus = GetLastError();
  8367. NlPrint(( NL_CRITICAL,
  8368. "SPN: ldap_init failed on %ws for %ws: %ld\n",
  8369. Update->UncDcName,
  8370. SamName,
  8371. NetStatus ));
  8372. goto Cleanup;
  8373. }
  8374. // 30 second timeout
  8375. LdapOption = 30;
  8376. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_TIMELIMIT, &LdapOption );
  8377. if ( LdapStatus != LDAP_SUCCESS ) {
  8378. NlPrint(( NL_CRITICAL,
  8379. "SPN: ldap_set_option LDAP_OPT_TIMELIMIT failed on %ws for %ws: %ld: %s\n",
  8380. Update->UncDcName,
  8381. SamName,
  8382. LdapStatus,
  8383. ldap_err2stringA( LdapStatus )));
  8384. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8385. goto Cleanup;
  8386. }
  8387. // Don't chase referals
  8388. LdapOption = PtrToLong(LDAP_OPT_OFF);
  8389. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_REFERRALS, &LdapOption );
  8390. if ( LdapStatus != LDAP_SUCCESS ) {
  8391. NlPrint(( NL_CRITICAL,
  8392. "SPN: ldap_set_option LDAP_OPT_REFERRALS failed on %ws for %ws: %ld: %s\n",
  8393. Update->UncDcName,
  8394. SamName,
  8395. LdapStatus,
  8396. ldap_err2stringA( LdapStatus )));
  8397. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8398. goto Cleanup;
  8399. }
  8400. // Set the option telling LDAP that I passed it an explicit DC name and
  8401. // that it can avoid the DsGetDcName.
  8402. LdapOption = PtrToLong(LDAP_OPT_ON);
  8403. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_AREC_EXCLUSIVE, &LdapOption );
  8404. if ( LdapStatus != LDAP_SUCCESS ) {
  8405. NlPrint(( NL_CRITICAL,
  8406. "SPN: ldap_set_option LDAP_OPT_AREC_EXCLUSIVE failed on %ws for %ws: %ld: %s\n",
  8407. Update->UncDcName,
  8408. SamName,
  8409. LdapStatus,
  8410. ldap_err2stringA( LdapStatus )));
  8411. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8412. goto Cleanup;
  8413. }
  8414. //
  8415. // Bind to the DC
  8416. //
  8417. LdapStatus = ldap_bind_s( LdapHandle,
  8418. NULL, // No DN of account to authenticate as
  8419. NULL, // Default credentials
  8420. LDAP_AUTH_NEGOTIATE );
  8421. if ( LdapStatus != LDAP_SUCCESS ) {
  8422. NlPrint(( NL_CRITICAL,
  8423. "SPN: Cannot ldap_bind to %ws for %ws: %ld: %s\n",
  8424. Update->UncDcName,
  8425. SamName,
  8426. LdapStatus,
  8427. ldap_err2stringA( LdapStatus )));
  8428. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8429. goto Cleanup;
  8430. }
  8431. //
  8432. // Write the modifications
  8433. //
  8434. LdapStatus = ldap_modify_extW( LdapHandle,
  8435. DnOfAccount,
  8436. Mods,
  8437. (PLDAPControl *) &ModifyControlArray,
  8438. NULL, // No client controls
  8439. &MessageNumber );
  8440. if ( LdapStatus != LDAP_SUCCESS ) {
  8441. NlPrint(( NL_CRITICAL,
  8442. "SPN: Cannot ldap_modify on %ws for %ws: %ld: %s\n",
  8443. Update->UncDcName,
  8444. DnOfAccount,
  8445. LdapStatus,
  8446. ldap_err2stringA( LdapStatus )));
  8447. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8448. goto Cleanup;
  8449. }
  8450. // Wait for the modify to complete
  8451. LdapTimeout.tv_sec = NlGlobalParameters.ShortApiCallPeriod / 1000, // Don't wait forever
  8452. LdapTimeout.tv_usec = 0;
  8453. LdapStatus = ldap_result( LdapHandle,
  8454. MessageNumber,
  8455. LDAP_MSG_ALL,
  8456. &LdapTimeout,
  8457. &LdapMessage );
  8458. switch ( LdapStatus ) {
  8459. case -1:
  8460. NlPrint(( NL_CRITICAL,
  8461. "SPN: Cannot ldap_result on %ws for %ws: %ld: %s\n",
  8462. Update->UncDcName,
  8463. SamName,
  8464. LdapHandle->ld_errno,
  8465. ldap_err2stringA( LdapHandle->ld_errno )));
  8466. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8467. goto Cleanup;
  8468. case 0:
  8469. NlPrint(( NL_CRITICAL,
  8470. "SPN: ldap_result timeout on %ws for %ws.\n",
  8471. Update->UncDcName,
  8472. SamName ));
  8473. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8474. goto Cleanup;
  8475. case LDAP_RES_MODIFY:
  8476. if ( LdapMessage->lm_returncode != 0 ) {
  8477. NlPrint(( NL_CRITICAL,
  8478. "SPN: Cannot ldap_result on %ws for %ws: %ld: %s\n",
  8479. Update->UncDcName,
  8480. SamName,
  8481. LdapMessage->lm_returncode,
  8482. ldap_err2stringA( LdapMessage->lm_returncode )));
  8483. NetStatus = LdapMapErrorToWin32(LdapMessage->lm_returncode);
  8484. goto Cleanup;
  8485. }
  8486. NlPrint(( NL_MISC,
  8487. "SPN: Set successfully on DC %ws\n",
  8488. Update->UncDcName ));
  8489. break; // This is what we expect
  8490. default:
  8491. NlPrint(( NL_CRITICAL,
  8492. "SPN: ldap_result unexpected result on %ws for %ws: %ld\n",
  8493. Update->UncDcName,
  8494. SamName,
  8495. LdapStatus ));
  8496. NetStatus = LdapMapErrorToWin32(LdapStatus);
  8497. goto Cleanup;
  8498. }
  8499. Cleanup:
  8500. //
  8501. // Log the failure in the event log, if requested.
  8502. // Try to output the most specific error.
  8503. //
  8504. if ( CrackStatus != DS_NAME_NO_ERROR && Update->WriteEventLogOnFailure ) {
  8505. //
  8506. // Try to log a more descriptive error message
  8507. //
  8508. if ( CrackStatus == DS_NAME_ERROR_NOT_UNIQUE ) {
  8509. LPWSTR MsgStrings[2];
  8510. MsgStrings[0] = Update->UncDcName;
  8511. MsgStrings[1] = SamName;
  8512. NlpWriteEventlog( NELOG_NetlogonSpnMultipleSamAccountNames,
  8513. EVENTLOG_ERROR_TYPE,
  8514. NULL,
  8515. 0,
  8516. MsgStrings,
  8517. 2 );
  8518. //
  8519. // Log a generic crack name error message
  8520. //
  8521. } else {
  8522. LPWSTR MsgStrings[4];
  8523. // Each byte of the status code will transform into one character 0-F
  8524. WCHAR NetStatusString[sizeof(WCHAR) * (sizeof(NetStatus) + 1)];
  8525. WCHAR CrackStatusString[sizeof(WCHAR) * (sizeof(CrackStatus) + 1)];
  8526. swprintf( NetStatusString, L"%lx", NetStatus );
  8527. swprintf( CrackStatusString, L"%lx", CrackStatus );
  8528. MsgStrings[0] = Update->UncDcName;
  8529. MsgStrings[1] = SamName;
  8530. MsgStrings[2] = NetStatusString;
  8531. MsgStrings[3] = CrackStatusString;
  8532. NlpWriteEventlog( NELOG_NetlogonSpnCrackNamesFailure,
  8533. EVENTLOG_ERROR_TYPE,
  8534. NULL,
  8535. 0,
  8536. MsgStrings,
  8537. 4 );
  8538. }
  8539. //
  8540. // Log the more generic error
  8541. //
  8542. } else if ( NetStatus != NO_ERROR && Update->WriteEventLogOnFailure ) {
  8543. if ( Update->SetDnsHostName ) {
  8544. LPWSTR MsgStrings[2];
  8545. if ( Update->DnsHostName != NULL ) {
  8546. MsgStrings[0] = Update->DnsHostName;
  8547. } else {
  8548. MsgStrings[0] = L"<UNAVAILABLE>";
  8549. }
  8550. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  8551. NlpWriteEventlog( NELOG_NetlogonFailedDnsHostNameUpdate,
  8552. EVENTLOG_ERROR_TYPE,
  8553. (LPBYTE)&NetStatus,
  8554. sizeof(NetStatus),
  8555. MsgStrings,
  8556. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  8557. }
  8558. if ( Update->SetSpn ) {
  8559. LPWSTR MsgStrings[3];
  8560. if ( DnsSpn != NULL ) {
  8561. MsgStrings[0] = DnsSpn;
  8562. } else {
  8563. MsgStrings[0] = L"<UNAVAILABLE>";
  8564. }
  8565. if ( NetbiosSpn != NULL ) {
  8566. MsgStrings[1] = NetbiosSpn;
  8567. } else {
  8568. MsgStrings[1] = L"<UNAVAILABLE>";
  8569. }
  8570. MsgStrings[2] = (LPWSTR) ULongToPtr( NetStatus );
  8571. NlpWriteEventlog( NELOG_NetlogonFailedSpnUpdate,
  8572. EVENTLOG_ERROR_TYPE,
  8573. (LPBYTE)&NetStatus,
  8574. sizeof(NetStatus),
  8575. MsgStrings,
  8576. 3 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  8577. }
  8578. }
  8579. if ( hDs ) {
  8580. DsUnBind( &hDs );
  8581. }
  8582. if ( CrackedName ) {
  8583. DsFreeNameResultW( CrackedName );
  8584. }
  8585. if ( LdapMessage != NULL ) {
  8586. ldap_msgfree( LdapMessage );
  8587. }
  8588. if ( LdapHandle != NULL ) {
  8589. ldap_unbind_s( LdapHandle );
  8590. }
  8591. if ( DnsSpn ) {
  8592. LocalFree( DnsSpn );
  8593. }
  8594. if ( NetbiosSpn ) {
  8595. LocalFree( NetbiosSpn );
  8596. }
  8597. if ( Update ) {
  8598. LocalFree( Update );
  8599. }
  8600. return 0;
  8601. }
  8602. NET_API_STATUS
  8603. NlSetDsSPN(
  8604. IN BOOLEAN Synchronous,
  8605. IN BOOLEAN SetSpn,
  8606. IN BOOLEAN SetDnsHostName,
  8607. IN PDOMAIN_INFO DomainInfo,
  8608. IN LPWSTR UncDcName,
  8609. IN LPWSTR ComputerName,
  8610. IN LPWSTR DnsHostName
  8611. )
  8612. /*++
  8613. Routine Description:
  8614. Queues an update request to the thread pool for later
  8615. execution in a worker thread.
  8616. Arguments:
  8617. Synchronous - TRUE if the operation is to complete before this procedure returns
  8618. SetSpn - TRUE if the SPN is to be updated
  8619. SetDnsHostName - TRUE if the Dns host name is to be updated
  8620. DomainInfo - Hosted Domain this object is in
  8621. UncDcName - UNC name of the DC to make this call on
  8622. ComputerName - Name of the computer. This is (usually)
  8623. equivalent to the netbios name, without
  8624. the '$' on the end.
  8625. DnsHostName - DNS Hostname of the computer. This is in
  8626. FQDN format: longcomputername.dns.domain.com
  8627. Return Value:
  8628. ERROR_NOT_ENOUGH_MEMORY - No memory to queue the worker request
  8629. NO_ERROR - Queued.
  8630. --*/
  8631. {
  8632. NET_API_STATUS NetStatus;
  8633. PNL_SPN_UPDATE Update;
  8634. DWORD Size;
  8635. DWORD NetbiosComputerNameSize;
  8636. DWORD DnsHostNameSize;
  8637. DWORD DcNameSize;
  8638. WCHAR NetbiosDomainName[DNLEN+1];
  8639. DWORD NetbiosDomainNameSize;
  8640. LPBYTE Where;
  8641. //
  8642. // Silently ignore clients with no DNS host name
  8643. //
  8644. if ( DnsHostName == NULL ) {
  8645. return NO_ERROR;
  8646. }
  8647. if ( !SetSpn && !SetDnsHostName ) {
  8648. return NO_ERROR;
  8649. }
  8650. //
  8651. // Sanity check computer name
  8652. //
  8653. if ( wcslen( ComputerName ) > CNLEN ) {
  8654. return ERROR_INVALID_PARAMETER;
  8655. }
  8656. //
  8657. // Grab the Netbios Domain Name
  8658. //
  8659. EnterCriticalSection( &NlGlobalDomainCritSect );
  8660. wcscpy( NetbiosDomainName, DomainInfo->DomUnicodeDomainName );
  8661. LeaveCriticalSection( &NlGlobalDomainCritSect );
  8662. //
  8663. // Allocate a workitem
  8664. //
  8665. DnsHostNameSize = wcslen( DnsHostName ) * sizeof(WCHAR) + sizeof(WCHAR);
  8666. NetbiosComputerNameSize = wcslen( ComputerName ) * sizeof(WCHAR) + sizeof(WCHAR);
  8667. DcNameSize = wcslen( UncDcName ) * sizeof(WCHAR) + sizeof(WCHAR);
  8668. NetbiosDomainNameSize = wcslen( NetbiosDomainName ) * sizeof(WCHAR) + sizeof(WCHAR);
  8669. Size = sizeof( NL_SPN_UPDATE ) +
  8670. DnsHostNameSize +
  8671. NetbiosComputerNameSize +
  8672. DcNameSize +
  8673. NetbiosDomainNameSize +
  8674. NL_MAX_DNS_LENGTH * sizeof(WCHAR) + sizeof(WCHAR);
  8675. Update = LocalAlloc( 0, Size );
  8676. if ( Update == NULL ) {
  8677. return ERROR_NOT_ENOUGH_MEMORY ;
  8678. }
  8679. //
  8680. // Build the update request:
  8681. //
  8682. Update->SetSpn = SetSpn;
  8683. Update->SetDnsHostName = SetDnsHostName;
  8684. Update->WriteEventLogOnFailure = FALSE;
  8685. Where = (LPBYTE) (Update + 1);
  8686. Update->DnsHostName = (LPWSTR)Where;
  8687. RtlCopyMemory( Where, DnsHostName, DnsHostNameSize );
  8688. Where += DnsHostNameSize;
  8689. Update->NetbiosComputerName = (LPWSTR)Where;
  8690. RtlCopyMemory( Where, ComputerName, NetbiosComputerNameSize );
  8691. Where += NetbiosComputerNameSize;
  8692. Update->UncDcName = (LPWSTR)Where;
  8693. RtlCopyMemory( Where, UncDcName, DcNameSize );
  8694. Where += DcNameSize;
  8695. Update->NetbiosDomainName = (LPWSTR)Where;
  8696. RtlCopyMemory( Where, NetbiosDomainName, NetbiosDomainNameSize );
  8697. Where += NetbiosDomainNameSize;
  8698. Update->DnsDomainName = (LPWSTR)Where;
  8699. NlCaptureDomainInfo( DomainInfo,
  8700. Update->DnsDomainName,
  8701. NULL );
  8702. Where += NL_MAX_DNS_LENGTH * sizeof(WCHAR) + sizeof(WCHAR);
  8703. //
  8704. // Either do the work now or queue it to a worker thread.
  8705. //
  8706. if ( Synchronous ) {
  8707. //
  8708. // On workstation where this call is synchronous,
  8709. // log any error in the event log.
  8710. //
  8711. Update->WriteEventLogOnFailure = TRUE;
  8712. (VOID) NlSetDsSPNWorker( Update );
  8713. } else {
  8714. //
  8715. // Queue it off to a worker thread. The update will take
  8716. // place from a different thread, so we won't have interesting
  8717. // deadlocks due to lookups.
  8718. //
  8719. NlPrint(( NL_MISC,
  8720. "NlSetDsSPN: Queuing SPN update for %ws on %ws.\n",
  8721. Update->DnsHostName,
  8722. Update->UncDcName ));
  8723. //
  8724. // REVIEW: how do I wait for this worker to finish executing when the
  8725. // service shuts down.
  8726. //
  8727. if ( !QueueUserWorkItem( NlSetDsSPNWorker, Update, 0 ) ) {
  8728. LocalFree( Update );
  8729. return ERROR_NOT_ENOUGH_MEMORY ;
  8730. }
  8731. NetStatus = NO_ERROR;
  8732. }
  8733. return NetStatus;
  8734. }
  8735. NET_API_STATUS NET_API_FUNCTION
  8736. DsrDeregisterDnsHostRecords (
  8737. IN LPWSTR ServerName OPTIONAL,
  8738. IN LPWSTR DnsDomainName OPTIONAL,
  8739. IN GUID *DomainGuid OPTIONAL,
  8740. IN GUID *DsaGuid OPTIONAL,
  8741. IN LPWSTR DnsHostName
  8742. )
  8743. /*++
  8744. Routine Description:
  8745. This function deletes all DNS entries associated with a particular
  8746. NtDsDsa object.
  8747. This routine does NOT delete A records registered by the DC. We have
  8748. no way of finding out the IP addresses of the long gone DC.
  8749. Only an Admin, Account Operator or Server Operator may call this
  8750. function.
  8751. Arguments:
  8752. DnsDomainName - DNS domain name of the domain the DC was in.
  8753. This need not be a domain hosted by this DC.
  8754. If NULL, it is implied to be the DnsHostName with the leftmost label
  8755. removed.
  8756. DomainGuid - Domain Guid of the domain.
  8757. If NULL, GUID specific names will not be removed.
  8758. DsaGuid - GUID of the NtdsDsa object that will be deleted.
  8759. If NULL, NtdsDsa specific names will not be removed.
  8760. DnsHostName - DNS host name of the DC whose DNS records are being deleted.
  8761. Return Value:
  8762. NO_ERROR - Success.
  8763. ERROR_NOT_SUPPORTED - The server specified is not a DC.
  8764. ERROR_ACCESS_DENIED - The caller is not allowed to perform this operation.
  8765. --*/
  8766. {
  8767. NTSTATUS Status;
  8768. NET_API_STATUS NetStatus;
  8769. //
  8770. // This APIis supported on DCs only
  8771. //
  8772. if ( NlGlobalMemberWorkstation ) {
  8773. return ERROR_NOT_SUPPORTED;
  8774. }
  8775. //
  8776. // Perform access validation on the caller
  8777. //
  8778. NetStatus = NetpAccessCheck(
  8779. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  8780. NETLOGON_CONTROL_ACCESS, // Desired access
  8781. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  8782. if ( NetStatus != NERR_Success) {
  8783. return ERROR_ACCESS_DENIED;
  8784. }
  8785. //
  8786. // Notify the service that DNS records need to be deleted
  8787. //
  8788. Status = I_NetNotifyNtdsDsaDeletion ( DnsDomainName,
  8789. DomainGuid,
  8790. DsaGuid,
  8791. DnsHostName );
  8792. if ( !NT_SUCCESS(Status) ) {
  8793. NetStatus = NetpNtStatusToApiStatus( Status );
  8794. NlPrint(( NL_CRITICAL,
  8795. "DsrDeregisterDnsHostRecords: Cannot I_NetNotifyNtdsDsaDeletion. %ld\n",
  8796. NetStatus ));
  8797. return NetStatus;
  8798. }
  8799. //
  8800. // Everything was successful
  8801. //
  8802. return NO_ERROR;
  8803. UNREFERENCED_PARAMETER( ServerName );
  8804. }