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.

6711 lines
194 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. logonapi.c
  5. Abstract:
  6. Remote Logon API routines.
  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. Madana - Fixed several bugs.
  15. --*/
  16. //
  17. // Common include files.
  18. //
  19. #include "logonsrv.h" // Include files common to entire service
  20. #pragma hdrstop
  21. //
  22. // Include files specific to this .c file
  23. //
  24. #include <accessp.h> // Routines shared with NetUser Apis
  25. #include <rpcutil.h> // NetpRpcStatusToApiStatus()
  26. #include <stdio.h> // sprintf().
  27. LPSTR
  28. NlpLogonTypeToText(
  29. IN NETLOGON_LOGON_INFO_CLASS LogonLevel
  30. )
  31. /*++
  32. Routine Description:
  33. Returns a text string corresponding to LogonLevel
  34. Arguments:
  35. LogonLevel - Type of logon
  36. Return Value:
  37. Printable text string
  38. None
  39. --*/
  40. {
  41. LPSTR LogonType;
  42. //
  43. // Compute the string describing the logon type
  44. //
  45. switch ( LogonLevel ) {
  46. case NetlogonInteractiveInformation:
  47. LogonType = "Interactive"; break;
  48. case NetlogonNetworkInformation:
  49. LogonType = "Network"; break;
  50. case NetlogonServiceInformation:
  51. LogonType = "Service"; break;
  52. case NetlogonInteractiveTransitiveInformation:
  53. LogonType = "Transitive Interactive"; break;
  54. case NetlogonNetworkTransitiveInformation:
  55. LogonType = "Transitive Network"; break;
  56. case NetlogonServiceTransitiveInformation:
  57. LogonType = "Transitive Service"; break;
  58. case NetlogonGenericInformation:
  59. LogonType = "Generic"; break;
  60. default:
  61. LogonType = "[Unknown]";
  62. }
  63. return LogonType;
  64. }
  65. #ifdef _DC_NETLOGON
  66. NET_API_STATUS
  67. NlEnsureClientIsNamedUser(
  68. IN PDOMAIN_INFO DomainInfo,
  69. IN LPWSTR UserName
  70. )
  71. /*++
  72. Routine Description:
  73. Ensure the client is the named user.
  74. Arguments:
  75. UserName - name of the user to check.
  76. Return Value:
  77. NT status code.
  78. --*/
  79. {
  80. NET_API_STATUS NetStatus;
  81. RPC_STATUS RpcStatus;
  82. NTSTATUS Status;
  83. HANDLE TokenHandle = NULL;
  84. PTOKEN_USER TokenUserInfo = NULL;
  85. ULONG TokenUserInfoSize;
  86. ULONG UserId;
  87. PSID UserSid;
  88. //
  89. // Get the relative ID of the specified user.
  90. //
  91. Status = NlSamOpenNamedUser( DomainInfo, UserName, NULL, &UserId, NULL );
  92. if ( !NT_SUCCESS(Status) ) {
  93. NlPrintDom(( NL_CRITICAL, DomainInfo,
  94. "NlEnsureClientIsNamedUser: %ws: NlSamOpenNamedUser failed 0x%lx\n",
  95. UserName,
  96. Status ));
  97. NetStatus = NetpNtStatusToApiStatus( Status );
  98. goto Cleanup;
  99. }
  100. //
  101. // Impersonate the client while we check him out.
  102. //
  103. RpcStatus = RpcImpersonateClient( NULL );
  104. if ( RpcStatus != RPC_S_OK ) {
  105. NlPrintDom(( NL_CRITICAL, DomainInfo,
  106. "NlEnsureClientIsNamedUser: %ws: RpcImpersonateClient failed 0x%lx\n",
  107. UserName,
  108. RpcStatus ));
  109. NetStatus = NetpRpcStatusToApiStatus( RpcStatus );
  110. goto Cleanup;
  111. }
  112. //
  113. // Compare the username specified with that in
  114. // the impersonation token to ensure the caller isn't bogus.
  115. //
  116. // Do this by opening the token,
  117. // querying the token user info,
  118. // and ensuring the returned SID is for this user.
  119. //
  120. Status = NtOpenThreadToken(
  121. NtCurrentThread(),
  122. TOKEN_QUERY,
  123. (BOOLEAN) TRUE, // Use the logon service's security context
  124. // to open the token
  125. &TokenHandle );
  126. if ( !NT_SUCCESS( Status )) {
  127. NlPrintDom(( NL_CRITICAL, DomainInfo,
  128. "NlEnsureClientIsNamedUser: %ws: NtOpenThreadToken failed 0x%lx\n",
  129. UserName,
  130. Status ));
  131. NetStatus = NetpNtStatusToApiStatus( Status );
  132. goto Cleanup;
  133. }
  134. //
  135. // Get the user's SID for the token.
  136. //
  137. Status = NtQueryInformationToken(
  138. TokenHandle,
  139. TokenUser,
  140. &TokenUserInfo,
  141. 0,
  142. &TokenUserInfoSize );
  143. if ( Status != STATUS_BUFFER_TOO_SMALL ) {
  144. NlPrintDom(( NL_CRITICAL, DomainInfo,
  145. "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread failed 0x%lx\n",
  146. UserName,
  147. Status ));
  148. NetStatus = NetpNtStatusToApiStatus( Status );
  149. goto Cleanup;
  150. }
  151. TokenUserInfo = NetpMemoryAllocate( TokenUserInfoSize );
  152. if ( TokenUserInfo == NULL ) {
  153. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  154. goto Cleanup;
  155. }
  156. Status = NtQueryInformationToken(
  157. TokenHandle,
  158. TokenUser,
  159. TokenUserInfo,
  160. TokenUserInfoSize,
  161. &TokenUserInfoSize );
  162. if ( !NT_SUCCESS(Status) ) {
  163. NlPrintDom(( NL_CRITICAL, DomainInfo,
  164. "NlEnsureClientIsNamedUser: %ws: NtOpenQueryInformationThread (again) failed 0x%lx\n",
  165. UserName,
  166. Status ));
  167. NetStatus = NetpNtStatusToApiStatus( Status );
  168. goto Cleanup;
  169. }
  170. UserSid = TokenUserInfo->User.Sid;
  171. //
  172. // Ensure the last subauthority matches the UserId
  173. //
  174. if ( UserId !=
  175. *RtlSubAuthoritySid( UserSid, (*RtlSubAuthorityCountSid(UserSid))-1 )){
  176. NlPrintDom(( NL_CRITICAL, DomainInfo,
  177. "NlEnsureClientIsNamedUser: %ws: UserId mismatch 0x%lx\n",
  178. UserName,
  179. UserId ));
  180. NlpDumpSid( NL_CRITICAL, UserSid );
  181. NetStatus = ERROR_ACCESS_DENIED;
  182. goto Cleanup;
  183. }
  184. //
  185. // Convert the User's sid to a DomainId and ensure it is our domain Id.
  186. //
  187. (*RtlSubAuthorityCountSid(UserSid)) --;
  188. if ( !RtlEqualSid( (PSID) DomainInfo->DomAccountDomainId, UserSid ) ) {
  189. NlPrintDom(( NL_CRITICAL, DomainInfo,
  190. "NlEnsureClientIsNamedUser: %ws: DomainId mismatch 0x%lx\n",
  191. UserName,
  192. UserId ));
  193. NlpDumpSid( NL_CRITICAL, UserSid );
  194. NlpDumpSid( NL_CRITICAL, (PSID) DomainInfo->DomAccountDomainId );
  195. NetStatus = ERROR_ACCESS_DENIED;
  196. goto Cleanup;
  197. }
  198. //
  199. // Done
  200. //
  201. NetStatus = NERR_Success;
  202. Cleanup:
  203. //
  204. // Clean up locally used resources.
  205. //
  206. if ( TokenHandle != NULL ) {
  207. (VOID) NtClose( TokenHandle );
  208. }
  209. if ( TokenUserInfo != NULL ) {
  210. NetpMemoryFree( TokenUserInfo );
  211. }
  212. //
  213. // revert to system, so that we can close
  214. // the user handle properly.
  215. //
  216. (VOID) RpcRevertToSelf();
  217. return NetStatus;
  218. }
  219. #endif // _DC_NETLOGON
  220. NET_API_STATUS
  221. NetrLogonUasLogon (
  222. IN LPWSTR ServerName,
  223. IN LPWSTR UserName,
  224. IN LPWSTR Workstation,
  225. OUT PNETLOGON_VALIDATION_UAS_INFO *ValidationInformation
  226. )
  227. /*++
  228. Routine Description:
  229. Server side of I_NetLogonUasLogon.
  230. This function is called by the XACT server when processing a
  231. I_NetWkstaUserLogon XACT SMB. This feature allows a UAS client to
  232. logon to a SAM domain controller.
  233. Arguments:
  234. ServerName -- Server to perform this operation on. Must be NULL.
  235. UserName -- Account name of the user logging on.
  236. Workstation -- The workstation from which the user is logging on.
  237. ValidationInformation -- Returns the requested validation
  238. information.
  239. Return Value:
  240. NERR_SUCCESS if there was no error. Otherwise, the error code is
  241. returned.
  242. --*/
  243. {
  244. #ifdef _WKSTA_NETLOGON
  245. return ERROR_NOT_SUPPORTED;
  246. UNREFERENCED_PARAMETER( ServerName );
  247. UNREFERENCED_PARAMETER( UserName );
  248. UNREFERENCED_PARAMETER( Workstation );
  249. UNREFERENCED_PARAMETER( ValidationInformation );
  250. #endif // _WKSTA_NETLOGON
  251. #ifdef _DC_NETLOGON
  252. NET_API_STATUS NetStatus;
  253. NTSTATUS Status;
  254. NETLOGON_INTERACTIVE_INFO LogonInteractive;
  255. PNETLOGON_VALIDATION_SAM_INFO SamInfo = NULL;
  256. PNETLOGON_VALIDATION_UAS_INFO usrlog1 = NULL;
  257. DWORD ValidationSize;
  258. LPWSTR EndOfVariableData;
  259. BOOLEAN Authoritative;
  260. BOOLEAN BadPasswordCountZeroed;
  261. LARGE_INTEGER TempTime;
  262. PDOMAIN_INFO DomainInfo = NULL;
  263. //
  264. // This API is not supported on workstations.
  265. //
  266. if ( NlGlobalMemberWorkstation ) {
  267. return ERROR_NOT_SUPPORTED;
  268. }
  269. //
  270. // This API can only be called locally. (By the XACT server).
  271. //
  272. // ??: Modify xactsrv to pass this information along
  273. if ( ServerName != NULL ) {
  274. return ERROR_INVALID_PARAMETER;
  275. }
  276. //
  277. // Initialization
  278. //
  279. *ValidationInformation = NULL;
  280. //
  281. // Lookup which domain this call pertains to.
  282. //
  283. DomainInfo = NlFindDomainByServerName( ServerName );
  284. if ( DomainInfo == NULL ) {
  285. NetStatus = ERROR_INVALID_COMPUTERNAME;
  286. goto Cleanup;
  287. }
  288. //
  289. // Perform access validation on the caller.
  290. //
  291. NetStatus = NetpAccessCheck(
  292. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  293. NETLOGON_UAS_LOGON_ACCESS, // Desired access
  294. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  295. if ( NetStatus != NERR_Success) {
  296. NlPrintDom((NL_CRITICAL, DomainInfo,
  297. "NetrLogonUasLogon of %ws from %ws failed NetpAccessCheck\n",
  298. UserName, Workstation));
  299. NetStatus = ERROR_ACCESS_DENIED;
  300. goto Cleanup;
  301. }
  302. //
  303. // Ensure the client is actually the named user.
  304. //
  305. // The server has already validated the password.
  306. // The XACT server has already verified that the workstation name is
  307. // correct.
  308. //
  309. NetStatus = NlEnsureClientIsNamedUser( DomainInfo, UserName );
  310. if ( NetStatus != NERR_Success ) {
  311. NlPrintDom((NL_CRITICAL, DomainInfo,
  312. "NetrLogonUasLogon of %ws from %ws failed NlEnsureClientIsNamedUser\n",
  313. UserName, Workstation));
  314. NetStatus = ERROR_ACCESS_DENIED;
  315. goto Cleanup;
  316. }
  317. //
  318. // Validate the user against the local SAM database.
  319. //
  320. RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL );
  321. LogonInteractive.Identity.ParameterControl = 0;
  322. RtlZeroMemory( &LogonInteractive.Identity.LogonId,
  323. sizeof(LogonInteractive.Identity.LogonId) );
  324. RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName );
  325. RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation );
  326. Status = MsvSamValidate( DomainInfo->DomSamAccountDomainHandle,
  327. TRUE,
  328. NullSecureChannel, // Skip password check
  329. &DomainInfo->DomUnicodeComputerNameString,
  330. &DomainInfo->DomUnicodeAccountDomainNameString,
  331. DomainInfo->DomAccountDomainId,
  332. NetlogonInteractiveInformation,
  333. &LogonInteractive,
  334. NetlogonValidationSamInfo,
  335. (PVOID *)&SamInfo,
  336. &Authoritative,
  337. &BadPasswordCountZeroed,
  338. MSVSAM_SPECIFIED );
  339. if ( !NT_SUCCESS( Status )) {
  340. NetStatus = NetpNtStatusToApiStatus( Status );
  341. goto Cleanup;
  342. }
  343. //
  344. // Allocate a return buffer
  345. //
  346. ValidationSize = sizeof( NETLOGON_VALIDATION_UAS_INFO ) +
  347. SamInfo->EffectiveName.Length + sizeof(WCHAR) +
  348. (wcslen( DomainInfo->DomUncUnicodeComputerName ) +1) * sizeof(WCHAR) +
  349. DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) +
  350. SamInfo->LogonScript.Length + sizeof(WCHAR);
  351. ValidationSize = ROUND_UP_COUNT( ValidationSize, ALIGN_WCHAR );
  352. usrlog1 = MIDL_user_allocate( ValidationSize );
  353. if ( usrlog1 == NULL ) {
  354. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  355. goto Cleanup;
  356. }
  357. //
  358. // Convert the SAM information to the right format for LM 2.0
  359. //
  360. EndOfVariableData = (LPWSTR) (((PCHAR)usrlog1) + ValidationSize);
  361. if ( !NetpCopyStringToBuffer(
  362. SamInfo->EffectiveName.Buffer,
  363. SamInfo->EffectiveName.Length / sizeof(WCHAR),
  364. (LPBYTE) (usrlog1 + 1),
  365. &EndOfVariableData,
  366. &usrlog1->usrlog1_eff_name ) ) {
  367. NetStatus = NERR_InternalError ;
  368. goto Cleanup;
  369. }
  370. Status = NlGetUserPriv(
  371. DomainInfo,
  372. SamInfo->GroupCount,
  373. (PGROUP_MEMBERSHIP) SamInfo->GroupIds,
  374. SamInfo->UserId,
  375. &usrlog1->usrlog1_priv,
  376. &usrlog1->usrlog1_auth_flags );
  377. if ( !NT_SUCCESS( Status )) {
  378. NetStatus = NetpNtStatusToApiStatus( Status );
  379. goto Cleanup;
  380. }
  381. usrlog1->usrlog1_num_logons = 0;
  382. usrlog1->usrlog1_bad_pw_count = SamInfo->BadPasswordCount;
  383. OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogonTime, TempTime);
  384. if ( !RtlTimeToSecondsSince1970( &TempTime,
  385. &usrlog1->usrlog1_last_logon) ) {
  386. usrlog1->usrlog1_last_logon = 0;
  387. }
  388. OLD_TO_NEW_LARGE_INTEGER( SamInfo->LogoffTime, TempTime);
  389. if ( !RtlTimeToSecondsSince1970( &TempTime,
  390. &usrlog1->usrlog1_last_logoff) ) {
  391. usrlog1->usrlog1_last_logoff = TIMEQ_FOREVER;
  392. }
  393. OLD_TO_NEW_LARGE_INTEGER( SamInfo->KickOffTime, TempTime);
  394. if ( !RtlTimeToSecondsSince1970( &TempTime,
  395. &usrlog1->usrlog1_logoff_time) ) {
  396. usrlog1->usrlog1_logoff_time = TIMEQ_FOREVER;
  397. }
  398. if ( !RtlTimeToSecondsSince1970( &TempTime,
  399. &usrlog1->usrlog1_kickoff_time) ) {
  400. usrlog1->usrlog1_kickoff_time = TIMEQ_FOREVER;
  401. }
  402. OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordLastSet, TempTime);
  403. usrlog1->usrlog1_password_age =
  404. NetpGetElapsedSeconds( &TempTime );
  405. OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordCanChange, TempTime);
  406. if ( !RtlTimeToSecondsSince1970( &TempTime,
  407. &usrlog1->usrlog1_pw_can_change) ) {
  408. usrlog1->usrlog1_pw_can_change = TIMEQ_FOREVER;
  409. }
  410. OLD_TO_NEW_LARGE_INTEGER( SamInfo->PasswordMustChange, TempTime);
  411. if ( !RtlTimeToSecondsSince1970( &TempTime,
  412. &usrlog1->usrlog1_pw_must_change) ) {
  413. usrlog1->usrlog1_pw_must_change = TIMEQ_FOREVER;
  414. }
  415. usrlog1->usrlog1_computer = DomainInfo->DomUncUnicodeComputerName;
  416. if ( !NetpPackString(
  417. &usrlog1->usrlog1_computer,
  418. (LPBYTE) (usrlog1 + 1),
  419. &EndOfVariableData )) {
  420. NetStatus = NERR_InternalError ;
  421. goto Cleanup;
  422. }
  423. if ( !NetpCopyStringToBuffer(
  424. DomainInfo->DomUnicodeDomainNameString.Buffer,
  425. DomainInfo->DomUnicodeDomainNameString.Length / sizeof(WCHAR),
  426. (LPBYTE) (usrlog1 + 1),
  427. &EndOfVariableData,
  428. &usrlog1->usrlog1_domain ) ) {
  429. NetStatus = NERR_InternalError ;
  430. goto Cleanup;
  431. }
  432. if ( !NetpCopyStringToBuffer(
  433. SamInfo->LogonScript.Buffer,
  434. SamInfo->LogonScript.Length / sizeof(WCHAR),
  435. (LPBYTE) (usrlog1 + 1),
  436. &EndOfVariableData,
  437. &usrlog1->usrlog1_script_path ) ) {
  438. NetStatus = NERR_InternalError ;
  439. goto Cleanup;
  440. }
  441. NetStatus = NERR_Success;
  442. //
  443. // Done
  444. //
  445. Cleanup:
  446. //
  447. // Clean up locally used resources.
  448. //
  449. if ( SamInfo != NULL ) {
  450. MIDL_user_free( SamInfo );
  451. }
  452. if ( NetStatus != NERR_Success ) {
  453. if ( usrlog1 != NULL ) {
  454. MIDL_user_free( usrlog1 );
  455. usrlog1 = NULL;
  456. }
  457. }
  458. if ( DomainInfo != NULL ) {
  459. NlDereferenceDomain( DomainInfo );
  460. }
  461. NlPrint((NL_LOGON,
  462. "%ws: NetrLogonUasLogon of %ws from %ws returns %lu\n",
  463. DomainInfo == NULL ? L"[Unknown]" : DomainInfo->DomUnicodeDomainName,
  464. UserName, Workstation, NetStatus ));
  465. *ValidationInformation = usrlog1;
  466. return(NetStatus);
  467. #endif // _DC_NETLOGON
  468. }
  469. NET_API_STATUS
  470. NetrLogonUasLogoff (
  471. IN LPWSTR ServerName OPTIONAL,
  472. IN LPWSTR UserName,
  473. IN LPWSTR Workstation,
  474. OUT PNETLOGON_LOGOFF_UAS_INFO LogoffInformation
  475. )
  476. /*++
  477. Routine Description:
  478. This function is called by the XACT server when processing a
  479. I_NetWkstaUserLogoff XACT SMB. This feature allows a UAS client to
  480. logoff from a SAM domain controller. The request is authenticated,
  481. the entry is removed for this user from the logon session table
  482. maintained by the Netlogon service for NetLogonEnum, and logoff
  483. information is returned to the caller.
  484. The server portion of I_NetLogonUasLogoff (in the Netlogon service)
  485. compares the user name and workstation name specified in the
  486. LogonInformation with the user name and workstation name from the
  487. impersonation token. If they don't match, I_NetLogonUasLogoff fails
  488. indicating the access is denied.
  489. Group SECURITY_LOCAL is refused access to this function. Membership
  490. in SECURITY_LOCAL implies that this call was made locally and not
  491. through the XACT server.
  492. The Netlogon service cannot be sure that this function was called by
  493. the XACT server. Therefore, the Netlogon service will not simply
  494. delete the entry from the logon session table. Rather, the logon
  495. session table entry will be marked invisible outside of the Netlogon
  496. service (i.e., it will not be returned by NetLogonEnum) until a valid
  497. LOGON_WKSTINFO_RESPONSE is received for the entry. The Netlogon
  498. service will immediately interrogate the client (as described above
  499. for LOGON_WKSTINFO_RESPONSE) and temporarily increase the
  500. interrogation frequency to at least once a minute. The logon session
  501. table entry will reappear as soon as a function of interrogation if
  502. this isn't a true logoff request.
  503. Arguments:
  504. ServerName -- Reserved. Must be NULL.
  505. UserName -- Account name of the user logging off.
  506. Workstation -- The workstation from which the user is logging
  507. off.
  508. LogoffInformation -- Returns the requested logoff information.
  509. Return Value:
  510. The Net status code.
  511. --*/
  512. {
  513. #ifdef _WKSTA_NETLOGON
  514. return ERROR_NOT_SUPPORTED;
  515. UNREFERENCED_PARAMETER( ServerName );
  516. UNREFERENCED_PARAMETER( UserName );
  517. UNREFERENCED_PARAMETER( Workstation );
  518. UNREFERENCED_PARAMETER( LogoffInformation );
  519. #endif // _WKSTA_NETLOGON
  520. #ifdef _DC_NETLOGON
  521. NET_API_STATUS NetStatus;
  522. NTSTATUS Status;
  523. PDOMAIN_INFO DomainInfo = NULL;
  524. NETLOGON_INTERACTIVE_INFO LogonInteractive;
  525. PNETLOGON_LOGOFF_UAS_INFO usrlog1 = NULL;
  526. //
  527. // This API is not supported on workstations.
  528. //
  529. if ( NlGlobalMemberWorkstation ) {
  530. return ERROR_NOT_SUPPORTED;
  531. }
  532. //
  533. // This API can only be called locally. (By the XACT server).
  534. //
  535. if ( ServerName != NULL ) {
  536. return ERROR_INVALID_PARAMETER;
  537. }
  538. //
  539. // Lookup which domain this call pertains to.
  540. //
  541. DomainInfo = NlFindDomainByServerName( ServerName );
  542. if ( DomainInfo == NULL ) {
  543. NetStatus = ERROR_INVALID_COMPUTERNAME;
  544. goto Cleanup;
  545. }
  546. //
  547. // Perform access validation on the caller.
  548. //
  549. NetStatus = NetpAccessCheck(
  550. NlGlobalNetlogonSecurityDescriptor, // Security descriptor
  551. NETLOGON_UAS_LOGOFF_ACCESS, // Desired access
  552. &NlGlobalNetlogonInfoMapping ); // Generic mapping
  553. if ( NetStatus != NERR_Success) {
  554. NlPrintDom((NL_CRITICAL, DomainInfo,
  555. "NetrLogonUasLogoff of %ws from %ws failed NetpAccessCheck\n",
  556. UserName, Workstation));
  557. NetStatus = ERROR_ACCESS_DENIED;
  558. goto Cleanup;
  559. }
  560. //
  561. // Ensure the client is actually the named user.
  562. //
  563. // The server has already validated the password.
  564. // The XACT server has already verified that the workstation name is
  565. // correct.
  566. //
  567. #ifdef notdef // Some clients (WFW 3.11) can call this over the null session
  568. NetStatus = NlEnsureClientIsNamedUser( DomainInfo, UserName );
  569. if ( NetStatus != NERR_Success ) {
  570. NlPrintDom((NL_CRITICAL, DomainInfo,
  571. "NetrLogonUasLogoff of %ws from %ws failed NlEnsureClientIsNamedUser\n",
  572. UserName, Workstation));
  573. NetStatus = ERROR_ACCESS_DENIED;
  574. goto Cleanup;
  575. }
  576. #endif // notdef
  577. //
  578. // Build the LogonInformation to return
  579. //
  580. LogoffInformation->Duration = 0;
  581. LogoffInformation->LogonCount = 0;
  582. //
  583. // Update the LastLogoff time in the SAM database.
  584. //
  585. RtlInitUnicodeString( &LogonInteractive.Identity.LogonDomainName, NULL );
  586. LogonInteractive.Identity.ParameterControl = 0;
  587. RtlZeroMemory( &LogonInteractive.Identity.LogonId,
  588. sizeof(LogonInteractive.Identity.LogonId) );
  589. RtlInitUnicodeString( &LogonInteractive.Identity.UserName, UserName );
  590. RtlInitUnicodeString( &LogonInteractive.Identity.Workstation, Workstation );
  591. Status = MsvSamLogoff(
  592. DomainInfo->DomSamAccountDomainHandle,
  593. NetlogonInteractiveInformation,
  594. &LogonInteractive );
  595. if (!NT_SUCCESS(Status)) {
  596. NetStatus = NetpNtStatusToApiStatus( Status );
  597. goto Cleanup;
  598. }
  599. //
  600. // Cleanup
  601. //
  602. Cleanup:
  603. //
  604. // Clean up locally used resources.
  605. //
  606. NlPrint((NL_LOGON,
  607. "%ws: NetrLogonUasLogoff of %ws from %ws returns %lu\n",
  608. DomainInfo == NULL ? L"[Unknown]" : DomainInfo->DomUnicodeDomainName,
  609. UserName, Workstation, NetStatus));
  610. if ( DomainInfo != NULL ) {
  611. NlDereferenceDomain( DomainInfo );
  612. }
  613. return NetStatus;
  614. #endif // _DC_NETLOGON
  615. }
  616. VOID
  617. NlpDecryptLogonInformation (
  618. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  619. IN OUT LPBYTE LogonInformation,
  620. IN PSESSION_INFO SessionInfo
  621. )
  622. /*++
  623. Routine Description:
  624. This function decrypts the sensitive information in the LogonInformation
  625. structure. The decryption is done in place.
  626. Arguments:
  627. LogonLevel -- Specifies the level of information given in
  628. LogonInformation.
  629. LogonInformation -- Specifies the description for the user
  630. logging on.
  631. SessionInfo -- The session key to encrypt with and negotiate flags
  632. Return Value:
  633. None.
  634. --*/
  635. {
  636. //
  637. // Only the interactive and service logon information is encrypted.
  638. //
  639. switch ( LogonLevel ) {
  640. case NetlogonInteractiveInformation:
  641. case NetlogonInteractiveTransitiveInformation:
  642. case NetlogonServiceInformation:
  643. case NetlogonServiceTransitiveInformation:
  644. {
  645. PNETLOGON_INTERACTIVE_INFO LogonInteractive;
  646. LogonInteractive =
  647. (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
  648. //
  649. // If both sides support RC4 encryption,
  650. // decrypt both the LM OWF and NT OWF passwords using RC4.
  651. //
  652. if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
  653. NlDecryptRC4( &LogonInteractive->LmOwfPassword,
  654. sizeof(LogonInteractive->LmOwfPassword),
  655. SessionInfo );
  656. NlDecryptRC4( &LogonInteractive->NtOwfPassword,
  657. sizeof(LogonInteractive->NtOwfPassword),
  658. SessionInfo );
  659. //
  660. // If the other side is running NT 3.1,
  661. // use the slower DES based encryption.
  662. //
  663. } else {
  664. NTSTATUS Status;
  665. ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
  666. ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
  667. //
  668. // Decrypt the LM_OWF password.
  669. //
  670. NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH ==
  671. LM_OWF_PASSWORD_LENGTH );
  672. NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
  673. EncryptedLmOwfPassword =
  674. * ((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword);
  675. Status = RtlDecryptLmOwfPwdWithLmOwfPwd(
  676. &EncryptedLmOwfPassword,
  677. (PLM_OWF_PASSWORD) &SessionInfo->SessionKey,
  678. &LogonInteractive->LmOwfPassword );
  679. NlAssert( NT_SUCCESS(Status) );
  680. //
  681. // Decrypt the NT_OWF password.
  682. //
  683. NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH ==
  684. NT_OWF_PASSWORD_LENGTH );
  685. NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
  686. EncryptedNtOwfPassword =
  687. * ((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword);
  688. Status = RtlDecryptNtOwfPwdWithNtOwfPwd(
  689. &EncryptedNtOwfPassword,
  690. (PNT_OWF_PASSWORD) &SessionInfo->SessionKey,
  691. &LogonInteractive->NtOwfPassword );
  692. NlAssert( NT_SUCCESS(Status) );
  693. }
  694. break;
  695. }
  696. case NetlogonGenericInformation:
  697. {
  698. PNETLOGON_GENERIC_INFO LogonGeneric;
  699. LogonGeneric =
  700. (PNETLOGON_GENERIC_INFO) LogonInformation;
  701. NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );
  702. if ( LogonGeneric->LogonData != NULL ) {
  703. NlDecryptRC4( LogonGeneric->LogonData,
  704. LogonGeneric->DataLength,
  705. SessionInfo );
  706. }
  707. break;
  708. }
  709. }
  710. return;
  711. }
  712. VOID
  713. NlpEncryptLogonInformation (
  714. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  715. IN OUT LPBYTE LogonInformation,
  716. IN PSESSION_INFO SessionInfo
  717. )
  718. /*++
  719. Routine Description:
  720. This function encrypts the sensitive information in the LogonInformation
  721. structure. The encryption is done in place.
  722. Arguments:
  723. LogonLevel -- Specifies the level of information given in
  724. LogonInformation.
  725. LogonInformation -- Specifies the description for the user
  726. logging on.
  727. SessionInfo -- The session key to encrypt with and negotiate flags
  728. Return Value:
  729. None.
  730. --*/
  731. {
  732. NTSTATUS Status;
  733. //
  734. // Only the interactive and service logon information is encrypted.
  735. //
  736. switch ( LogonLevel ) {
  737. case NetlogonInteractiveInformation:
  738. case NetlogonInteractiveTransitiveInformation:
  739. case NetlogonServiceInformation:
  740. case NetlogonServiceTransitiveInformation:
  741. {
  742. PNETLOGON_INTERACTIVE_INFO LogonInteractive;
  743. LogonInteractive =
  744. (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
  745. //
  746. // If both sides support RC4 encryption, use it.
  747. // encrypt both the LM OWF and NT OWF passwords using RC4.
  748. //
  749. if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
  750. NlEncryptRC4( &LogonInteractive->LmOwfPassword,
  751. sizeof(LogonInteractive->LmOwfPassword),
  752. SessionInfo );
  753. NlEncryptRC4( &LogonInteractive->NtOwfPassword,
  754. sizeof(LogonInteractive->NtOwfPassword),
  755. SessionInfo );
  756. //
  757. // If the other side is running NT 3.1,
  758. // use the slower DES based encryption.
  759. //
  760. } else {
  761. ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
  762. ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
  763. //
  764. // Encrypt the LM_OWF password.
  765. //
  766. NlAssert( ENCRYPTED_LM_OWF_PASSWORD_LENGTH ==
  767. LM_OWF_PASSWORD_LENGTH );
  768. NlAssert(LM_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
  769. Status = RtlEncryptLmOwfPwdWithLmOwfPwd(
  770. &LogonInteractive->LmOwfPassword,
  771. (PLM_OWF_PASSWORD) &SessionInfo->SessionKey,
  772. &EncryptedLmOwfPassword );
  773. NlAssert( NT_SUCCESS(Status) );
  774. *((PENCRYPTED_LM_OWF_PASSWORD) &LogonInteractive->LmOwfPassword) =
  775. EncryptedLmOwfPassword;
  776. //
  777. // Encrypt the NT_OWF password.
  778. //
  779. NlAssert( ENCRYPTED_NT_OWF_PASSWORD_LENGTH ==
  780. NT_OWF_PASSWORD_LENGTH );
  781. NlAssert(NT_OWF_PASSWORD_LENGTH == sizeof(SessionInfo->SessionKey));
  782. Status = RtlEncryptNtOwfPwdWithNtOwfPwd(
  783. &LogonInteractive->NtOwfPassword,
  784. (PNT_OWF_PASSWORD) &SessionInfo->SessionKey,
  785. &EncryptedNtOwfPassword );
  786. NlAssert( NT_SUCCESS(Status) );
  787. *((PENCRYPTED_NT_OWF_PASSWORD) &LogonInteractive->NtOwfPassword) =
  788. EncryptedNtOwfPassword;
  789. }
  790. break;
  791. }
  792. case NetlogonGenericInformation:
  793. {
  794. PNETLOGON_GENERIC_INFO LogonGeneric;
  795. LogonGeneric =
  796. (PNETLOGON_GENERIC_INFO) LogonInformation;
  797. //
  798. // If both sides support RC4 encryption, use it.
  799. // encrypt both the LM OWF and NT OWF passwords using RC4.
  800. //
  801. NlAssert( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION );
  802. NlEncryptRC4( LogonGeneric->LogonData,
  803. LogonGeneric->DataLength,
  804. SessionInfo );
  805. break;
  806. }
  807. }
  808. return;
  809. }
  810. VOID
  811. NlpDecryptValidationInformation (
  812. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  813. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  814. IN OUT LPBYTE ValidationInformation,
  815. IN PSESSION_INFO SessionInfo
  816. )
  817. /*++
  818. Routine Description:
  819. This function decrypts the sensitive information in the
  820. ValidationInformation structure. The decryption is done in place.
  821. Arguments:
  822. LogonLevel -- Specifies the Logon level used to obtain
  823. ValidationInformation.
  824. ValidationLevel -- Specifies the level of information given in
  825. ValidationInformation.
  826. ValidationInformation -- Specifies the description for the user
  827. logging on.
  828. SessionInfo -- The session key to encrypt with and negotiated flags.
  829. Return Value:
  830. None.
  831. --*/
  832. {
  833. PNETLOGON_VALIDATION_SAM_INFO ValidationInfo;
  834. PNETLOGON_VALIDATION_GENERIC_INFO GenericInfo;
  835. //
  836. // Only network logons and generic contain information which is sensitive.
  837. //
  838. // NetlogonValidationSamInfo4 isn't encrypted on purpose. NlEncryptRC4 has the problem
  839. // described in its header. Couple that with the fact that the entire session is
  840. // now encrypted.
  841. //
  842. if ( (LogonLevel != NetlogonNetworkInformation) &&
  843. (LogonLevel != NetlogonNetworkTransitiveInformation) &&
  844. (LogonLevel != NetlogonGenericInformation) ) {
  845. return;
  846. }
  847. if ( ValidationLevel == NetlogonValidationSamInfo ||
  848. ValidationLevel == NetlogonValidationSamInfo2 ) {
  849. ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation;
  850. //
  851. // If we're suppossed to use RC4,
  852. // Decrypt both the NT and LM session keys using RC4.
  853. //
  854. if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
  855. NlDecryptRC4( &ValidationInfo->UserSessionKey,
  856. sizeof(ValidationInfo->UserSessionKey),
  857. SessionInfo );
  858. NlDecryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
  859. SAMINFO_LM_SESSION_KEY_SIZE,
  860. SessionInfo );
  861. //
  862. // If the other side is running NT 3.1,
  863. // be compatible.
  864. //
  865. } else {
  866. NTSTATUS Status;
  867. CLEAR_BLOCK ClearBlock;
  868. DWORD i;
  869. LPBYTE DataBuffer =
  870. (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY];
  871. //
  872. // Decrypt the LmSessionKey
  873. //
  874. NlAssert( CLEAR_BLOCK_LENGTH == CYPHER_BLOCK_LENGTH );
  875. NlAssert( (SAMINFO_LM_SESSION_KEY_SIZE % CLEAR_BLOCK_LENGTH) == 0 );
  876. //
  877. // Loop decrypting a block at a time
  878. //
  879. for (i=0; i<SAMINFO_LM_SESSION_KEY_SIZE/CLEAR_BLOCK_LENGTH; i++ ) {
  880. Status = RtlDecryptBlock(
  881. (PCYPHER_BLOCK)DataBuffer,
  882. (PBLOCK_KEY)&SessionInfo->SessionKey,
  883. &ClearBlock );
  884. NlAssert( NT_SUCCESS( Status ) );
  885. //
  886. // Copy the clear text back into the original buffer.
  887. //
  888. RtlCopyMemory( DataBuffer, &ClearBlock, CLEAR_BLOCK_LENGTH );
  889. DataBuffer += CLEAR_BLOCK_LENGTH;
  890. }
  891. }
  892. } else if ( ValidationLevel == NetlogonValidationGenericInfo ||
  893. ValidationLevel == NetlogonValidationGenericInfo2 ) {
  894. //
  895. // Decrypt all the data in the generic info
  896. //
  897. GenericInfo = (PNETLOGON_VALIDATION_GENERIC_INFO) ValidationInformation;
  898. if (GenericInfo->DataLength != 0) {
  899. NlDecryptRC4( GenericInfo->ValidationData,
  900. GenericInfo->DataLength,
  901. SessionInfo );
  902. }
  903. }
  904. return;
  905. }
  906. VOID
  907. NlpEncryptValidationInformation (
  908. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  909. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  910. IN OUT LPBYTE ValidationInformation,
  911. IN PSESSION_INFO SessionInfo
  912. )
  913. /*++
  914. Routine Description:
  915. This function encrypts the sensitive information in the
  916. ValidationInformation structure. The encryption is done in place.
  917. Arguments:
  918. LogonLevel -- Specifies the Logon level used to obtain
  919. ValidationInformation.
  920. ValidationLevel -- Specifies the level of information given in
  921. ValidationInformation.
  922. ValidationInformation -- Specifies the description for the user
  923. logging on.
  924. SessionInfo -- The session key to encrypt with and negotiated flags.
  925. Return Value:
  926. None.
  927. --*/
  928. {
  929. PNETLOGON_VALIDATION_SAM_INFO ValidationInfo;
  930. PNETLOGON_VALIDATION_GENERIC_INFO GenericInfo;
  931. //
  932. // Only network logons and generic contain information which is sensitive.
  933. //
  934. // NetlogonValidationSamInfo4 isn't encrypted on purpose. NlEncryptRC4 has the problem
  935. // described in its header. Couple that with the fact that the entire session is
  936. // now encrypted.
  937. //
  938. if ( (LogonLevel != NetlogonNetworkInformation) &&
  939. (LogonLevel != NetlogonNetworkTransitiveInformation) &&
  940. (LogonLevel != NetlogonGenericInformation) ) {
  941. return;
  942. }
  943. if ( ValidationLevel == NetlogonValidationSamInfo ||
  944. ValidationLevel == NetlogonValidationSamInfo2 ) {
  945. ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO) ValidationInformation;
  946. //
  947. // If we're suppossed to use RC4,
  948. // Encrypt both the NT and LM session keys using RC4.
  949. //
  950. if ( SessionInfo->NegotiatedFlags & NETLOGON_SUPPORTS_RC4_ENCRYPTION ) {
  951. NlEncryptRC4( &ValidationInfo->UserSessionKey,
  952. sizeof(ValidationInfo->UserSessionKey),
  953. SessionInfo );
  954. NlEncryptRC4( &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
  955. SAMINFO_LM_SESSION_KEY_SIZE,
  956. SessionInfo );
  957. //
  958. // If the other side is running NT 3.1,
  959. // be compatible.
  960. //
  961. } else {
  962. NTSTATUS Status;
  963. CLEAR_BLOCK ClearBlock;
  964. DWORD i;
  965. LPBYTE DataBuffer =
  966. (LPBYTE) &ValidationInfo->ExpansionRoom[SAMINFO_LM_SESSION_KEY];
  967. //
  968. // Encrypt the LmSessionKey
  969. //
  970. // Loop decrypting a block at a time
  971. //
  972. for (i=0; i<SAMINFO_LM_SESSION_KEY_SIZE/CLEAR_BLOCK_LENGTH; i++ ) {
  973. //
  974. // Copy the clear text onto the stack
  975. //
  976. RtlCopyMemory( &ClearBlock, DataBuffer, CLEAR_BLOCK_LENGTH );
  977. Status = RtlEncryptBlock(
  978. &ClearBlock,
  979. (PBLOCK_KEY)&SessionInfo->SessionKey,
  980. (PCYPHER_BLOCK)DataBuffer );
  981. NlAssert( NT_SUCCESS( Status ) );
  982. DataBuffer += CLEAR_BLOCK_LENGTH;
  983. }
  984. }
  985. } else if ( ValidationLevel == NetlogonValidationGenericInfo ||
  986. ValidationLevel == NetlogonValidationGenericInfo2 ) {
  987. //
  988. // Encrypt all the data in the generic info
  989. //
  990. GenericInfo = (PNETLOGON_VALIDATION_GENERIC_INFO) ValidationInformation;
  991. if (GenericInfo->DataLength != 0) {
  992. NlEncryptRC4( GenericInfo->ValidationData,
  993. GenericInfo->DataLength,
  994. SessionInfo );
  995. }
  996. }
  997. return;
  998. }
  999. NTSTATUS
  1000. NlpUserValidateHigher (
  1001. IN PCLIENT_SESSION ClientSession,
  1002. IN BOOLEAN DoingIndirectTrust,
  1003. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  1004. IN LPBYTE LogonInformation,
  1005. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  1006. OUT LPBYTE * ValidationInformation,
  1007. OUT PBOOLEAN Authoritative,
  1008. IN OUT PULONG ExtraFlags
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. This function sends a user validation request to a higher authority.
  1013. Arguments:
  1014. ClientSession -- Secure channel to send this request over. The Client
  1015. Session should be referenced.
  1016. DoingIndirectTrust -- If TRUE, the Client Session merely represents the
  1017. next closer hop and is not the final destination.
  1018. LogonLevel -- Specifies the level of information given in
  1019. LogonInformation. Has already been validated.
  1020. LogonInformation -- Specifies the description for the user
  1021. logging on.
  1022. ValidationLevel -- Specifies the level of information returned in
  1023. ValidationInformation. Must be NetlogonValidationSamInfo,
  1024. NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4
  1025. ValidationInformation -- Returns the requested validation
  1026. information. This buffer must be freed using MIDL_user_free.
  1027. Authoritative -- Returns whether the status returned is an
  1028. authoritative status which should be returned to the original
  1029. caller. If not, this logon request may be tried again on another
  1030. domain controller. This parameter is returned regardless of the
  1031. status code.
  1032. ExtraFlags -- Accepts and returns a DWORD to the caller.
  1033. The DWORD contains NL_EXFLAGS_* values.
  1034. Return Value:
  1035. STATUS_SUCCESS: if there was no error.
  1036. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
  1037. STATUS_NO_TRUST_LSA_SECRET:
  1038. STATUS_TRUSTED_DOMAIN_FAILURE:
  1039. STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  1040. can't authenticate with higer authority
  1041. Otherwise, the error code is returned.
  1042. --*/
  1043. {
  1044. NTSTATUS Status;
  1045. NETLOGON_AUTHENTICATOR OurAuthenticator;
  1046. NETLOGON_AUTHENTICATOR ReturnAuthenticator;
  1047. BOOLEAN FirstTry = TRUE;
  1048. BOOLEAN TryForDs = TRUE;
  1049. BOOLEAN AmWriter = FALSE;
  1050. BOOLEAN DoingGeneric;
  1051. SESSION_INFO SessionInfo;
  1052. NETLOGON_VALIDATION_INFO_CLASS RemoteValidationLevel;
  1053. PCLIENT_API OrigClientApi = NULL;
  1054. PCLIENT_API ClientApi;
  1055. BOOLEAN RpcFailed;
  1056. ULONG MaxExtraFlags;
  1057. //
  1058. // Allocate a slot for doing a concurrent API call
  1059. //
  1060. // Do this before grabbing the write lock since the threads
  1061. // using the slots need to grab the write lock to free the slot.
  1062. //
  1063. // We may end up not using this slot if concurrent API isn't supported.
  1064. // But in that case, this isn't a valuable resource so allocating one
  1065. // doesn't hurt.
  1066. //
  1067. NlAssert( ClientSession->CsReferenceCount > 0 );
  1068. OrigClientApi = NlAllocateClientApi(
  1069. ClientSession,
  1070. WRITER_WAIT_PERIOD );
  1071. if ( OrigClientApi == NULL ) {
  1072. NlPrintCs((NL_CRITICAL, ClientSession,
  1073. "NlpUserValidateHigher: Can't allocate Client API slot.\n" ));
  1074. *Authoritative = TRUE;
  1075. Status = STATUS_NO_LOGON_SERVERS;
  1076. goto Cleanup;
  1077. }
  1078. //
  1079. // Mark us as a writer of the ClientSession
  1080. //
  1081. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  1082. NlPrintCs((NL_CRITICAL, ClientSession,
  1083. "NlpUserValidateHigher: Can't become writer of client session.\n" ));
  1084. *Authoritative = TRUE;
  1085. Status = STATUS_NO_LOGON_SERVERS;
  1086. goto Cleanup;
  1087. }
  1088. AmWriter = TRUE;
  1089. //
  1090. // Determine if we're doing a generic logon.
  1091. //
  1092. DoingGeneric = (LogonLevel == NetlogonGenericInformation ||
  1093. ValidationLevel == NetlogonValidationGenericInfo ||
  1094. ValidationLevel == NetlogonValidationGenericInfo2);
  1095. //
  1096. // If we don't currently have a session set up to the higher authority,
  1097. // set one up.
  1098. //
  1099. // For generic passthrough or indirect trust, ask for an NT 5 DC.
  1100. // For Interactive logon, ask for a close DC.
  1101. //
  1102. FirstTryFailed:
  1103. Status = NlEnsureSessionAuthenticated(
  1104. ClientSession,
  1105. (( DoingGeneric || DoingIndirectTrust || *ExtraFlags != 0 ) ? CS_DISCOVERY_HAS_DS : 0) |
  1106. ((LogonLevel == NetlogonInteractiveInformation || LogonLevel == NetlogonInteractiveTransitiveInformation )? CS_DISCOVERY_IS_CLOSE : 0) );
  1107. if ( !NT_SUCCESS(Status) ) {
  1108. switch(Status) {
  1109. case STATUS_NO_TRUST_LSA_SECRET:
  1110. case STATUS_NO_TRUST_SAM_ACCOUNT:
  1111. case STATUS_ACCESS_DENIED:
  1112. case STATUS_NO_LOGON_SERVERS:
  1113. break;
  1114. default:
  1115. if ( !NlpIsNtStatusResourceError( Status )) {
  1116. Status = STATUS_NO_LOGON_SERVERS;
  1117. }
  1118. break;
  1119. }
  1120. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  1121. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  1122. *Authoritative = TRUE;
  1123. goto Cleanup;
  1124. }
  1125. SessionInfo.SessionKey = ClientSession->CsSessionKey;
  1126. SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
  1127. //
  1128. // Ensure the DC supports the ExtraFlags we're passing it.
  1129. //
  1130. if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_CROSS_FOREST ) {
  1131. MaxExtraFlags = NL_EXFLAGS_EXPEDITE_TO_ROOT | NL_EXFLAGS_CROSS_FOREST_HOP;
  1132. } else {
  1133. MaxExtraFlags = 0;
  1134. }
  1135. if ( (*ExtraFlags & ~MaxExtraFlags) != 0 ) {
  1136. NlPrintCs((NL_CRITICAL, ClientSession,
  1137. "NlpUserValidateHigher: Can't pass these ExtraFlags to old DC: %lx %lx\n",
  1138. *ExtraFlags,
  1139. MaxExtraFlags ));
  1140. Status = STATUS_NO_LOGON_SERVERS;
  1141. *Authoritative = TRUE;
  1142. goto Cleanup;
  1143. }
  1144. //
  1145. // If the target is an NT 4.0 (or lower) DC,
  1146. // check to see if an NT 5.0 DC would be better.
  1147. //
  1148. if ((SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_GENERIC_PASSTHRU) == 0 &&
  1149. ( DoingGeneric || DoingIndirectTrust ) ) {
  1150. //
  1151. // Simply fail if only an NT 4 DC is available.
  1152. //
  1153. *Authoritative = TRUE;
  1154. if ( DoingGeneric ) {
  1155. NlPrintCs((NL_CRITICAL, ClientSession,
  1156. "NlpUserValidateHigher: Can't do generic passthru to NT 4 DC.\n" ));
  1157. Status = STATUS_INVALID_INFO_CLASS;
  1158. } else {
  1159. NlPrintCs((NL_CRITICAL, ClientSession,
  1160. "NlpUserValidateHigher: Can't do transitive trust to NT 4 DC.\n" ));
  1161. Status = STATUS_NO_LOGON_SERVERS;
  1162. }
  1163. goto Cleanup;
  1164. }
  1165. //
  1166. // Convert the validation level to one the remote DC understands.
  1167. //
  1168. if ( !DoingGeneric ) {
  1169. //
  1170. // DCs that don't understand extra SIDs require NetlogonValidationSamInfo
  1171. //
  1172. if (!(SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_MULTIPLE_SIDS)) {
  1173. RemoteValidationLevel = NetlogonValidationSamInfo;
  1174. //
  1175. // DCs that don't understand cross forest trust don't understand NetlogonValidationSamInfo4
  1176. //
  1177. // Info4 doesn't have sensitive information encrytped (since NlEncryptRC4 is
  1178. // buggy and there are many more field that need encryption). So, avoid info4
  1179. // unless the entire traffic is encrypted.
  1180. //
  1181. } else if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_CROSS_FOREST) == 0 ||
  1182. (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_AUTH_RPC) == 0 ||
  1183. !NlGlobalParameters.SealSecureChannel ) {
  1184. RemoteValidationLevel = NetlogonValidationSamInfo2;
  1185. } else {
  1186. RemoteValidationLevel = ValidationLevel;
  1187. }
  1188. } else {
  1189. RemoteValidationLevel = ValidationLevel;
  1190. }
  1191. //
  1192. // If this DC supports concurrent RPC calls,
  1193. // and we're signing or sealing,
  1194. // then we're OK to do concurrent RPC.
  1195. //
  1196. // Otherwise, use the shared RPC slot.
  1197. //
  1198. if ( (SessionInfo.NegotiatedFlags &
  1199. (NETLOGON_SUPPORTS_CONCURRENT_RPC|NETLOGON_SUPPORTS_AUTH_RPC)) ==
  1200. (NETLOGON_SUPPORTS_CONCURRENT_RPC|NETLOGON_SUPPORTS_AUTH_RPC)) {
  1201. ClientApi = OrigClientApi;
  1202. } else {
  1203. ClientApi = &ClientSession->CsClientApi[0];
  1204. }
  1205. //
  1206. // Build the Authenticator for this request on the secure channel
  1207. //
  1208. // Concurrent RPC uses a signed and sealed secure channel so it doesn't need
  1209. // an authenticator.
  1210. //
  1211. if ( !UseConcurrentRpc( ClientSession, ClientApi ) ) {
  1212. NlBuildAuthenticator(
  1213. &ClientSession->CsAuthenticationSeed,
  1214. &ClientSession->CsSessionKey,
  1215. &OurAuthenticator );
  1216. }
  1217. //
  1218. // Make the request across the secure channel.
  1219. //
  1220. NlpEncryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo );
  1221. RpcFailed = FALSE;
  1222. NL_API_START_EX( Status, ClientSession, TRUE, ClientApi ) {
  1223. //
  1224. // If the called DC doesn't support the new transitive opcodes,
  1225. // map the opcodes back to do the best we can.
  1226. //
  1227. RpcFailed = FALSE;
  1228. if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_TRANSITIVE) == 0 ) {
  1229. switch (LogonLevel ) {
  1230. case NetlogonInteractiveTransitiveInformation:
  1231. LogonLevel = NetlogonInteractiveInformation; break;
  1232. case NetlogonServiceTransitiveInformation:
  1233. LogonLevel = NetlogonServiceInformation; break;
  1234. case NetlogonNetworkTransitiveInformation:
  1235. LogonLevel = NetlogonNetworkInformation; break;
  1236. }
  1237. }
  1238. NlAssert( ClientSession->CsUncServerName != NULL );
  1239. if ( UseConcurrentRpc( ClientSession, ClientApi ) ) {
  1240. LPWSTR UncServerName;
  1241. //
  1242. // Drop the write lock to allow other concurrent callers to proceed.
  1243. //
  1244. NlResetWriterClientSession( ClientSession );
  1245. AmWriter = FALSE;
  1246. //
  1247. // Since we have no locks locked,
  1248. // grab the name of the DC to remote to.
  1249. //
  1250. Status = NlCaptureServerClientSession (
  1251. ClientSession,
  1252. &UncServerName,
  1253. NULL );
  1254. if ( !NT_SUCCESS(Status) ) {
  1255. *Authoritative = TRUE;
  1256. if ( !NlpIsNtStatusResourceError( Status )) {
  1257. Status = STATUS_NO_LOGON_SERVERS;
  1258. }
  1259. } else {
  1260. //
  1261. // Do the RPC call with no locks locked.
  1262. //
  1263. Status = I_NetLogonSamLogonEx(
  1264. ClientApi->CaRpcHandle,
  1265. UncServerName,
  1266. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  1267. LogonLevel,
  1268. LogonInformation,
  1269. RemoteValidationLevel,
  1270. ValidationInformation,
  1271. Authoritative,
  1272. ExtraFlags,
  1273. &RpcFailed );
  1274. NetApiBufferFree( UncServerName );
  1275. }
  1276. //
  1277. // Become a writer again
  1278. //
  1279. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  1280. NlPrintCs((NL_CRITICAL, ClientSession,
  1281. "NlpUserValidateHigher: Can't become writer (again) of client session.\n" ));
  1282. // Don't leak validation information
  1283. if ( *ValidationInformation ) {
  1284. MIDL_user_free( *ValidationInformation );
  1285. *ValidationInformation = NULL;
  1286. }
  1287. *Authoritative = TRUE;
  1288. Status = STATUS_NO_LOGON_SERVERS;
  1289. } else {
  1290. AmWriter = TRUE;
  1291. }
  1292. //
  1293. // Do non-concurrent RPC
  1294. //
  1295. } else {
  1296. //
  1297. // If the DC supports the new 'WithFlags' API,
  1298. // use it.
  1299. //
  1300. if ( SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_CROSS_FOREST ) {
  1301. Status = I_NetLogonSamLogonWithFlags(
  1302. ClientSession->CsUncServerName,
  1303. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  1304. &OurAuthenticator,
  1305. &ReturnAuthenticator,
  1306. LogonLevel,
  1307. LogonInformation,
  1308. RemoteValidationLevel,
  1309. ValidationInformation,
  1310. Authoritative,
  1311. ExtraFlags );
  1312. //
  1313. // Otherwise use the old API.
  1314. //
  1315. } else {
  1316. Status = I_NetLogonSamLogon(
  1317. ClientSession->CsUncServerName,
  1318. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  1319. &OurAuthenticator,
  1320. &ReturnAuthenticator,
  1321. LogonLevel,
  1322. LogonInformation,
  1323. RemoteValidationLevel,
  1324. ValidationInformation,
  1325. Authoritative );
  1326. }
  1327. }
  1328. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  1329. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  1330. // NOTE: This call may drop the secure channel behind our back
  1331. } NL_API_ELSE_EX( Status, ClientSession, TRUE, AmWriter, ClientApi ) {
  1332. } NL_API_END;
  1333. NlpDecryptLogonInformation( LogonLevel, LogonInformation, &SessionInfo );
  1334. if ( NT_SUCCESS(Status) ) {
  1335. NlAssert( *ValidationInformation != NULL );
  1336. }
  1337. //
  1338. // If we couldn't become writer again after the remote call,
  1339. // early out to avoid use the ClientSession.
  1340. //
  1341. if ( !AmWriter ) {
  1342. goto Cleanup;
  1343. }
  1344. //
  1345. // Verify authenticator of the server on the other side and update our seed.
  1346. //
  1347. // If the server denied access or the server's authenticator is wrong,
  1348. // Force a re-authentication.
  1349. //
  1350. //
  1351. NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Seed = " ));
  1352. NlpDumpBuffer(NL_CHALLENGE_RES, &ClientSession->CsAuthenticationSeed, sizeof(ClientSession->CsAuthenticationSeed) );
  1353. NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: SessionKey = " ));
  1354. NlpDumpBuffer(NL_CHALLENGE_RES, &ClientSession->CsSessionKey, sizeof(ClientSession->CsSessionKey) );
  1355. if ( !UseConcurrentRpc( ClientSession, ClientApi ) ) {
  1356. NlPrint((NL_CHALLENGE_RES,"NlpUserValidateHigher: Return Authenticator = " ));
  1357. NlpDumpBuffer(NL_CHALLENGE_RES, &ReturnAuthenticator.Credential, sizeof(ReturnAuthenticator.Credential) );
  1358. }
  1359. if ( NlpDidDcFail( Status ) ||
  1360. RpcFailed ||
  1361. (!UseConcurrentRpc( ClientSession, ClientApi ) &&
  1362. !NlUpdateSeed(
  1363. &ClientSession->CsAuthenticationSeed,
  1364. &ReturnAuthenticator.Credential,
  1365. &ClientSession->CsSessionKey) ) ) {
  1366. NlPrintCs(( NL_CRITICAL, ClientSession,
  1367. "NlpUserValidateHigher: denying access after status: 0x%lx %lx\n",
  1368. Status,
  1369. RpcFailed ));
  1370. //
  1371. // Preserve any status indicating a communication error.
  1372. //
  1373. // If another thread already dropped the secure channel,
  1374. // don't do it again now.
  1375. //
  1376. if ( NT_SUCCESS(Status) ) {
  1377. Status = STATUS_ACCESS_DENIED;
  1378. }
  1379. if ( ClientApi->CaSessionCount == ClientSession->CsSessionCount ) {
  1380. NlSetStatusClientSession( ClientSession, Status );
  1381. }
  1382. //
  1383. // Perhaps the netlogon service on the server has just restarted.
  1384. // Try just once to set up a session to the server again.
  1385. //
  1386. if ( FirstTry ) {
  1387. FirstTry = FALSE;
  1388. goto FirstTryFailed;
  1389. }
  1390. *Authoritative = TRUE;
  1391. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  1392. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  1393. goto Cleanup;
  1394. }
  1395. //
  1396. // Clean up after a successful call to higher authority.
  1397. //
  1398. if ( NT_SUCCESS(Status) ) {
  1399. //
  1400. // The server encrypted the validation information before sending it
  1401. // over the wire. Decrypt it.
  1402. //
  1403. NlpDecryptValidationInformation (
  1404. LogonLevel,
  1405. RemoteValidationLevel,
  1406. *ValidationInformation,
  1407. &SessionInfo );
  1408. //
  1409. // If the caller wants a newer info level than we got from the remote side,
  1410. // convert it to VALIDATION_SAM_INFO4 (which is a superset of what our caller wants).
  1411. //
  1412. if ( RemoteValidationLevel != ValidationLevel) {
  1413. if ( (RemoteValidationLevel == NetlogonValidationSamInfo2 ||
  1414. RemoteValidationLevel == NetlogonValidationSamInfo ) &&
  1415. (ValidationLevel == NetlogonValidationSamInfo2 ||
  1416. ValidationLevel == NetlogonValidationSamInfo4) ) {
  1417. NTSTATUS TempStatus;
  1418. TempStatus = NlpAddResourceGroupsToSamInfo (
  1419. RemoteValidationLevel,
  1420. (PNETLOGON_VALIDATION_SAM_INFO4 *) ValidationInformation,
  1421. NULL ); // No resource groups to add
  1422. if ( !NT_SUCCESS( TempStatus )) {
  1423. *ValidationInformation = NULL;
  1424. *Authoritative = FALSE;
  1425. Status = TempStatus;
  1426. goto Cleanup;
  1427. }
  1428. } else {
  1429. NlAssert(!"Bad validation level");
  1430. }
  1431. }
  1432. //
  1433. // Ensure the returned SID and domain name are correct.
  1434. // Filter out SIDs for quarantined domains.
  1435. //
  1436. if ((ValidationLevel == NetlogonValidationSamInfo4) ||
  1437. (ValidationLevel == NetlogonValidationSamInfo2) ||
  1438. (ValidationLevel == NetlogonValidationSamInfo)) {
  1439. PNETLOGON_VALIDATION_SAM_INFO ValidationInfo;
  1440. ValidationInfo =
  1441. (PNETLOGON_VALIDATION_SAM_INFO) *ValidationInformation;
  1442. //
  1443. // If we validated on a trusted domain,
  1444. // the higher authority must have returned his own domain name,
  1445. // and must have returned his own domain sid.
  1446. //
  1447. if ( ClientSession->CsSecureChannelType == TrustedDomainSecureChannel ||
  1448. ClientSession->CsSecureChannelType == TrustedDnsDomainSecureChannel ||
  1449. ClientSession->CsSecureChannelType == WorkstationSecureChannel ) {
  1450. //
  1451. // If we validated on our primary domain,
  1452. // only verify the domain sid if the primary domain itself validated
  1453. // the logon.
  1454. //
  1455. if ( (ClientSession->CsNetbiosDomainName.Buffer != NULL &&
  1456. RtlEqualDomainName( &ValidationInfo->LogonDomainName,
  1457. &ClientSession->CsNetbiosDomainName )) &&
  1458. !RtlEqualSid( ValidationInfo->LogonDomainId,
  1459. ClientSession->CsDomainId ) ) {
  1460. Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
  1461. MIDL_user_free( *ValidationInformation );
  1462. *ValidationInformation = NULL;
  1463. *Authoritative = TRUE;
  1464. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  1465. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  1466. }
  1467. }
  1468. //
  1469. // Filter out SIDs for quarantined domains and interforest domains as needed
  1470. //
  1471. if ( IsDomainSecureChannelType(ClientSession->CsSecureChannelType) &&
  1472. *ValidationInformation != NULL ) {
  1473. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1474. Status = LsaIFilterSids( ClientSession->CsDnsDomainName.Length ?
  1475. &ClientSession->CsDnsDomainName :
  1476. NULL,
  1477. TRUST_DIRECTION_OUTBOUND,
  1478. (ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST) ?
  1479. TRUST_TYPE_UPLEVEL : TRUST_TYPE_DOWNLEVEL,
  1480. ClientSession->CsTrustAttributes,
  1481. ClientSession->CsDomainId,
  1482. ValidationLevel,
  1483. *ValidationInformation );
  1484. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1485. if ( !NT_SUCCESS(Status) ) {
  1486. NlAssert( !"[NETLOGON] LsaIFilterSids failed" );
  1487. NlPrint(( NL_CRITICAL, "NlpUserValidateHigher: LsaIFilterSids failed 0x%lx\n", Status ));
  1488. MIDL_user_free( *ValidationInformation );
  1489. *ValidationInformation = NULL;
  1490. *Authoritative = TRUE;
  1491. }
  1492. }
  1493. }
  1494. }
  1495. Cleanup:
  1496. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  1497. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  1498. //
  1499. // We are no longer a writer of the client session.
  1500. //
  1501. if ( AmWriter ) {
  1502. NlResetWriterClientSession( ClientSession );
  1503. }
  1504. //
  1505. // Free the concurrent API slot
  1506. //
  1507. if ( OrigClientApi ) {
  1508. NlFreeClientApi( ClientSession, OrigClientApi );
  1509. }
  1510. return Status;
  1511. }
  1512. NTSTATUS
  1513. NlpUserLogoffHigher (
  1514. IN PCLIENT_SESSION ClientSession,
  1515. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  1516. IN LPBYTE LogonInformation
  1517. )
  1518. /*++
  1519. Routine Description:
  1520. This function sends a user validation request to a higher authority.
  1521. Arguments:
  1522. ClientSession -- Secure channel to send this request over. The Client
  1523. Session should be referenced.
  1524. LogonLevel -- Specifies the level of information given in
  1525. LogonInformation. Has already been validated.
  1526. LogonInformation -- Specifies the description for the user
  1527. logging on.
  1528. Return Value:
  1529. STATUS_SUCCESS: if there was no error.
  1530. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
  1531. STATUS_NO_TRUST_LSA_SECRET:
  1532. STATUS_TRUSTED_DOMAIN_FAILURE:
  1533. STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  1534. can't authenticate with higer authority
  1535. Otherwise, the error code is returned.
  1536. --*/
  1537. {
  1538. NTSTATUS Status;
  1539. NETLOGON_AUTHENTICATOR OurAuthenticator;
  1540. NETLOGON_AUTHENTICATOR ReturnAuthenticator;
  1541. BOOLEAN FirstTry = TRUE;
  1542. //
  1543. // Mark us as a writer of the ClientSession
  1544. //
  1545. NlAssert( ClientSession->CsReferenceCount > 0 );
  1546. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  1547. NlPrintCs((NL_CRITICAL, ClientSession,
  1548. "NlpUserLogoffHigher: Can't become writer of client session.\n"));
  1549. return STATUS_NO_LOGON_SERVERS;
  1550. }
  1551. //
  1552. // If we don't currently have a session set up to the higher authority,
  1553. // set one up.
  1554. //
  1555. FirstTryFailed:
  1556. Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
  1557. if ( !NT_SUCCESS(Status) ) {
  1558. switch(Status) {
  1559. case STATUS_NO_TRUST_LSA_SECRET:
  1560. case STATUS_NO_TRUST_SAM_ACCOUNT:
  1561. case STATUS_ACCESS_DENIED:
  1562. case STATUS_NO_LOGON_SERVERS:
  1563. break;
  1564. default:
  1565. if ( !NlpIsNtStatusResourceError( Status )) {
  1566. Status = STATUS_NO_LOGON_SERVERS;
  1567. }
  1568. break;
  1569. }
  1570. goto Cleanup;
  1571. }
  1572. //
  1573. // Build the Authenticator for this request on the secure channel
  1574. //
  1575. NlBuildAuthenticator(
  1576. &ClientSession->CsAuthenticationSeed,
  1577. &ClientSession->CsSessionKey,
  1578. &OurAuthenticator );
  1579. //
  1580. // Make the request across the secure channel.
  1581. //
  1582. NL_API_START( Status, ClientSession, TRUE ) {
  1583. NlAssert( ClientSession->CsUncServerName != NULL );
  1584. Status = I_NetLogonSamLogoff(
  1585. ClientSession->CsUncServerName,
  1586. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  1587. &OurAuthenticator,
  1588. &ReturnAuthenticator,
  1589. LogonLevel,
  1590. LogonInformation );
  1591. // NOTE: This call may drop the secure channel behind our back
  1592. } NL_API_ELSE( Status, ClientSession, TRUE ) {
  1593. } NL_API_END;
  1594. //
  1595. // Verify authenticator of the server on the other side and update our seed.
  1596. //
  1597. // If the server denied access or the server's authenticator is wrong,
  1598. // Force a re-authentication.
  1599. //
  1600. //
  1601. if ( NlpDidDcFail( Status ) ||
  1602. !NlUpdateSeed(
  1603. &ClientSession->CsAuthenticationSeed,
  1604. &ReturnAuthenticator.Credential,
  1605. &ClientSession->CsSessionKey) ) {
  1606. NlPrintCs(( NL_CRITICAL, ClientSession,
  1607. "NlpUserLogoffHigher: denying access after status: 0x%lx\n",
  1608. Status ));
  1609. //
  1610. // Preserve any status indicating a communication error.
  1611. //
  1612. if ( NT_SUCCESS(Status) ) {
  1613. Status = STATUS_ACCESS_DENIED;
  1614. }
  1615. NlSetStatusClientSession( ClientSession, Status );
  1616. //
  1617. // Perhaps the netlogon service in the server has just restarted.
  1618. // Try just once to set up a session to the server again.
  1619. //
  1620. if ( FirstTry ) {
  1621. FirstTry = FALSE;
  1622. goto FirstTryFailed;
  1623. }
  1624. goto Cleanup;
  1625. }
  1626. Cleanup:
  1627. //
  1628. // We are no longer a writer of the client session.
  1629. //
  1630. NlResetWriterClientSession( ClientSession );
  1631. return Status;
  1632. }
  1633. #ifdef _DC_NETLOGON
  1634. VOID
  1635. NlScavengeOldFailedLogons(
  1636. IN PDOMAIN_INFO DomainInfo
  1637. )
  1638. /*++
  1639. Routine Description:
  1640. This function removes all expired failed user logon entries
  1641. from the list of expired logons for the specified domain.
  1642. Arguments:
  1643. DomainInfo - Domain this BDC is a member of.
  1644. Return Value:
  1645. None
  1646. --*/
  1647. {
  1648. PLIST_ENTRY UserLogonEntry = NULL;
  1649. PNL_FAILED_USER_LOGON UserLogon = NULL;
  1650. ULONG CurrentTime;
  1651. ULONG ElapsedTime;
  1652. CurrentTime = GetTickCount();
  1653. LOCK_TRUST_LIST( DomainInfo );
  1654. UserLogonEntry = DomainInfo->DomFailedUserLogonList.Flink;
  1655. while ( UserLogonEntry != &DomainInfo->DomFailedUserLogonList ) {
  1656. UserLogon = CONTAINING_RECORD( UserLogonEntry, NL_FAILED_USER_LOGON, FuNext );
  1657. UserLogonEntry = UserLogonEntry->Flink;
  1658. //
  1659. // If time has wrapped, account for it
  1660. //
  1661. if ( CurrentTime >= UserLogon->FuLastTimeSentToPdc ) {
  1662. ElapsedTime = CurrentTime - UserLogon->FuLastTimeSentToPdc;
  1663. } else {
  1664. ElapsedTime = (0xFFFFFFFF - UserLogon->FuLastTimeSentToPdc) + CurrentTime;
  1665. }
  1666. //
  1667. // If this entry hasn't been touched in 3 update timeouts, remove it
  1668. //
  1669. if ( ElapsedTime >= (3 * NL_FAILED_USER_FORWARD_LOGON_TIMEOUT) ) {
  1670. RemoveEntryList( &UserLogon->FuNext );
  1671. LocalFree( UserLogon );
  1672. }
  1673. }
  1674. UNLOCK_TRUST_LIST( DomainInfo );
  1675. }
  1676. NTSTATUS
  1677. NlpUserValidateOnPdc (
  1678. IN PDOMAIN_INFO DomainInfo,
  1679. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  1680. IN LPBYTE LogonInformation,
  1681. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  1682. IN BOOL UseNegativeCache,
  1683. OUT LPBYTE * ValidationInformation,
  1684. OUT PBOOLEAN Authoritative
  1685. )
  1686. /*++
  1687. Routine Description:
  1688. This function normally sends a user validation request to the PDC in this
  1689. same domain. Currently, this is called from a BDC after getting a password
  1690. mismatch. The theory is that the password might be right on the PDC but
  1691. it merely hasn't replicated yet.
  1692. However, once the number of logon failures for the given user reaches a
  1693. certain threshold, we refrain from this forwarding for some period of time
  1694. to avoid PDC overload. We then retry the forwarding once every so often.
  1695. This scheme ensures that we accomodate a certain number of mistyped user
  1696. passwords and we then periodically retry to authenticate the user on the PDC.
  1697. No validation request will be sent if the registry value of AvoidPdcOnWan has
  1698. been set to TRUE and PDC and BDC are on different sites. In this case the
  1699. function returns with STATUS_NO_SUCH_USER error.
  1700. Arguments:
  1701. DomainInfo - Domain this BDC is a member of.
  1702. LogonLevel -- Specifies the level of information given in
  1703. LogonInformation. Has already been validated.
  1704. LogonInformation -- Specifies the description for the user
  1705. logging on.
  1706. ValidationLevel -- Specifies the level of information returned in
  1707. ValidationInformation. Must be NetlogonValidationSamInfo,
  1708. NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4
  1709. UseNegativeCache -- If TRUE, the negative cache of failed user
  1710. logons forwarded to the PDC will be used to decide whether
  1711. it's time to retry to forward this logon.
  1712. ValidationInformation -- Returns the requested validation
  1713. information. This buffer must be freed using MIDL_user_free.
  1714. Authoritative -- Returns whether the status returned is an
  1715. authoritative status which should be returned to the original
  1716. caller. If not, this logon request may be tried again on another
  1717. domain controller. This parameter is returned regardless of the
  1718. status code.
  1719. Return Value:
  1720. STATUS_SUCCESS: if there was no error.
  1721. STATUS_NO_LOGON_SERVERS: cannot connect to the higher authority.
  1722. STATUS_NO_SUCH_USER: won't validate a user against info on PDC
  1723. on a remote site provided the registry value of AvoidPdcOnWan
  1724. is TRUE.
  1725. STATUS_NO_TRUST_LSA_SECRET:
  1726. STATUS_TRUSTED_DOMAIN_FAILURE:
  1727. STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  1728. can't authenticate with higer authority
  1729. Otherwise, the error code is returned.
  1730. --*/
  1731. {
  1732. NTSTATUS Status = STATUS_SUCCESS;
  1733. NTSTATUS TmpStatus;
  1734. PCLIENT_SESSION ClientSession = NULL;
  1735. BOOLEAN IsSameSite;
  1736. DWORD ExtraFlags = 0;
  1737. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  1738. PSAMPR_DOMAIN_INFO_BUFFER DomainLockout = NULL;
  1739. BOOL LockoutEnabled = FALSE;
  1740. PLIST_ENTRY FailedUserEntry;
  1741. PNL_FAILED_USER_LOGON FailedUser = NULL;
  1742. LPWSTR UserName = NULL;
  1743. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  1744. //
  1745. // If this isn't a BDC,
  1746. // There's nothing to do here.
  1747. //
  1748. if ( DomainInfo->DomRole != RoleBackup ) {
  1749. return STATUS_INVALID_DOMAIN_ROLE;
  1750. }
  1751. //
  1752. // If the registry value of AvoidPdcOnWan is TRUE and PDC is on
  1753. // a remote site, do not send anything to PDC and return with
  1754. // STATUS_NO_SUCH_USER error.
  1755. //
  1756. if ( NlGlobalParameters.AvoidPdcOnWan ) {
  1757. //
  1758. // Determine whether the PDC is on the same site
  1759. //
  1760. Status = SamISameSite( &IsSameSite );
  1761. if ( !NT_SUCCESS(Status) ) {
  1762. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1763. "NlpUserValidateOnPdc: Cannot SamISameSite.\n" ));
  1764. goto Cleanup;
  1765. }
  1766. if ( !IsSameSite ) {
  1767. NlPrintDom((NL_LOGON, DomainInfo,
  1768. "NlpUserValidateOnPdc: Ignored a user validation on a PDC in remote site.\n"));
  1769. *Authoritative = FALSE;
  1770. Status = STATUS_NO_SUCH_USER;
  1771. goto Cleanup;
  1772. } else {
  1773. NlPrintDom((NL_LOGON, DomainInfo,
  1774. "NlpUserValidateOnPdc: BDC and PDC are in the same site.\n"));
  1775. }
  1776. }
  1777. //
  1778. // See if it's time to send this user logon to PDC.
  1779. //
  1780. if ( UseNegativeCache ) {
  1781. BOOL AvoidSend = FALSE;
  1782. UserName = LocalAlloc( 0, LogonInfo->UserName.Length + sizeof(WCHAR) );
  1783. if ( UserName == NULL ) {
  1784. Status = STATUS_NO_MEMORY;
  1785. goto Cleanup;
  1786. }
  1787. RtlCopyMemory( UserName, LogonInfo->UserName.Buffer, LogonInfo->UserName.Length );
  1788. UserName[ LogonInfo->UserName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
  1789. LOCK_TRUST_LIST( DomainInfo );
  1790. for ( FailedUserEntry = DomainInfo->DomFailedUserLogonList.Flink;
  1791. FailedUserEntry != &DomainInfo->DomFailedUserLogonList;
  1792. FailedUserEntry = FailedUserEntry->Flink ) {
  1793. FailedUser = CONTAINING_RECORD( FailedUserEntry, NL_FAILED_USER_LOGON, FuNext );
  1794. //
  1795. // If this is the entry for this user, check if it's time to forward
  1796. // this logon to the PDC. In any case, remove this entry from the list
  1797. // and then insert it at the front so that the list stays sorted by the
  1798. // entry access time.
  1799. //
  1800. if ( NlNameCompare(UserName, FailedUser->FuUserName, NAMETYPE_USER) == 0 ) {
  1801. ULONG TimeElapsed = NetpDcElapsedTime( FailedUser->FuLastTimeSentToPdc );
  1802. //
  1803. // If we have exceeded the threshold for failed forwarded logon count
  1804. // and we recently sent this failed logon to the PDC,
  1805. // avoid forwarding this logon to the PDC
  1806. //
  1807. if ( FailedUser->FuBadLogonCount > NL_FAILED_USER_MAX_LOGON_COUNT &&
  1808. TimeElapsed < NL_FAILED_USER_FORWARD_LOGON_TIMEOUT ) {
  1809. AvoidSend = TRUE;
  1810. }
  1811. RemoveEntryList( &FailedUser->FuNext );
  1812. break;
  1813. }
  1814. FailedUser = NULL;
  1815. }
  1816. //
  1817. // Insert the entry at the front of the list
  1818. //
  1819. if ( FailedUser != NULL ) {
  1820. InsertHeadList( &DomainInfo->DomFailedUserLogonList, &FailedUser->FuNext );
  1821. }
  1822. UNLOCK_TRUST_LIST( DomainInfo );
  1823. //
  1824. // If this user logon failed recently, avoid sending it to PDC
  1825. //
  1826. if ( AvoidSend ) {
  1827. NlPrintDom(( NL_LOGON, DomainInfo,
  1828. "Avoid send to PDC since user %ws failed recently\n",
  1829. UserName ));
  1830. Status = STATUS_NO_SUCH_USER;
  1831. goto Cleanup;
  1832. }
  1833. }
  1834. //
  1835. // We are sending this logon to the PDC ....
  1836. //
  1837. ClientSession = NlRefDomClientSession( DomainInfo );
  1838. if ( ClientSession == NULL ) {
  1839. Status = STATUS_INVALID_DOMAIN_ROLE;
  1840. goto Cleanup;
  1841. }
  1842. //
  1843. // The normal pass-thru authentication logic handles this quite nicely.
  1844. //
  1845. Status = NlpUserValidateHigher(
  1846. ClientSession,
  1847. FALSE,
  1848. LogonLevel,
  1849. LogonInformation,
  1850. ValidationLevel,
  1851. ValidationInformation,
  1852. Authoritative,
  1853. &ExtraFlags );
  1854. #if NETLOGONDBG
  1855. if ( NT_SUCCESS(Status) ) {
  1856. IF_NL_DEBUG( LOGON ) {
  1857. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  1858. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
  1859. &((PNETLOGON_LEVEL)LogonInformation)->LogonInteractive;
  1860. NlPrintDom((NL_LOGON, DomainInfo,
  1861. "SamLogon: %s logon of %wZ\\%wZ from %wZ successfully handled on PDC.\n",
  1862. NlpLogonTypeToText( LogonLevel ),
  1863. &LogonInfo->LogonDomainName,
  1864. &LogonInfo->UserName,
  1865. &LogonInfo->Workstation ));
  1866. }
  1867. }
  1868. #endif // NETLOGONDBG
  1869. //
  1870. // Determine if the account lockout policy is enabled.
  1871. //
  1872. // Do not destroy Status below. Otherwise, if NlpUserValidateHigher
  1873. // succeeded we would leak ValidationInformation.
  1874. //
  1875. TmpStatus = SamrQueryInformationDomain(
  1876. DomainInfo->DomSamAccountDomainHandle,
  1877. DomainLockoutInformation,
  1878. &DomainLockout );
  1879. if ( !NT_SUCCESS(TmpStatus) ) {
  1880. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1881. "NlpUserValidateOnPdc: SamrQueryInformationDomain failed: 0x%lx\n", TmpStatus ));
  1882. } else if ( ((DOMAIN_LOCKOUT_INFORMATION *)DomainLockout)->LockoutThreshold > 0 ) {
  1883. LockoutEnabled = TRUE;
  1884. }
  1885. //
  1886. // Update the list of forwarded failed user logons
  1887. //
  1888. // If the lockout policy is enabled we should continue
  1889. // to forward logons to PDC (i.e. we should not cache
  1890. // this failure) to keep the right lockout count until
  1891. // the account becomes locked on the PDC.
  1892. //
  1893. FailedUser = NULL;
  1894. if ( UseNegativeCache &&
  1895. (!LockoutEnabled || Status == STATUS_ACCOUNT_LOCKED_OUT) ) {
  1896. ULONG FailedUserCount = 0;
  1897. LOCK_TRUST_LIST( DomainInfo );
  1898. for ( FailedUserEntry = DomainInfo->DomFailedUserLogonList.Flink;
  1899. FailedUserEntry != &DomainInfo->DomFailedUserLogonList;
  1900. FailedUserEntry = FailedUserEntry->Flink ) {
  1901. FailedUser = CONTAINING_RECORD( FailedUserEntry, NL_FAILED_USER_LOGON, FuNext );
  1902. //
  1903. // If this is the entry for this user, remove it from the list.
  1904. // If it stays on the list, we will re-insert it at the front
  1905. // so that the list stays sorted by the entry access time.
  1906. //
  1907. if ( NlNameCompare(UserName, FailedUser->FuUserName, NAMETYPE_USER) == 0 ) {
  1908. RemoveEntryList( &FailedUser->FuNext );
  1909. break;
  1910. }
  1911. FailedUserCount ++;
  1912. FailedUser = NULL;
  1913. }
  1914. //
  1915. // Remember if this user logon failed
  1916. //
  1917. if ( !NT_SUCCESS(Status) ) {
  1918. //
  1919. // If there is no entry for this user, allocate one
  1920. //
  1921. if ( FailedUser == NULL ) {
  1922. ULONG UserNameSize;
  1923. UserNameSize = (wcslen(UserName) + 1) * sizeof(WCHAR);
  1924. FailedUser = LocalAlloc( LMEM_ZEROINIT, sizeof(NL_FAILED_USER_LOGON) +
  1925. UserNameSize );
  1926. if ( FailedUser == NULL ) {
  1927. UNLOCK_TRUST_LIST( DomainInfo );
  1928. //
  1929. // Do not destroy Status.
  1930. // Return whatever NlpUserValidateHigher returned.
  1931. //
  1932. goto Cleanup;
  1933. }
  1934. //
  1935. // Fill it in
  1936. //
  1937. RtlCopyMemory( &FailedUser->FuUserName, UserName, UserNameSize );
  1938. //
  1939. // If we have too many entries,
  1940. // remove the least recently used one and free it.
  1941. //
  1942. if ( FailedUserCount >= NL_MAX_FAILED_USER_LOGONS ) {
  1943. PLIST_ENTRY LastEntry = RemoveTailList( &DomainInfo->DomFailedUserLogonList );
  1944. LocalFree( CONTAINING_RECORD(LastEntry, NL_FAILED_USER_LOGON, FuNext) );
  1945. }
  1946. }
  1947. //
  1948. // Remember when this user logon was sent to PDC last time
  1949. //
  1950. FailedUser->FuLastTimeSentToPdc = GetTickCount();
  1951. //
  1952. // Increment the bad logon count for this user
  1953. //
  1954. FailedUser->FuBadLogonCount ++;
  1955. //
  1956. // Insert the entry at the front of the list
  1957. //
  1958. InsertHeadList( &DomainInfo->DomFailedUserLogonList, &FailedUser->FuNext );
  1959. //
  1960. // If this logon succeeded,
  1961. // just free the unlinked failed logon entry (if any)
  1962. //
  1963. } else if ( FailedUser != NULL ) {
  1964. LocalFree( FailedUser );
  1965. }
  1966. UNLOCK_TRUST_LIST( DomainInfo );
  1967. }
  1968. Cleanup:
  1969. if ( ClientSession != NULL ) {
  1970. NlUnrefClientSession( ClientSession );
  1971. }
  1972. if ( UserName != NULL ) {
  1973. LocalFree( UserName );
  1974. }
  1975. if ( DomainLockout != NULL ) {
  1976. SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainLockout,
  1977. DomainLockoutInformation );
  1978. }
  1979. return Status;
  1980. }
  1981. NTSTATUS
  1982. NlpResetBadPwdCountOnPdc(
  1983. IN PDOMAIN_INFO DomainInfo,
  1984. IN PUNICODE_STRING LogonUser
  1985. )
  1986. /*++
  1987. Routine Description:
  1988. This function zeros the BadPasswordCount field for the specified user
  1989. on the PDC through NetLogon Secure Channel.
  1990. Arguments:
  1991. DomainInfo - Domain this BDC is a member of.
  1992. LogonUse -- The user whose BadPasswordCount is to be zeroed.
  1993. Return Value:
  1994. NTSTATUS code .
  1995. it may fail with STATUS_UNKNOWN_REVISION, which means the PDC doesn't
  1996. know how to handle the new OP code, in this case, we should fail back
  1997. to the old fashion.
  1998. --*/
  1999. {
  2000. NTSTATUS NtStatus = STATUS_SUCCESS;
  2001. SAMPR_HANDLE UserHandle = 0;
  2002. LPWSTR pUserNameStr = NULL;
  2003. //
  2004. // If this isn't a BDC,
  2005. // There's nothing to do here.
  2006. //
  2007. if ( DomainInfo->DomRole != RoleBackup ) {
  2008. return STATUS_INVALID_DOMAIN_ROLE;
  2009. }
  2010. //
  2011. // Allocate the user name string
  2012. //
  2013. pUserNameStr = LocalAlloc( 0, LogonUser->Length + sizeof(WCHAR) );
  2014. if (NULL == pUserNameStr)
  2015. {
  2016. return( STATUS_NO_MEMORY );
  2017. }
  2018. RtlCopyMemory( pUserNameStr, LogonUser->Buffer, LogonUser->Length );
  2019. pUserNameStr[ LogonUser->Length/sizeof(WCHAR) ] = L'\0';
  2020. //
  2021. // Get the user's handle to the local SAM database
  2022. //
  2023. NtStatus = NlSamOpenNamedUser( DomainInfo,
  2024. pUserNameStr,
  2025. &UserHandle,
  2026. NULL,
  2027. NULL
  2028. );
  2029. //
  2030. // Reset the bad password count on PDC
  2031. //
  2032. if (NT_SUCCESS(NtStatus))
  2033. {
  2034. NtStatus = SamIResetBadPwdCountOnPdc(UserHandle);
  2035. }
  2036. if ( NULL != pUserNameStr) {
  2037. LocalFree( pUserNameStr );
  2038. }
  2039. if ( 0 != UserHandle ) {
  2040. SamrCloseHandle( &UserHandle );
  2041. }
  2042. return( NtStatus );
  2043. }
  2044. VOID
  2045. NlpZeroBadPasswordCountOnPdc (
  2046. IN PDOMAIN_INFO DomainInfo,
  2047. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2048. IN LPBYTE LogonInformation
  2049. )
  2050. /*++
  2051. Routine Description:
  2052. This function zeros the BadPasswordCount field for the specified user
  2053. on the PDC.
  2054. Arguments:
  2055. DomainInfo - Domain this BDC is a member of.
  2056. LogonLevel -- Specifies the level of information given in
  2057. LogonInformation. Has already been validated.
  2058. LogonInformation -- Specifies the description for the user
  2059. logging on.
  2060. Return Value:
  2061. None.
  2062. --*/
  2063. {
  2064. NTSTATUS Status;
  2065. BOOLEAN Authoritative;
  2066. LPBYTE ValidationInformation = NULL;
  2067. //
  2068. // If this isn't a BDC,
  2069. // There's nothing to do here.
  2070. //
  2071. if ( DomainInfo->DomRole != RoleBackup ) {
  2072. return;
  2073. }
  2074. //
  2075. // We only call this function on a BDC and if the BDC has just zeroed
  2076. // the BadPasswordCount because of successful logon.
  2077. // First, try to zero bad pwd count directly through NetLogon
  2078. // Secure Channel, if it fails with UNKNOWN_REVISION, which means
  2079. // PDC doesn't know how to handle the new OP code, will try to
  2080. // do the logon over again on the PDC, thus that bad pwd count get
  2081. // zero'ed.
  2082. //
  2083. Status = NlpResetBadPwdCountOnPdc(
  2084. DomainInfo,
  2085. &((PNETLOGON_LOGON_IDENTITY_INFO)LogonInformation)->UserName
  2086. );
  2087. if (!NT_SUCCESS(Status) &&
  2088. (STATUS_UNKNOWN_REVISION == Status) )
  2089. {
  2090. Status = NlpUserValidateOnPdc (
  2091. DomainInfo,
  2092. LogonLevel,
  2093. LogonInformation,
  2094. NetlogonValidationSamInfo,
  2095. FALSE, // avoid negative cache of failed user logons
  2096. &ValidationInformation,
  2097. &Authoritative );
  2098. if ( NT_SUCCESS(Status) ) {
  2099. MIDL_user_free( ValidationInformation );
  2100. }
  2101. }
  2102. }
  2103. #endif // _DC_NETLOGON
  2104. NTSTATUS
  2105. NlpZeroBadPasswordCountLocally (
  2106. IN PDOMAIN_INFO DomainInfo,
  2107. PUNICODE_STRING LogonUser
  2108. )
  2109. /*++
  2110. Routine Description:
  2111. This function zeros the BadPasswordCount field for the specified user
  2112. on this BDC.
  2113. Arguments:
  2114. DomainInfo - Domain this BDC is a member of.
  2115. LogonUser -- The user whose BadPasswordCount is to be zeroed.
  2116. Return Value:
  2117. Status of operation.
  2118. --*/
  2119. {
  2120. NTSTATUS Status = STATUS_SUCCESS;
  2121. SAMPR_HANDLE UserHandle = 0;
  2122. SAMPR_USER_INFO_BUFFER UserInfo;
  2123. LPWSTR pUserNameStr = NULL;
  2124. //
  2125. // If this isn't a BDC,
  2126. // There's nothing to do here.
  2127. //
  2128. if ( DomainInfo->DomRole != RoleBackup ) {
  2129. return STATUS_INVALID_DOMAIN_ROLE;
  2130. }
  2131. //
  2132. // Allocate the user name string
  2133. //
  2134. pUserNameStr = LocalAlloc( 0, LogonUser->Length + sizeof(WCHAR) );
  2135. if ( pUserNameStr == NULL ) {
  2136. Status = STATUS_NO_MEMORY;
  2137. goto Cleanup;
  2138. }
  2139. RtlCopyMemory( pUserNameStr, LogonUser->Buffer, LogonUser->Length );
  2140. pUserNameStr[ LogonUser->Length/sizeof(WCHAR) ] = L'\0';
  2141. //
  2142. // Get the user's handle to the local SAM database
  2143. //
  2144. Status = NlSamOpenNamedUser( DomainInfo,
  2145. pUserNameStr,
  2146. &UserHandle,
  2147. NULL,
  2148. NULL);
  2149. if ( !NT_SUCCESS(Status) ) {
  2150. NlPrint(( NL_CRITICAL,
  2151. "NlpZeroBadPasswordCountLocally: NlSamOpenNamedUser failed 0x%lx",
  2152. Status ));
  2153. goto Cleanup;
  2154. }
  2155. //
  2156. // Prepare the user info
  2157. //
  2158. RtlZeroMemory(&(UserInfo.Internal2), sizeof(USER_INTERNAL2_INFORMATION));
  2159. UserInfo.Internal2.StatisticsToApply |= USER_LOGON_STAT_BAD_PWD_COUNT;
  2160. //
  2161. // Reset the bad password count
  2162. //
  2163. Status = SamrSetInformationUser( UserHandle,
  2164. UserInternal2Information,
  2165. &UserInfo);
  2166. if ( !NT_SUCCESS(Status) ) {
  2167. NlPrint(( NL_CRITICAL,
  2168. "NlpZeroBadPasswordCountLocally: SamrSetInformationUser failed 0x%lx",
  2169. Status ));
  2170. goto Cleanup;
  2171. }
  2172. Cleanup:
  2173. if ( pUserNameStr != NULL ) {
  2174. LocalFree( pUserNameStr );
  2175. }
  2176. if ( UserHandle != 0 ) {
  2177. SamrCloseHandle(&UserHandle);
  2178. }
  2179. return Status;
  2180. }
  2181. NTSTATUS
  2182. NlpUserValidate (
  2183. IN PDOMAIN_INFO DomainInfo,
  2184. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  2185. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2186. IN LPBYTE LogonInformation,
  2187. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  2188. OUT LPBYTE * ValidationInformation,
  2189. OUT PBOOLEAN Authoritative,
  2190. IN OUT PULONG ExtraFlags,
  2191. IN BOOLEAN Recursed
  2192. )
  2193. /*++
  2194. Routine Description:
  2195. This function processes an interactive or network logon.
  2196. It is a worker routine for I_NetSamLogon. I_NetSamLogon handles the
  2197. details of validating the caller. This function handles the details
  2198. of whether to validate locally or pass the request on. MsvValidateSam
  2199. does the actual local validation.
  2200. session table only in the domain defining the specified user's
  2201. account.
  2202. This service is also used to process a re-logon request.
  2203. Arguments:
  2204. DomainInfo - Hosted domain this logon is for.
  2205. SecureChannelType -- Type of secure channel this request was made over.
  2206. OrigClientIsNt4 -- True if our caller is running NT 4.0
  2207. LogonLevel -- Specifies the level of information given in
  2208. LogonInformation. Has already been validated.
  2209. LogonInformation -- Specifies the description for the user
  2210. logging on.
  2211. ValidationLevel -- Specifies the level of information returned in
  2212. ValidationInformation. Must be NetlogonValidationSamInfo,
  2213. NetlogonValidationSamInfo2, or NetlogonValidationSamInfo4.
  2214. ValidationInformation -- Returns the requested validation
  2215. information. This buffer must be freed using MIDL_user_free.
  2216. Authoritative -- Returns whether the status returned is an
  2217. authoritative status which should be returned to the original
  2218. caller. If not, this logon request may be tried again on another
  2219. domain controller. This parameter is returned regardless of the
  2220. status code.
  2221. ExtraFlags -- Accepts and returns a DWORD to the caller.
  2222. The DWORD contains NL_EXFLAGS_* values.
  2223. Recursed - TRUE if this is a recursive call. This routine sometimes translates
  2224. the account name in the logon information to a more explicit form (e.g., from a
  2225. UPN to a <DnsDomainName>\<SamAccountName> form). After it does that, it simply
  2226. calls this routine again. This boolean is TRUE on that second call.
  2227. Return Value:
  2228. STATUS_SUCCESS: if there was no error.
  2229. Otherwise, the error code is
  2230. returned.
  2231. --*/
  2232. {
  2233. NTSTATUS Status;
  2234. NTSTATUS DefaultStatus = STATUS_NO_SUCH_USER;
  2235. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  2236. PCLIENT_SESSION ClientSession = NULL;
  2237. DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
  2238. BOOLEAN BadPasswordCountZeroed;
  2239. BOOLEAN LogonToLocalDomain;
  2240. LPWSTR RealSamAccountName = NULL;
  2241. LPWSTR RealDomainName = NULL;
  2242. ULONG RealExtraFlags = 0; // Flags to pass to the next hop
  2243. BOOLEAN ExpediteToRoot = ((*ExtraFlags) & NL_EXFLAGS_EXPEDITE_TO_ROOT) != 0;
  2244. BOOLEAN CrossForestHop = ((*ExtraFlags) & NL_EXFLAGS_CROSS_FOREST_HOP) != 0;
  2245. //
  2246. // Initialization
  2247. //
  2248. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  2249. *Authoritative = FALSE;
  2250. //
  2251. // The DNS domain for a workstation is the domain its a member of, so
  2252. // don't match based on that.
  2253. //
  2254. LogonToLocalDomain = RtlEqualDomainName( &LogonInfo->LogonDomainName,
  2255. &DomainInfo->DomUnicodeAccountDomainNameString ) ||
  2256. ((DomainInfo->DomRole != RoleMemberWorkstation ) &&
  2257. NlEqualDnsNameU( &LogonInfo->LogonDomainName,
  2258. &DomainInfo->DomUnicodeDnsDomainNameString ) ) ;
  2259. //
  2260. // Check to see if the account is in the local SAM database.
  2261. //
  2262. // The Theory:
  2263. // If a particular database is absolutely requested,
  2264. // we only try the account in the requested database.
  2265. //
  2266. // In the event that an account exists in multiple places in the hierarchy,
  2267. // we want to find the version of the account that is closest to the
  2268. // logged on machine (i.e., workstation first, primary domain, then
  2269. // trusted domain.). So we always try to local database before going
  2270. // to a higher authority.
  2271. //
  2272. // Finally, handle the case that this call is from a BDC in our own domain
  2273. // just checking to see if the PDC (us) has a better copy of the account
  2274. // than it does.
  2275. //
  2276. if ( !ExpediteToRoot &&
  2277. ( (LogonInfo->LogonDomainName.Length == 0 && !CrossForestHop ) ||
  2278. LogonToLocalDomain ||
  2279. SecureChannelType == ServerSecureChannel )) {
  2280. //
  2281. // If we are not doing a generic passthrough, just call SAM
  2282. //
  2283. if ( LogonLevel != NetlogonGenericInformation ) {
  2284. NETLOGON_LOGON_INFO_CLASS MsvLogonLevel;
  2285. //
  2286. // Indicate we've already tried the specified account and
  2287. // we won't need to try it again locally.
  2288. //
  2289. AccountsToTry &= ~MSVSAM_SPECIFIED;
  2290. //
  2291. // Don't confuse MSV with the transitive LogonLevels
  2292. //
  2293. switch (LogonLevel ) {
  2294. case NetlogonInteractiveTransitiveInformation:
  2295. MsvLogonLevel = NetlogonInteractiveInformation; break;
  2296. case NetlogonServiceTransitiveInformation:
  2297. MsvLogonLevel = NetlogonServiceInformation; break;
  2298. case NetlogonNetworkTransitiveInformation:
  2299. MsvLogonLevel = NetlogonNetworkInformation; break;
  2300. default:
  2301. MsvLogonLevel = LogonLevel; break;
  2302. }
  2303. Status = MsvSamValidate( DomainInfo->DomSamAccountDomainHandle,
  2304. TRUE, // UasCompatibilityMode,
  2305. SecureChannelType,
  2306. &DomainInfo->DomUnicodeComputerNameString,
  2307. &DomainInfo->DomUnicodeAccountDomainNameString,
  2308. DomainInfo->DomAccountDomainId,
  2309. MsvLogonLevel,
  2310. LogonInformation,
  2311. ValidationLevel,
  2312. (PVOID *)ValidationInformation,
  2313. Authoritative,
  2314. &BadPasswordCountZeroed,
  2315. MSVSAM_SPECIFIED );
  2316. //
  2317. // If this is a BDC and we zeroed the BadPasswordCount field,
  2318. // allow the PDC to do the same thing.
  2319. //
  2320. if ( BadPasswordCountZeroed ) {
  2321. NlpZeroBadPasswordCountOnPdc ( DomainInfo, LogonLevel, LogonInformation );
  2322. }
  2323. //
  2324. // If the request is explicitly for this domain,
  2325. // The STATUS_NO_SUCH_USER answer is authoritative.
  2326. //
  2327. if ( LogonToLocalDomain && Status == STATUS_NO_SUCH_USER ) {
  2328. *Authoritative = TRUE;
  2329. }
  2330. //
  2331. // If this is one of our BDCs calling,
  2332. // return with whatever answer we got locally.
  2333. //
  2334. if ( SecureChannelType == ServerSecureChannel ) {
  2335. DefaultStatus = Status;
  2336. goto Cleanup;
  2337. }
  2338. } else {
  2339. PNETLOGON_GENERIC_INFO GenericInfo;
  2340. NETLOGON_VALIDATION_GENERIC_INFO GenericValidation;
  2341. NTSTATUS ProtocolStatus;
  2342. GenericInfo = (PNETLOGON_GENERIC_INFO) LogonInformation;
  2343. GenericValidation.ValidationData = NULL;
  2344. GenericValidation.DataLength = 0;
  2345. //
  2346. // We are doing generic passthrough, so call the LSA
  2347. //
  2348. // LogonData is opaque to Netlogon. The caller made sure that any
  2349. // pointers within LogonData are actually offsets within LogonData.
  2350. // So, tell the package that the Client's buffer base is 0.
  2351. //
  2352. Status = LsaICallPackagePassthrough(
  2353. &GenericInfo->PackageName,
  2354. 0, // Indicate pointers are relative.
  2355. GenericInfo->LogonData,
  2356. GenericInfo->DataLength,
  2357. (PVOID *) &GenericValidation.ValidationData,
  2358. &GenericValidation.DataLength,
  2359. &ProtocolStatus
  2360. );
  2361. if (NT_SUCCESS(Status)) {
  2362. Status = ProtocolStatus;
  2363. }
  2364. //
  2365. // This is always authoritative.
  2366. //
  2367. *Authoritative = TRUE;
  2368. //
  2369. // If the call succeeded, allocate the return message.
  2370. //
  2371. if (NT_SUCCESS(Status)) {
  2372. PNETLOGON_VALIDATION_GENERIC_INFO ReturnInfo;
  2373. ULONG ValidationLength;
  2374. ValidationLength = sizeof(*ReturnInfo) + GenericValidation.DataLength;
  2375. ReturnInfo = (PNETLOGON_VALIDATION_GENERIC_INFO) MIDL_user_allocate(
  2376. ValidationLength
  2377. );
  2378. if (ReturnInfo != NULL) {
  2379. if ( GenericValidation.DataLength == 0 ||
  2380. GenericValidation.ValidationData == NULL ) {
  2381. ReturnInfo->DataLength = 0;
  2382. ReturnInfo->ValidationData = NULL;
  2383. } else {
  2384. ReturnInfo->DataLength = GenericValidation.DataLength;
  2385. ReturnInfo->ValidationData = (PUCHAR) (ReturnInfo + 1);
  2386. RtlCopyMemory(
  2387. ReturnInfo->ValidationData,
  2388. GenericValidation.ValidationData,
  2389. ReturnInfo->DataLength );
  2390. }
  2391. *ValidationInformation = (PBYTE) ReturnInfo;
  2392. } else {
  2393. Status = STATUS_INSUFFICIENT_RESOURCES;
  2394. }
  2395. if (GenericValidation.ValidationData != NULL) {
  2396. LsaIFreeReturnBuffer(GenericValidation.ValidationData);
  2397. }
  2398. }
  2399. DefaultStatus = Status;
  2400. goto Cleanup;
  2401. }
  2402. //
  2403. // If the local SAM database authoritatively handled the logon attempt,
  2404. // just return.
  2405. //
  2406. if ( *Authoritative ) {
  2407. DefaultStatus = Status;
  2408. #ifdef _DC_NETLOGON
  2409. //
  2410. // If the problem is just that the password is wrong,
  2411. // try again on the PDC where the password may already be changed.
  2412. //
  2413. if ( BAD_PASSWORD(Status) ) {
  2414. BOOLEAN TempAuthoritative;
  2415. Status = NlpUserValidateOnPdc (
  2416. DomainInfo,
  2417. LogonLevel,
  2418. LogonInformation,
  2419. ValidationLevel,
  2420. TRUE, // use negative cache of failed user logons
  2421. ValidationInformation,
  2422. &TempAuthoritative );
  2423. // Ignore failures from the PDC (except where it has newer information)
  2424. if ( NT_SUCCESS(Status) || BAD_PASSWORD(Status) ) {
  2425. DefaultStatus = Status;
  2426. *Authoritative = TempAuthoritative;
  2427. }
  2428. // On success, zero bad password locally.
  2429. // Ignore error as it's not critical operation.
  2430. if ( NT_SUCCESS(Status) && !NlGlobalMemberWorkstation ) {
  2431. NlpZeroBadPasswordCountLocally( DomainInfo, &LogonInfo->UserName );
  2432. }
  2433. }
  2434. #endif // _DC_NETLOGON
  2435. goto Cleanup;
  2436. }
  2437. DefaultStatus = Status;
  2438. }
  2439. //
  2440. // If the request in not for this domain,
  2441. // or the domain name isn't specified (and we haven't found the account yet)
  2442. // or our caller asked us to send the request to the root of the forest,
  2443. // send the request to a higher authority.
  2444. //
  2445. if ( !LogonToLocalDomain ||
  2446. LogonInfo->LogonDomainName.Length == 0 ||
  2447. ExpediteToRoot ) {
  2448. //
  2449. // If this machine is a workstation,
  2450. // send the request to the Primary Domain.
  2451. //
  2452. if ( NlGlobalMemberWorkstation ) {
  2453. NlAssert( !ExpediteToRoot );
  2454. NlAssert( !CrossForestHop );
  2455. ClientSession = NlRefDomClientSession( DomainInfo);
  2456. if ( ClientSession == NULL ) {
  2457. *Authoritative = FALSE;
  2458. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  2459. goto Cleanup;
  2460. }
  2461. Status = NlpUserValidateHigher(
  2462. ClientSession,
  2463. FALSE,
  2464. LogonLevel,
  2465. LogonInformation,
  2466. ValidationLevel,
  2467. ValidationInformation,
  2468. Authoritative,
  2469. &RealExtraFlags );
  2470. NlUnrefClientSession( ClientSession );
  2471. ClientSession = NULL;
  2472. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  2473. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  2474. //
  2475. // return more appropriate error
  2476. //
  2477. if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
  2478. (Status == STATUS_ACCESS_DENIED) ) {
  2479. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  2480. }
  2481. //
  2482. // If the primary domain authoritatively handled the logon attempt,
  2483. // just return.
  2484. //
  2485. if ( *Authoritative ) {
  2486. //
  2487. // If we didn't actually talk to the primary domain,
  2488. // check locally if the domain requested is a trusted domain.
  2489. // (This list is only a cache so we had to try to contact the
  2490. // primary domain.)
  2491. //
  2492. // Note: I can't differentiate between there not being a logon server
  2493. // in my primary domain and there not being a logon sever in
  2494. // a trusted domain. So, both cases go into the code below.
  2495. //
  2496. if ( Status == STATUS_NO_LOGON_SERVERS ) {
  2497. //
  2498. // If no domain name was specified,
  2499. // check if the user name is a UPN.
  2500. //
  2501. if ( LogonLevel != NetlogonGenericInformation &&
  2502. LogonInfo->LogonDomainName.Length == 0 ) {
  2503. ULONG i;
  2504. for ( i=0; i<LogonInfo->UserName.Length/sizeof(WCHAR); i++) {
  2505. //
  2506. // If this is a UPN logon,
  2507. // assume that the domain is a trusted domain.
  2508. //
  2509. // ???: it isn't sufficient to check for an @.
  2510. // This might be a user account with an @ in it.
  2511. //
  2512. // This makes UPNs eligible for cached logon.
  2513. // But it doesn't make UPNs eligible for guest
  2514. // account logon.
  2515. if ( LogonInfo->UserName.Buffer[i] == L'@') {
  2516. DefaultStatus = Status;
  2517. goto Cleanup;
  2518. }
  2519. }
  2520. }
  2521. //
  2522. // If the domain specified is trusted,
  2523. // then return the status to the caller.
  2524. // otherwise just press on.
  2525. if ( NlIsDomainTrusted ( &LogonInfo->LogonDomainName ) ) {
  2526. DefaultStatus = Status;
  2527. goto Cleanup;
  2528. } else {
  2529. //
  2530. // Set the return codes to look as though the primary
  2531. // determined this is an untrusted domain.
  2532. //
  2533. *Authoritative = FALSE;
  2534. Status = STATUS_NO_SUCH_USER;
  2535. }
  2536. } else {
  2537. DefaultStatus = Status;
  2538. goto Cleanup;
  2539. }
  2540. }
  2541. if ( Status != STATUS_NO_SUCH_USER ) {
  2542. DefaultStatus = Status;
  2543. }
  2544. //
  2545. // This machine is a Domain Controller.
  2546. //
  2547. // This request is either a pass-thru request by a workstation in
  2548. // our domain, or this request came directly from the MSV
  2549. // authentication package.
  2550. //
  2551. // In either case, pass the request to the trusted domain.
  2552. //
  2553. } else {
  2554. BOOLEAN TransitiveUsed;
  2555. //
  2556. // If the request was passed to us from a trusting domain DC,
  2557. // and the caller doesn't want transitive trust to be used,
  2558. // then avoid using transitive trust. (Generic passthrough
  2559. // may always be transitive (it was introduced in NT5).)
  2560. //
  2561. if ( IsDomainSecureChannelType(SecureChannelType) &&
  2562. LogonLevel != NetlogonInteractiveTransitiveInformation &&
  2563. LogonLevel != NetlogonServiceTransitiveInformation &&
  2564. LogonLevel != NetlogonNetworkTransitiveInformation &&
  2565. LogonLevel != NetlogonGenericInformation ) {
  2566. DefaultStatus = STATUS_NO_SUCH_USER;
  2567. goto Cleanup;
  2568. }
  2569. //
  2570. // If our caller asked us to expedite this request to the domain at the forest root,
  2571. // find the client session to our parent.
  2572. //
  2573. if ( ExpediteToRoot ) {
  2574. //
  2575. // Only do this if we're not already at the root
  2576. //
  2577. if ( (DomainInfo->DomFlags & DOM_FOREST_ROOT) == 0 ) {
  2578. ClientSession = NlRefDomParentClientSession( DomainInfo );
  2579. if ( ClientSession == NULL ) {
  2580. NlPrintDom((NL_LOGON, DomainInfo,
  2581. "NlpUserValidate: Can't find parent domain in forest '%wZ'\n",
  2582. &NlGlobalUnicodeDnsForestNameString ));
  2583. DefaultStatus = STATUS_NO_SUCH_USER;
  2584. goto Cleanup;
  2585. }
  2586. //
  2587. // Tell the DC we're calling to continue expediting to root.
  2588. //
  2589. RealExtraFlags |= NL_EXFLAGS_EXPEDITE_TO_ROOT;
  2590. }
  2591. //
  2592. // It the domain is explicitly given,
  2593. // simply find the client session for that domain.
  2594. //
  2595. } else {
  2596. if ( LogonInfo->LogonDomainName.Length != 0 ) {
  2597. //
  2598. // Find a client session for the domain.
  2599. //
  2600. // If we just hopped from another forest,
  2601. // require that LogonDomainName be a domain in the forest.
  2602. //
  2603. ClientSession = NlFindNamedClientSession(
  2604. DomainInfo,
  2605. &LogonInfo->LogonDomainName,
  2606. NL_RETURN_CLOSEST_HOP |
  2607. (CrossForestHop ?
  2608. NL_REQUIRE_DOMAIN_IN_FOREST : 0),
  2609. &TransitiveUsed );
  2610. }
  2611. }
  2612. //
  2613. // If the client session hasn't yet been found,
  2614. // Try to find the domain name by ask the GC.
  2615. //
  2616. // Avoid this step if the call came from a DC. It should have done the GC lookup.
  2617. // Even that's OK if the caller was expediting to root or
  2618. // hopping into this forest from a trusting forest.
  2619. // Avoid this step if this machine already did the GC lookup.
  2620. //
  2621. if ( LogonLevel != NetlogonGenericInformation &&
  2622. ClientSession == NULL &&
  2623. ( !IsDomainSecureChannelType(SecureChannelType) ||
  2624. (ExpediteToRoot || CrossForestHop) ) &&
  2625. !Recursed ) {
  2626. //
  2627. // Find the domain the account is in from one of the following sources:
  2628. // The LSA FTinfo match routine if this is a DC at the root of the forest.
  2629. // The local DS for UPN lookup.
  2630. // The GC form UPN lookup or unqualified SAM account names.
  2631. // By datagram send to all directly trusted domains not in this forest.
  2632. //
  2633. Status = NlPickDomainWithAccount (
  2634. DomainInfo,
  2635. &LogonInfo->UserName,
  2636. &LogonInfo->LogonDomainName,
  2637. USER_NORMAL_ACCOUNT,
  2638. SecureChannelType,
  2639. ExpediteToRoot,
  2640. CrossForestHop,
  2641. &RealSamAccountName,
  2642. &RealDomainName,
  2643. &RealExtraFlags );
  2644. //
  2645. // If we're a DC at the root of the forest
  2646. // and the account is in a trusted forest,
  2647. // send the request to the other forest.
  2648. //
  2649. if ( NT_SUCCESS(Status) &&
  2650. (RealExtraFlags & NL_EXFLAGS_CROSS_FOREST_HOP) != 0 ) {
  2651. UNICODE_STRING RealDomainNameString;
  2652. RtlInitUnicodeString( &RealDomainNameString, RealDomainName );
  2653. ClientSession = NlFindNamedClientSession(
  2654. DomainInfo,
  2655. &RealDomainNameString,
  2656. NL_DIRECT_TRUST_REQUIRED,
  2657. &TransitiveUsed );
  2658. //
  2659. // Further qualify the ClientSession by ensure the found domain isn't
  2660. // in our forest and that the F bit is set.
  2661. //
  2662. if ( ClientSession != NULL ) {
  2663. if ( (ClientSession->CsTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) == 0 ) {
  2664. NlPrintCs((NL_CRITICAL, ClientSession,
  2665. "NlpUserValidate: %wZ\\%wZ: trusted forest '%wZ' doesn't have F bit set.\n",
  2666. &LogonInfo->LogonDomainName,
  2667. &LogonInfo->UserName,
  2668. &RealDomainNameString ));
  2669. DefaultStatus = STATUS_NO_SUCH_USER;
  2670. goto Cleanup;
  2671. }
  2672. if (ClientSession->CsFlags & CS_DOMAIN_IN_FOREST) {
  2673. NlPrintCs((NL_CRITICAL, ClientSession,
  2674. "NlpUserValidate: %wZ\\%wZ: trusted forest '%wZ' is in my forest\n",
  2675. &LogonInfo->LogonDomainName,
  2676. &LogonInfo->UserName,
  2677. &RealDomainNameString ));
  2678. DefaultStatus = STATUS_NO_SUCH_USER;
  2679. goto Cleanup;
  2680. }
  2681. } else {
  2682. NlPrintDom((NL_CRITICAL, DomainInfo,
  2683. "NlpUserValidate: %wZ\\%wZ: Can't find trusted forest '%wZ'\n",
  2684. &LogonInfo->LogonDomainName,
  2685. &LogonInfo->UserName,
  2686. &RealDomainNameString ));
  2687. DefaultStatus = STATUS_NO_SUCH_USER;
  2688. goto Cleanup;
  2689. }
  2690. //
  2691. // Fill in the account name and domain name and try again
  2692. //
  2693. } else if ( NT_SUCCESS(Status) ) {
  2694. PNETLOGON_LOGON_IDENTITY_INFO NewLogonInfo = NULL;
  2695. PNETLOGON_LOGON_IDENTITY_INFO LogonInfoToUse;
  2696. //
  2697. // If we found the real account name,
  2698. // allocate a copy of the logon info to put the new information in.
  2699. //
  2700. // Some pointers will continue to point to the old buffer.
  2701. //
  2702. if ( RealDomainName != NULL && RealSamAccountName != NULL ) {
  2703. ULONG BytesToCopy;
  2704. switch ( LogonLevel ) {
  2705. case NetlogonInteractiveInformation:
  2706. case NetlogonInteractiveTransitiveInformation:
  2707. BytesToCopy = sizeof(NETLOGON_INTERACTIVE_INFO);break;
  2708. case NetlogonNetworkInformation:
  2709. case NetlogonNetworkTransitiveInformation:
  2710. BytesToCopy = sizeof(NETLOGON_NETWORK_INFO);break;
  2711. case NetlogonServiceInformation:
  2712. case NetlogonServiceTransitiveInformation:
  2713. BytesToCopy = sizeof(NETLOGON_SERVICE_INFO);break;
  2714. default:
  2715. *Authoritative = FALSE;
  2716. DefaultStatus = STATUS_INVALID_PARAMETER;
  2717. goto Cleanup;
  2718. }
  2719. NewLogonInfo = LocalAlloc( 0, BytesToCopy );
  2720. if ( NewLogonInfo == NULL ) {
  2721. *Authoritative = FALSE;
  2722. DefaultStatus = STATUS_INSUFFICIENT_RESOURCES;
  2723. goto Cleanup;
  2724. }
  2725. RtlCopyMemory( NewLogonInfo, LogonInfo, BytesToCopy );
  2726. LogonInfoToUse = NewLogonInfo;
  2727. //
  2728. // Put the newly found domain name and user name
  2729. // into the NewLogonInfo.
  2730. //
  2731. RtlInitUnicodeString( &NewLogonInfo->LogonDomainName,
  2732. RealDomainName );
  2733. RtlInitUnicodeString( &NewLogonInfo->UserName,
  2734. RealSamAccountName );
  2735. //
  2736. // Otherwise, continue with the current account name
  2737. //
  2738. } else {
  2739. LogonInfoToUse = LogonInfo;
  2740. }
  2741. //
  2742. // Call this routine again now that we have better information.
  2743. //
  2744. DefaultStatus = NlpUserValidate(
  2745. DomainInfo,
  2746. SecureChannelType,
  2747. LogonLevel,
  2748. (LPBYTE)LogonInfoToUse,
  2749. ValidationLevel,
  2750. ValidationInformation,
  2751. Authoritative,
  2752. &RealExtraFlags,
  2753. TRUE ); // A recursive call
  2754. if ( NewLogonInfo != NULL ) {
  2755. LocalFree( NewLogonInfo );
  2756. }
  2757. // Don't let this routine try again
  2758. AccountsToTry = 0;
  2759. goto Cleanup;
  2760. }
  2761. }
  2762. //
  2763. // If a trusted domain was determined,
  2764. // pass the logon request to the trusted domain.
  2765. //
  2766. if ( ClientSession != NULL ) {
  2767. //
  2768. // If this request was passed to us from a trusted domain,
  2769. // Check to see if it is OK to pass the request further.
  2770. //
  2771. if ( IsDomainSecureChannelType( SecureChannelType ) ) {
  2772. //
  2773. // If the trust isn't an NT 5.0 trust,
  2774. // avoid doing the trust transitively.
  2775. //
  2776. LOCK_TRUST_LIST( DomainInfo );
  2777. if ( (ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST ) == 0 ) {
  2778. UNLOCK_TRUST_LIST( DomainInfo );
  2779. NlPrintCs((NL_LOGON, ClientSession,
  2780. "SamLogon: Avoid transitive trust on NT 4 trust." ));
  2781. DefaultStatus = STATUS_NO_SUCH_USER;
  2782. goto Cleanup;
  2783. }
  2784. UNLOCK_TRUST_LIST( DomainInfo );
  2785. }
  2786. Status = NlpUserValidateHigher(
  2787. ClientSession,
  2788. TransitiveUsed,
  2789. LogonLevel,
  2790. LogonInformation,
  2791. ValidationLevel,
  2792. ValidationInformation,
  2793. Authoritative,
  2794. &RealExtraFlags );
  2795. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  2796. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  2797. //
  2798. // return more appropriate error
  2799. //
  2800. if( (Status == STATUS_NO_TRUST_LSA_SECRET) ||
  2801. (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
  2802. (Status == STATUS_ACCESS_DENIED) ) {
  2803. Status = STATUS_TRUSTED_DOMAIN_FAILURE;
  2804. }
  2805. //
  2806. // Since the request is explicitly for a trusted domain,
  2807. // The STATUS_NO_SUCH_USER answer is authoritative.
  2808. //
  2809. if ( Status == STATUS_NO_SUCH_USER ) {
  2810. *Authoritative = TRUE;
  2811. }
  2812. //
  2813. // If the trusted domain authoritatively handled the
  2814. // logon attempt, just return.
  2815. //
  2816. if ( *Authoritative ) {
  2817. DefaultStatus = Status;
  2818. goto Cleanup;
  2819. }
  2820. DefaultStatus = Status;
  2821. }
  2822. }
  2823. }
  2824. //
  2825. // We have no authoritative answer from a higher authority and
  2826. // DefaultStatus is the higher authority's response.
  2827. //
  2828. NlAssert( ! *Authoritative );
  2829. Cleanup:
  2830. NlAssert( !NT_SUCCESS(DefaultStatus) || DefaultStatus == STATUS_SUCCESS );
  2831. NlAssert( !NT_SUCCESS(DefaultStatus) || *ValidationInformation != NULL );
  2832. //
  2833. // Dereference any client session
  2834. //
  2835. if ( ClientSession != NULL ) {
  2836. NlUnrefClientSession( ClientSession );
  2837. ClientSession = NULL;
  2838. }
  2839. //
  2840. // If this is a network logon and this call is non-passthru,
  2841. // Try one last time to log on.
  2842. //
  2843. if ( (LogonLevel == NetlogonNetworkInformation ||
  2844. LogonLevel == NetlogonNetworkTransitiveInformation ) &&
  2845. SecureChannelType == MsvApSecureChannel ) {
  2846. //
  2847. // If the only reason we can't log the user on is that he has
  2848. // no user account, logging him on as guest is OK.
  2849. //
  2850. // There are actaully two cases here:
  2851. // * If the response is Authoritative, then the specified domain
  2852. // is trusted but the user has no account in the domain.
  2853. //
  2854. // * If the response in non-authoritative, then the specified domain
  2855. // is an untrusted domain.
  2856. //
  2857. // In either case, then right thing to do is to try the guest account.
  2858. //
  2859. if ( DefaultStatus != STATUS_NO_SUCH_USER &&
  2860. DefaultStatus != STATUS_ACCOUNT_DISABLED ) {
  2861. AccountsToTry &= ~MSVSAM_GUEST;
  2862. }
  2863. //
  2864. // If this is not an authoritative response,
  2865. // then the domain specified isn't a trusted domain.
  2866. // try the specified account name too.
  2867. //
  2868. // The specified account name will probably be a remote account
  2869. // with the same username and password.
  2870. //
  2871. if ( *Authoritative ) {
  2872. AccountsToTry &= ~MSVSAM_SPECIFIED;
  2873. }
  2874. //
  2875. // Validate against the Local Sam database.
  2876. //
  2877. if ( AccountsToTry != 0 ) {
  2878. BOOLEAN TempAuthoritative;
  2879. NETLOGON_LOGON_INFO_CLASS MsvLogonLevel;
  2880. //
  2881. // Don't confuse MSV with the transitive LogonLevels
  2882. //
  2883. switch (LogonLevel ) {
  2884. case NetlogonInteractiveTransitiveInformation:
  2885. MsvLogonLevel = NetlogonInteractiveInformation; break;
  2886. case NetlogonServiceTransitiveInformation:
  2887. MsvLogonLevel = NetlogonServiceInformation; break;
  2888. case NetlogonNetworkTransitiveInformation:
  2889. MsvLogonLevel = NetlogonNetworkInformation; break;
  2890. default:
  2891. MsvLogonLevel = LogonLevel; break;
  2892. }
  2893. Status = MsvSamValidate(
  2894. DomainInfo->DomSamAccountDomainHandle,
  2895. TRUE, // UasCompatibilityMode,
  2896. SecureChannelType,
  2897. &DomainInfo->DomUnicodeComputerNameString,
  2898. &DomainInfo->DomUnicodeAccountDomainNameString,
  2899. DomainInfo->DomAccountDomainId,
  2900. MsvLogonLevel,
  2901. LogonInformation,
  2902. ValidationLevel,
  2903. (PVOID *)ValidationInformation,
  2904. &TempAuthoritative,
  2905. &BadPasswordCountZeroed,
  2906. AccountsToTry );
  2907. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  2908. NlAssert( !NT_SUCCESS(Status) || *ValidationInformation != NULL );
  2909. //
  2910. // If this is a BDC and we zeroed the BadPasswordCount field,
  2911. // allow the PDC to do the same thing.
  2912. //
  2913. if ( BadPasswordCountZeroed ) {
  2914. NlpZeroBadPasswordCountOnPdc ( DomainInfo, LogonLevel, LogonInformation );
  2915. }
  2916. //
  2917. // If the local SAM database authoritatively handled the
  2918. // logon attempt,
  2919. // just return.
  2920. //
  2921. if ( TempAuthoritative ) {
  2922. DefaultStatus = Status;
  2923. *Authoritative = TRUE;
  2924. //
  2925. // If the problem is just that the password is wrong,
  2926. // try again on the PDC where the password may already be
  2927. // changed.
  2928. //
  2929. if ( BAD_PASSWORD(Status) ) {
  2930. Status = NlpUserValidateOnPdc (
  2931. DomainInfo,
  2932. LogonLevel,
  2933. LogonInformation,
  2934. ValidationLevel,
  2935. TRUE, // use negative cache of failed user logons
  2936. ValidationInformation,
  2937. &TempAuthoritative );
  2938. // Ignore failures from the PDC (except where it has newer information)
  2939. if ( NT_SUCCESS(Status) || BAD_PASSWORD(Status) ) {
  2940. DefaultStatus = Status;
  2941. *Authoritative = TempAuthoritative;
  2942. }
  2943. // On success, zero bad password locally on this BDC.
  2944. // Ignore error as it's not critical operation.
  2945. if ( NT_SUCCESS(Status) && !NlGlobalMemberWorkstation ) {
  2946. NlpZeroBadPasswordCountLocally( DomainInfo, &LogonInfo->UserName );
  2947. }
  2948. }
  2949. //
  2950. // Here we must choose between the non-authoritative status in
  2951. // DefaultStatus and the non-authoritative status from the local
  2952. // SAM lookup. Use the one from the higher authority unless it
  2953. // isn't interesting.
  2954. //
  2955. } else {
  2956. if ( DefaultStatus == STATUS_NO_SUCH_USER ) {
  2957. DefaultStatus = Status;
  2958. }
  2959. }
  2960. }
  2961. }
  2962. if ( RealSamAccountName != NULL ) {
  2963. NetApiBufferFree( RealSamAccountName );
  2964. }
  2965. if ( RealDomainName != NULL ) {
  2966. NetApiBufferFree( RealDomainName );
  2967. }
  2968. //
  2969. // Add in the resource groups if the caller is expecting them -
  2970. // if this is the last domain controller on the authentication path
  2971. // before returning to the machine being logged on to.
  2972. //
  2973. if (((SecureChannelType == WorkstationSecureChannel) ||
  2974. (SecureChannelType == MsvApSecureChannel)) &&
  2975. (SecureChannelType != UasServerSecureChannel) &&
  2976. (ValidationLevel == NetlogonValidationSamInfo2 || ValidationLevel == NetlogonValidationSamInfo4 ) &&
  2977. !NlGlobalMemberWorkstation &&
  2978. NT_SUCCESS(DefaultStatus)) {
  2979. Status = NlpExpandResourceGroupMembership(
  2980. ValidationLevel,
  2981. (PNETLOGON_VALIDATION_SAM_INFO4 *) ValidationInformation,
  2982. DomainInfo
  2983. );
  2984. if (!NT_SUCCESS(Status)) {
  2985. DefaultStatus = Status;
  2986. }
  2987. }
  2988. return DefaultStatus;
  2989. }
  2990. #if NETLOGONDBG
  2991. VOID
  2992. NlPrintLogonParameters(
  2993. IN PDOMAIN_INFO DomainInfo OPTIONAL,
  2994. IN LPWSTR ComputerName OPTIONAL,
  2995. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2996. IN PNETLOGON_LEVEL LogonInformation,
  2997. IN ULONG ExtraFlags,
  2998. IN PULONG NtStatusPointer OPTIONAL
  2999. )
  3000. /*++
  3001. Routine Description:
  3002. Prints the parameters to NlpLogonSamLogon.
  3003. Arguments:
  3004. Same as NlpLogonSamLogon except:
  3005. NtStatusPointer - If NULL, call is being entered.
  3006. If not NULL, points to the return status of the API.
  3007. Return Value:
  3008. None
  3009. --*/
  3010. {
  3011. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  3012. //
  3013. // Print the entire text on a single line
  3014. //
  3015. EnterCriticalSection( &NlGlobalLogFileCritSect );
  3016. //
  3017. // Print the common information
  3018. //
  3019. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation->LogonInteractive;
  3020. NlPrintDom(( NL_LOGON, DomainInfo,
  3021. "SamLogon: %s logon of %wZ\\%wZ from %wZ",
  3022. NlpLogonTypeToText( LogonLevel ),
  3023. &LogonInfo->LogonDomainName,
  3024. &LogonInfo->UserName,
  3025. &LogonInfo->Workstation ));
  3026. //
  3027. // Print the computer name
  3028. //
  3029. if ( ComputerName != NULL ) {
  3030. NlPrint(( NL_LOGON, " (via %ws%)", ComputerName ));
  3031. }
  3032. //
  3033. // Print the PackageName
  3034. //
  3035. if ( LogonLevel == NetlogonGenericInformation ) {
  3036. NlPrint(( NL_LOGON, " Package:%wZ", &LogonInformation->LogonGeneric->PackageName ));
  3037. }
  3038. //
  3039. // Print the ExtraFlags
  3040. //
  3041. if ( ExtraFlags != 0 ) {
  3042. NlPrint(( NL_LOGON, " ExFlags:%lx", ExtraFlags ));
  3043. }
  3044. //
  3045. // Print the status code
  3046. //
  3047. if ( NtStatusPointer == NULL ) {
  3048. NlPrint(( NL_LOGON, " Entered\n" ));
  3049. } else {
  3050. NlPrint(( NL_LOGON, " Returns 0x%lX\n", *NtStatusPointer ));
  3051. }
  3052. LeaveCriticalSection( &NlGlobalLogFileCritSect );
  3053. }
  3054. #else // NETLOGONDBG
  3055. #define NlPrintLogonParameters(_a, _b, _c, _d, _e, _f )
  3056. #endif // NETLOGONDBG
  3057. NTSTATUS
  3058. NlpLogonSamLogon (
  3059. IN handle_t ContextHandle OPTIONAL,
  3060. IN LPWSTR LogonServer OPTIONAL,
  3061. IN LPWSTR ComputerName OPTIONAL,
  3062. IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
  3063. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
  3064. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  3065. IN PNETLOGON_LEVEL LogonInformation,
  3066. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  3067. OUT PNETLOGON_VALIDATION ValidationInformation,
  3068. OUT PBOOLEAN Authoritative,
  3069. IN OUT PULONG ExtraFlags
  3070. )
  3071. /*++
  3072. Routine Description:
  3073. This function is called by an NT client to process an interactive or
  3074. network logon. This function passes a domain name, user name and
  3075. credentials to the Netlogon service and returns information needed to
  3076. build a token. It is called in three instances:
  3077. * It is called by the LSA's MSV1_0 authentication package for any
  3078. NT DC. The MSV1_0 authentication
  3079. package calls SAM directly on workstations. In this
  3080. case, this function is a local function and requires the caller
  3081. to have SE_TCB privilege. The local Netlogon service will
  3082. either handle this request directly (validating the request with
  3083. the local SAM database) or will forward this request to the
  3084. appropriate domain controller as documented in sections 2.4 and
  3085. 2.5.
  3086. * It is called by a Netlogon service on a workstation to a DC in
  3087. the Primary Domain of the workstation as documented in section
  3088. 2.4. In this case, this function uses a secure channel set up
  3089. between the two Netlogon services.
  3090. * It is called by a Netlogon service on a DC to a DC in a trusted
  3091. domain as documented in section 2.5. In this case, this
  3092. function uses a secure channel set up between the two Netlogon
  3093. services.
  3094. The Netlogon service validates the specified credentials. If they
  3095. are valid, adds an entry for this LogonId, UserName, and Workstation
  3096. into the logon session table. The entry is added to the logon
  3097. session table only in the domain defining the specified user's
  3098. account.
  3099. This service is also used to process a re-logon request.
  3100. Arguments:
  3101. LogonServer -- Supplies the name of the logon server to process
  3102. this logon request. This field should be null to indicate
  3103. this is a call from the MSV1_0 authentication package to the
  3104. local Netlogon service.
  3105. ComputerName -- Name of the machine making the call. This field
  3106. should be null to indicate this is a call from the MSV1_0
  3107. authentication package to the local Netlogon service.
  3108. Authenticator -- supplied by the client. This field should be
  3109. null to indicate this is a call from the MSV1_0
  3110. authentication package to the local Netlogon service.
  3111. ReturnAuthenticator -- Receives an authenticator returned by the
  3112. server. This field should be null to indicate this is a call
  3113. from the MSV1_0 authentication package to the local Netlogon
  3114. service.
  3115. LogonLevel -- Specifies the level of information given in
  3116. LogonInformation.
  3117. LogonInformation -- Specifies the description for the user
  3118. logging on.
  3119. ValidationLevel -- Specifies the level of information returned in
  3120. ValidationInformation. Must be NetlogonValidationSamInfo,
  3121. NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4.
  3122. ValidationInformation -- Returns the requested validation
  3123. information. This buffer must be freed using MIDL_user_free.
  3124. Authoritative -- Returns whether the status returned is an
  3125. authoritative status which should be returned to the original
  3126. caller. If not, this logon request may be tried again on another
  3127. domain controller. This parameter is returned regardless of the
  3128. status code.
  3129. ExtraFlags -- Accepts and returns a DWORD to the caller.
  3130. The DWORD contains NL_EXFLAGS_* values.
  3131. Return Value:
  3132. STATUS_SUCCESS: if there was no error.
  3133. STATUS_NO_LOGON_SERVERS -- no domain controller in the requested
  3134. domain is currently available to validate the logon request.
  3135. STATUS_NO_TRUST_LSA_SECRET -- there is no secret account in the
  3136. local LSA database to establish a secure channel to a DC.
  3137. STATUS_TRUSTED_DOMAIN_FAILURE -- the secure channel setup between
  3138. the domain controllers of the trust domains to pass-through
  3139. validate the logon request failed.
  3140. STATUS_TRUSTED_RELATIONSHIP_FAILURE -- the secure channel setup
  3141. between the workstation and the DC failed.
  3142. STATUS_INVALID_INFO_CLASS -- Either LogonLevel or ValidationLevel is
  3143. invalid.
  3144. STATUS_INVALID_PARAMETER -- Another Parameter is invalid.
  3145. STATUS_ACCESS_DENIED -- The caller does not have access to call this
  3146. API.
  3147. STATUS_NO_SUCH_USER -- Indicates that the user specified in
  3148. LogonInformation does not exist. This status should not be returned
  3149. to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
  3150. STATUS_WRONG_PASSWORD -- Indicates that the password information in
  3151. LogonInformation was incorrect. This status should not be returned
  3152. to the originally caller. It should be mapped to STATUS_LOGON_FAILURE.
  3153. STATUS_INVALID_LOGON_HOURES -- The user is not authorized to logon
  3154. at this time.
  3155. STATUS_INVALID_WORKSTATION -- The user is not authorized to logon
  3156. from the specified workstation.
  3157. STATUS_PASSWORD_EXPIRED -- The password for the user has expired.
  3158. STATUS_ACCOUNT_DISABLED -- The user's account has been disabled.
  3159. .
  3160. .
  3161. .
  3162. --*/
  3163. {
  3164. NTSTATUS Status;
  3165. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  3166. PDOMAIN_INFO DomainInfo = NULL;
  3167. PSERVER_SESSION ServerSession;
  3168. NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
  3169. SESSION_INFO SessionInfo;
  3170. NETLOGON_LOGON_INFO_CLASS OrigLogonLevel = LogonLevel;
  3171. //
  3172. // Initialization
  3173. //
  3174. *Authoritative = TRUE;
  3175. ValidationInformation->ValidationSam = NULL;
  3176. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
  3177. LogonInformation->LogonInteractive;
  3178. //
  3179. // If caller is calling when the netlogon service isn't running,
  3180. // tell it so.
  3181. //
  3182. if ( !NlStartNetlogonCall() ) {
  3183. return STATUS_NETLOGON_NOT_STARTED;
  3184. }
  3185. //
  3186. // Check if LogonInfo is valid. It should be, otherwise it is
  3187. // an unappropriate use of this function.
  3188. //
  3189. if ( LogonInfo == NULL ) {
  3190. return STATUS_INVALID_PARAMETER;
  3191. }
  3192. //
  3193. // Lookup which domain this call pertains to.
  3194. //
  3195. DomainInfo = NlFindDomainByServerName( LogonServer );
  3196. IF_NL_DEBUG( LOGON ) {
  3197. NlPrintLogonParameters( DomainInfo, ComputerName, OrigLogonLevel, LogonInformation, *ExtraFlags, NULL );
  3198. }
  3199. if ( DomainInfo == NULL ) {
  3200. Status = STATUS_INVALID_COMPUTER_NAME;
  3201. goto Cleanup;
  3202. }
  3203. //
  3204. // Check the ValidationLevel
  3205. //
  3206. switch (ValidationLevel) {
  3207. case NetlogonValidationSamInfo:
  3208. case NetlogonValidationSamInfo2:
  3209. case NetlogonValidationSamInfo4:
  3210. case NetlogonValidationGenericInfo:
  3211. case NetlogonValidationGenericInfo2:
  3212. break;
  3213. default:
  3214. *Authoritative = TRUE;
  3215. Status = STATUS_INVALID_INFO_CLASS;
  3216. goto Cleanup;
  3217. }
  3218. //
  3219. // Check the LogonLevel
  3220. //
  3221. switch ( LogonLevel ) {
  3222. case NetlogonInteractiveInformation:
  3223. case NetlogonInteractiveTransitiveInformation:
  3224. case NetlogonNetworkInformation:
  3225. case NetlogonNetworkTransitiveInformation:
  3226. case NetlogonServiceInformation:
  3227. case NetlogonServiceTransitiveInformation:
  3228. //
  3229. // Check that the ValidationLevel is consistent with the LogonLevel
  3230. //
  3231. switch (ValidationLevel) {
  3232. case NetlogonValidationSamInfo:
  3233. case NetlogonValidationSamInfo2:
  3234. case NetlogonValidationSamInfo4:
  3235. break;
  3236. default:
  3237. *Authoritative = TRUE;
  3238. Status = STATUS_INVALID_INFO_CLASS;
  3239. goto Cleanup;
  3240. }
  3241. break;
  3242. case NetlogonGenericInformation:
  3243. //
  3244. // Check that the ValidationLevel is consistent with the LogonLevel
  3245. //
  3246. switch (ValidationLevel) {
  3247. case NetlogonValidationGenericInfo:
  3248. case NetlogonValidationGenericInfo2:
  3249. break;
  3250. default:
  3251. *Authoritative = TRUE;
  3252. Status = STATUS_INVALID_INFO_CLASS;
  3253. goto Cleanup;
  3254. }
  3255. break;
  3256. default:
  3257. *Authoritative = TRUE;
  3258. Status = STATUS_INVALID_INFO_CLASS;
  3259. goto Cleanup;
  3260. }
  3261. //
  3262. // If we're being called from the MSV Authentication Package,
  3263. // require SE_TCB privilege.
  3264. //
  3265. if ( ContextHandle == NULL &&
  3266. LogonServer == NULL &&
  3267. ComputerName == NULL &&
  3268. Authenticator == NULL &&
  3269. ReturnAuthenticator == NULL ) {
  3270. //
  3271. // ??: Do as I said
  3272. //
  3273. SecureChannelType = MsvApSecureChannel;
  3274. SessionInfo.NegotiatedFlags = NETLOGON_SUPPORTS_MASK;
  3275. //
  3276. //
  3277. //
  3278. // If we're being called from another Netlogon Server,
  3279. // Verify the secure channel information.
  3280. //
  3281. } else {
  3282. //
  3283. // This API is not supported on workstations.
  3284. //
  3285. if ( NlGlobalMemberWorkstation ) {
  3286. Status = STATUS_NOT_SUPPORTED;
  3287. goto Cleanup;
  3288. }
  3289. //
  3290. // Arguments are no longer optional.
  3291. //
  3292. // Either the authenticators must be present or the Context handle must be.
  3293. //
  3294. if ( LogonServer == NULL ||
  3295. ComputerName == NULL ||
  3296. (( Authenticator == NULL || ReturnAuthenticator == NULL ) &&
  3297. ContextHandle == NULL ) ) {
  3298. *Authoritative = TRUE;
  3299. Status = STATUS_INVALID_PARAMETER;
  3300. goto Cleanup;
  3301. }
  3302. //
  3303. // Find the server session entry for this session.
  3304. //
  3305. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  3306. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  3307. if (ServerSession == NULL) {
  3308. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3309. *Authoritative = FALSE;
  3310. Status = STATUS_ACCESS_DENIED;
  3311. goto Cleanup;
  3312. }
  3313. //
  3314. // now verify the Authenticator and update seed if OK
  3315. //
  3316. if ( Authenticator != NULL ) {
  3317. Status = NlCheckAuthenticator( ServerSession,
  3318. Authenticator,
  3319. ReturnAuthenticator);
  3320. if ( !NT_SUCCESS(Status) ) {
  3321. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3322. *Authoritative = FALSE;
  3323. goto Cleanup;
  3324. }
  3325. //
  3326. // If no authenticator,
  3327. // ensure the caller used secure RPC.
  3328. //
  3329. } else {
  3330. NET_API_STATUS NetStatus;
  3331. RPC_AUTHZ_HANDLE RpcPrivs;
  3332. ULONG AuthnLevel;
  3333. ULONG AuthnSvc;
  3334. //
  3335. // Determine the client binding info.
  3336. //
  3337. NetStatus = RpcBindingInqAuthClient(
  3338. ContextHandle,
  3339. &RpcPrivs,
  3340. NULL, // SPN not needed
  3341. &AuthnLevel,
  3342. &AuthnSvc,
  3343. NULL );
  3344. if ( NetStatus != NO_ERROR ) {
  3345. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3346. *Authoritative = FALSE;
  3347. NlPrintDom((NL_CRITICAL, DomainInfo,
  3348. "SamLogon: %s logon of %wZ\\%wZ from %wZ: Cannot RpcBindingInqAuthClient %ld\n",
  3349. NlpLogonTypeToText( OrigLogonLevel ),
  3350. &LogonInfo->LogonDomainName,
  3351. &LogonInfo->UserName,
  3352. &LogonInfo->Workstation,
  3353. NetStatus ));
  3354. Status = NetpApiStatusToNtStatus( NetStatus );
  3355. goto Cleanup;
  3356. }
  3357. //
  3358. // Ensure we're using the netlogon SSPI and
  3359. // are signing or sealing.
  3360. //
  3361. if ( AuthnSvc != RPC_C_AUTHN_NETLOGON ) {
  3362. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3363. NlPrintDom((NL_CRITICAL, DomainInfo,
  3364. "SamLogon: %s logon of %wZ\\%wZ from %wZ: Not using Netlogon SSPI: %ld\n",
  3365. NlpLogonTypeToText( OrigLogonLevel ),
  3366. &LogonInfo->LogonDomainName,
  3367. &LogonInfo->UserName,
  3368. &LogonInfo->Workstation,
  3369. AuthnSvc ));
  3370. Status = STATUS_ACCESS_DENIED;
  3371. goto Cleanup;
  3372. }
  3373. if ( AuthnLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY &&
  3374. AuthnLevel != RPC_C_AUTHN_LEVEL_PKT_INTEGRITY ) {
  3375. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3376. NlPrintDom((NL_CRITICAL, DomainInfo,
  3377. "SamLogon: %s logon of %wZ\\%wZ from %wZ: Not signing or sealing: %ld\n",
  3378. NlpLogonTypeToText( OrigLogonLevel ),
  3379. &LogonInfo->LogonDomainName,
  3380. &LogonInfo->UserName,
  3381. &LogonInfo->Workstation,
  3382. AuthnLevel ));
  3383. Status = STATUS_ACCESS_DENIED;
  3384. goto Cleanup;
  3385. }
  3386. }
  3387. SecureChannelType = ServerSession->SsSecureChannelType;
  3388. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  3389. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  3390. //
  3391. // The cross forest hop bit is only valid if this TDO on FOREST_TRANSITIVE trusts
  3392. //
  3393. if ( ((*ExtraFlags) & NL_EXFLAGS_CROSS_FOREST_HOP) != 0 &&
  3394. (ServerSession->SsFlags & SS_FOREST_TRANSITIVE) == 0 ) {
  3395. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3396. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  3397. "NlpLogonSamLogon: %ws failed because F bit isn't set on the TDO\n",
  3398. ComputerName ));
  3399. *Authoritative = TRUE;
  3400. Status = STATUS_NO_SUCH_USER;
  3401. goto Cleanup;
  3402. }
  3403. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3404. //
  3405. // Decrypt the password information
  3406. //
  3407. NlpDecryptLogonInformation ( LogonLevel, (LPBYTE) LogonInfo, &SessionInfo );
  3408. }
  3409. #ifdef _DC_NETLOGON
  3410. //
  3411. // If the logon service is paused then don't process this logon
  3412. // request any further.
  3413. //
  3414. if ( NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED ||
  3415. !NlGlobalParameters.SysVolReady ||
  3416. NlGlobalDsPaused ) {
  3417. //
  3418. // Don't reject logons originating inside this
  3419. // machine. Such requests aren't really pass-thru requests.
  3420. //
  3421. // Don't reject logons from a BDC in our own domain. These logons
  3422. // support account lockout and authentication of users whose password
  3423. // has been updated on the PDC but not the BDC. Such pass-thru
  3424. // requests can only be handled by the PDC of the domain.
  3425. //
  3426. if ( SecureChannelType != MsvApSecureChannel &&
  3427. SecureChannelType != ServerSecureChannel ) {
  3428. //
  3429. // Return STATUS_ACCESS_DENIED to convince the caller to drop the
  3430. // secure channel to this logon server and reconnect to some other
  3431. // logon server.
  3432. //
  3433. *Authoritative = FALSE;
  3434. Status = STATUS_ACCESS_DENIED;
  3435. goto Cleanup;
  3436. }
  3437. }
  3438. #endif // _DC_NETLOGON
  3439. //
  3440. // If this is a workstation or MSV secure channel,
  3441. // and the caller isn't NT 4 in a mixed mode domain
  3442. // ask the DC to do transitive trust.
  3443. //
  3444. // For NT 4 in mixed mode, avoid transitive trust since that's what they'd
  3445. // get if they'd stumbled upon an NT 4 BDC.
  3446. // For NT 4 in native mode, give NT 4 the full capability.
  3447. // For NT 5 in mixed mode, we "prefer" an NT 5 DC so do transitive trust to
  3448. // be as compatible with kerberos as possible.
  3449. //
  3450. if ( (SecureChannelType == MsvApSecureChannel || SecureChannelType == WorkstationSecureChannel) &&
  3451. !((SessionInfo.NegotiatedFlags & ~NETLOGON_SUPPORTS_NT4_MASK) == 0 &&
  3452. SamIMixedDomain( DomainInfo->DomSamServerHandle ) ) ) {
  3453. switch (LogonLevel ) {
  3454. case NetlogonInteractiveInformation:
  3455. LogonLevel = NetlogonInteractiveTransitiveInformation; break;
  3456. case NetlogonServiceInformation:
  3457. LogonLevel = NetlogonServiceTransitiveInformation; break;
  3458. case NetlogonNetworkInformation:
  3459. LogonLevel = NetlogonNetworkTransitiveInformation; break;
  3460. }
  3461. }
  3462. //
  3463. // Validate the Request.
  3464. //
  3465. Status = NlpUserValidate( DomainInfo,
  3466. SecureChannelType,
  3467. LogonLevel,
  3468. (LPBYTE) LogonInfo,
  3469. ValidationLevel,
  3470. (LPBYTE *)&ValidationInformation->ValidationSam,
  3471. Authoritative,
  3472. ExtraFlags,
  3473. FALSE ); // Not a recursive call
  3474. if ( !NT_SUCCESS(Status) ) {
  3475. //
  3476. // If this is an NT 3.1 client,
  3477. // map NT 3.5 status codes to their NT 3.1 equivalents.
  3478. //
  3479. // The NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT bit is really the wrong bit
  3480. // to be using, but all NT3.5 clients have it set and all NT3.1 clients
  3481. // don't, so it'll work for our purposes.
  3482. //
  3483. if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_ACCOUNT_LOCKOUT) == 0 ) {
  3484. switch ( Status ) {
  3485. case STATUS_PASSWORD_MUST_CHANGE:
  3486. Status = STATUS_PASSWORD_EXPIRED;
  3487. break;
  3488. case STATUS_ACCOUNT_LOCKED_OUT:
  3489. Status = STATUS_ACCOUNT_DISABLED;
  3490. break;
  3491. }
  3492. }
  3493. goto Cleanup;
  3494. }
  3495. NlAssert( !NT_SUCCESS(Status) || Status == STATUS_SUCCESS );
  3496. NlAssert( !NT_SUCCESS(Status) || ValidationInformation->ValidationSam != NULL );
  3497. //
  3498. // If the validation information is being returned to a client on another
  3499. // machine, encrypt it before sending it over the wire.
  3500. //
  3501. if ( SecureChannelType != MsvApSecureChannel ) {
  3502. NlpEncryptValidationInformation (
  3503. LogonLevel,
  3504. ValidationLevel,
  3505. *((LPBYTE *) ValidationInformation),
  3506. &SessionInfo );
  3507. }
  3508. Status = STATUS_SUCCESS;
  3509. //
  3510. // Cleanup up before returning.
  3511. //
  3512. Cleanup:
  3513. if ( !NT_SUCCESS(Status) ) {
  3514. if (ValidationInformation->ValidationSam != NULL) {
  3515. MIDL_user_free( ValidationInformation->ValidationSam );
  3516. ValidationInformation->ValidationSam = NULL;
  3517. }
  3518. }
  3519. IF_NL_DEBUG( LOGON ) {
  3520. NlPrintLogonParameters( DomainInfo, ComputerName, OrigLogonLevel, LogonInformation, *ExtraFlags, &Status );
  3521. }
  3522. if ( DomainInfo != NULL ) {
  3523. NlDereferenceDomain( DomainInfo );
  3524. }
  3525. //
  3526. // Indicate that the calling thread has left netlogon.dll
  3527. //
  3528. NlEndNetlogonCall();
  3529. return Status;
  3530. }
  3531. NTSTATUS
  3532. NetrLogonSamLogon (
  3533. IN LPWSTR LogonServer OPTIONAL,
  3534. IN LPWSTR ComputerName OPTIONAL,
  3535. IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
  3536. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
  3537. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  3538. IN PNETLOGON_LEVEL LogonInformation,
  3539. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  3540. OUT PNETLOGON_VALIDATION ValidationInformation,
  3541. OUT PBOOLEAN Authoritative
  3542. )
  3543. /*++
  3544. Routine Description:
  3545. Non_concurrent implementation of NTLM passthrough logon API.
  3546. Arguments:
  3547. See NlpLogonSamLogon.
  3548. Return Value:
  3549. See NlpLogonSamLogon.
  3550. --*/
  3551. {
  3552. ULONG ExtraFlags = 0;
  3553. return NlpLogonSamLogon( NULL, // No ContextHandle,
  3554. LogonServer,
  3555. ComputerName,
  3556. Authenticator,
  3557. ReturnAuthenticator,
  3558. LogonLevel,
  3559. LogonInformation,
  3560. ValidationLevel,
  3561. ValidationInformation,
  3562. Authoritative,
  3563. &ExtraFlags );
  3564. }
  3565. NTSTATUS
  3566. NetrLogonSamLogonWithFlags (
  3567. IN LPWSTR LogonServer OPTIONAL,
  3568. IN LPWSTR ComputerName OPTIONAL,
  3569. IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
  3570. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
  3571. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  3572. IN PNETLOGON_LEVEL LogonInformation,
  3573. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  3574. OUT PNETLOGON_VALIDATION ValidationInformation,
  3575. OUT PBOOLEAN Authoritative,
  3576. IN OUT PULONG ExtraFlags
  3577. )
  3578. /*++
  3579. Routine Description:
  3580. Non_concurrent implementation of NTLM passthrough logon API (with flags)
  3581. Arguments:
  3582. See NlpLogonSamLogon.
  3583. Return Value:
  3584. See NlpLogonSamLogon.
  3585. --*/
  3586. {
  3587. return NlpLogonSamLogon( NULL, // No ContextHandle,
  3588. LogonServer,
  3589. ComputerName,
  3590. Authenticator,
  3591. ReturnAuthenticator,
  3592. LogonLevel,
  3593. LogonInformation,
  3594. ValidationLevel,
  3595. ValidationInformation,
  3596. Authoritative,
  3597. ExtraFlags );
  3598. }
  3599. NTSTATUS
  3600. NetrLogonSamLogonEx (
  3601. IN handle_t ContextHandle,
  3602. IN LPWSTR LogonServer OPTIONAL,
  3603. IN LPWSTR ComputerName OPTIONAL,
  3604. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  3605. IN PNETLOGON_LEVEL LogonInformation,
  3606. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  3607. OUT PNETLOGON_VALIDATION ValidationInformation,
  3608. OUT PBOOLEAN Authoritative,
  3609. IN OUT PULONG ExtraFlags
  3610. )
  3611. /*++
  3612. Routine Description:
  3613. Concurrent implementation of NTLM passthrough logon API.
  3614. Arguments:
  3615. See NlpLogonSamLogon.
  3616. Return Value:
  3617. See NlpLogonSamLogon.
  3618. --*/
  3619. {
  3620. //
  3621. // Sanity check to ensure we don't lead the common routine astray.
  3622. //
  3623. if ( ContextHandle == NULL ) {
  3624. return STATUS_ACCESS_DENIED;
  3625. }
  3626. return NlpLogonSamLogon( ContextHandle,
  3627. LogonServer,
  3628. ComputerName,
  3629. NULL, // Authenticator
  3630. NULL, // ReturnAuthenticator
  3631. LogonLevel,
  3632. LogonInformation,
  3633. ValidationLevel,
  3634. ValidationInformation,
  3635. Authoritative,
  3636. ExtraFlags );
  3637. }
  3638. NTSTATUS
  3639. NetrLogonSamLogoff (
  3640. IN LPWSTR LogonServer OPTIONAL,
  3641. IN LPWSTR ComputerName OPTIONAL,
  3642. IN PNETLOGON_AUTHENTICATOR Authenticator OPTIONAL,
  3643. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator OPTIONAL,
  3644. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  3645. IN PNETLOGON_LEVEL LogonInformation
  3646. )
  3647. /*++
  3648. Routine Description:
  3649. This function is called by an NT client to process an interactive
  3650. logoff. It is not called for the network logoff case since the
  3651. Netlogon service does not maintain any context for network logons.
  3652. This function does the following. It authenticates the request. It
  3653. updates the logon statistics in the SAM database on whichever machine
  3654. or domain defines this user account. It updates the logon session
  3655. table in the primary domain of the machine making the request. And
  3656. it returns logoff information to the caller.
  3657. This function is called in same scenarios that I_NetLogonSamLogon is
  3658. called:
  3659. * It is called by the LSA's MSV1_0 authentication package to
  3660. support LsaApLogonTerminated. In this case, this function is a
  3661. local function and requires the caller to have SE_TCB privilege.
  3662. The local Netlogon service will either handle this request
  3663. directly (if LogonDomainName indicates this request was
  3664. validated locally) or will forward this request to the
  3665. appropriate domain controller as documented in sections 2.4 and
  3666. 2.5.
  3667. * It is called by a Netlogon service on a workstation to a DC in
  3668. the Primary Domain of the workstation as documented in section
  3669. 2.4. In this case, this function uses a secure channel set up
  3670. between the two Netlogon services.
  3671. * It is called by a Netlogon service on a DC to a DC in a trusted
  3672. domain as documented in section 2.5. In this case, this
  3673. function uses a secure channel set up between the two Netlogon
  3674. services.
  3675. When this function is a remote function, it is sent to the DC over a
  3676. NULL session.
  3677. Arguments:
  3678. LogonServer -- Supplies the name of the logon server which logged
  3679. this user on. This field should be null to indicate this is
  3680. a call from the MSV1_0 authentication package to the local
  3681. Netlogon service.
  3682. ComputerName -- Name of the machine making the call. This field
  3683. should be null to indicate this is a call from the MSV1_0
  3684. authentication package to the local Netlogon service.
  3685. Authenticator -- supplied by the client. This field should be
  3686. null to indicate this is a call from the MSV1_0
  3687. authentication package to the local Netlogon service.
  3688. ReturnAuthenticator -- Receives an authenticator returned by the
  3689. server. This field should be null to indicate this is a call
  3690. from the MSV1_0 authentication package to the local Netlogon
  3691. service.
  3692. LogonLevel -- Specifies the level of information given in
  3693. LogonInformation.
  3694. LogonInformation -- Specifies the logon domain name, logon Id,
  3695. user name and workstation name of the user logging off.
  3696. Return Value:
  3697. --*/
  3698. {
  3699. NTSTATUS Status;
  3700. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  3701. PDOMAIN_INFO DomainInfo = NULL;
  3702. #ifdef _DC_NETLOGON
  3703. PSERVER_SESSION ServerSession;
  3704. #endif // _DC_NETLOGON
  3705. NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
  3706. PCLIENT_SESSION ClientSession;
  3707. //
  3708. // Initialization
  3709. //
  3710. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO)
  3711. LogonInformation->LogonInteractive;
  3712. //
  3713. // Check if LogonInfo is valid. It should be, otherwise it is
  3714. // an unappropriate use of this function.
  3715. //
  3716. if ( LogonInfo == NULL ) {
  3717. return STATUS_INVALID_PARAMETER;
  3718. }
  3719. //
  3720. // If caller is calling when the netlogon service isn't running,
  3721. // tell it so.
  3722. //
  3723. if ( !NlStartNetlogonCall() ) {
  3724. return STATUS_NETLOGON_NOT_STARTED;
  3725. }
  3726. //
  3727. // Lookup which domain this call pertains to.
  3728. //
  3729. DomainInfo = NlFindDomainByServerName( LogonServer );
  3730. #if NETLOGONDBG
  3731. NlPrintDom((NL_LOGON, DomainInfo,
  3732. "NetrLogonSamLogoff: %s logoff of %wZ\\%wZ from %wZ Entered\n",
  3733. NlpLogonTypeToText( LogonLevel ),
  3734. &LogonInfo->LogonDomainName,
  3735. &LogonInfo->UserName,
  3736. &LogonInfo->Workstation ));
  3737. #endif // NETLOGONDBG
  3738. if ( DomainInfo == NULL ) {
  3739. Status = STATUS_INVALID_COMPUTER_NAME;
  3740. goto Cleanup;
  3741. }
  3742. //
  3743. // Check the LogonLevel
  3744. //
  3745. if ( LogonLevel != NetlogonInteractiveInformation ) {
  3746. Status = STATUS_INVALID_INFO_CLASS;
  3747. goto Cleanup;
  3748. }
  3749. //
  3750. // Sanity check the username and domain name.
  3751. //
  3752. if ( LogonInfo->UserName.Length == 0 ||
  3753. LogonInfo->UserName.Buffer == NULL ||
  3754. LogonInfo->LogonDomainName.Length == 0 ||
  3755. LogonInfo->LogonDomainName.Buffer == NULL ) {
  3756. Status = STATUS_INVALID_PARAMETER;
  3757. goto Cleanup;
  3758. }
  3759. //
  3760. // If we've been called from the local msv1_0,
  3761. // special case the secure channel type.
  3762. //
  3763. if ( LogonServer == NULL &&
  3764. ComputerName == NULL &&
  3765. Authenticator == NULL &&
  3766. ReturnAuthenticator == NULL ) {
  3767. SecureChannelType = MsvApSecureChannel;
  3768. //
  3769. // If we're being called from another Netlogon Server,
  3770. // Verify the secure channel information.
  3771. //
  3772. } else {
  3773. //
  3774. // This API is not supported on workstations.
  3775. //
  3776. if ( NlGlobalMemberWorkstation ) {
  3777. Status = STATUS_NOT_SUPPORTED;
  3778. goto Cleanup;
  3779. }
  3780. //
  3781. // Arguments are no longer optional.
  3782. //
  3783. if ( LogonServer == NULL ||
  3784. ComputerName == NULL ||
  3785. Authenticator == NULL ||
  3786. ReturnAuthenticator == NULL ) {
  3787. Status = STATUS_INVALID_PARAMETER;
  3788. goto Cleanup;
  3789. }
  3790. //
  3791. // Find the server session entry for this secure channel.
  3792. //
  3793. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  3794. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  3795. if (ServerSession == NULL) {
  3796. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3797. Status = STATUS_ACCESS_DENIED;
  3798. goto Cleanup;
  3799. }
  3800. //
  3801. // Now verify the Authenticator and update seed if OK
  3802. //
  3803. Status = NlCheckAuthenticator(
  3804. ServerSession,
  3805. Authenticator,
  3806. ReturnAuthenticator);
  3807. if ( !NT_SUCCESS(Status) ) {
  3808. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3809. goto Cleanup;
  3810. }
  3811. SecureChannelType = ServerSession->SsSecureChannelType;
  3812. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3813. }
  3814. //
  3815. // If this is the domain that logged this user on,
  3816. // update the logon statistics.
  3817. //
  3818. if ( RtlEqualDomainName( &LogonInfo->LogonDomainName,
  3819. &DomainInfo->DomUnicodeAccountDomainNameString ) ) {
  3820. Status = MsvSamLogoff(
  3821. DomainInfo->DomSamAccountDomainHandle,
  3822. LogonLevel,
  3823. LogonInfo );
  3824. if ( !NT_SUCCESS(Status) ) {
  3825. goto Cleanup;
  3826. }
  3827. //
  3828. // If this is not the domain that logged this user on,
  3829. // pass the request to a higher authority.
  3830. //
  3831. } else {
  3832. //
  3833. // If this machine is a workstation,
  3834. // send the request to the Primary Domain.
  3835. //
  3836. if ( NlGlobalMemberWorkstation ) {
  3837. ClientSession = NlRefDomClientSession( DomainInfo );
  3838. if ( ClientSession == NULL ) {
  3839. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  3840. goto Cleanup;
  3841. }
  3842. Status = NlpUserLogoffHigher(
  3843. ClientSession,
  3844. LogonLevel,
  3845. (LPBYTE) LogonInfo );
  3846. NlUnrefClientSession( ClientSession );
  3847. //
  3848. // return more appropriate error
  3849. //
  3850. if( (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
  3851. (Status == STATUS_ACCESS_DENIED) ) {
  3852. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  3853. }
  3854. goto Cleanup;
  3855. //
  3856. // This machine is a Domain Controller.
  3857. //
  3858. // This request is either a pass-thru request by a workstation in
  3859. // our domain, or this request came directly from the MSV
  3860. // authentication package.
  3861. //
  3862. // In either case, pass the request to the trusted domain.
  3863. //
  3864. } else {
  3865. BOOLEAN TransitiveUsed;
  3866. //
  3867. // Send the request to the appropriate Trusted Domain.
  3868. //
  3869. // Find the ClientSession structure for the domain.
  3870. //
  3871. ClientSession =
  3872. NlFindNamedClientSession( DomainInfo,
  3873. &LogonInfo->LogonDomainName,
  3874. NL_RETURN_CLOSEST_HOP,
  3875. &TransitiveUsed );
  3876. if ( ClientSession == NULL ) {
  3877. Status = STATUS_NO_SUCH_DOMAIN;
  3878. goto Cleanup;
  3879. }
  3880. //
  3881. // If this request was passed to us from a trusted domain,
  3882. // Check to see if it is OK to pass the request further.
  3883. //
  3884. if ( IsDomainSecureChannelType( SecureChannelType ) ) {
  3885. //
  3886. // If the trust isn't an NT 5.0 trust,
  3887. // avoid doing the trust transitively.
  3888. //
  3889. LOCK_TRUST_LIST( DomainInfo );
  3890. if ( (ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST ) == 0 ) {
  3891. UNLOCK_TRUST_LIST( DomainInfo );
  3892. NlPrintCs((NL_LOGON, ClientSession,
  3893. "SamLogoff: Avoid transitive trust on NT 4 trust." ));
  3894. NlUnrefClientSession( ClientSession );
  3895. Status = STATUS_NO_SUCH_USER;
  3896. goto Cleanup;
  3897. }
  3898. UNLOCK_TRUST_LIST( DomainInfo );
  3899. }
  3900. Status = NlpUserLogoffHigher(
  3901. ClientSession,
  3902. LogonLevel,
  3903. (LPBYTE) LogonInfo );
  3904. NlUnrefClientSession( ClientSession );
  3905. //
  3906. // return more appropriate error
  3907. //
  3908. if( (Status == STATUS_NO_TRUST_LSA_SECRET) ||
  3909. (Status == STATUS_NO_TRUST_SAM_ACCOUNT) ||
  3910. (Status == STATUS_ACCESS_DENIED) ) {
  3911. Status = STATUS_TRUSTED_DOMAIN_FAILURE;
  3912. }
  3913. }
  3914. }
  3915. Cleanup:
  3916. //
  3917. // If the request failed, be carefull to not leak authentication
  3918. // information.
  3919. //
  3920. if ( Status == STATUS_ACCESS_DENIED ) {
  3921. if ( ReturnAuthenticator != NULL ) {
  3922. RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  3923. }
  3924. }
  3925. #if NETLOGONDBG
  3926. NlPrintDom((NL_LOGON, DomainInfo,
  3927. "NetrLogonSamLogoff: %s logoff of %wZ\\%wZ from %wZ returns %lX\n",
  3928. NlpLogonTypeToText( LogonLevel ),
  3929. &LogonInfo->LogonDomainName,
  3930. &LogonInfo->UserName,
  3931. &LogonInfo->Workstation,
  3932. Status ));
  3933. #endif // NETLOGONDBG
  3934. if ( DomainInfo != NULL ) {
  3935. NlDereferenceDomain( DomainInfo );
  3936. }
  3937. //
  3938. // Indicate that the calling thread has left netlogon.dll
  3939. //
  3940. NlEndNetlogonCall();
  3941. return Status;
  3942. }
  3943. NTSTATUS NET_API_FUNCTION
  3944. NetrLogonSendToSam (
  3945. IN LPWSTR PrimaryName OPTIONAL,
  3946. IN LPWSTR ComputerName OPTIONAL,
  3947. IN PNETLOGON_AUTHENTICATOR Authenticator,
  3948. OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
  3949. IN LPBYTE OpaqueBuffer,
  3950. IN ULONG OpaqueBufferSize
  3951. )
  3952. /*++
  3953. Routine Description:
  3954. This function sends an opaque buffer from SAM on a BDC to SAM on the PDC.
  3955. The original use of this routine will be to allow the BDC to forward user
  3956. account password changes to the PDC.
  3957. Arguments:
  3958. PrimaryName -- Computer name of the PDC to remote the call to.
  3959. ComputerName -- Name of the machine making the call.
  3960. Authenticator -- supplied by the client.
  3961. ReturnAuthenticator -- Receives an authenticator returned by the
  3962. server.
  3963. OpaqueBuffer - Buffer to be passed to the SAM service on the PDC.
  3964. The buffer will be encrypted on the wire.
  3965. OpaqueBufferSize - Size (in bytes) of OpaqueBuffer.
  3966. Return Value:
  3967. STATUS_SUCCESS: Message successfully sent to PDC
  3968. STATUS_NO_MEMORY: There is not enough memory to complete the operation
  3969. STATUS_NO_SUCH_DOMAIN: DomainName does not correspond to a hosted domain
  3970. STATUS_NO_LOGON_SERVERS: PDC is not currently available
  3971. STATUS_NOT_SUPPORTED: PDC does not support this operation
  3972. --*/
  3973. {
  3974. NTSTATUS Status;
  3975. PDOMAIN_INFO DomainInfo = NULL;
  3976. PSERVER_SESSION ServerSession;
  3977. SESSION_INFO SessionInfo;
  3978. //
  3979. // This API is not supported on workstations.
  3980. //
  3981. if ( NlGlobalMemberWorkstation ) {
  3982. return STATUS_NOT_SUPPORTED;
  3983. }
  3984. //
  3985. // Lookup which domain this call pertains to.
  3986. //
  3987. DomainInfo = NlFindDomainByServerName( PrimaryName );
  3988. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  3989. "NetrLogonSendToSam: %ws: Entered\n",
  3990. ComputerName ));
  3991. if ( DomainInfo == NULL ) {
  3992. Status = STATUS_INVALID_COMPUTER_NAME;
  3993. goto Cleanup;
  3994. }
  3995. //
  3996. // This call is only allowed to a PDC.
  3997. //
  3998. if ( DomainInfo->DomRole != RolePrimary ) {
  3999. NlPrintDom((NL_CRITICAL, DomainInfo,
  4000. "NetrLogonSendToSam: Call only valid to a PDC.\n" ));
  4001. Status = STATUS_ACCESS_DENIED;
  4002. goto Cleanup;
  4003. }
  4004. //
  4005. // Get the Session key for this session.
  4006. //
  4007. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  4008. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  4009. if (ServerSession == NULL) {
  4010. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4011. Status = STATUS_ACCESS_DENIED;
  4012. goto Cleanup;
  4013. }
  4014. SessionInfo.SessionKey = ServerSession->SsSessionKey;
  4015. SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
  4016. //
  4017. // now verify the Authenticator and update seed if OK
  4018. //
  4019. Status = NlCheckAuthenticator( ServerSession,
  4020. Authenticator,
  4021. ReturnAuthenticator);
  4022. if ( !NT_SUCCESS(Status) ) {
  4023. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4024. goto Cleanup;
  4025. }
  4026. //
  4027. // Call is only allowed from a BDC.
  4028. //
  4029. if ( ServerSession->SsSecureChannelType != ServerSecureChannel ) {
  4030. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4031. NlPrintDom((NL_CRITICAL, DomainInfo,
  4032. "NetrLogonSendToSam: Call only valid from a BDC.\n" ));
  4033. Status = STATUS_ACCESS_DENIED;
  4034. goto Cleanup;
  4035. }
  4036. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  4037. //
  4038. // Decrypt the message before passing it to SAM
  4039. //
  4040. NlDecryptRC4( OpaqueBuffer,
  4041. OpaqueBufferSize,
  4042. &SessionInfo );
  4043. // #ifdef notdef
  4044. Status = SamISetPasswordInfoOnPdc(
  4045. DomainInfo->DomSamAccountDomainHandle,
  4046. OpaqueBuffer,
  4047. OpaqueBufferSize );
  4048. // #endif // notdef
  4049. if ( !NT_SUCCESS( Status )) {
  4050. NlPrintDom((NL_CRITICAL, DomainInfo,
  4051. "NetrLogonSendToSam: Cannot NewCallToSam %lX\n",
  4052. Status));
  4053. goto Cleanup;
  4054. }
  4055. Status = STATUS_SUCCESS;
  4056. //
  4057. // Common exit point
  4058. //
  4059. Cleanup:
  4060. //
  4061. // If the request failed, be carefull to not leak authentication
  4062. // information.
  4063. //
  4064. if ( Status == STATUS_ACCESS_DENIED ) {
  4065. RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) );
  4066. }
  4067. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4068. "NetrLogonSendToSam: %ws: returns 0x%lX\n",
  4069. ComputerName,
  4070. Status ));
  4071. if ( DomainInfo != NULL ) {
  4072. NlDereferenceDomain( DomainInfo );
  4073. }
  4074. return Status;
  4075. }
  4076. NTSTATUS
  4077. I_NetLogonSendToSamOnPdc(
  4078. IN LPWSTR DomainName,
  4079. IN LPBYTE OpaqueBuffer,
  4080. IN ULONG OpaqueBufferSize
  4081. )
  4082. /*++
  4083. Routine Description:
  4084. This function sends an opaque buffer from SAM on a BDC to SAM on the PDC of
  4085. the specified domain.
  4086. The original use of this routine will be to allow the BDC to forward user
  4087. account password changes to the PDC.
  4088. The function will not send any buffer from BDC to PDC which are on different
  4089. sites provided the registry value of AvoidPdcOnWan has been set to TRUE.
  4090. Arguments:
  4091. DomainName - Identifies the hosted domain that this request applies to.
  4092. DomainName may be the Netbios domain name or the DNS domain name.
  4093. NULL implies the primary domain hosted by this DC.
  4094. OpaqueBuffer - Buffer to be passed to the SAM service on the PDC.
  4095. The buffer will be encrypted on the wire.
  4096. OpaqueBufferSize - Size (in bytes) of OpaqueBuffer.
  4097. Return Value:
  4098. STATUS_SUCCESS: Message successfully sent to PDC
  4099. STATUS_NO_MEMORY: There is not enough memory to complete the operation
  4100. STATUS_NO_SUCH_DOMAIN: DomainName does not correspond to a hosted domain
  4101. STATUS_NO_LOGON_SERVERS: PDC is not currently available
  4102. STATUS_NOT_SUPPORTED: PDC does not support this operation
  4103. --*/
  4104. {
  4105. NTSTATUS Status;
  4106. NETLOGON_AUTHENTICATOR OurAuthenticator;
  4107. NETLOGON_AUTHENTICATOR ReturnAuthenticator;
  4108. PDOMAIN_INFO DomainInfo = NULL;
  4109. PCLIENT_SESSION ClientSession = NULL;
  4110. SESSION_INFO SessionInfo;
  4111. BOOLEAN FirstTry = TRUE;
  4112. BOOLEAN AmWriter = FALSE;
  4113. BOOLEAN IsSameSite;
  4114. LPBYTE EncryptedBuffer = NULL;
  4115. ULONG EncryptedBufferSize;
  4116. //
  4117. // If caller is calling when the netlogon service isn't running,
  4118. // tell it so.
  4119. //
  4120. if ( !NlStartNetlogonCall() ) {
  4121. return STATUS_NETLOGON_NOT_STARTED;
  4122. }
  4123. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4124. "I_NetLogonSendToSamOnPdc: Sending buffer to PDC of %ws\n",
  4125. DomainName ));
  4126. NlpDumpBuffer( NL_SESSION_MORE, OpaqueBuffer, OpaqueBufferSize );
  4127. //
  4128. // Find the Hosted domain.
  4129. //
  4130. DomainInfo = NlFindDomain( DomainName, NULL, FALSE );
  4131. if ( DomainInfo == NULL ) {
  4132. Status = STATUS_NO_SUCH_DOMAIN;
  4133. goto Cleanup;
  4134. }
  4135. //
  4136. // Ensure this is a BDC.
  4137. //
  4138. if ( DomainInfo->DomRole == RolePrimary ) {
  4139. NlPrintDom((NL_CRITICAL, DomainInfo,
  4140. "I_NetLogonSendToSamOnPdc: not allowed on PDC.\n"));
  4141. Status = STATUS_NO_LOGON_SERVERS;
  4142. goto Cleanup;
  4143. }
  4144. //
  4145. // If the registry value of AvoidPdcOnWan is TRUE and PDC is on a remote site,
  4146. // do not send anything and return with a success.
  4147. //
  4148. if ( NlGlobalParameters.AvoidPdcOnWan ) {
  4149. //
  4150. // Determine whether the PDC is in the same site
  4151. //
  4152. Status = SamISameSite( &IsSameSite );
  4153. if ( !NT_SUCCESS(Status) ) {
  4154. NlPrintDom(( NL_CRITICAL, DomainInfo,
  4155. "I_NetLogonSendToSamOnPdc: Cannot SamISameSite.\n" ));
  4156. goto Cleanup;
  4157. }
  4158. if ( !IsSameSite ) {
  4159. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4160. "I_NetLogonSendToSamOnPdc: Ignored sending to a PDC on a remote site.\n"));
  4161. Status = STATUS_SUCCESS;
  4162. goto Cleanup;
  4163. } else {
  4164. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4165. "I_NetLogonSendToSamOnPdc: BDC and PDC are on the same site.\n"));
  4166. }
  4167. }
  4168. //
  4169. // Reference the client session.
  4170. //
  4171. ClientSession = NlRefDomClientSession( DomainInfo );
  4172. if ( ClientSession == NULL ) {
  4173. NlPrintDom((NL_CRITICAL, DomainInfo,
  4174. "I_NetLogonSendToSamOnPdc: This BDC has no client session with the PDC.\n"));
  4175. Status = STATUS_NO_LOGON_SERVERS;
  4176. goto Cleanup;
  4177. }
  4178. //
  4179. // Become a Writer of the ClientSession.
  4180. //
  4181. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4182. NlPrintDom((NL_CRITICAL, DomainInfo,
  4183. "I_NetLogonSendToSamOnPdc: Can't become writer of client session.\n"));
  4184. Status = STATUS_NO_LOGON_SERVERS;
  4185. goto Cleanup;
  4186. }
  4187. AmWriter = TRUE;
  4188. //
  4189. // If the session isn't authenticated,
  4190. // do so now.
  4191. //
  4192. FirstTryFailed:
  4193. Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
  4194. if ( !NT_SUCCESS(Status) ) {
  4195. goto Cleanup;
  4196. }
  4197. SessionInfo.SessionKey = ClientSession->CsSessionKey;
  4198. SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
  4199. //
  4200. // If the PDC doesn't support the new function,
  4201. // fail now.
  4202. //
  4203. if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_PDC_PASSWORD) == 0 ) {
  4204. NlPrintDom((NL_CRITICAL, DomainInfo,
  4205. "I_NetLogonSendToSamOnPdc: %ws: PDC doesn't support this function.\n",
  4206. DomainName ));
  4207. Status = STATUS_NOT_SUPPORTED;
  4208. goto Cleanup;
  4209. }
  4210. //
  4211. // Build the Authenticator for this request to the PDC.
  4212. //
  4213. NlBuildAuthenticator(
  4214. &ClientSession->CsAuthenticationSeed,
  4215. &ClientSession->CsSessionKey,
  4216. &OurAuthenticator);
  4217. //
  4218. // Encrypt the data before we send it on the wire.
  4219. //
  4220. if ( EncryptedBuffer != NULL ) {
  4221. LocalFree( EncryptedBuffer );
  4222. EncryptedBuffer = NULL;
  4223. }
  4224. EncryptedBufferSize = OpaqueBufferSize;
  4225. EncryptedBuffer = LocalAlloc( 0, OpaqueBufferSize );
  4226. if ( EncryptedBuffer == NULL ) {
  4227. Status = STATUS_NO_MEMORY;
  4228. goto Cleanup;
  4229. }
  4230. RtlCopyMemory( EncryptedBuffer, OpaqueBuffer, OpaqueBufferSize );
  4231. NlEncryptRC4( EncryptedBuffer,
  4232. EncryptedBufferSize,
  4233. &SessionInfo );
  4234. //
  4235. // Change the password on the machine our connection is to.
  4236. //
  4237. NL_API_START( Status, ClientSession, TRUE ) {
  4238. NlAssert( ClientSession->CsUncServerName != NULL );
  4239. Status = I_NetLogonSendToSam( ClientSession->CsUncServerName,
  4240. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  4241. &OurAuthenticator,
  4242. &ReturnAuthenticator,
  4243. EncryptedBuffer,
  4244. EncryptedBufferSize );
  4245. // NOTE: This call may drop the secure channel behind our back
  4246. } NL_API_ELSE( Status, ClientSession, TRUE ) {
  4247. } NL_API_END;
  4248. //
  4249. // Now verify primary's authenticator and update our seed
  4250. //
  4251. if ( NlpDidDcFail( Status ) ||
  4252. !NlUpdateSeed( &ClientSession->CsAuthenticationSeed,
  4253. &ReturnAuthenticator.Credential,
  4254. &ClientSession->CsSessionKey) ) {
  4255. NlPrintCs(( NL_CRITICAL, ClientSession,
  4256. "I_NetLogonSendToSamOnPdc: denying access after status: 0x%lx\n",
  4257. Status ));
  4258. //
  4259. // Preserve any status indicating a communication error.
  4260. //
  4261. if ( NT_SUCCESS(Status) ) {
  4262. Status = STATUS_ACCESS_DENIED;
  4263. }
  4264. NlSetStatusClientSession( ClientSession, Status );
  4265. //
  4266. // Perhaps the netlogon service on the server has just restarted.
  4267. // Try just once to set up a session to the server again.
  4268. //
  4269. if ( FirstTry ) {
  4270. FirstTry = FALSE;
  4271. goto FirstTryFailed;
  4272. }
  4273. }
  4274. //
  4275. // Common exit
  4276. //
  4277. Cleanup:
  4278. if ( ClientSession != NULL ) {
  4279. if ( AmWriter ) {
  4280. NlResetWriterClientSession( ClientSession );
  4281. }
  4282. NlUnrefClientSession( ClientSession );
  4283. }
  4284. if ( EncryptedBuffer != NULL ) {
  4285. LocalFree( EncryptedBuffer );
  4286. EncryptedBuffer = NULL;
  4287. }
  4288. if ( !NT_SUCCESS(Status) ) {
  4289. NlPrintDom((NL_CRITICAL, DomainInfo,
  4290. "I_NetLogonSendToSamOnPdc: %ws: failed %lX\n",
  4291. DomainName,
  4292. Status));
  4293. }
  4294. if ( DomainInfo != NULL ) {
  4295. NlDereferenceDomain( DomainInfo );
  4296. }
  4297. //
  4298. // Indicate that the calling thread has left netlogon.dll
  4299. //
  4300. NlEndNetlogonCall();
  4301. return Status;
  4302. }
  4303. NTSTATUS
  4304. I_NetLogonGetDirectDomain(
  4305. IN LPWSTR HostedDomainName,
  4306. IN LPWSTR TrustedDomainName,
  4307. OUT LPWSTR *DirectDomainName
  4308. )
  4309. /*++
  4310. Routine Description:
  4311. This function returns the name of a domain in the enterprise and returns
  4312. the name of a domain that is one hop closer.
  4313. Arguments:
  4314. HostedDomainName - Identifies the hosted domain that this request applies to.
  4315. DomainName may be the Netbios domain name or the DNS domain name.
  4316. NULL implies the primary domain hosted by this machine.
  4317. TrustedDomainName - Identifies the domain the trust relationship is to.
  4318. DomainName may be the Netbios domain name or the DNS domain name.
  4319. DirectDomainName - Returns the DNS domain name of the domain that is
  4320. one hop closer to TrustedDomainName. If there is a direct trust to
  4321. TrustedDomainName, NULL is returned.
  4322. The buffer must be freed using I_NetLogonFree.
  4323. Return Value:
  4324. STATUS_SUCCESS: The auth data was successfully returned.
  4325. STATUS_NO_MEMORY: There is not enough memory to complete the operation
  4326. STATUS_NETLOGON_NOT_STARTED: Netlogon is not running
  4327. STATUS_NO_SUCH_DOMAIN: HostedDomainName does not correspond to a hosted domain, OR
  4328. TrustedDomainName is not a trusted domain.
  4329. --*/
  4330. {
  4331. NTSTATUS Status;
  4332. PDOMAIN_INFO DomainInfo = NULL;
  4333. PCLIENT_SESSION ClientSession = NULL;
  4334. BOOLEAN TransitiveUsed;
  4335. UNICODE_STRING TrustedDomainNameString;
  4336. //
  4337. // If caller is calling when the netlogon service isn't running,
  4338. // tell it so.
  4339. //
  4340. *DirectDomainName = NULL;
  4341. if ( !NlStartNetlogonCall() ) {
  4342. return STATUS_NETLOGON_NOT_STARTED;
  4343. }
  4344. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4345. "I_NetLogonDirectDomainName: %ws %ws\n",
  4346. HostedDomainName,
  4347. TrustedDomainName ));
  4348. //
  4349. // Find the Hosted domain.
  4350. //
  4351. DomainInfo = NlFindDomain( HostedDomainName, NULL, FALSE );
  4352. if ( DomainInfo == NULL ) {
  4353. Status = STATUS_NO_SUCH_DOMAIN;
  4354. goto Cleanup;
  4355. }
  4356. //
  4357. // Reference the client session.
  4358. //
  4359. RtlInitUnicodeString( &TrustedDomainNameString, TrustedDomainName );
  4360. ClientSession = NlFindNamedClientSession( DomainInfo,
  4361. &TrustedDomainNameString,
  4362. NL_RETURN_CLOSEST_HOP,
  4363. &TransitiveUsed );
  4364. if ( ClientSession == NULL ) {
  4365. NlPrintDom((NL_CRITICAL, DomainInfo,
  4366. "I_NetLogonDirectDomainName: %ws: No such trusted domain\n",
  4367. TrustedDomainName ));
  4368. Status = STATUS_NO_SUCH_DOMAIN;
  4369. goto Cleanup;
  4370. }
  4371. //
  4372. // If this is a transitive trust,
  4373. // return the name of the domain to the caller.
  4374. //
  4375. if ( TransitiveUsed ) {
  4376. LOCK_TRUST_LIST( DomainInfo );
  4377. if ( ClientSession->CsDnsDomainName.Buffer != NULL ) {
  4378. *DirectDomainName =
  4379. NetpAllocWStrFromWStr( ClientSession->CsDnsDomainName.Buffer );
  4380. } else {
  4381. UNLOCK_TRUST_LIST( DomainInfo );
  4382. NlPrintDom((NL_CRITICAL, DomainInfo,
  4383. "I_NetLogonDirectDomainName: %ws: No DNS domain name\n",
  4384. TrustedDomainName ));
  4385. Status = STATUS_NO_SUCH_DOMAIN;
  4386. goto Cleanup;
  4387. }
  4388. UNLOCK_TRUST_LIST( DomainInfo );
  4389. if ( *DirectDomainName == NULL ) {
  4390. Status = STATUS_NO_MEMORY;
  4391. goto Cleanup;
  4392. }
  4393. }
  4394. //
  4395. // Common exit
  4396. //
  4397. Status = STATUS_SUCCESS;
  4398. Cleanup:
  4399. if ( ClientSession != NULL ) {
  4400. NlUnrefClientSession( ClientSession );
  4401. }
  4402. if ( !NT_SUCCESS(Status) ) {
  4403. NlPrintDom((NL_CRITICAL, DomainInfo,
  4404. "I_NetLogonDirectDomainName: %ws: failed %lX\n",
  4405. TrustedDomainName,
  4406. Status));
  4407. }
  4408. if ( DomainInfo != NULL ) {
  4409. NlDereferenceDomain( DomainInfo );
  4410. }
  4411. //
  4412. // Indicate that the calling thread has left netlogon.dll
  4413. //
  4414. NlEndNetlogonCall();
  4415. return Status;
  4416. }
  4417. NTSTATUS
  4418. I_NetLogonGetAuthDataEx(
  4419. IN LPWSTR HostedDomainName,
  4420. IN LPWSTR TrustedDomainName,
  4421. IN BOOLEAN ResetChannel,
  4422. IN ULONG Flags,
  4423. OUT LPWSTR *ServerName,
  4424. OUT PNL_OS_VERSION ServerOsVersion,
  4425. OUT LPWSTR *ServerPrincipleName,
  4426. OUT PVOID *ClientContext,
  4427. OUT PULONG AuthnLevel
  4428. )
  4429. /*++
  4430. Routine Description:
  4431. This function returns the data that a caller could passed to
  4432. RpcBindingSetAuthInfoW to do an RPC call using the Netlogon security package.
  4433. The returned data is valid for the life of Netlogon's secure channel to
  4434. the current DC. There is no way for the caller to determine that lifetime.
  4435. So, the caller should be prepared for access to be denied and respond to that
  4436. by calling I_NetLogonGetAuthData again.
  4437. Once the returned data is passed to RpcBindingSetAuthInfoW, the data should
  4438. not be deallocated until after the binding handle is closed.
  4439. Arguments:
  4440. HostedDomainName - Identifies the hosted domain that this request applies to.
  4441. DomainName may be the Netbios domain name or the DNS domain name.
  4442. NULL implies the primary domain hosted by this machine.
  4443. TrustedDomainName - Identifies the domain the trust relationship is to.
  4444. DomainName may be the Netbios domain name or the DNS domain name.
  4445. ResetChannel - If true, specifies that the netlogon secure channel
  4446. is to be reset.
  4447. Flags - Flags defining which ClientContext to return:
  4448. NL_DIRECT_TRUST_REQUIRED: Indicates that STATUS_NO_SUCH_DOMAIN should be returned
  4449. if TrustedDomainName is not directly trusted.
  4450. NL_RETURN_CLOSEST_HOP: Indicates that for indirect trust, the "closest hop"
  4451. session should be returned rather than the actual session
  4452. NL_ROLE_PRIMARY_OK: Indicates that if this is a PDC, it's OK to return
  4453. the client session to the primary domain.
  4454. NL_REQUIRE_DOMAIN_IN_FOREST - Indicates that STATUS_NO_SUCH_DOMAIN should be returned
  4455. if TrustedDomainName is not a domain in the forest.
  4456. ServerName - UNC name of a DC in the trusted domain.
  4457. The caller should RPC to the named DC. This DC is the only DC
  4458. that has the server side context associated with the ClientContext
  4459. given below.
  4460. The buffer must be freed using I_NetLogonFree.
  4461. ServerOsVersion - Returns the operating system version of the machine named ServerName.
  4462. ServerPrincipleName - ServerPrincipleName to pass to RpcBindingSetAutInfo.
  4463. (See note above about when this data can be deallocated.)
  4464. The buffer must be freed using I_NetLogonFree.
  4465. ClientContext - Authentication data to pass as AuthIdentity to RpcBindingSetAutInfo.
  4466. (See note above about when this data can be deallocated.)
  4467. The buffer must be freed using I_NetLogonFree.
  4468. Note this OUT parameter is NULL if ServerName doesn't support this
  4469. functionality.
  4470. AuthnLevel - Authentication level Netlogon will use for its secure
  4471. channel. This value will be one of:
  4472. RPC_C_AUTHN_LEVEL_PKT_PRIVACY: Sign and seal
  4473. RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: Sign only
  4474. The caller can ignore this value and independently choose an authentication
  4475. level.
  4476. Return Value:
  4477. STATUS_SUCCESS: The auth data was successfully returned.
  4478. STATUS_NO_MEMORY: There is not enough memory to complete the operation
  4479. STATUS_NETLOGON_NOT_STARTED: Netlogon is not running
  4480. STATUS_NO_SUCH_DOMAIN: HostedDomainName does not correspond to a hosted domain, OR
  4481. TrustedDomainName is not a trusted domain.
  4482. STATUS_NO_LOGON_SERVERS: No DCs are not currently available
  4483. --*/
  4484. {
  4485. NTSTATUS Status = STATUS_SUCCESS;
  4486. PDOMAIN_INFO DomainInfo = NULL;
  4487. PCLIENT_SESSION ClientSession = NULL;
  4488. BOOLEAN AmWriter = FALSE;
  4489. UNICODE_STRING TrustedDomainNameString;
  4490. LPWSTR LocalServerPrincipleName = NULL;
  4491. LPWSTR LocalServerName = NULL;
  4492. PVOID LocalClientContext = NULL;
  4493. ULONG LocalAuthnLevel = 0;
  4494. //
  4495. // If caller is calling when the netlogon service isn't running,
  4496. // tell it so.
  4497. //
  4498. if ( !NlStartNetlogonCall() ) {
  4499. return STATUS_NETLOGON_NOT_STARTED;
  4500. }
  4501. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4502. "I_NetLogonGetAuthData: %ws %ws\n",
  4503. HostedDomainName,
  4504. TrustedDomainName ));
  4505. //
  4506. // Find the Hosted domain.
  4507. //
  4508. DomainInfo = NlFindDomain( HostedDomainName, NULL, FALSE );
  4509. if ( DomainInfo == NULL ) {
  4510. Status = STATUS_NO_SUCH_DOMAIN;
  4511. goto Cleanup;
  4512. }
  4513. //
  4514. // Reference the client session.
  4515. //
  4516. RtlInitUnicodeString( &TrustedDomainNameString, TrustedDomainName );
  4517. ClientSession = NlFindNamedClientSession( DomainInfo,
  4518. &TrustedDomainNameString,
  4519. Flags,
  4520. NULL );
  4521. if ( ClientSession == NULL ) {
  4522. NlPrintDom((NL_CRITICAL, DomainInfo,
  4523. "I_NetLogonGetAuthData: %ws: No such trusted domain\n",
  4524. TrustedDomainName ));
  4525. Status = STATUS_NO_SUCH_DOMAIN;
  4526. goto Cleanup;
  4527. }
  4528. //
  4529. // Get the server principal name
  4530. //
  4531. LocalServerPrincipleName =
  4532. NetpAllocWStrFromWStr( ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer );
  4533. if ( LocalServerPrincipleName == NULL ) {
  4534. Status = STATUS_NO_MEMORY;
  4535. goto Cleanup;
  4536. }
  4537. //
  4538. // Get the auth level
  4539. //
  4540. LocalAuthnLevel = NlGlobalParameters.SealSecureChannel ?
  4541. RPC_C_AUTHN_LEVEL_PKT_PRIVACY :
  4542. RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
  4543. //
  4544. // Become a Writer of the ClientSession.
  4545. //
  4546. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4547. NlPrintDom((NL_CRITICAL, DomainInfo,
  4548. "I_NetLogonGetAuthData: Can't become writer of client session.\n"));
  4549. Status = STATUS_NO_LOGON_SERVERS;
  4550. goto Cleanup;
  4551. }
  4552. AmWriter = TRUE;
  4553. //
  4554. // If the caller asked us to reset the channel,
  4555. // do so now.
  4556. //
  4557. if ( ResetChannel ) {
  4558. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  4559. }
  4560. //
  4561. // If the session isn't authenticated,
  4562. // do so now.
  4563. //
  4564. Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
  4565. if ( !NT_SUCCESS(Status) ) {
  4566. goto Cleanup;
  4567. }
  4568. //
  4569. // Get the server name
  4570. //
  4571. LocalServerName = NetpAllocWStrFromWStr( ClientSession->CsUncServerName );
  4572. if ( LocalServerName == NULL ) {
  4573. Status = STATUS_NO_MEMORY;
  4574. goto Cleanup;
  4575. }
  4576. //
  4577. // Get the client context, if available
  4578. //
  4579. if ((ClientSession->CsNegotiatedFlags & NETLOGON_SUPPORTS_LSA_AUTH_RPC) != 0 ) {
  4580. LocalClientContext = NlBuildAuthData( ClientSession );
  4581. if ( LocalClientContext == NULL ) {
  4582. Status = STATUS_NO_MEMORY;
  4583. goto Cleanup;
  4584. }
  4585. }
  4586. //
  4587. // Determine the OS version
  4588. //
  4589. if ( ClientSession->CsNegotiatedFlags & ~NETLOGON_SUPPORTS_WIN2000_MASK ) {
  4590. *ServerOsVersion = NlWhistler;
  4591. } else if ( ClientSession->CsNegotiatedFlags & ~NETLOGON_SUPPORTS_NT4_MASK ) {
  4592. *ServerOsVersion = NlWin2000;
  4593. } else if ( ClientSession->CsNegotiatedFlags & ~NETLOGON_SUPPORTS_NT351_MASK ) {
  4594. *ServerOsVersion = NlNt40;
  4595. } else if ( ClientSession->CsNegotiatedFlags != 0 ) {
  4596. *ServerOsVersion = NlNt351;
  4597. } else {
  4598. *ServerOsVersion = NlNt35_or_older;
  4599. }
  4600. //
  4601. // Common exit
  4602. //
  4603. Cleanup:
  4604. if ( ClientSession != NULL ) {
  4605. if ( AmWriter ) {
  4606. NlResetWriterClientSession( ClientSession );
  4607. }
  4608. NlUnrefClientSession( ClientSession );
  4609. }
  4610. if ( !NT_SUCCESS(Status) ) {
  4611. NlPrintDom((NL_CRITICAL, DomainInfo,
  4612. "I_NetLogonGetAuthData: %ws: failed %lX\n",
  4613. TrustedDomainName,
  4614. Status));
  4615. }
  4616. if ( DomainInfo != NULL ) {
  4617. NlDereferenceDomain( DomainInfo );
  4618. }
  4619. //
  4620. // Return the data on success
  4621. //
  4622. if ( NT_SUCCESS(Status) ) {
  4623. *ServerPrincipleName = LocalServerPrincipleName;
  4624. *AuthnLevel = LocalAuthnLevel;
  4625. *ServerName = LocalServerName;
  4626. if ( ClientContext != NULL ) {
  4627. *ClientContext = LocalClientContext;
  4628. LocalClientContext = NULL;
  4629. }
  4630. } else {
  4631. if ( LocalServerPrincipleName != NULL ) {
  4632. NetApiBufferFree( LocalServerPrincipleName );
  4633. }
  4634. if ( LocalServerName != NULL ) {
  4635. NetApiBufferFree( LocalServerName );
  4636. }
  4637. }
  4638. if ( LocalClientContext != NULL ) {
  4639. I_NetLogonFree( LocalClientContext );
  4640. }
  4641. //
  4642. // Indicate that the calling thread has left netlogon.dll
  4643. //
  4644. NlEndNetlogonCall();
  4645. return Status;
  4646. }
  4647. NET_API_STATUS
  4648. NetrGetDCName (
  4649. IN LPWSTR ServerName OPTIONAL,
  4650. IN LPWSTR DomainName OPTIONAL,
  4651. OUT LPWSTR *Buffer
  4652. )
  4653. /*++
  4654. Routine Description:
  4655. Get the name of the primary domain controller for a domain.
  4656. Arguments:
  4657. ServerName - name of remote server (null for local)
  4658. DomainName - name of domain (null for primary)
  4659. Buffer - Returns a pointer to an allcated buffer containing the
  4660. servername of the PDC of the domain. The server name is prefixed
  4661. by \\. The buffer should be deallocated using NetApiBufferFree.
  4662. Return Value:
  4663. NERR_Success - Success. Buffer contains PDC name prefixed by \\.
  4664. NERR_DCNotFound No DC found for this domain.
  4665. ERROR_INVALID_NAME Badly formed domain name
  4666. --*/
  4667. {
  4668. #ifdef _WKSTA_NETLOGON
  4669. return ERROR_NOT_SUPPORTED;
  4670. UNREFERENCED_PARAMETER( ServerName );
  4671. UNREFERENCED_PARAMETER( DomainName );
  4672. UNREFERENCED_PARAMETER( Buffer );
  4673. #endif // _WKSTA_NETLOGON
  4674. #ifdef _DC_NETLOGON
  4675. NET_API_STATUS NetStatus;
  4676. UNREFERENCED_PARAMETER( ServerName );
  4677. //
  4678. // This API is not supported on workstations.
  4679. //
  4680. if ( NlGlobalMemberWorkstation ) {
  4681. return ERROR_NOT_SUPPORTED;
  4682. }
  4683. //
  4684. // Simply call the API which handles the local case specially.
  4685. //
  4686. NetStatus = NetGetDCName( NULL, DomainName, (LPBYTE *)Buffer );
  4687. return NetStatus;
  4688. #endif // _DC_NETLOGON
  4689. }
  4690. NET_API_STATUS
  4691. DsrGetDcNameEx(
  4692. IN LPWSTR ComputerName OPTIONAL,
  4693. IN LPWSTR DomainName OPTIONAL,
  4694. IN GUID *DomainGuid OPTIONAL,
  4695. IN LPWSTR SiteName OPTIONAL,
  4696. IN ULONG Flags,
  4697. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  4698. )
  4699. /*++
  4700. Routine Description:
  4701. Same as DsGetDcNameW except:
  4702. * This is the RPC server side implementation.
  4703. Arguments:
  4704. Same as DsGetDcNameW except as above.
  4705. Return Value:
  4706. Same as DsGetDcNameW except as above.
  4707. --*/
  4708. {
  4709. return DsrGetDcNameEx2( ComputerName,
  4710. NULL, // No Account name
  4711. 0, // No Allowable account control bits
  4712. DomainName,
  4713. DomainGuid,
  4714. SiteName,
  4715. Flags,
  4716. DomainControllerInfo );
  4717. }
  4718. VOID
  4719. DsFlagsToString(
  4720. IN DWORD Flags,
  4721. OUT LPSTR Buffer
  4722. )
  4723. /*++
  4724. Routine Description:
  4725. Routine to convert DsGetDcName flags to a printable string
  4726. Arguments:
  4727. Flags - flags to convert.
  4728. Buffer - buffer large enough for the longest string.
  4729. Return Value:
  4730. Buffer containing printable string.
  4731. Free using LocalFree.
  4732. --*/
  4733. {
  4734. //
  4735. // Build a string for each bit.
  4736. //
  4737. *Buffer = '\0';
  4738. if ( Flags & DS_FORCE_REDISCOVERY ) {
  4739. strcat( Buffer, "FORCE " );
  4740. Flags &= ~DS_FORCE_REDISCOVERY;
  4741. }
  4742. if ( Flags & DS_DIRECTORY_SERVICE_REQUIRED ) {
  4743. strcat( Buffer, "DS " );
  4744. Flags &= ~DS_DIRECTORY_SERVICE_REQUIRED;
  4745. }
  4746. if ( Flags & DS_DIRECTORY_SERVICE_PREFERRED ) {
  4747. strcat( Buffer, "DSP " );
  4748. Flags &= ~DS_DIRECTORY_SERVICE_PREFERRED;
  4749. }
  4750. if ( Flags & DS_GC_SERVER_REQUIRED ) {
  4751. strcat( Buffer, "GC " );
  4752. Flags &= ~DS_GC_SERVER_REQUIRED;
  4753. }
  4754. if ( Flags & DS_PDC_REQUIRED ) {
  4755. strcat( Buffer, "PDC " );
  4756. Flags &= ~DS_PDC_REQUIRED;
  4757. }
  4758. if ( Flags & DS_IP_REQUIRED ) {
  4759. strcat( Buffer, "IP " );
  4760. Flags &= ~DS_IP_REQUIRED;
  4761. }
  4762. if ( Flags & DS_KDC_REQUIRED ) {
  4763. strcat( Buffer, "KDC " );
  4764. Flags &= ~DS_KDC_REQUIRED;
  4765. }
  4766. if ( Flags & DS_TIMESERV_REQUIRED ) {
  4767. strcat( Buffer, "TIMESERV " );
  4768. Flags &= ~DS_TIMESERV_REQUIRED;
  4769. }
  4770. if ( Flags & DS_WRITABLE_REQUIRED ) {
  4771. strcat( Buffer, "WRITABLE " );
  4772. Flags &= ~DS_WRITABLE_REQUIRED;
  4773. }
  4774. if ( Flags & DS_GOOD_TIMESERV_PREFERRED ) {
  4775. strcat( Buffer, "GTIMESERV " );
  4776. Flags &= ~DS_GOOD_TIMESERV_PREFERRED;
  4777. }
  4778. if ( Flags & DS_AVOID_SELF ) {
  4779. strcat( Buffer, "AVOIDSELF " );
  4780. Flags &= ~DS_AVOID_SELF;
  4781. }
  4782. if ( Flags & DS_ONLY_LDAP_NEEDED ) {
  4783. strcat( Buffer, "LDAPONLY " );
  4784. Flags &= ~DS_ONLY_LDAP_NEEDED;
  4785. }
  4786. if ( Flags & DS_BACKGROUND_ONLY ) {
  4787. strcat( Buffer, "BACKGROUND " );
  4788. Flags &= ~DS_BACKGROUND_ONLY;
  4789. }
  4790. if ( Flags & DS_IS_FLAT_NAME ) {
  4791. strcat( Buffer, "NETBIOS " );
  4792. Flags &= ~DS_IS_FLAT_NAME;
  4793. }
  4794. if ( Flags & DS_IS_DNS_NAME ) {
  4795. strcat( Buffer, "DNS " );
  4796. Flags &= ~DS_IS_DNS_NAME;
  4797. }
  4798. if ( Flags & DS_RETURN_DNS_NAME ) {
  4799. strcat( Buffer, "RET_DNS " );
  4800. Flags &= ~DS_RETURN_DNS_NAME;
  4801. }
  4802. if ( Flags & DS_RETURN_FLAT_NAME ) {
  4803. strcat( Buffer, "RET_NETBIOS " );
  4804. Flags &= ~DS_RETURN_FLAT_NAME;
  4805. }
  4806. if ( Flags ) {
  4807. sprintf( &Buffer[strlen(Buffer)], "0x%lx ", Flags );
  4808. }
  4809. }
  4810. NET_API_STATUS
  4811. DsrGetDcNameEx2(
  4812. IN LPWSTR ComputerName OPTIONAL,
  4813. IN LPWSTR AccountName OPTIONAL,
  4814. IN ULONG AllowableAccountControlBits,
  4815. IN LPWSTR DomainName OPTIONAL,
  4816. IN GUID *DomainGuid OPTIONAL,
  4817. IN LPWSTR SiteName OPTIONAL,
  4818. IN ULONG Flags,
  4819. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  4820. )
  4821. /*++
  4822. Routine Description:
  4823. Same as DsGetDcNameW except:
  4824. AccountName - Account name to pass on the ping request.
  4825. If NULL, no account name will be sent.
  4826. AllowableAccountControlBits - Mask of allowable account types for AccountName.
  4827. * This is the RPC server side implementation.
  4828. Arguments:
  4829. Same as DsGetDcNameW except as above.
  4830. Return Value:
  4831. Same as DsGetDcNameW except as above.
  4832. --*/
  4833. {
  4834. NET_API_STATUS NetStatus;
  4835. PDOMAIN_INFO DomainInfo;
  4836. LPWSTR CapturedInfo = NULL;
  4837. LPWSTR CapturedDnsDomainName;
  4838. LPWSTR CapturedDnsForestName;
  4839. LPWSTR CapturedSiteName;
  4840. GUID CapturedDomainGuidBuffer;
  4841. GUID *CapturedDomainGuid;
  4842. LPSTR FlagsBuffer;
  4843. ULONG InternalFlags = 0;
  4844. LPWSTR DnsDomainTrustName = NULL;
  4845. LPWSTR NetbiosDomainTrustName = NULL;
  4846. LPWSTR NetlogonDnsDomainTrustName = NULL;
  4847. LPWSTR NetlogonNetbiosDomainTrustName = NULL;
  4848. UNICODE_STRING LsaDnsDomainTrustName = {0};
  4849. UNICODE_STRING LsaNetbiosDomainTrustName = {0};
  4850. BOOL HaveDnsServers;
  4851. //
  4852. // If caller is calling when the netlogon service isn't running,
  4853. // tell it so.
  4854. //
  4855. if ( !NlStartNetlogonCall() ) {
  4856. return STATUS_NETLOGON_NOT_STARTED;
  4857. }
  4858. //
  4859. // Allocate a temp buffer
  4860. // (Don't put it on the stack since we don't want to commit a huge stack.)
  4861. //
  4862. CapturedInfo = LocalAlloc( 0,
  4863. (NL_MAX_DNS_LENGTH+1)*sizeof(WCHAR) +
  4864. (NL_MAX_DNS_LENGTH+1)*sizeof(WCHAR) +
  4865. (NL_MAX_DNS_LABEL_LENGTH+1)*sizeof(WCHAR)
  4866. + 200 );
  4867. if ( CapturedInfo == NULL ) {
  4868. return STATUS_NO_MEMORY;
  4869. }
  4870. CapturedDnsDomainName = CapturedInfo;
  4871. CapturedDnsForestName = &CapturedDnsDomainName[NL_MAX_DNS_LENGTH+1];
  4872. CapturedSiteName = &CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
  4873. FlagsBuffer = (LPSTR)&CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1];
  4874. IF_NL_DEBUG( MISC ) {
  4875. DsFlagsToString( Flags, FlagsBuffer );
  4876. }
  4877. //
  4878. // Lookup which domain this call pertains to.
  4879. //
  4880. DomainInfo = NlFindDomainByServerName( ComputerName );
  4881. if ( DomainInfo == NULL ) {
  4882. // Default to primary domain to handle the case where the ComputerName
  4883. // is an IP address.
  4884. // ?? Perhaps we should simply always use the primary domain
  4885. DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
  4886. if ( DomainInfo == NULL ) {
  4887. NetStatus = ERROR_INVALID_COMPUTERNAME;
  4888. goto Cleanup;
  4889. }
  4890. }
  4891. //
  4892. // Be verbose
  4893. //
  4894. NlPrintDom((NL_MISC, DomainInfo,
  4895. "DsGetDcName function called: Dom:%ws Acct:%ws Flags: %s\n",
  4896. DomainName,
  4897. AccountName,
  4898. FlagsBuffer ));
  4899. //
  4900. // If the caller didn't specify a site name,
  4901. // default to our site name.
  4902. //
  4903. if ( !ARGUMENT_PRESENT(SiteName) ) {
  4904. if ( NlCaptureSiteName( CapturedSiteName ) ) {
  4905. SiteName = CapturedSiteName;
  4906. InternalFlags |= DS_SITENAME_DEFAULTED;
  4907. }
  4908. }
  4909. //
  4910. // If the caller passed a domain name,
  4911. // and the domain is trusted,
  4912. // determine the Netbios and DNS versions of the domain name.
  4913. //
  4914. if ( DomainName != NULL ) {
  4915. //
  4916. // First try to get the names from netlogon's trusted domain list
  4917. //
  4918. NetStatus = NlGetTrustedDomainNames (
  4919. DomainInfo,
  4920. DomainName,
  4921. &NetlogonDnsDomainTrustName,
  4922. &NetlogonNetbiosDomainTrustName );
  4923. if ( NetStatus != NO_ERROR ) {
  4924. goto Cleanup;
  4925. }
  4926. DnsDomainTrustName = NetlogonDnsDomainTrustName;
  4927. NetbiosDomainTrustName = NetlogonNetbiosDomainTrustName;
  4928. //
  4929. // If that didn't work,
  4930. // try getting better information from LSA's logon session list
  4931. //
  4932. if ( DnsDomainTrustName == NULL || NetbiosDomainTrustName == NULL ) {
  4933. NTSTATUS Status;
  4934. UNICODE_STRING DomainNameString;
  4935. RtlInitUnicodeString( &DomainNameString, DomainName );
  4936. Status = LsaIGetNbAndDnsDomainNames(
  4937. &DomainNameString,
  4938. &LsaDnsDomainTrustName,
  4939. &LsaNetbiosDomainTrustName );
  4940. if ( !NT_SUCCESS(Status) ) {
  4941. NetStatus = NetpNtStatusToApiStatus( Status );
  4942. goto Cleanup;
  4943. }
  4944. //
  4945. // If the LSA returned names,
  4946. // use them
  4947. //
  4948. if ( LsaDnsDomainTrustName.Buffer != NULL &&
  4949. LsaNetbiosDomainTrustName.Buffer != NULL ) {
  4950. DnsDomainTrustName = LsaDnsDomainTrustName.Buffer;
  4951. NetbiosDomainTrustName = LsaNetbiosDomainTrustName.Buffer;
  4952. }
  4953. }
  4954. }
  4955. //
  4956. // Pass the request to the common implementation.
  4957. //
  4958. // When DsIGetDcName is called from netlogon,
  4959. // it has both the Netbios and DNS domain name available for the primary
  4960. // domain. That can trick DsGetDcName into returning DNS host name of a
  4961. // DC in the primary domain. However, on IPX only systems, that won't work.
  4962. // Avoid that problem by not passing the DNS domain name of the primary domain
  4963. // if there are no DNS servers.
  4964. //
  4965. CapturedDomainGuid = NlCaptureDomainInfo( DomainInfo,
  4966. CapturedDnsDomainName,
  4967. &CapturedDomainGuidBuffer );
  4968. NlCaptureDnsForestName( CapturedDnsForestName );
  4969. HaveDnsServers = NlDnsHasDnsServers();
  4970. NetStatus = DsIGetDcName(
  4971. DomainInfo->DomUncUnicodeComputerName+2,
  4972. AccountName,
  4973. AllowableAccountControlBits,
  4974. DomainName,
  4975. CapturedDnsForestName,
  4976. DomainGuid,
  4977. SiteName,
  4978. Flags,
  4979. InternalFlags,
  4980. DomainInfo,
  4981. NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000,
  4982. DomainInfo->DomUnicodeDomainName,
  4983. HaveDnsServers ? CapturedDnsDomainName : NULL,
  4984. CapturedDomainGuid,
  4985. HaveDnsServers ? DnsDomainTrustName : NULL,
  4986. NetbiosDomainTrustName,
  4987. DomainControllerInfo );
  4988. if ( NetStatus != ERROR_NO_SUCH_DOMAIN ) {
  4989. goto Cleanup;
  4990. }
  4991. //
  4992. // Clean up locally used resources.
  4993. //
  4994. Cleanup:
  4995. NlPrintDom((NL_MISC, DomainInfo,
  4996. "DsGetDcName function returns %ld: Dom:%ws Acct:%ws Flags: %s\n",
  4997. NetStatus,
  4998. DomainName,
  4999. AccountName,
  5000. FlagsBuffer ));
  5001. if ( DomainInfo != NULL ) {
  5002. NlDereferenceDomain( DomainInfo );
  5003. }
  5004. if ( CapturedInfo != NULL ) {
  5005. LocalFree( CapturedInfo );
  5006. }
  5007. if ( NetlogonDnsDomainTrustName != NULL ) {
  5008. NetApiBufferFree( NetlogonDnsDomainTrustName );
  5009. }
  5010. if ( NetlogonNetbiosDomainTrustName != NULL ) {
  5011. NetApiBufferFree( NetlogonNetbiosDomainTrustName );
  5012. }
  5013. if ( LsaDnsDomainTrustName.Buffer != NULL ) {
  5014. LsaIFreeHeap( LsaDnsDomainTrustName.Buffer );
  5015. }
  5016. if ( LsaNetbiosDomainTrustName.Buffer != NULL ) {
  5017. LsaIFreeHeap( LsaNetbiosDomainTrustName.Buffer );
  5018. }
  5019. //
  5020. // Indicate that the calling thread has left netlogon.dll
  5021. //
  5022. NlEndNetlogonCall();
  5023. return NetStatus;
  5024. }
  5025. NET_API_STATUS
  5026. DsrGetDcName(
  5027. IN LPWSTR ComputerName OPTIONAL,
  5028. IN LPWSTR DomainName OPTIONAL,
  5029. IN GUID *DomainGuid OPTIONAL,
  5030. IN GUID *SiteGuid OPTIONAL,
  5031. IN ULONG Flags,
  5032. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  5033. )
  5034. /*++
  5035. Routine Description:
  5036. Same as DsGetDcNameW except:
  5037. * This is the RPC server side implementation.
  5038. Arguments:
  5039. Same as DsGetDcNameW except as above.
  5040. Return Value:
  5041. Same as DsGetDcNameW except as above.
  5042. --*/
  5043. {
  5044. return DsrGetDcNameEx2( ComputerName,
  5045. NULL, // No Account name
  5046. 0, // No Allowable account control bits
  5047. DomainName,
  5048. DomainGuid,
  5049. NULL, // No site name
  5050. Flags,
  5051. DomainControllerInfo );
  5052. UNREFERENCED_PARAMETER( SiteGuid );
  5053. }