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

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