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

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