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.

3474 lines
114 KiB

  1. //depot/Lab03_N/DS/security/protocols/msv_sspi/msvsam.c#37 - edit change 16740 (text)
  2. /*++
  3. Copyright (c) 1987-1999 Microsoft Corporation
  4. Module Name:
  5. msvsam.c
  6. Abstract:
  7. Sam account validation interface.
  8. These routines are shared by the MSV authentication package and
  9. the Netlogon service.
  10. Author:
  11. Cliff Van Dyke (cliffv) 15-Jan-1992
  12. Environment:
  13. User mode only.
  14. Contains NT-specific code.
  15. Requires ANSI C extensions: slash-slash comments, long external names.
  16. Revision History:
  17. Chandana Surlu 21-Jul-96 Stolen from \\kernel\razzle3\src\security\msv1_0\msvsam.c
  18. JClark 28-Jun-2000 Added WMI Trace Logging Support
  19. Fixed bug 73583 - Password Expiration and Subauth DLLs
  20. --*/
  21. #include <global.h>
  22. #undef EXTERN
  23. #include "msp.h"
  24. #include "nlp.h"
  25. #include <stddef.h> // offsetof()
  26. #include <msaudite.h> // SE_AUDITID_xxx
  27. #include "trace.h" // WMI Tracing goo
  28. ///////////////////////////////////////////////////////////////////////
  29. // //
  30. // SubAuth package zero helper routine //
  31. // //
  32. ///////////////////////////////////////////////////////////////////////
  33. NTSTATUS
  34. Msv1_0SubAuthenticationRoutineZero(
  35. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  36. IN PVOID LogonInformation,
  37. IN ULONG Flags,
  38. IN PUSER_ALL_INFORMATION UserAll,
  39. OUT PULONG WhichFields,
  40. OUT PULONG UserFlags,
  41. OUT PBOOLEAN Authoritative,
  42. OUT PLARGE_INTEGER LogoffTime,
  43. OUT PLARGE_INTEGER KickoffTime
  44. );
  45. BOOLEAN
  46. MsvpCheckPreviousPassword(
  47. IN BOOLEAN UasCompatibilityRequired,
  48. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  49. IN PVOID LogonInformation,
  50. IN SAMPR_HANDLE DomainHandle,
  51. PUNICODE_STRING UserName
  52. );
  53. BOOLEAN
  54. MsvpLm3ValidateResponse (
  55. IN PNT_OWF_PASSWORD pNtOwfPassword,
  56. IN PUNICODE_STRING pUserName,
  57. IN PUNICODE_STRING pLogonDomainName,
  58. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  59. IN PMSV1_0_LM3_RESPONSE pLm3Response,
  60. OUT PUSER_SESSION_KEY UserSessionKey,
  61. OUT PLM_SESSION_KEY LmSessionKey
  62. )
  63. {
  64. UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH];
  65. ULONG i;
  66. // compute the response again
  67. MsvpLm3Response (
  68. pNtOwfPassword,
  69. pUserName,
  70. pLogonDomainName,
  71. ChallengeToClient,
  72. pLm3Response,
  73. Response,
  74. UserSessionKey,
  75. LmSessionKey
  76. );
  77. // compare with what we were passed
  78. i = (ULONG) RtlCompareMemory(
  79. pLm3Response->Response,
  80. Response,
  81. MSV1_0_NTLM3_RESPONSE_LENGTH
  82. );
  83. #if 0
  84. //
  85. // If the NTLMv2 client computes a challenge/response, based on a missing
  86. // supplied LogonDomainName, it will use NULL and effectively not mix in
  87. // the LogonDomainName in the hash computation. On the server side, when
  88. // netlogon calls, we get passed non-NULL, and of course that fails to
  89. // compute the same hash. One somewhat slimy, but interesting way to fix
  90. // this problem would be to retry the hash computation against a NULL
  91. // domain, as below. This may be more attractive than changing netlogon,
  92. // and trivial to backport & test on Win2k SP3.
  93. //
  94. // Scott Field (SField) 11-6-2001
  95. //
  96. if ( (MSV1_0_NTLM3_RESPONSE_LENGTH != i) && pLogonDomainName->Length ) {
  97. UNICODE_STRING EmptyString = {0};
  98. MsvpLm3Response (
  99. pNtOwfPassword,
  100. pUserName,
  101. &EmptyString,
  102. ChallengeToClient,
  103. pLm3Response,
  104. Response,
  105. UserSessionKey,
  106. LmSessionKey
  107. );
  108. // compare with what we were passed
  109. i = (ULONG) RtlCompareMemory(
  110. pLm3Response->Response,
  111. Response,
  112. MSV1_0_NTLM3_RESPONSE_LENGTH
  113. );
  114. }
  115. #endif
  116. SspPrint((SSP_NTLM_V2, "MsvpLm3ValidateResponse returning %s\n", (i == MSV1_0_NTLM3_RESPONSE_LENGTH) ? "true" : "false"));
  117. return (BOOLEAN) (i == MSV1_0_NTLM3_RESPONSE_LENGTH);
  118. }
  119. BOOLEAN
  120. MsvpNtlm3ValidateResponse (
  121. IN PNT_OWF_PASSWORD pNtOwfPassword,
  122. IN PUNICODE_STRING pUserName,
  123. IN PUNICODE_STRING pLogonDomainName,
  124. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  125. IN PMSV1_0_NTLM3_RESPONSE pNtlm3Response,
  126. IN ULONG Ntlm3ResponseLength,
  127. IN PMSV1_0_LM3_RESPONSE pLm3Response,
  128. IN ULONG Lm3ResponseLength,
  129. OUT PUSER_SESSION_KEY UserSessionKey,
  130. OUT PLM_SESSION_KEY LmSessionKey
  131. )
  132. {
  133. UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH];
  134. ULONG i;
  135. LARGE_INTEGER Time;
  136. NTSTATUS Status;
  137. #ifndef USE_CONSTANT_CHALLENGE
  138. const LONGLONG TicksPerSecond = 10*1000*1000; // 100 ns ticks per second
  139. #endif
  140. //
  141. // three success scenarios:
  142. // 1) NTResponse provided is NTLMv2 and validation against NTResponse succeeds -- compute
  143. // separate User and Lm sessionkeys.
  144. // 2) NTResponse provided is NTLMv2 and validation against NTResponse fails, validation
  145. // against LMResponse succeeds -- compute separate User and Lm sessionkeys in the same
  146. // manner as #1 above.
  147. // 3) NTResponse is not provided, and validation against LMResponse succeeds -- compute
  148. // session keys as such: User session key is derived from LMResponse, and LM session key
  149. // is a truncated form of User sessionkey.
  150. //
  151. //
  152. // check the NTChallengeResponse first, if appropriate...
  153. //
  154. while (Ntlm3ResponseLength >= MSV1_0_NTLM3_MIN_NT_RESPONSE_LENGTH)
  155. {
  156. //
  157. // check version numbers
  158. //
  159. if (pNtlm3Response->RespType > 1 ||
  160. pNtlm3Response->HiRespType < 1)
  161. {
  162. break;
  163. }
  164. //
  165. // check that the timestamp isn't too old
  166. //
  167. Status = NtQuerySystemTime( &Time );
  168. ASSERT( NT_SUCCESS(Status) );
  169. SspPrint((SSP_NTLM_V2, "MsvpNtlm3ValidateResponse: local %#I64x, response %#I64x\n", Time, pNtlm3Response->TimeStamp));
  170. #ifndef USE_CONSTANT_CHALLENGE
  171. //
  172. // make sure time hasn't expired
  173. // don't forget that client's clock could be behind ours
  174. //
  175. if (Time.QuadPart > (LONGLONG)pNtlm3Response->TimeStamp)
  176. {
  177. if (Time.QuadPart - (LONGLONG)pNtlm3Response->TimeStamp >
  178. (MSV1_0_MAX_NTLM3_LIFE*TicksPerSecond))
  179. {
  180. break;
  181. }
  182. } else if ((LONGLONG)pNtlm3Response->TimeStamp - Time.QuadPart >
  183. (MSV1_0_MAX_NTLM3_LIFE*TicksPerSecond)) {
  184. break;
  185. }
  186. #endif
  187. // compute the response itself
  188. MsvpNtlm3Response(
  189. pNtOwfPassword,
  190. pUserName,
  191. pLogonDomainName,
  192. (Ntlm3ResponseLength-sizeof(MSV1_0_NTLM3_RESPONSE)),
  193. ChallengeToClient,
  194. pNtlm3Response,
  195. Response,
  196. UserSessionKey,
  197. LmSessionKey
  198. );
  199. // compare with what we were passed
  200. i = (ULONG) RtlCompareMemory(
  201. pNtlm3Response->Response,
  202. Response,
  203. MSV1_0_NTLM3_RESPONSE_LENGTH
  204. );
  205. //
  206. // If the NTLMv2 client computes a challenge/response, based on a missing
  207. // supplied LogonDomainName, it will use NULL and effectively not mix in
  208. // the LogonDomainName in the hash computation. On the server side, when
  209. // netlogon calls, we get passed non-NULL, and of course that fails to
  210. // compute the same hash. One somewhat slimy, but interesting way to fix
  211. // this problem would be to retry the hash computation against a NULL
  212. // domain, as below. This may be more attractive than changing netlogon,
  213. // and trivial to backport & test on Win2k SP3.
  214. //
  215. // Scott Field (SField) 11-6-2001
  216. //
  217. if ( (MSV1_0_NTLM3_RESPONSE_LENGTH != i) && pLogonDomainName->Length )
  218. {
  219. UNICODE_STRING EmptyString = {0};
  220. MsvpNtlm3Response(
  221. pNtOwfPassword,
  222. pUserName,
  223. &EmptyString,
  224. (Ntlm3ResponseLength-sizeof(MSV1_0_NTLM3_RESPONSE)),
  225. ChallengeToClient,
  226. pNtlm3Response,
  227. Response,
  228. UserSessionKey,
  229. LmSessionKey
  230. );
  231. // compare with what we were passed
  232. i = (ULONG) RtlCompareMemory(
  233. pNtlm3Response->Response,
  234. Response,
  235. MSV1_0_NTLM3_RESPONSE_LENGTH
  236. );
  237. }
  238. if( i == MSV1_0_NTLM3_RESPONSE_LENGTH )
  239. {
  240. return TRUE;
  241. }
  242. break;
  243. }
  244. //
  245. // if we got here, the NTLMv2 NTChallengeResponse failed or was missing.
  246. //
  247. if ( Lm3ResponseLength == LM_RESPONSE_LENGTH )
  248. {
  249. UNICODE_STRING EmptyString = {0};
  250. if (!MsvpLm3ValidateResponse(
  251. pNtOwfPassword,
  252. pUserName,
  253. pLogonDomainName,
  254. ChallengeToClient,
  255. pLm3Response,
  256. UserSessionKey,
  257. LmSessionKey
  258. ))
  259. {
  260. //
  261. // try with a NULL domain, per the above. This will carry through
  262. // to the Ntlm3Response below.
  263. //
  264. if ( (pLogonDomainName->Length == 0) ||
  265. !MsvpLm3ValidateResponse(
  266. pNtOwfPassword,
  267. pUserName,
  268. &EmptyString,
  269. ChallengeToClient,
  270. pLm3Response,
  271. UserSessionKey,
  272. LmSessionKey
  273. ) )
  274. {
  275. return FALSE;
  276. }
  277. }
  278. if ( Ntlm3ResponseLength >= MSV1_0_NTLM3_MIN_NT_RESPONSE_LENGTH )
  279. {
  280. //
  281. // if the NTChallengeResponse was provided, but failed,
  282. // compute session keys based the same way as a success case would have.
  283. // this is required as the client does not know that the LM field was used
  284. // to successfully authenticate.
  285. //
  286. MsvpNtlm3Response(
  287. pNtOwfPassword,
  288. pUserName,
  289. pLogonDomainName,
  290. (Ntlm3ResponseLength - sizeof(MSV1_0_NTLM3_RESPONSE)),
  291. ChallengeToClient,
  292. pNtlm3Response,
  293. Response,
  294. UserSessionKey,
  295. LmSessionKey
  296. );
  297. }
  298. return TRUE;
  299. }
  300. return FALSE;
  301. }
  302. BOOLEAN
  303. MsvpPasswordValidate (
  304. IN BOOLEAN UasCompatibilityRequired,
  305. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  306. IN PVOID LogonInformation,
  307. IN PUSER_INTERNAL1_INFORMATION Passwords,
  308. OUT PULONG UserFlags,
  309. OUT PUSER_SESSION_KEY UserSessionKey,
  310. OUT PLM_SESSION_KEY LmSessionKey
  311. )
  312. /*++
  313. Routine Description:
  314. Process an interactive, network, or session logon. It calls
  315. SamIUserValidation, validates the passed in credentials, updates the logon
  316. statistics and packages the result for return to the caller.
  317. This routine is called directly from the MSV Authentication package
  318. on any system where LanMan is not installed. This routine is called
  319. from the Netlogon Service otherwise.
  320. Arguments:
  321. UasCompatibilityRequired -- True, if UAS compatibility is required.
  322. LogonLevel -- Specifies the level of information given in
  323. LogonInformation.
  324. LogonInformation -- Specifies the description for the user
  325. logging on. The LogonDomainName field should be ignored.
  326. The caller is responsible for validating this field.
  327. Passwords -- Specifies the passwords for the user account.
  328. UserFlags -- Returns flags identifying how the password was validated.
  329. Returns LOGON_NOENCRYPTION if the password wasn't encrypted
  330. Returns LOGON_USED_LM_PASSWORD if the LM password from SAM was used.
  331. UserSessionKey -- Returns the NT User session key for this network logon
  332. session.
  333. LmSessionKey -- Returns the LM compatible session key for this network
  334. logon session.
  335. Return Value:
  336. TRUE -- Password validation is successful
  337. FALSE -- Password validation failed
  338. --*/
  339. {
  340. NTSTATUS Status;
  341. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  342. PNETLOGON_INTERACTIVE_INFO LogonInteractiveInfo;
  343. PNETLOGON_NETWORK_INFO LogonNetworkInfo;
  344. BOOLEAN AlreadyValidated = FALSE;
  345. BOOLEAN TryLmResponse = TRUE;
  346. UNICODE_STRING NullUnicodeString;
  347. ULONG NtLmProtocolSupported;
  348. //
  349. // Initialization.
  350. //
  351. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  352. *UserFlags = LOGON_NTLMV2_ENABLED;
  353. RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
  354. RtlZeroMemory( LmSessionKey, sizeof(*LmSessionKey) );
  355. RtlInitUnicodeString( &NullUnicodeString, NULL );
  356. //
  357. // Ensure the OWF password is always defined
  358. //
  359. if ( !Passwords->NtPasswordPresent ){
  360. RtlCopyMemory( &Passwords->NtOwfPassword,
  361. &NlpNullNtOwfPassword,
  362. sizeof(Passwords->NtOwfPassword) );
  363. }
  364. if ( !Passwords->LmPasswordPresent ){
  365. RtlCopyMemory( &Passwords->LmOwfPassword,
  366. &NlpNullLmOwfPassword,
  367. sizeof(Passwords->LmOwfPassword) );
  368. }
  369. //
  370. // Handle interactive/service validation.
  371. //
  372. // Simply compare the OWF password passed in with the one from the
  373. // SAM database.
  374. //
  375. switch ( LogonLevel ) {
  376. case NetlogonInteractiveInformation:
  377. case NetlogonServiceInformation:
  378. ASSERT( offsetof( NETLOGON_INTERACTIVE_INFO, LmOwfPassword)
  379. == offsetof( NETLOGON_SERVICE_INFO, LmOwfPassword) );
  380. ASSERT( offsetof( NETLOGON_INTERACTIVE_INFO, NtOwfPassword)
  381. == offsetof( NETLOGON_SERVICE_INFO, NtOwfPassword) );
  382. LogonInteractiveInfo =
  383. (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
  384. //
  385. // If we're in UasCompatibilityMode,
  386. // and we don't have the NT password in SAM (but do have LM password),
  387. // validate against the LM version of the password.
  388. //
  389. if ( UasCompatibilityRequired &&
  390. !Passwords->NtPasswordPresent &&
  391. Passwords->LmPasswordPresent ) {
  392. if ( RtlCompareMemory( &Passwords->LmOwfPassword,
  393. &LogonInteractiveInfo->LmOwfPassword,
  394. LM_OWF_PASSWORD_LENGTH ) !=
  395. LM_OWF_PASSWORD_LENGTH ) {
  396. return FALSE;
  397. }
  398. *UserFlags |= LOGON_USED_LM_PASSWORD;
  399. //
  400. // In all other circumstances, use the NT version of the password.
  401. // This enforces case sensitivity.
  402. //
  403. } else {
  404. if ( RtlCompareMemory( &Passwords->NtOwfPassword,
  405. &LogonInteractiveInfo->NtOwfPassword,
  406. NT_OWF_PASSWORD_LENGTH ) !=
  407. NT_OWF_PASSWORD_LENGTH ) {
  408. return FALSE;
  409. }
  410. }
  411. break;
  412. //
  413. // Handle network logon validation.
  414. //
  415. case NetlogonNetworkInformation:
  416. {
  417. BOOLEAN TriedNtLm2 = FALSE;
  418. //
  419. // First, assume the passed password information is a challenge
  420. // response.
  421. //
  422. LogonNetworkInfo =
  423. (PNETLOGON_NETWORK_INFO) LogonInformation;
  424. SspPrint((SSP_CRED,
  425. "MsvpPasswordValidate NetworkLogon ParameterControl %#x, user \"%wZ\", domain \"%wZ\", NtRespLen %#x, LmRespLen %#x\n",
  426. LogonNetworkInfo->Identity.ParameterControl,
  427. &LogonNetworkInfo->Identity.UserName,
  428. &LogonNetworkInfo->Identity.LogonDomainName,
  429. LogonNetworkInfo->NtChallengeResponse.Length,
  430. LogonNetworkInfo->LmChallengeResponse.Length));
  431. // If the NT response is an NTLM3 response, do NTLM3 or NTLM3 with LM OWF
  432. // if the length is > NT_RESPONSE_LENGTH, then it's an NTLM3 response
  433. if (LogonNetworkInfo->NtChallengeResponse.Length > NT_RESPONSE_LENGTH)
  434. {
  435. //
  436. // this routine will try both NTChallengeResponse and LmChallengeResponse
  437. // validation if appropriate.
  438. //
  439. AlreadyValidated = MsvpNtlm3ValidateResponse(
  440. &Passwords->NtOwfPassword,
  441. &LogonNetworkInfo->Identity.UserName,
  442. &LogonNetworkInfo->Identity.LogonDomainName,
  443. (PUCHAR)&LogonNetworkInfo->LmChallenge,
  444. (PMSV1_0_NTLM3_RESPONSE) LogonNetworkInfo->NtChallengeResponse.Buffer,
  445. LogonNetworkInfo->NtChallengeResponse.Length,
  446. (PMSV1_0_LM3_RESPONSE) LogonNetworkInfo->LmChallengeResponse.Buffer,
  447. LogonNetworkInfo->LmChallengeResponse.Length,
  448. UserSessionKey,
  449. LmSessionKey
  450. );
  451. SspPrint((SSP_NTLM_V2, "MsvpPasswordValidate MsvpNtlm3ValidateResponse(NtResponse) returns %s\n", AlreadyValidated ? "true" : "false"));
  452. //
  453. // because a Subauth may have been used, we will only return failure
  454. // here if we know the request was NTLMv2.
  455. //
  456. if( AlreadyValidated ||
  457. (LogonNetworkInfo->Identity.ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) ) {
  458. return AlreadyValidated;
  459. }
  460. TriedNtLm2 = TRUE;
  461. }
  462. //
  463. // check the LM3 response based on NT OWF hash next
  464. // this will be recieved from Win9x server with NTLMv2 client
  465. //
  466. if ((LogonNetworkInfo->NtChallengeResponse.Length != NT_RESPONSE_LENGTH) &&
  467. (LogonNetworkInfo->LmChallengeResponse.Length == NT_RESPONSE_LENGTH) &&
  468. (!TriedNtLm2) )
  469. {
  470. //
  471. // we only reach here if we haven't tried to satisfy the NTLMv2 response.
  472. // ie: the NTChallengeResponse is not populated with NTLMv2.
  473. //
  474. AlreadyValidated = MsvpNtlm3ValidateResponse (
  475. &Passwords->NtOwfPassword,
  476. &LogonNetworkInfo->Identity.UserName,
  477. &LogonNetworkInfo->Identity.LogonDomainName,
  478. (PUCHAR)&LogonNetworkInfo->LmChallenge,
  479. (PMSV1_0_NTLM3_RESPONSE) LogonNetworkInfo->NtChallengeResponse.Buffer,
  480. LogonNetworkInfo->NtChallengeResponse.Length,
  481. (PMSV1_0_LM3_RESPONSE) LogonNetworkInfo->LmChallengeResponse.Buffer,
  482. LogonNetworkInfo->LmChallengeResponse.Length,
  483. UserSessionKey,
  484. LmSessionKey
  485. );
  486. SspPrint((SSP_NTLM_V2, "MsvpPasswordValidate MsvpNtlm3ValidateResponse(LmResponse) returns %s\n", AlreadyValidated ? "true" : "false"));
  487. if (AlreadyValidated)
  488. return TRUE;
  489. }
  490. NtLmProtocolSupported = NtLmGlobalLmProtocolSupported;
  491. // if we're requiring all clients (Win9x and NT) to have been upgraded, fail out now
  492. //if (NtLmProtocolSupported >= RefuseNtlm)
  493. //return FALSE;
  494. // if that fails, check the NTLM response if there is one of the
  495. // appropriate size in either NT response or LM response
  496. if (!AlreadyValidated &&
  497. (NtLmProtocolSupported < RefuseNtlm) &&
  498. (Passwords->NtPasswordPresent || (!Passwords->NtPasswordPresent && !Passwords->LmPasswordPresent)) &&
  499. (LogonNetworkInfo->NtChallengeResponse.Length == NT_RESPONSE_LENGTH ||
  500. LogonNetworkInfo->LmChallengeResponse.Length == NT_RESPONSE_LENGTH)) {
  501. NT_RESPONSE NtResponse;
  502. //
  503. // NT response is present and hash exists, don't try the LM respnose.
  504. //
  505. if( LogonNetworkInfo->NtChallengeResponse.Length == NT_RESPONSE_LENGTH )
  506. {
  507. TryLmResponse = FALSE;
  508. }
  509. //
  510. // Compute what the response should be.
  511. //
  512. Status = RtlCalculateNtResponse(
  513. &LogonNetworkInfo->LmChallenge,
  514. &Passwords->NtOwfPassword,
  515. &NtResponse );
  516. if ( NT_SUCCESS(Status) ) {
  517. //
  518. // If the responses match, the passwords are valid.
  519. // Try the NT response first, then the LM response
  520. //
  521. if ( RtlCompareMemory(
  522. LogonNetworkInfo->
  523. NtChallengeResponse.Buffer,
  524. &NtResponse,
  525. LogonNetworkInfo->NtChallengeResponse.Length ) ==
  526. NT_RESPONSE_LENGTH ) {
  527. AlreadyValidated = TRUE;
  528. } else if ( RtlCompareMemory(
  529. LogonNetworkInfo->
  530. LmChallengeResponse.Buffer,
  531. &NtResponse,
  532. LogonNetworkInfo->LmChallengeResponse.Length ) ==
  533. NT_RESPONSE_LENGTH ) {
  534. AlreadyValidated = TRUE;
  535. }
  536. }
  537. }
  538. // if we're requiring all Win9x clients to have been upgraded, fail out now
  539. //if (!AlreadyValidated && NtLmProtocolSupported >= RefuseLm)
  540. // return FALSE;
  541. //
  542. // if the LM response is the right size
  543. // validate against the LM version of the response
  544. // this applies also when both NTOWF and LMOWF are not present in SAM.
  545. //
  546. if (!AlreadyValidated &&
  547. ( TryLmResponse ) &&
  548. ( NtLmProtocolSupported < RefuseLm ) &&
  549. ( LogonNetworkInfo->LmChallengeResponse.Length == LM_RESPONSE_LENGTH ) &&
  550. ( (Passwords->LmPasswordPresent) || (!Passwords->LmPasswordPresent && !Passwords->NtPasswordPresent) )
  551. ) {
  552. LM_RESPONSE LmResponse;
  553. //
  554. // Compute what the response should be.
  555. //
  556. Status = RtlCalculateLmResponse(
  557. &LogonNetworkInfo->LmChallenge,
  558. &Passwords->LmOwfPassword,
  559. &LmResponse );
  560. if ( NT_SUCCESS(Status) ) {
  561. //
  562. // If the responses match, the passwords are valid.
  563. //
  564. if ( RtlCompareMemory(
  565. LogonNetworkInfo->
  566. LmChallengeResponse.Buffer,
  567. &LmResponse,
  568. LM_RESPONSE_LENGTH ) ==
  569. LM_RESPONSE_LENGTH ) {
  570. AlreadyValidated = TRUE;
  571. *UserFlags |= LOGON_USED_LM_PASSWORD;
  572. }
  573. }
  574. }
  575. //
  576. // If we haven't already validated this user,
  577. // Validate a Cleartext password on a Network logon request.
  578. //
  579. if ( !AlreadyValidated ) {
  580. // If Cleartext passwords are not allowed,
  581. // indicate the password doesn't match.
  582. //
  583. if((LogonInfo->ParameterControl & CLEARTEXT_PASSWORD_ALLOWED) == 0){
  584. return FALSE;
  585. }
  586. //
  587. // Compute the OWF password for the specified Cleartext password and
  588. // compare that to the OWF password retrieved from SAM.
  589. //
  590. //
  591. // If we're in UasCompatibilityMode,
  592. // and we don't have the NT password in SAM or
  593. // we don't have the NT password supplied by the caller.
  594. // validate against the LM version of the password.
  595. //
  596. // if neither password are present, we validate against
  597. // the empty computed LMOWF.
  598. //
  599. if ( UasCompatibilityRequired &&
  600. (NtLmProtocolSupported < RefuseLm) &&
  601. ((Passwords->LmPasswordPresent) || (!Passwords->LmPasswordPresent && !Passwords->NtPasswordPresent)) &&
  602. (!Passwords->NtPasswordPresent ||
  603. LogonNetworkInfo->NtChallengeResponse.Length == 0 ) ) {
  604. LM_OWF_PASSWORD LmOwfPassword;
  605. CHAR LmPassword[LM20_PWLEN+1];
  606. USHORT i;
  607. //
  608. // Compute the LmOwfPassword for the cleartext password passed in.
  609. // (Enforce length restrictions on LanMan compatible passwords.)
  610. //
  611. if ( LogonNetworkInfo->LmChallengeResponse.Length >
  612. sizeof(LmPassword) ) {
  613. return FALSE;
  614. }
  615. RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
  616. for (i = 0; i < LogonNetworkInfo->LmChallengeResponse.Length; i++) {
  617. LmPassword[i] =
  618. RtlUpperChar(LogonNetworkInfo->LmChallengeResponse.Buffer[i]);
  619. }
  620. (VOID) RtlCalculateLmOwfPassword( LmPassword, &LmOwfPassword );
  621. if ( RtlCompareMemory( &Passwords->LmOwfPassword,
  622. &LmOwfPassword,
  623. LM_OWF_PASSWORD_LENGTH ) !=
  624. LM_OWF_PASSWORD_LENGTH ) {
  625. //
  626. // Try the case preserved clear text password, too.
  627. // (I know of no client that does this,
  628. // but it is compatible with the LM 2.x server.)
  629. //
  630. RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
  631. RtlCopyMemory(
  632. &LmPassword,
  633. LogonNetworkInfo->LmChallengeResponse.Buffer,
  634. LogonNetworkInfo->LmChallengeResponse.Length);
  635. (VOID) RtlCalculateLmOwfPassword( LmPassword,
  636. &LmOwfPassword );
  637. if ( RtlCompareMemory( &Passwords->LmOwfPassword,
  638. &LmOwfPassword,
  639. LM_OWF_PASSWORD_LENGTH ) !=
  640. LM_OWF_PASSWORD_LENGTH ) {
  641. RtlSecureZeroMemory(LmPassword, sizeof(LmPassword));
  642. RtlSecureZeroMemory(&LmOwfPassword, sizeof(LmOwfPassword));
  643. return FALSE;
  644. }
  645. RtlSecureZeroMemory(LmPassword, sizeof(LmPassword));
  646. RtlSecureZeroMemory(&LmOwfPassword, sizeof(LmOwfPassword));
  647. }
  648. *UserFlags |= LOGON_USED_LM_PASSWORD;
  649. //
  650. // In all other circumstances, use the NT version of the password.
  651. // This enforces case sensitivity.
  652. //
  653. } else {
  654. NT_OWF_PASSWORD NtOwfPassword;
  655. //
  656. // Compute the NtOwfPassword for the cleartext password passed in.
  657. //
  658. Status = RtlCalculateNtOwfPassword(
  659. (PUNICODE_STRING)
  660. &LogonNetworkInfo->NtChallengeResponse,
  661. &NtOwfPassword );
  662. if ( RtlCompareMemory( &Passwords->NtOwfPassword,
  663. &NtOwfPassword,
  664. NT_OWF_PASSWORD_LENGTH ) !=
  665. NT_OWF_PASSWORD_LENGTH ) {
  666. RtlSecureZeroMemory(&NtOwfPassword, sizeof(NtOwfPassword));
  667. return FALSE;
  668. }
  669. RtlSecureZeroMemory(&NtOwfPassword, sizeof(NtOwfPassword));
  670. }
  671. *UserFlags |= LOGON_NOENCRYPTION;
  672. }
  673. //
  674. // ASSERT: the network logon has been authenticated
  675. //
  676. // Compute the session keys.
  677. //
  678. // If the client negotiated a non-NT protocol,
  679. // use the lanman session key as the UserSessionKey.
  680. //
  681. if ( LogonNetworkInfo->NtChallengeResponse.Length == 0 ) {
  682. ASSERT( sizeof(*UserSessionKey) >= sizeof(*LmSessionKey) );
  683. //
  684. // win9x depends on the last 8 bytes to be zero, zero it out now
  685. //
  686. RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
  687. RtlCopyMemory( UserSessionKey,
  688. &Passwords->LmOwfPassword,
  689. sizeof(*LmSessionKey) );
  690. } else {
  691. //
  692. // Return the NT UserSessionKey unless this is an account
  693. // that doesn't have the NT version of the password.
  694. // (A null password counts as a password).
  695. //
  696. if ( Passwords->NtPasswordPresent || !Passwords->LmPasswordPresent){
  697. Status = RtlCalculateUserSessionKeyNt(
  698. (PNT_RESPONSE) NULL, // Argument not used
  699. &Passwords->NtOwfPassword,
  700. UserSessionKey );
  701. ASSERT( NT_SUCCESS(Status) );
  702. }
  703. }
  704. //
  705. // Return the LM SessionKey unless this is an account
  706. // that doesn't have the LM version of the password.
  707. // (A null password counts as a password).
  708. //
  709. if ( Passwords->LmPasswordPresent || !Passwords->NtPasswordPresent ) {
  710. RtlCopyMemory( LmSessionKey,
  711. &Passwords->LmOwfPassword,
  712. sizeof(*LmSessionKey) );
  713. }
  714. break;
  715. }
  716. //
  717. // Any other LogonLevel is an internal error.
  718. //
  719. default:
  720. return FALSE;
  721. }
  722. return TRUE;
  723. }
  724. BOOLEAN
  725. MsvpEqualSidPrefix(
  726. IN PSID DomainSid,
  727. IN PSID GroupSid
  728. )
  729. /*++
  730. Routine Description:
  731. This routine checks to see if the specified group sid came from the
  732. specified domain by verifying that the domain portion of the group sid
  733. is equal to the domain sid.
  734. Arguments:
  735. DomainSid - Sid of the domain for comparison.
  736. GroupSid - Sid of the group for comparison
  737. Returns:
  738. TRUE - The group sid came from the specified domain.
  739. FALSE - The group sid did not come from the specified domain.
  740. --*/
  741. {
  742. PISID LocalGroupSid = (PISID) GroupSid;
  743. PISID LocalDomainSid = (PISID) DomainSid;
  744. if ((LocalGroupSid->SubAuthorityCount == LocalDomainSid->SubAuthorityCount + 1) &&
  745. RtlEqualMemory(
  746. RtlIdentifierAuthoritySid(LocalDomainSid),
  747. RtlIdentifierAuthoritySid(LocalGroupSid),
  748. RtlLengthRequiredSid(
  749. LocalDomainSid->SubAuthorityCount
  750. ) - FIELD_OFFSET(SID,IdentifierAuthority)
  751. )) {
  752. return(TRUE);
  753. }
  754. return(FALSE);
  755. }
  756. NTSTATUS
  757. MsvpFilterGroupMembership(
  758. IN PSID_AND_ATTRIBUTES_LIST CompleteMembership,
  759. IN PSID LogonDomainId,
  760. OUT PSAMPR_GET_GROUPS_BUFFER LocalMembership,
  761. OUT PSID_AND_ATTRIBUTES_LIST GlobalMembership,
  762. OUT PULONG GlobalMembershipSize
  763. )
  764. /*++
  765. Routine Description:
  766. This routine separates the complete transitive group membership into
  767. portions from this domain and portions from others.
  768. Arguments:
  769. CompleteMembership - The complete transitive membership.
  770. LogonDomainId - SID of the logon domain, used for compressing group
  771. membership.
  772. LocalMembership - Receives a list of rids corresponding to groups in this
  773. domain. The list should be freed with MIDL_user_free.
  774. GlobalMembership - Recevies a list of sids corresponding to groups in
  775. other domain. The list, but not the sids, should be free with
  776. MIDL_user_free.
  777. GlobalMembershipSize - Size, in bytes, of the sids in the global membership
  778. and the size of the SID_AND_ATTRIBUTES structures.
  779. Returns:
  780. STATUS_SUCCESS on success
  781. STATUS_INSUFFICIENT_RESOURCES on for memory allocation failures.
  782. --*/
  783. {
  784. NTSTATUS Status = STATUS_SUCCESS;
  785. ULONG LocalCount = 0;
  786. ULONG GlobalCount = 0;
  787. ULONG GlobalSize = 0;
  788. ULONG Index;
  789. LocalMembership->MembershipCount = 0;
  790. LocalMembership->Groups = NULL;
  791. GlobalMembership->Count = 0;
  792. GlobalMembership->SidAndAttributes = NULL;
  793. //
  794. // Define a flag so we don't have to do the comparison twice.
  795. //
  796. #define MSVP_LOCAL_GROUP_ATTR 0x20000000
  797. for (Index = 0; Index < CompleteMembership->Count ; Index++ ) {
  798. ASSERT((CompleteMembership->SidAndAttributes[Index].Attributes & MSVP_LOCAL_GROUP_ATTR) == 0);
  799. if (MsvpEqualSidPrefix(
  800. LogonDomainId,
  801. CompleteMembership->SidAndAttributes[Index].Sid
  802. )) {
  803. CompleteMembership->SidAndAttributes[Index].Attributes |= MSVP_LOCAL_GROUP_ATTR;
  804. LocalCount++;
  805. } else {
  806. GlobalCount++;
  807. GlobalSize += sizeof(SID_AND_ATTRIBUTES) + RtlLengthSid(CompleteMembership->SidAndAttributes[Index].Sid);
  808. }
  809. }
  810. //
  811. // Allocate the arrays for the output
  812. //
  813. if (LocalCount != 0)
  814. {
  815. LocalMembership->Groups = (PGROUP_MEMBERSHIP) I_NtLmAllocate(LocalCount * sizeof(GROUP_MEMBERSHIP));
  816. if (LocalMembership->Groups == NULL) {
  817. Status = STATUS_INSUFFICIENT_RESOURCES;
  818. goto Cleanup;
  819. }
  820. LocalMembership->MembershipCount = LocalCount;
  821. }
  822. if (GlobalCount != 0)
  823. {
  824. GlobalMembership->SidAndAttributes = (PSID_AND_ATTRIBUTES) I_NtLmAllocate(GlobalCount * sizeof(SID_AND_ATTRIBUTES));
  825. if (GlobalMembership->SidAndAttributes == NULL) {
  826. Status = STATUS_INSUFFICIENT_RESOURCES;
  827. goto Cleanup;
  828. }
  829. GlobalMembership->Count = GlobalCount;
  830. }
  831. //
  832. // Loop through again copy the rid or sid into the respective array
  833. //
  834. LocalCount = 0;
  835. GlobalCount = 0;
  836. for (Index = 0; Index < CompleteMembership->Count ; Index++ ) {
  837. if ((CompleteMembership->SidAndAttributes[Index].Attributes & MSVP_LOCAL_GROUP_ATTR) != 0) {
  838. LocalMembership->Groups[LocalCount].Attributes = CompleteMembership->SidAndAttributes[Index].Attributes & ~MSVP_LOCAL_GROUP_ATTR;
  839. LocalMembership->Groups[LocalCount].RelativeId =
  840. *RtlSubAuthoritySid(
  841. CompleteMembership->SidAndAttributes[Index].Sid,
  842. *RtlSubAuthorityCountSid(
  843. CompleteMembership->SidAndAttributes[Index].Sid
  844. ) - 1
  845. );
  846. LocalCount++;
  847. } else {
  848. GlobalMembership->SidAndAttributes[GlobalCount] = CompleteMembership->SidAndAttributes[Index];
  849. GlobalCount++;
  850. }
  851. }
  852. *GlobalMembershipSize = GlobalSize;
  853. Cleanup:
  854. if (!NT_SUCCESS(Status)) {
  855. if (LocalMembership->Groups != NULL)
  856. {
  857. I_NtLmFree(LocalMembership->Groups);
  858. LocalMembership->Groups = NULL;
  859. }
  860. if (GlobalMembership->SidAndAttributes != NULL)
  861. {
  862. I_NtLmFree(GlobalMembership->SidAndAttributes);
  863. GlobalMembership->SidAndAttributes = NULL;
  864. }
  865. }
  866. return(Status);
  867. }
  868. NTSTATUS
  869. MsvpSamValidate (
  870. IN SAMPR_HANDLE DomainHandle,
  871. IN BOOLEAN UasCompatibilityRequired,
  872. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  873. IN PUNICODE_STRING LogonServer,
  874. IN PUNICODE_STRING LogonDomainName,
  875. IN PSID LogonDomainId,
  876. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  877. IN PVOID LogonInformation,
  878. IN ULONG GuestRelativeId,
  879. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  880. OUT PVOID * ValidationInformation,
  881. OUT PBOOLEAN Authoritative,
  882. OUT PBOOLEAN BadPasswordCountZeroed
  883. )
  884. /*++
  885. Routine Description:
  886. Process an interactive, network, or session logon. It calls
  887. SamIUserValidation, validates the passed in credentials, updates the logon
  888. statistics and packages the result for return to the caller.
  889. This routine is called by MsvSamValidate.
  890. Arguments:
  891. DomainHandle -- Specifies a handle to the SamDomain to use to
  892. validate the request.
  893. UasCompatibilityRequired -- TRUE iff UasCompatibilityMode is on.
  894. SecureChannelType -- The secure channel type this request was made on.
  895. When netlogon on the BDC is called, the user is actually
  896. already authenticated (via PDC) through a prior "net use"
  897. from the win9x client. Netlogon merely returns the validation
  898. info to the win9x caller. To do that Netlogon calls
  899. MsvSamValidate passing NullSecureChannel as the 3rd parameter
  900. indicating to skip the password check.
  901. LogonServer -- Specifies the server name of the caller.
  902. LogonDomainName -- Specifies the domain of the caller.
  903. LogonDomainId -- Specifies the DomainId of the domain of the caller.
  904. LogonLevel -- Specifies the level of information given in
  905. LogonInformation.
  906. LogonInformation -- Specifies the description for the user
  907. logging on. The LogonDomainName field should be ignored.
  908. The caller is responsible for validating this field.
  909. GuestRelativeId - If non-zero, specifies the relative ID of the account
  910. to validate against.
  911. ValidationLevel -- Specifies the level of information returned in
  912. ValidationInformation. Must be NetlogonValidationSamInfo,
  913. NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4.
  914. ValidationInformation -- Returns the requested validation
  915. information. This buffer must be freed user MIDL_user_free.
  916. This information is only return on STATUS_SUCCESS.
  917. Authoritative -- Returns whether the status returned is an
  918. authoritative status which should be returned to the original
  919. caller. If not, this logon request may be tried again on another
  920. domain controller. This parameter is returned regardless of the
  921. status code.
  922. BadPasswordCountZeroed - Returns TRUE iff we zeroed the BadPasswordCount
  923. field of this user.
  924. Return Value:
  925. STATUS_SUCCESS: if there was no error.
  926. STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
  927. STATUS_NO_SUCH_USER: The specified user has no account.
  928. STATUS_WRONG_PASSWORD: The password was invalid.
  929. Other return codes from SamIUserValidation
  930. --*/
  931. {
  932. NTSTATUS Status;
  933. NTSTATUS SubAuthExStatus = STATUS_SUCCESS;
  934. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  935. SAMPR_HANDLE UserHandle = NULL;
  936. ULONG RelativeId = GuestRelativeId;
  937. ULONG SamFlags;
  938. PSID LocalSidUser = NULL;
  939. PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
  940. PSAMPR_USER_ALL_INFORMATION UserAll = NULL;
  941. SAMPR_GET_GROUPS_BUFFER GroupsBuffer;
  942. ULONG UserFlags = 0;
  943. USER_SESSION_KEY UserSessionKey = {0};
  944. LM_SESSION_KEY LmSessionKey = {0};
  945. ULONG WhichFields = 0;
  946. UNICODE_STRING LocalUserName;
  947. UNICODE_STRING LocalWorkstation;
  948. ULONG UserAccountControl;
  949. LARGE_INTEGER LogonTime;
  950. LARGE_INTEGER LogoffTime = {0};
  951. LARGE_INTEGER KickoffTime = {0};
  952. UNICODE_STRING Upn;
  953. LARGE_INTEGER AccountExpires;
  954. LARGE_INTEGER PasswordMustChange;
  955. LARGE_INTEGER PasswordLastSet;
  956. PNETLOGON_VALIDATION_SAM_INFO4 ValidationSam = NULL;
  957. ULONG ValidationSamSize;
  958. PUCHAR Where;
  959. ULONG Index;
  960. SAMPR_RETURNED_USTRING_ARRAY NameArray;
  961. SAMPR_ULONG_ARRAY UseArray;
  962. SID_AND_ATTRIBUTES_LIST GroupMembership;
  963. SID_AND_ATTRIBUTES_LIST GlobalGroupMembership;
  964. ULONG GlobalMembershipSize = 0;
  965. MSV1_0_VALIDATION_INFO SubAuthValidationInformation;
  966. BOOLEAN fSubAuthEx = FALSE;
  967. BOOLEAN fMachineAccount;
  968. ULONG ActionsPerformed = 0;
  969. PSID UserSid = NULL;
  970. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  971. //
  972. // check if caller requested that logon only target specified domain.
  973. //
  974. if( LogonInfo->ParameterControl & MSV1_0_TRY_SPECIFIED_DOMAIN_ONLY &&
  975. LogonInfo->LogonDomainName.Length ) {
  976. //
  977. // common case is a match for LogonDomainName, so avoid taking locks
  978. // until mis-match occurs.
  979. //
  980. if(!RtlEqualDomainName( &LogonInfo->LogonDomainName, LogonDomainName )) {
  981. WCHAR LocalTarget[ DNS_MAX_NAME_LENGTH + 1 ];
  982. WCHAR SpecifiedTarget[ DNS_MAX_NAME_LENGTH + 1 ];
  983. ULONG cchLocalTarget = 0;
  984. ULONG cchSpecifiedTarget = 0;
  985. //
  986. // pickup the local target name, based on whether this computer is
  987. // a domain controller.
  988. //
  989. RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
  990. if( NlpWorkstation ) {
  991. if( (NtLmGlobalUnicodeDnsComputerNameString.Length + sizeof(WCHAR)) <=
  992. sizeof( LocalTarget ) ) {
  993. RtlCopyMemory(
  994. LocalTarget,
  995. NtLmGlobalUnicodeDnsComputerName,
  996. NtLmGlobalUnicodeDnsComputerNameString.Length
  997. );
  998. cchLocalTarget = (NtLmGlobalUnicodeDnsComputerNameString.Length) /
  999. sizeof(WCHAR);
  1000. }
  1001. } else {
  1002. if( (NtLmGlobalUnicodeDnsDomainNameString.Length + sizeof(WCHAR)) <=
  1003. sizeof( LocalTarget ) ) {
  1004. RtlCopyMemory(
  1005. LocalTarget,
  1006. NtLmGlobalUnicodeDnsDomainName,
  1007. NtLmGlobalUnicodeDnsDomainNameString.Length
  1008. );
  1009. cchLocalTarget = (NtLmGlobalUnicodeDnsDomainNameString.Length) /
  1010. sizeof(WCHAR);
  1011. }
  1012. }
  1013. RtlReleaseResource(&NtLmGlobalCritSect);
  1014. //
  1015. // pull out target name.
  1016. //
  1017. if( (LogonInfo->LogonDomainName.Length + sizeof(WCHAR)) <= sizeof( SpecifiedTarget ) ) {
  1018. cchSpecifiedTarget = (LogonInfo->LogonDomainName.Length) / sizeof(WCHAR);
  1019. RtlCopyMemory(
  1020. SpecifiedTarget,
  1021. LogonInfo->LogonDomainName.Buffer,
  1022. LogonInfo->LogonDomainName.Length
  1023. );
  1024. }
  1025. if ( cchLocalTarget && cchSpecifiedTarget ) {
  1026. LocalTarget[ cchLocalTarget ] = L'\0';
  1027. SpecifiedTarget[ cchSpecifiedTarget ] = L'\0';
  1028. if(!DnsNameCompare_W( LocalTarget, SpecifiedTarget ) ) {
  1029. *Authoritative = FALSE;
  1030. return STATUS_NO_SUCH_USER;
  1031. }
  1032. }
  1033. }
  1034. }
  1035. //
  1036. // Initialization.
  1037. //
  1038. RtlZeroMemory(
  1039. &SubAuthValidationInformation,
  1040. sizeof(MSV1_0_VALIDATION_INFO));
  1041. SubAuthValidationInformation.Authoritative = TRUE;
  1042. SubAuthValidationInformation.WhichFields = 0;
  1043. NameArray.Count = 0;
  1044. NameArray.Element = NULL;
  1045. UseArray.Count = 0;
  1046. UseArray.Element = NULL;
  1047. *BadPasswordCountZeroed = FALSE;
  1048. GroupMembership.Count = 0;
  1049. GroupMembership.SidAndAttributes = NULL;
  1050. GlobalGroupMembership.Count = 0;
  1051. GlobalGroupMembership.SidAndAttributes = NULL;
  1052. GroupsBuffer.MembershipCount = 0;
  1053. GroupsBuffer.Groups = NULL;
  1054. RtlInitUnicodeString( &Upn, NULL );
  1055. (VOID) NtQuerySystemTime( &LogonTime );
  1056. //
  1057. // Determine what account types are valid.
  1058. //
  1059. // Normal user accounts are always allowed.
  1060. //
  1061. UserAccountControl = USER_NORMAL_ACCOUNT;
  1062. *Authoritative = TRUE;
  1063. switch ( LogonLevel ) {
  1064. case NetlogonInteractiveInformation:
  1065. case NetlogonServiceInformation:
  1066. break;
  1067. case NetlogonNetworkInformation:
  1068. //
  1069. // Local user (Temp Duplicate) accounts are only used on the machine
  1070. // being directly logged onto.
  1071. // (Nor are interactive or service logons allowed to them.)
  1072. //
  1073. if ( SecureChannelType == MsvApSecureChannel ) {
  1074. UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
  1075. }
  1076. //
  1077. // Machine accounts can be accessed on network connections.
  1078. //
  1079. UserAccountControl |= USER_INTERDOMAIN_TRUST_ACCOUNT |
  1080. USER_WORKSTATION_TRUST_ACCOUNT |
  1081. USER_SERVER_TRUST_ACCOUNT;
  1082. break;
  1083. default:
  1084. *Authoritative = TRUE;
  1085. return STATUS_INVALID_INFO_CLASS;
  1086. }
  1087. //
  1088. // Check the ValidationLevel
  1089. //
  1090. switch (ValidationLevel) {
  1091. case NetlogonValidationSamInfo:
  1092. case NetlogonValidationSamInfo2:
  1093. case NetlogonValidationSamInfo4:
  1094. break;
  1095. default:
  1096. *Authoritative = TRUE;
  1097. return STATUS_INVALID_INFO_CLASS;
  1098. }
  1099. //
  1100. // Convert the user name to a RelativeId.
  1101. //
  1102. if ( RelativeId != 0 ) {
  1103. UCHAR cDomainSubAuthorities;
  1104. UCHAR SubAuthIndex;
  1105. ULONG cbLocalSidUser;
  1106. PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority;
  1107. //
  1108. // build a Sid out of the DomainId and the supplied Rid.
  1109. //
  1110. cDomainSubAuthorities = *RtlSubAuthorityCountSid( LogonDomainId );
  1111. pIdentifierAuthority = RtlIdentifierAuthoritySid( LogonDomainId );
  1112. cbLocalSidUser = RtlLengthRequiredSid( (ULONG)(cDomainSubAuthorities + 1) );
  1113. LocalSidUser = I_NtLmAllocate( cbLocalSidUser );
  1114. if (LocalSidUser == NULL) {
  1115. *Authoritative = FALSE;
  1116. Status = STATUS_INSUFFICIENT_RESOURCES;
  1117. goto Cleanup;
  1118. }
  1119. Status = RtlInitializeSid(LocalSidUser, pIdentifierAuthority, (UCHAR)((DWORD)cDomainSubAuthorities+1));
  1120. if(!NT_SUCCESS(Status)) {
  1121. *Authoritative = FALSE;
  1122. goto Cleanup;
  1123. }
  1124. //
  1125. // loop copying subauthorities.
  1126. //
  1127. for( SubAuthIndex = 0 ; SubAuthIndex < cDomainSubAuthorities ; SubAuthIndex++ )
  1128. {
  1129. *RtlSubAuthoritySid( LocalSidUser, (ULONG)SubAuthIndex ) =
  1130. *RtlSubAuthoritySid( LogonDomainId, (ULONG)SubAuthIndex );
  1131. }
  1132. //
  1133. // append relative ID.
  1134. //
  1135. *RtlSubAuthoritySid(LocalSidUser, cDomainSubAuthorities) = RelativeId;
  1136. LocalUserName.Buffer = LocalSidUser;
  1137. LocalUserName.Length = (USHORT)cbLocalSidUser;
  1138. LocalUserName.MaximumLength = (USHORT)cbLocalSidUser;
  1139. SamFlags = SAM_OPEN_BY_SID;
  1140. } else {
  1141. LocalUserName = LogonInfo->UserName;
  1142. SamFlags = 0;
  1143. }
  1144. //
  1145. // if this is a domain controller, and, we get a logon request that
  1146. // looks like a possible UPN, set the flag...
  1147. //
  1148. if( (SamFlags == 0) &&
  1149. (LogonInfo->LogonDomainName.Buffer == NULL) &&
  1150. !NlpWorkstation
  1151. )
  1152. {
  1153. SamFlags |= SAM_OPEN_BY_UPN_OR_ACCOUNTNAME;
  1154. }
  1155. //
  1156. // Open the user account.
  1157. //
  1158. if (( LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL ) ||
  1159. ( LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL_EX ) ||
  1160. NlpSubAuthZeroExists ) {
  1161. //
  1162. // Fetch all attributes in the presence of a subauthentication DLL
  1163. //
  1164. Status = I_SamIGetUserLogonInformation(
  1165. DomainHandle,
  1166. SamFlags,
  1167. &LocalUserName,
  1168. &UserAllInfo,
  1169. &GroupMembership,
  1170. &UserHandle
  1171. );
  1172. } else {
  1173. //
  1174. // Performance optimization:
  1175. // Fetch only select attributes in the absence of a subauthentication DLL
  1176. //
  1177. Status = I_SamIGetUserLogonInformationEx(
  1178. DomainHandle,
  1179. SamFlags,
  1180. &LocalUserName,
  1181. USER_ALL_ACCOUNTEXPIRES |
  1182. USER_ALL_BADPASSWORDCOUNT |
  1183. USER_ALL_FULLNAME |
  1184. USER_ALL_HOMEDIRECTORY |
  1185. USER_ALL_HOMEDIRECTORYDRIVE |
  1186. USER_ALL_LASTLOGON |
  1187. USER_ALL_LMPASSWORDPRESENT |
  1188. USER_ALL_LOGONCOUNT |
  1189. USER_ALL_LOGONHOURS |
  1190. USER_ALL_NTPASSWORDPRESENT |
  1191. USER_ALL_OWFPASSWORD |
  1192. USER_ALL_PARAMETERS |
  1193. USER_ALL_PASSWORDCANCHANGE |
  1194. USER_ALL_PASSWORDLASTSET |
  1195. USER_ALL_PASSWORDMUSTCHANGE |
  1196. USER_ALL_PRIMARYGROUPID |
  1197. USER_ALL_PROFILEPATH |
  1198. USER_ALL_SCRIPTPATH |
  1199. USER_ALL_USERACCOUNTCONTROL |
  1200. USER_ALL_USERID |
  1201. USER_ALL_USERNAME |
  1202. USER_ALL_WORKSTATIONS,
  1203. &UserAllInfo,
  1204. &GroupMembership,
  1205. &UserHandle
  1206. );
  1207. }
  1208. if ( !NT_SUCCESS(Status) ) {
  1209. UserHandle = NULL;
  1210. *Authoritative = FALSE;
  1211. Status = STATUS_NO_SUCH_USER;
  1212. goto Cleanup;
  1213. }
  1214. if ( !NlpWorkstation )
  1215. {
  1216. BOOLEAN UpnDefaulted;
  1217. //
  1218. // get the UPN. Ignore the failure, as, there is existing logic
  1219. // for dealing with the lack of Upn.
  1220. //
  1221. I_SamIUPNFromUserHandle(
  1222. UserHandle,
  1223. &UpnDefaulted,
  1224. &Upn
  1225. );
  1226. }
  1227. UserAll = &UserAllInfo->All;
  1228. if ( RelativeId != 0 )
  1229. {
  1230. //
  1231. // reset LocalUserName to be an actual username rather than a Sid
  1232. // for Guest logon. This allows proper audit later on.
  1233. //
  1234. RtlCopyMemory( &LocalUserName, &UserAll->UserName, sizeof(LocalUserName) );
  1235. }
  1236. //
  1237. // pickup RelativeId from looked up information.
  1238. //
  1239. RelativeId = UserAll->UserId;
  1240. //
  1241. // If the account type isn't allowed,
  1242. // Treat this as though the User Account doesn't exist.
  1243. //
  1244. // SubAuthentication packages can be more specific than this test but
  1245. // they can't become less restrictive.
  1246. //
  1247. if ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) {
  1248. *Authoritative = FALSE;
  1249. Status = STATUS_NO_SUCH_USER;
  1250. goto Cleanup;
  1251. }
  1252. //
  1253. // determine if machine account, if so, certain failures are treated
  1254. // as Authoritative, to prevent fallback to guest and returning incorrect
  1255. // error codes.
  1256. //
  1257. if ( (UserAll->UserAccountControl & USER_MACHINE_ACCOUNT_MASK) != 0 ) {
  1258. fMachineAccount = TRUE;
  1259. } else {
  1260. fMachineAccount = FALSE;
  1261. }
  1262. OLD_TO_NEW_LARGE_INTEGER( UserAll->AccountExpires, AccountExpires );
  1263. //
  1264. // If there is a SubAuthentication DLL,
  1265. // call it to do all the authentication work.
  1266. //
  1267. if ( (LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL) &&
  1268. (!(LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL_EX))) {
  1269. ULONG LocalUserFlags = 0;
  1270. ULONG Flags = 0;
  1271. //
  1272. // Ensure the account isn't locked out.
  1273. //
  1274. // admin (rid==500) is not locked out iff in either of the following cases:
  1275. // 1) on console and interactive logon
  1276. // 2) DOMAIN_LOCKOUT_ADMINS is not set
  1277. //
  1278. // We did this regardless before NT 5.0. Now, we do it when either
  1279. // No SubAuth package is specified or
  1280. // New SubAuth package asks us to do account lockout test
  1281. // But, for those who call with old SubAuth packages, they will expect
  1282. // us to do the dirty work.
  1283. //
  1284. if ( (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) &&
  1285. (SecureChannelType != NullSecureChannel) )
  1286. {
  1287. BOOL LockOut = TRUE;
  1288. if (RelativeId == DOMAIN_USER_RID_ADMIN) {
  1289. //
  1290. // if the process is configured to allow over-ride
  1291. // (true if physical console logon), then don't enforce lockout.
  1292. //
  1293. if ((LogonLevel == NetlogonInteractiveInformation)
  1294. && ( NtLmCheckProcessOption( MSV1_0_OPTION_DISABLE_ADMIN_LOCKOUT ) & MSV1_0_OPTION_DISABLE_ADMIN_LOCKOUT )) {
  1295. LockOut = FALSE;
  1296. } else {
  1297. PDOMAIN_PASSWORD_INFORMATION DomainPasswordInfo = NULL;
  1298. Status = I_SamrQueryInformationDomain(
  1299. DomainHandle,
  1300. DomainPasswordInformation,
  1301. (PSAMPR_DOMAIN_INFO_BUFFER *)&DomainPasswordInfo );
  1302. if (!NT_SUCCESS(Status)) {
  1303. Status = STATUS_INTERNAL_ERROR;
  1304. *Authoritative = TRUE;
  1305. goto Cleanup;
  1306. }
  1307. if (0 == (DomainPasswordInfo->PasswordProperties & DOMAIN_LOCKOUT_ADMINS)) {
  1308. LockOut = FALSE;
  1309. }
  1310. SamFreeMemory(DomainPasswordInfo);
  1311. }
  1312. }
  1313. if (LockOut) {
  1314. //
  1315. // Since the UI strongly encourages admins to disable user
  1316. // accounts rather than delete them. Treat disabled acccount as
  1317. // non-authoritative allowing the search to continue for other
  1318. // accounts by the same name.
  1319. //
  1320. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  1321. *Authoritative = fMachineAccount;
  1322. } else {
  1323. *Authoritative = TRUE;
  1324. }
  1325. Status = STATUS_ACCOUNT_LOCKED_OUT;
  1326. goto Cleanup;
  1327. }
  1328. }
  1329. if ( SecureChannelType != MsvApSecureChannel ) {
  1330. Flags |= MSV1_0_PASSTHRU;
  1331. }
  1332. if ( GuestRelativeId != 0 ) {
  1333. Flags |= MSV1_0_GUEST_LOGON;
  1334. }
  1335. Status = Msv1_0SubAuthenticationRoutine(
  1336. LogonLevel,
  1337. LogonInformation,
  1338. Flags,
  1339. (PUSER_ALL_INFORMATION) UserAll,
  1340. &WhichFields,
  1341. &LocalUserFlags,
  1342. Authoritative,
  1343. &LogoffTime,
  1344. &KickoffTime );
  1345. if ( !NT_SUCCESS(Status) ) {
  1346. goto Cleanup;
  1347. }
  1348. //
  1349. // Sanity check what the SubAuthentication package returned
  1350. //
  1351. if ( (WhichFields & ~USER_ALL_PARAMETERS) != 0 ) {
  1352. Status = STATUS_INTERNAL_ERROR;
  1353. *Authoritative = TRUE;
  1354. goto Cleanup;
  1355. }
  1356. UserFlags |= LocalUserFlags;
  1357. } else { // we may still have an NT 5.0 SubAuth dll
  1358. //
  1359. // If there is an NT 5.0 SubAuthentication DLL,
  1360. // call it to do all the authentication work.
  1361. //
  1362. if ( (LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL_EX))
  1363. {
  1364. ULONG Flags = 0;
  1365. if ( SecureChannelType != MsvApSecureChannel ) {
  1366. Flags |= MSV1_0_PASSTHRU;
  1367. }
  1368. if ( GuestRelativeId != 0 ) {
  1369. Flags |= MSV1_0_GUEST_LOGON;
  1370. }
  1371. Status = Msv1_0SubAuthenticationRoutineEx(
  1372. LogonLevel,
  1373. LogonInformation,
  1374. Flags,
  1375. (PUSER_ALL_INFORMATION) UserAll,
  1376. (SAM_HANDLE)UserHandle,
  1377. &SubAuthValidationInformation,
  1378. &ActionsPerformed );
  1379. *Authoritative = SubAuthValidationInformation.Authoritative;
  1380. if ( !NT_SUCCESS(Status) ) {
  1381. goto Cleanup;
  1382. }
  1383. // We need to do this because even if any of the following checks
  1384. // fail, ARAP stills wants the returned blobs from the subauth
  1385. // package to be returned to the caller.
  1386. fSubAuthEx = TRUE;
  1387. }
  1388. //
  1389. // Ensure the account isn't locked out.
  1390. //
  1391. // admin (rid==500) is not locked out iff in either of the following cases:
  1392. // 1) on console and interactive logon
  1393. // 2) DOMAIN_LOCKOUT_ADMINS is not set
  1394. //
  1395. if ((ActionsPerformed & MSV1_0_SUBAUTH_LOCKOUT) == 0)
  1396. {
  1397. if ( (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) &&
  1398. (SecureChannelType != NullSecureChannel) )
  1399. {
  1400. BOOL LockOut = TRUE;
  1401. if (RelativeId == DOMAIN_USER_RID_ADMIN) {
  1402. //
  1403. // if the process is configured to allow over-ride
  1404. // (true if physical console logon), then don't enforce lockout.
  1405. //
  1406. if ((LogonLevel == NetlogonInteractiveInformation)
  1407. && ( NtLmCheckProcessOption( MSV1_0_OPTION_DISABLE_ADMIN_LOCKOUT ) & MSV1_0_OPTION_DISABLE_ADMIN_LOCKOUT )) {
  1408. LockOut = FALSE;
  1409. } else {
  1410. PDOMAIN_PASSWORD_INFORMATION DomainPasswordInfo = NULL;
  1411. Status = I_SamrQueryInformationDomain(
  1412. DomainHandle,
  1413. DomainPasswordInformation,
  1414. (PSAMPR_DOMAIN_INFO_BUFFER *)&DomainPasswordInfo );
  1415. if (!NT_SUCCESS(Status)) {
  1416. Status = STATUS_INTERNAL_ERROR;
  1417. *Authoritative = TRUE;
  1418. goto Cleanup;
  1419. }
  1420. if (0 == (DomainPasswordInfo->PasswordProperties & DOMAIN_LOCKOUT_ADMINS)) {
  1421. LockOut = FALSE;
  1422. }
  1423. SamFreeMemory(DomainPasswordInfo);
  1424. }
  1425. }
  1426. if (LockOut) {
  1427. //
  1428. // Since the UI strongly encourages admins to disable user
  1429. // accounts rather than delete them. Treat disabled acccount as
  1430. // non-authoritative allowing the search to continue for other
  1431. // accounts by the same name.
  1432. //
  1433. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  1434. *Authoritative = fMachineAccount;
  1435. } else {
  1436. *Authoritative = TRUE;
  1437. }
  1438. Status = STATUS_ACCOUNT_LOCKED_OUT;
  1439. goto Cleanup;
  1440. }
  1441. }
  1442. }
  1443. //
  1444. // Check the password if there's no subauth or if subauth did
  1445. // not already check password.
  1446. //
  1447. if ((ActionsPerformed & MSV1_0_SUBAUTH_PASSWORD) == 0)
  1448. {
  1449. if ( SecureChannelType != NullSecureChannel ) {
  1450. USER_INTERNAL1_INFORMATION Passwords;
  1451. //
  1452. // Copy the password info to the right structure.
  1453. //
  1454. Passwords.NtPasswordPresent = UserAll->NtPasswordPresent;
  1455. if ( UserAll->NtPasswordPresent ) {
  1456. Passwords.NtOwfPassword =
  1457. *((PNT_OWF_PASSWORD)(UserAll->NtOwfPassword.Buffer));
  1458. }
  1459. Passwords.LmPasswordPresent = UserAll->LmPasswordPresent;
  1460. if ( UserAll->LmPasswordPresent ) {
  1461. Passwords.LmOwfPassword =
  1462. *((PLM_OWF_PASSWORD)(UserAll->LmOwfPassword.Buffer));
  1463. }
  1464. //
  1465. // If the password specified doesn't match the SAM password,
  1466. // then we've got a password mismatch.
  1467. //
  1468. if ( ! MsvpPasswordValidate (
  1469. UasCompatibilityRequired,
  1470. LogonLevel,
  1471. LogonInformation,
  1472. &Passwords,
  1473. &UserFlags,
  1474. &UserSessionKey,
  1475. &LmSessionKey ) ) {
  1476. //
  1477. // If this is a guest logon and the guest account has no password,
  1478. // let the user log on.
  1479. //
  1480. // This special case check is after the MsvpPasswordValidate to
  1481. // give MsvpPasswordValidate every opportunity to compute the
  1482. // correct values for UserSessionKey and LmSessionKey.
  1483. //
  1484. if ( GuestRelativeId != 0 &&
  1485. !UserAll->NtPasswordPresent &&
  1486. !UserAll->LmPasswordPresent ) {
  1487. RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
  1488. RtlZeroMemory( &LmSessionKey, sizeof(LmSessionKey) );
  1489. //
  1490. // The password mismatched. We treat STATUS_WRONG_PASSWORD as
  1491. // an authoritative response. Our caller may choose to do otherwise.
  1492. //
  1493. } else {
  1494. Status = STATUS_WRONG_PASSWORD;
  1495. //
  1496. // Since the UI strongly encourages admins to disable user
  1497. // accounts rather than delete them. Treat disabled acccount as
  1498. // non-authoritative allowing the search to continue for other
  1499. // accounts by the same name.
  1500. //
  1501. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  1502. *Authoritative = fMachineAccount;
  1503. } else {
  1504. *Authoritative = TRUE;
  1505. }
  1506. goto Cleanup;
  1507. }
  1508. }
  1509. }
  1510. //
  1511. // If SubAuth DLL checked the password, then it implicitly
  1512. // checked the password's expiration, and we don't have to.
  1513. //
  1514. } else { // end if((ActionsPerformed & MSV1_0_SUBAUTH_PASSWORD) == 0)
  1515. ActionsPerformed |= MSV1_0_SUBAUTH_PASSWORD_EXPIRY;
  1516. }
  1517. //
  1518. // Check if the account is disabled if there's no subauth or if
  1519. // subauth has not already checked.
  1520. //
  1521. if ((ActionsPerformed & MSV1_0_SUBAUTH_ACCOUNT_DISABLED) == 0)
  1522. {
  1523. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  1524. //
  1525. // Since the UI strongly encourages admins to disable user
  1526. // accounts rather than delete them. Treat disabled acccount as
  1527. // non-authoritative allowing the search to continue for other
  1528. // accounts by the same name.
  1529. //
  1530. *Authoritative = fMachineAccount;
  1531. Status = STATUS_ACCOUNT_DISABLED;
  1532. goto Cleanup;
  1533. }
  1534. }
  1535. //
  1536. // Prevent some things from effecting the Administrator user
  1537. //
  1538. if (RelativeId != DOMAIN_USER_RID_ADMIN) {
  1539. //
  1540. // Check if the account has expired if there's no subauth or if
  1541. // subauth has not already checked.
  1542. //
  1543. if ((ActionsPerformed & MSV1_0_SUBAUTH_ACCOUNT_EXPIRY) == 0)
  1544. {
  1545. OLD_TO_NEW_LARGE_INTEGER( UserAll->AccountExpires, AccountExpires );
  1546. if ( AccountExpires.QuadPart != 0 &&
  1547. LogonTime.QuadPart >= AccountExpires.QuadPart ) {
  1548. *Authoritative = TRUE;
  1549. Status = STATUS_ACCOUNT_EXPIRED;
  1550. goto Cleanup;
  1551. }
  1552. }
  1553. //
  1554. // The password is valid, check to see if the password is expired.
  1555. // (SAM will have appropriately set PasswordMustChange to reflect
  1556. // USER_DONT_EXPIRE_PASSWORD)
  1557. //
  1558. // We only check password expiration if we also checked the password.
  1559. //
  1560. if ((ActionsPerformed & MSV1_0_SUBAUTH_PASSWORD_EXPIRY) == 0)
  1561. {
  1562. OLD_TO_NEW_LARGE_INTEGER( UserAll->PasswordMustChange, PasswordMustChange );
  1563. OLD_TO_NEW_LARGE_INTEGER( UserAll->PasswordLastSet, PasswordLastSet );
  1564. if ( SecureChannelType != NullSecureChannel ) {
  1565. if ( LogonTime.QuadPart >= PasswordMustChange.QuadPart ) {
  1566. if ( PasswordLastSet.QuadPart == 0 ) {
  1567. Status = STATUS_PASSWORD_MUST_CHANGE;
  1568. } else {
  1569. Status = STATUS_PASSWORD_EXPIRED;
  1570. }
  1571. *Authoritative = TRUE;
  1572. goto Cleanup;
  1573. }
  1574. }
  1575. }
  1576. }
  1577. //
  1578. // Validate the workstation the user logged on from.
  1579. //
  1580. // Ditch leading \\ on workstation name before passing it to SAM.
  1581. //
  1582. LocalWorkstation = LogonInfo->Workstation;
  1583. if ( LocalWorkstation.Length > 0 &&
  1584. LocalWorkstation.Buffer[0] == L'\\' &&
  1585. LocalWorkstation.Buffer[1] == L'\\' ) {
  1586. LocalWorkstation.Buffer += 2;
  1587. LocalWorkstation.Length -= 2*sizeof(WCHAR);
  1588. LocalWorkstation.MaximumLength -= 2*sizeof(WCHAR);
  1589. }
  1590. //
  1591. // Check if SAM found some more specific reason to not allow logon.
  1592. //
  1593. Status = I_SamIAccountRestrictions(
  1594. UserHandle,
  1595. &LocalWorkstation,
  1596. (PUNICODE_STRING) &UserAll->WorkStations,
  1597. (PLOGON_HOURS) &UserAll->LogonHours,
  1598. &LogoffTime,
  1599. &KickoffTime );
  1600. if ( !NT_SUCCESS(Status) ) {
  1601. *Authoritative = TRUE;
  1602. goto Cleanup;
  1603. }
  1604. //
  1605. // If there is a SubAuthentication package zero, call it
  1606. //
  1607. if (NlpSubAuthZeroExists) {
  1608. ULONG LocalUserFlags = 0;
  1609. ULONG Flags = 0;
  1610. if ( SecureChannelType != MsvApSecureChannel ) {
  1611. Flags |= MSV1_0_PASSTHRU;
  1612. }
  1613. if ( GuestRelativeId != 0 ) {
  1614. Flags |= MSV1_0_GUEST_LOGON;
  1615. }
  1616. Status = Msv1_0SubAuthenticationRoutineZero(
  1617. LogonLevel,
  1618. LogonInformation,
  1619. Flags,
  1620. (PUSER_ALL_INFORMATION) UserAll,
  1621. &WhichFields,
  1622. &LocalUserFlags,
  1623. Authoritative,
  1624. &LogoffTime,
  1625. &KickoffTime );
  1626. if ( !NT_SUCCESS(Status) ) {
  1627. goto Cleanup;
  1628. }
  1629. //
  1630. // Sanity check what the SubAuthentication package returned
  1631. //
  1632. if ( (WhichFields & ~USER_ALL_PARAMETERS) != 0 ) {
  1633. Status = STATUS_INTERNAL_ERROR;
  1634. *Authoritative = TRUE;
  1635. goto Cleanup;
  1636. }
  1637. UserFlags |= LocalUserFlags;
  1638. }
  1639. }
  1640. if ( AccountExpires.QuadPart != 0
  1641. && KickoffTime.QuadPart > AccountExpires.QuadPart )
  1642. {
  1643. KickoffTime = AccountExpires;
  1644. }
  1645. //
  1646. // If the account is a machine account,
  1647. // let the caller know he got the password right.
  1648. // (But don't let him actually log on).
  1649. //
  1650. // But, for NT 5.0, we must allow accounts with account control
  1651. // USER_WORKSTATION_TRUST_ACCOUNT for remote boot clients who
  1652. // will logon with their machine accounts
  1653. if ( (UserAll->UserAccountControl & USER_MACHINE_ACCOUNT_MASK) != 0 ) {
  1654. if (UserAll->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) {
  1655. Status = STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
  1656. } else if (UserAll->UserAccountControl &
  1657. USER_WORKSTATION_TRUST_ACCOUNT) {
  1658. if ( (LogonInfo->ParameterControl & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT) == 0) {
  1659. Status = STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
  1660. } else {
  1661. UNICODE_STRING MachineAccountName;
  1662. NTSTATUS TempStatus;
  1663. //
  1664. // if the password was correct, and it happened to match
  1665. // the machine name, dis-allow it regardless.
  1666. //
  1667. //
  1668. // Compute the lower case user name.
  1669. //
  1670. TempStatus = RtlDowncaseUnicodeString( &MachineAccountName,
  1671. (PUNICODE_STRING)&UserAll->UserName,
  1672. TRUE );
  1673. if( NT_SUCCESS( TempStatus ) )
  1674. {
  1675. USHORT LastChar = MachineAccountName.Length / sizeof(WCHAR);
  1676. if( LastChar )
  1677. {
  1678. if( MachineAccountName.Buffer[LastChar-1] == L'$' )
  1679. {
  1680. MachineAccountName.Length -= sizeof(WCHAR);
  1681. }
  1682. if( LastChar > LM20_PWLEN )
  1683. {
  1684. MachineAccountName.Length = LM20_PWLEN * sizeof(WCHAR);
  1685. }
  1686. }
  1687. if ( UserAll->NtPasswordPresent ) {
  1688. NT_OWF_PASSWORD NtOwfMachineName;
  1689. NT_OWF_PASSWORD *pOwf;
  1690. pOwf = ((PNT_OWF_PASSWORD)(UserAll->NtOwfPassword.Buffer));
  1691. RtlCalculateNtOwfPassword(
  1692. &MachineAccountName,
  1693. &NtOwfMachineName );
  1694. if ( RtlCompareMemory( pOwf,
  1695. &NtOwfMachineName,
  1696. NT_OWF_PASSWORD_LENGTH ) ==
  1697. NT_OWF_PASSWORD_LENGTH ) {
  1698. Status = STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
  1699. }
  1700. }
  1701. RtlFreeUnicodeString( &MachineAccountName );
  1702. }
  1703. }
  1704. } else if (UserAll->UserAccountControl & USER_SERVER_TRUST_ACCOUNT) {
  1705. if ( (LogonInfo->ParameterControl & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT) == 0) {
  1706. Status = STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
  1707. } else {
  1708. //
  1709. // it's a server trust account.
  1710. // treat same way as workstation trust account.
  1711. UNICODE_STRING MachineAccountName;
  1712. NTSTATUS TempStatus;
  1713. // NOTE: some code here is duplicated from above.
  1714. // this will be merged when a new Rtl has been added for
  1715. // computing initial machine password from machine acct name.
  1716. //
  1717. //
  1718. // if the password was correct, and it happened to match
  1719. // the machine name, dis-allow it regardless.
  1720. //
  1721. //
  1722. // Compute the lower case user name.
  1723. //
  1724. TempStatus = RtlDowncaseUnicodeString( &MachineAccountName,
  1725. (PUNICODE_STRING)&UserAll->UserName,
  1726. TRUE );
  1727. if( NT_SUCCESS( TempStatus ) )
  1728. {
  1729. USHORT LastChar = MachineAccountName.Length / sizeof(WCHAR);
  1730. if( LastChar )
  1731. {
  1732. if( MachineAccountName.Buffer[LastChar-1] == L'$' )
  1733. {
  1734. MachineAccountName.Length -= sizeof(WCHAR);
  1735. }
  1736. if( LastChar > LM20_PWLEN )
  1737. {
  1738. MachineAccountName.Length = LM20_PWLEN * sizeof(WCHAR);
  1739. }
  1740. }
  1741. if ( UserAll->NtPasswordPresent ) {
  1742. NT_OWF_PASSWORD NtOwfMachineName;
  1743. NT_OWF_PASSWORD *pOwf;
  1744. pOwf = ((PNT_OWF_PASSWORD)(UserAll->NtOwfPassword.Buffer));
  1745. RtlCalculateNtOwfPassword(
  1746. &MachineAccountName,
  1747. &NtOwfMachineName );
  1748. if ( RtlCompareMemory( pOwf,
  1749. &NtOwfMachineName,
  1750. NT_OWF_PASSWORD_LENGTH ) ==
  1751. NT_OWF_PASSWORD_LENGTH ) {
  1752. Status = STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
  1753. }
  1754. }
  1755. RtlFreeUnicodeString( &MachineAccountName );
  1756. }
  1757. //
  1758. // Let the client know that this was
  1759. // a server trust account
  1760. //
  1761. UserFlags |= LOGON_SERVER_TRUST_ACCOUNT;
  1762. }
  1763. } else {
  1764. Status = STATUS_NO_SUCH_USER;
  1765. }
  1766. if (!NT_SUCCESS(Status)) {
  1767. *Authoritative = TRUE;
  1768. goto Cleanup;
  1769. }
  1770. }
  1771. //
  1772. // don't allow blank password logons.
  1773. //
  1774. if(
  1775. (RelativeId != DOMAIN_USER_RID_GUEST)
  1776. &&
  1777. (!NtLmGlobalAllowBlankPassword)
  1778. &&
  1779. (!UserAll->NtPasswordPresent || (RtlCompareMemory(
  1780. &NlpNullNtOwfPassword,
  1781. UserAll->NtOwfPassword.Buffer,
  1782. NT_OWF_PASSWORD_LENGTH
  1783. ) == NT_OWF_PASSWORD_LENGTH))
  1784. &&
  1785. (!UserAll->LmPasswordPresent || (RtlCompareMemory(
  1786. &NlpNullLmOwfPassword,
  1787. UserAll->LmOwfPassword.Buffer,
  1788. LM_OWF_PASSWORD_LENGTH
  1789. ) == LM_OWF_PASSWORD_LENGTH))
  1790. &&
  1791. ( ((NtLmCheckProcessOption( MSV1_0_OPTION_ALLOW_BLANK_PASSWORD )) & MSV1_0_OPTION_ALLOW_BLANK_PASSWORD) == 0 )
  1792. )
  1793. {
  1794. *Authoritative = FALSE;
  1795. Status = STATUS_ACCOUNT_RESTRICTION;
  1796. goto Cleanup;
  1797. }
  1798. //
  1799. // Filter the groups into global groups (from other domains) and local
  1800. // groups (from this domain).
  1801. //
  1802. Status = MsvpFilterGroupMembership(
  1803. &GroupMembership,
  1804. LogonDomainId,
  1805. &GroupsBuffer,
  1806. &GlobalGroupMembership,
  1807. &GlobalMembershipSize
  1808. );
  1809. if ( !NT_SUCCESS(Status) ) {
  1810. *Authoritative = FALSE;
  1811. goto Cleanup;
  1812. }
  1813. Cleanup:
  1814. if (NT_SUCCESS(Status) || fSubAuthEx)
  1815. {
  1816. UNICODE_STRING ReturnDnsDomainName;
  1817. BOOLEAN UseDefaultUpn = FALSE;
  1818. //
  1819. // Allocate a return buffer for validation information.
  1820. // (Return less information for a network logon)
  1821. // (Return UserParameters for a MNS logon)
  1822. //
  1823. ValidationSamSize = sizeof( NETLOGON_VALIDATION_SAM_INFO4 ) +
  1824. GroupsBuffer.MembershipCount * sizeof(GROUP_MEMBERSHIP) +
  1825. LogonDomainName->Length + sizeof(WCHAR) +
  1826. LogonServer->Length + sizeof(WCHAR) +
  1827. RtlLengthSid( LogonDomainId );
  1828. //
  1829. // all logon types get the username, as, this could be mapped
  1830. // to guest account, for instance, and logon session needs correct
  1831. // names.
  1832. //
  1833. ValidationSamSize +=
  1834. UserAll->UserName.Length + sizeof(WCHAR) ;
  1835. if ( LogonLevel != NetlogonNetworkInformation ) {
  1836. ValidationSamSize +=
  1837. UserAll->FullName.Length + sizeof(WCHAR) +
  1838. UserAll->ScriptPath.Length + sizeof(WCHAR)+
  1839. UserAll->ProfilePath.Length + sizeof(WCHAR) +
  1840. UserAll->HomeDirectory.Length + sizeof(WCHAR) +
  1841. UserAll->HomeDirectoryDrive.Length + sizeof(WCHAR);
  1842. }
  1843. if ( LogonInfo->ParameterControl & MSV1_0_RETURN_USER_PARAMETERS ) {
  1844. ValidationSamSize +=
  1845. UserAll->Parameters.Length + sizeof(WCHAR);
  1846. } else if ( LogonInfo->ParameterControl & MSV1_0_RETURN_PROFILE_PATH ) {
  1847. ValidationSamSize +=
  1848. UserAll->ProfilePath.Length + sizeof(WCHAR);
  1849. }
  1850. //
  1851. // If the caller can handle extra groups, let them have the groups from
  1852. // other domains.
  1853. //
  1854. if ( ValidationLevel == NetlogonValidationSamInfo2 ||
  1855. ValidationLevel == NetlogonValidationSamInfo4 ) {
  1856. ValidationSamSize += GlobalMembershipSize;
  1857. }
  1858. //
  1859. // If the caller wants the logon domain in DNS and UPN form,
  1860. // grab it.
  1861. //
  1862. if ( ValidationLevel == NetlogonValidationSamInfo4 ) {
  1863. //
  1864. // Grab the dns name of the account domain.
  1865. //
  1866. RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
  1867. if( NlpWorkstation ) {
  1868. //ReturnDnsDomainName = NtLmGlobalUnicodeDnsComputerNameString;
  1869. //
  1870. // for local accounts, DnsDomainName doesn't exist.
  1871. //
  1872. RtlInitUnicodeString( &ReturnDnsDomainName, L"" );
  1873. } else {
  1874. ReturnDnsDomainName = NtLmGlobalUnicodeDnsDomainNameString;
  1875. }
  1876. ValidationSamSize += ReturnDnsDomainName.Length + sizeof(WCHAR);
  1877. //
  1878. // If we couldn't get the UPN from SAM,
  1879. // build the default one.
  1880. //
  1881. if( Upn.Buffer != NULL )
  1882. {
  1883. UseDefaultUpn = FALSE;
  1884. ValidationSamSize += (Upn.Length + sizeof(WCHAR));
  1885. } else {
  1886. UseDefaultUpn = TRUE;
  1887. if ( !NlpWorkstation )
  1888. {
  1889. ValidationSamSize +=
  1890. UserAll->UserName.Length +
  1891. sizeof(WCHAR) +
  1892. ReturnDnsDomainName.Length + sizeof(WCHAR);
  1893. }
  1894. }
  1895. }
  1896. ValidationSamSize = ROUND_UP_COUNT( ValidationSamSize, sizeof(WCHAR) );
  1897. ValidationSam = MIDL_user_allocate( ValidationSamSize );
  1898. if ( ValidationSam == NULL ) {
  1899. *Authoritative = FALSE;
  1900. fSubAuthEx = FALSE; // avoid nasty loop condition
  1901. Status = STATUS_NO_MEMORY;
  1902. RtlReleaseResource(&NtLmGlobalCritSect);
  1903. goto Cleanup;
  1904. }
  1905. //
  1906. // Default unused fields (and ExpansionRoom) to zero.
  1907. //
  1908. RtlZeroMemory( ValidationSam, ValidationSamSize );
  1909. //
  1910. // Copy the scalars to the validation buffer.
  1911. //
  1912. Where = (PUCHAR) (ValidationSam + 1);
  1913. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_LOGOFF_TIME) != 0) {
  1914. NEW_TO_OLD_LARGE_INTEGER( SubAuthValidationInformation.LogoffTime, ValidationSam->LogoffTime );
  1915. }
  1916. else {
  1917. NEW_TO_OLD_LARGE_INTEGER( LogoffTime, ValidationSam->LogoffTime );
  1918. }
  1919. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_KICKOFF_TIME) != 0) {
  1920. NEW_TO_OLD_LARGE_INTEGER( SubAuthValidationInformation.KickoffTime, ValidationSam->KickOffTime );
  1921. }
  1922. else {
  1923. NEW_TO_OLD_LARGE_INTEGER( KickoffTime, ValidationSam->KickOffTime );
  1924. }
  1925. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_SESSION_KEY) != 0) {
  1926. ValidationSam->UserSessionKey = SubAuthValidationInformation.SessionKey;
  1927. }
  1928. else {
  1929. ValidationSam->UserSessionKey = UserSessionKey;
  1930. }
  1931. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_USER_FLAGS) != 0) {
  1932. ValidationSam->UserFlags = SubAuthValidationInformation.UserFlags;
  1933. }
  1934. else {
  1935. ValidationSam->UserFlags = UserFlags;
  1936. }
  1937. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_USER_ID) != 0) {
  1938. ValidationSam->UserId = SubAuthValidationInformation.UserId;
  1939. }
  1940. else {
  1941. ValidationSam->UserId = UserAll->UserId;
  1942. }
  1943. ValidationSam->LogonTime = UserAll->LastLogon;
  1944. ValidationSam->PasswordLastSet = UserAll->PasswordLastSet;
  1945. ValidationSam->PasswordCanChange = UserAll->PasswordCanChange;
  1946. ValidationSam->PasswordMustChange = UserAll->PasswordMustChange;
  1947. ValidationSam->LogonCount = UserAll->LogonCount;
  1948. ValidationSam->BadPasswordCount = UserAll->BadPasswordCount;
  1949. ValidationSam->PrimaryGroupId = UserAll->PrimaryGroupId;
  1950. ValidationSam->GroupCount = GroupsBuffer.MembershipCount;
  1951. ASSERT( SAMINFO_LM_SESSION_KEY_SIZE == sizeof(LmSessionKey) );
  1952. RtlCopyMemory( &ValidationSam->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
  1953. &LmSessionKey,
  1954. SAMINFO_LM_SESSION_KEY_SIZE );
  1955. ValidationSam->ExpansionRoom[SAMINFO_USER_ACCOUNT_CONTROL] = UserAll->UserAccountControl;
  1956. // Save any status for subuath users not returned by the subauth package
  1957. if (fSubAuthEx)
  1958. {
  1959. ValidationSam->ExpansionRoom[SAMINFO_SUBAUTH_STATUS] = Status;
  1960. }
  1961. //
  1962. // Copy ULONG aligned data to the validation buffer.
  1963. //
  1964. RtlCopyMemory(
  1965. Where,
  1966. GroupsBuffer.Groups,
  1967. GroupsBuffer.MembershipCount * sizeof(GROUP_MEMBERSHIP) );
  1968. ValidationSam->GroupIds = (PGROUP_MEMBERSHIP) Where;
  1969. Where += GroupsBuffer.MembershipCount * sizeof(GROUP_MEMBERSHIP);
  1970. RtlCopyMemory(
  1971. Where,
  1972. LogonDomainId,
  1973. RtlLengthSid( LogonDomainId ) );
  1974. ValidationSam->LogonDomainId = (PSID) Where;
  1975. Where += RtlLengthSid( LogonDomainId );
  1976. //
  1977. // If the client asked for extra information, return that
  1978. // we support it
  1979. //
  1980. if ( ValidationLevel == NetlogonValidationSamInfo2 ||
  1981. ValidationLevel == NetlogonValidationSamInfo4 ) {
  1982. ValidationSam->UserFlags |= LOGON_EXTRA_SIDS;
  1983. if (GlobalMembershipSize != 0) {
  1984. ULONG SidLength;
  1985. ValidationSam->SidCount = GlobalGroupMembership.Count;
  1986. ValidationSam->ExtraSids = (PNETLOGON_SID_AND_ATTRIBUTES) Where;
  1987. Where += ValidationSam->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES);
  1988. //
  1989. // Copy all the extra sids into the buffer
  1990. //
  1991. for (Index = 0; Index < ValidationSam->SidCount ; Index++ ) {
  1992. ValidationSam->ExtraSids[Index].Attributes = GlobalGroupMembership.SidAndAttributes[Index].Attributes;
  1993. ValidationSam->ExtraSids[Index].Sid = Where;
  1994. SidLength = RtlLengthSid(GlobalGroupMembership.SidAndAttributes[Index].Sid);
  1995. RtlCopyMemory(
  1996. ValidationSam->ExtraSids[Index].Sid,
  1997. GlobalGroupMembership.SidAndAttributes[Index].Sid,
  1998. SidLength
  1999. );
  2000. Where += SidLength;
  2001. }
  2002. }
  2003. }
  2004. //
  2005. // Copy WCHAR aligned data to the validation buffer.
  2006. // (Return less information for a network logon)
  2007. //
  2008. Where = ROUND_UP_POINTER( Where, sizeof(WCHAR) );
  2009. NlpPutString( &ValidationSam->EffectiveName,
  2010. (PUNICODE_STRING)&UserAll->UserName,
  2011. &Where );
  2012. if ( LogonLevel != NetlogonNetworkInformation ) {
  2013. NlpPutString( &ValidationSam->FullName,
  2014. (PUNICODE_STRING)&UserAll->FullName,
  2015. &Where );
  2016. NlpPutString( &ValidationSam->LogonScript,
  2017. (PUNICODE_STRING)&UserAll->ScriptPath,
  2018. &Where );
  2019. NlpPutString( &ValidationSam->ProfilePath,
  2020. (PUNICODE_STRING)&UserAll->ProfilePath,
  2021. &Where );
  2022. NlpPutString( &ValidationSam->HomeDirectory,
  2023. (PUNICODE_STRING)&UserAll->HomeDirectory,
  2024. &Where );
  2025. NlpPutString( &ValidationSam->HomeDirectoryDrive,
  2026. (PUNICODE_STRING)&UserAll->HomeDirectoryDrive,
  2027. &Where );
  2028. }
  2029. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_LOGON_SERVER) != 0) {
  2030. NlpPutString( &ValidationSam->LogonServer,
  2031. &SubAuthValidationInformation.LogonServer,
  2032. &Where );
  2033. }
  2034. else {
  2035. NlpPutString( &ValidationSam->LogonServer,
  2036. LogonServer,
  2037. &Where );
  2038. }
  2039. if ((SubAuthValidationInformation.WhichFields & MSV1_0_VALIDATION_LOGON_DOMAIN) != 0) {
  2040. NlpPutString( &ValidationSam->LogonDomainName,
  2041. &SubAuthValidationInformation.LogonDomainName,
  2042. &Where );
  2043. }
  2044. else {
  2045. NlpPutString( &ValidationSam->LogonDomainName,
  2046. LogonDomainName,
  2047. &Where );
  2048. }
  2049. //
  2050. // If the caller wants the logon domain in DNS and UPN form,
  2051. // return them.
  2052. //
  2053. if ( ValidationLevel == NetlogonValidationSamInfo4 ) {
  2054. //
  2055. // Copy the DNS domain name into the allocated buffer
  2056. //
  2057. NlpPutString( &ValidationSam->DnsLogonDomainName,
  2058. &ReturnDnsDomainName,
  2059. &Where );
  2060. //
  2061. // If we couldn't get the UPN from SAM,
  2062. // copy the default one into the buffer.
  2063. //
  2064. if ( !NlpWorkstation )
  2065. {
  2066. if( UseDefaultUpn )
  2067. {
  2068. UNICODE_STRING TempString1;
  2069. UNICODE_STRING TempString2;
  2070. UNICODE_STRING TempString3;
  2071. UNICODE_STRING AtString;
  2072. NlpPutString( &TempString1,
  2073. (PUNICODE_STRING)&UserAll->UserName,
  2074. &Where );
  2075. Where -= sizeof(WCHAR);
  2076. RtlInitUnicodeString( &AtString, L"@" );
  2077. NlpPutString( &TempString2,
  2078. &AtString,
  2079. &Where );
  2080. Where -= sizeof(WCHAR);
  2081. NlpPutString( &TempString3,
  2082. &ReturnDnsDomainName,
  2083. &Where );
  2084. ValidationSam->Upn.Buffer = TempString1.Buffer;
  2085. ValidationSam->Upn.Length =
  2086. TempString1.Length +
  2087. TempString2.Length +
  2088. TempString3.Length;
  2089. ValidationSam->Upn.MaximumLength = ValidationSam->Upn.Length + sizeof(WCHAR);
  2090. } else {
  2091. UNICODE_STRING TempString1;
  2092. NlpPutString( &TempString1,
  2093. &Upn,
  2094. &Where );
  2095. Where -= sizeof(WCHAR);
  2096. ValidationSam->Upn.Buffer = TempString1.Buffer;
  2097. ValidationSam->Upn.Length = TempString1.Length;
  2098. ValidationSam->Upn.MaximumLength = ValidationSam->Upn.Length;
  2099. }
  2100. }
  2101. //
  2102. // Drop the lock that we've held since we grabbed pointer to the globals
  2103. //
  2104. RtlReleaseResource(&NtLmGlobalCritSect);
  2105. }
  2106. //
  2107. // Kludge: Pass back UserParameters in HomeDirectoryDrive since we
  2108. // can't change the NETLOGON_VALIDATION_SAM_INFO structure between
  2109. // releases NT 3.1 and NT 3.5. HomeDirectoryDrive was NULL for release 3.1
  2110. // so we'll use that field.
  2111. //
  2112. if ( LogonInfo->ParameterControl & MSV1_0_RETURN_USER_PARAMETERS ) {
  2113. NlpPutString( &ValidationSam->HomeDirectoryDrive,
  2114. (PUNICODE_STRING)&UserAll->Parameters,
  2115. &Where );
  2116. } else if ( LogonInfo->ParameterControl & MSV1_0_RETURN_PROFILE_PATH ) {
  2117. NlpPutString( &ValidationSam->HomeDirectoryDrive,
  2118. (PUNICODE_STRING)&UserAll->ProfilePath,
  2119. &Where );
  2120. ValidationSam->UserFlags |= LOGON_PROFILE_PATH_RETURNED;
  2121. }
  2122. *Authoritative = TRUE;
  2123. //
  2124. // For SubAuthEx, we save away the original Status to make decisions
  2125. // later on about additional processing to perform.
  2126. //
  2127. if( fSubAuthEx ) {
  2128. SubAuthExStatus = Status;
  2129. }
  2130. Status = STATUS_SUCCESS;
  2131. }
  2132. //
  2133. // Cleanup up before returning.
  2134. //
  2135. //
  2136. // If the User Parameters have been changed,
  2137. // write them back to SAM.
  2138. //
  2139. if ( NT_SUCCESS(Status) &&
  2140. (WhichFields & USER_ALL_PARAMETERS) )
  2141. {
  2142. SAMPR_USER_INFO_BUFFER UserInfo;
  2143. UserInfo.Parameters.Parameters = UserAll->Parameters;
  2144. Status = I_SamrSetInformationUser(
  2145. UserHandle,
  2146. UserParametersInformation,
  2147. &UserInfo );
  2148. }
  2149. //
  2150. // Update the logon statistics.
  2151. //
  2152. if ( NT_SUCCESS( SubAuthExStatus ) &&
  2153. ( NT_SUCCESS(Status)
  2154. || Status == STATUS_WRONG_PASSWORD
  2155. || Status == STATUS_NO_LOGON_SERVERS ) ) {
  2156. SAM_LOGON_STATISTICS LogonStats;
  2157. RtlZeroMemory(&LogonStats, sizeof(LogonStats));
  2158. if ( NT_SUCCESS( Status ) ) {
  2159. if ( LogonLevel == NetlogonInteractiveInformation ) {
  2160. LogonStats.StatisticsToApply =
  2161. USER_LOGON_INTER_SUCCESS_LOGON;
  2162. } else {
  2163. //
  2164. // On network logons,
  2165. // only update the statistics on 'success' if explicitly asked,
  2166. // or the Bad Password count will be zeroed.
  2167. //
  2168. LogonStats.StatisticsToApply =
  2169. USER_LOGON_NET_SUCCESS_LOGON | USER_LOGON_NO_WRITE;
  2170. if ( (LogonInfo->ParameterControl & MSV1_0_UPDATE_LOGON_STATISTICS) ||
  2171. UserAll->BadPasswordCount != 0 ) {
  2172. LogonStats.StatisticsToApply &= ~USER_LOGON_NO_WRITE;
  2173. }
  2174. }
  2175. // Tell the caller we zeroed the bad password count
  2176. if ( UserAll->BadPasswordCount != 0 ) {
  2177. *BadPasswordCountZeroed = TRUE;
  2178. }
  2179. } else {
  2180. if (Status == STATUS_WRONG_PASSWORD) {
  2181. LogonStats.StatisticsToApply = USER_LOGON_BAD_PASSWORD_WKSTA;
  2182. LogonStats.Workstation = LogonInfo->Workstation;
  2183. //
  2184. // if it didn't match one of the (two) previous, it was a bad password.
  2185. //
  2186. if(!MsvpCheckPreviousPassword(
  2187. UasCompatibilityRequired,
  2188. LogonLevel,
  2189. LogonInformation,
  2190. DomainHandle,
  2191. &LocalUserName
  2192. ))
  2193. {
  2194. LogonStats.StatisticsToApply |= USER_LOGON_BAD_PASSWORD;
  2195. }
  2196. } else {
  2197. LogonStats.StatisticsToApply = USER_LOGON_NO_LOGON_SERVERS;
  2198. if (LogonLevel == NetlogonInteractiveInformation) {
  2199. LogonStats.StatisticsToApply |= USER_LOGON_INTER_FAILURE;
  2200. }
  2201. }
  2202. }
  2203. if ( LogonStats.StatisticsToApply != 0 ) {
  2204. NTSTATUS LogonStatus;
  2205. LogonStats.StatisticsToApply |= USER_LOGON_TYPE_NTLM;
  2206. LogonStatus = I_SamIUpdateLogonStatistics(
  2207. UserHandle,
  2208. &LogonStats );
  2209. }
  2210. }
  2211. //
  2212. // Audit this logon. We don't audit failures for the guest account because
  2213. // they are so frequent.
  2214. //
  2215. if (GuestRelativeId == 0 || NT_SUCCESS(Status)) {
  2216. NTSTATUS AuditStatus;
  2217. AuditStatus = Status;
  2218. //
  2219. // if there was a possibly un-successful SubAuthEx status, use it
  2220. //
  2221. if( NT_SUCCESS( AuditStatus ) && fSubAuthEx ) {
  2222. AuditStatus = SubAuthExStatus;
  2223. }
  2224. if ( ValidationSam ) {
  2225. UserSid = NlpMakeDomainRelativeSid(
  2226. ValidationSam->LogonDomainId,
  2227. ValidationSam->UserId
  2228. );
  2229. } else {
  2230. UserSid = NULL;
  2231. }
  2232. I_LsaIAuditAccountLogonEx(
  2233. SE_AUDITID_ACCOUNT_LOGON,
  2234. (BOOLEAN) NT_SUCCESS(AuditStatus),
  2235. &NlpMsv1_0PackageName,
  2236. &LocalUserName,
  2237. &LogonInfo->Workstation,
  2238. AuditStatus,
  2239. UserSid
  2240. );
  2241. if ( ValidationSam && UserSid ) {
  2242. LsaFunctions->FreeLsaHeap( UserSid );
  2243. UserSid = NULL;
  2244. }
  2245. }
  2246. //
  2247. // Return the validation buffer to the caller.
  2248. //
  2249. if ( !NT_SUCCESS(Status) ) {
  2250. if (ValidationSam != NULL) {
  2251. MIDL_user_free( ValidationSam );
  2252. ValidationSam = NULL;
  2253. }
  2254. }
  2255. *ValidationInformation = ValidationSam;
  2256. //
  2257. // Free locally used resources.
  2258. //
  2259. I_SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &NameArray );
  2260. I_SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
  2261. if ( UserAllInfo != NULL ) {
  2262. I_SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
  2263. }
  2264. if( Upn.Buffer != NULL )
  2265. {
  2266. I_SamIFreeVoid( Upn.Buffer );
  2267. }
  2268. if (GroupMembership.SidAndAttributes != NULL)
  2269. {
  2270. I_SamIFreeSidAndAttributesList(&GroupMembership);
  2271. }
  2272. if ( GroupsBuffer.Groups != NULL ) {
  2273. I_NtLmFree(GroupsBuffer.Groups);
  2274. }
  2275. if ( GlobalGroupMembership.SidAndAttributes != NULL ) {
  2276. I_NtLmFree(GlobalGroupMembership.SidAndAttributes);
  2277. }
  2278. if ( UserHandle != NULL ) {
  2279. I_SamrCloseHandle( &UserHandle );
  2280. }
  2281. if (SubAuthValidationInformation.LogonDomainName.Buffer != NULL) {
  2282. MIDL_user_free(SubAuthValidationInformation.LogonDomainName.Buffer);
  2283. }
  2284. if (SubAuthValidationInformation.LogonServer.Buffer != NULL) {
  2285. MIDL_user_free(SubAuthValidationInformation.LogonServer.Buffer);
  2286. }
  2287. if (LocalSidUser != NULL) {
  2288. I_NtLmFree(LocalSidUser);
  2289. }
  2290. return Status;
  2291. }
  2292. BOOLEAN
  2293. MsvpCheckPreviousPassword(
  2294. IN BOOLEAN UasCompatibilityRequired,
  2295. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2296. IN PVOID LogonInformation,
  2297. IN SAMPR_HANDLE DomainHandle,
  2298. PUNICODE_STRING UserName
  2299. )
  2300. {
  2301. PSAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE PrivateData;
  2302. NTSTATUS Status;
  2303. SAMPR_HANDLE UserHandle = NULL;
  2304. PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
  2305. PSAMPR_USER_ALL_INFORMATION UserAll = NULL;
  2306. SID_AND_ATTRIBUTES_LIST GroupMembership;
  2307. USER_INTERNAL1_INFORMATION Passwords;
  2308. NT_OWF_PASSWORD OldPasswordData = {0}; // Previous password
  2309. NT_OWF_PASSWORD OldPasswordDataSecond = {0}; // Previous previous password
  2310. USER_SESSION_KEY UserSessionKey;
  2311. LM_SESSION_KEY LmSessionKey;
  2312. ULONG UserFlags;
  2313. BOOLEAN TryPrevious = FALSE;
  2314. BOOLEAN TryPreviousPrevious = FALSE;
  2315. BOOLEAN fMatchesOld = FALSE;
  2316. GroupMembership.SidAndAttributes = NULL;
  2317. //
  2318. // query the account, to get the PRIVATEDATA ( password history ).
  2319. //
  2320. Status = I_SamIGetUserLogonInformationEx(
  2321. DomainHandle,
  2322. SAM_NO_MEMBERSHIPS,
  2323. UserName,
  2324. USER_ALL_USERID | USER_ALL_PRIVATEDATA | USER_ALL_OWFPASSWORD,
  2325. &UserAllInfo,
  2326. &GroupMembership,
  2327. &UserHandle
  2328. );
  2329. if(!NT_SUCCESS(Status))
  2330. {
  2331. return FALSE;
  2332. }
  2333. UserAll = &UserAllInfo->All;
  2334. if (UserAll->PrivateData.Length >= sizeof(SAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE))
  2335. {
  2336. PrivateData= (PSAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE) UserAll->PrivateData.Buffer;
  2337. if (PrivateData->DataType == SamPrivateDataPassword)
  2338. {
  2339. //
  2340. // The old password is the 2nd entry
  2341. //
  2342. if (PrivateData->NtPasswordHistory.Length >= 2* sizeof(ENCRYPTED_NT_OWF_PASSWORD))
  2343. {
  2344. //
  2345. // Decrypt the old password with the RID. The history starts
  2346. // at the first byte after the structure.
  2347. //
  2348. Status = RtlDecryptNtOwfPwdWithIndex(
  2349. (PENCRYPTED_NT_OWF_PASSWORD) (PrivateData + 1) + 1,
  2350. (PLONG)&UserAll->UserId,
  2351. &OldPasswordData
  2352. );
  2353. if (!NT_SUCCESS(Status))
  2354. {
  2355. goto Cleanup;
  2356. }
  2357. TryPrevious = TRUE;
  2358. }
  2359. // Now check for the second previous password - this will be the third password in history
  2360. if (PrivateData->NtPasswordHistory.Length >= 3 * sizeof(ENCRYPTED_NT_OWF_PASSWORD))
  2361. {
  2362. //
  2363. // Decrypt the old password with the RID. The history starts
  2364. // at the first byte after the structure.
  2365. //
  2366. Status = RtlDecryptNtOwfPwdWithIndex(
  2367. (PENCRYPTED_NT_OWF_PASSWORD) (PrivateData + 1) + 2,
  2368. (PLONG)&UserAll->UserId,
  2369. &OldPasswordDataSecond
  2370. );
  2371. if (!NT_SUCCESS(Status))
  2372. {
  2373. goto Cleanup;
  2374. }
  2375. TryPreviousPrevious = TRUE;
  2376. }
  2377. }
  2378. }
  2379. //
  2380. // call password validate for each.
  2381. //
  2382. Passwords.NtPasswordPresent = TRUE;
  2383. Passwords.LmPasswordPresent = FALSE;
  2384. if( TryPrevious )
  2385. {
  2386. CopyMemory( &(Passwords.NtOwfPassword), &OldPasswordData, sizeof(OldPasswordData) );
  2387. //
  2388. // If the password specified doesn't match the SAM password,
  2389. // then we've got a password mismatch.
  2390. //
  2391. if ( MsvpPasswordValidate (
  2392. UasCompatibilityRequired,
  2393. LogonLevel,
  2394. LogonInformation,
  2395. &Passwords,
  2396. &UserFlags,
  2397. &UserSessionKey,
  2398. &LmSessionKey ) )
  2399. {
  2400. RtlSecureZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
  2401. RtlSecureZeroMemory( &LmSessionKey, sizeof(LmSessionKey) );
  2402. fMatchesOld = TRUE;
  2403. goto Cleanup;
  2404. }
  2405. }
  2406. if( TryPreviousPrevious )
  2407. {
  2408. CopyMemory( &(Passwords.NtOwfPassword), &OldPasswordDataSecond, sizeof(OldPasswordDataSecond) );
  2409. //
  2410. // If the password specified doesn't match the SAM password,
  2411. // then we've got a password mismatch.
  2412. //
  2413. if ( MsvpPasswordValidate (
  2414. UasCompatibilityRequired,
  2415. LogonLevel,
  2416. LogonInformation,
  2417. &Passwords,
  2418. &UserFlags,
  2419. &UserSessionKey,
  2420. &LmSessionKey ) )
  2421. {
  2422. RtlSecureZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
  2423. RtlSecureZeroMemory( &LmSessionKey, sizeof(LmSessionKey) );
  2424. fMatchesOld = TRUE;
  2425. goto Cleanup;
  2426. }
  2427. }
  2428. Cleanup:
  2429. RtlSecureZeroMemory( &OldPasswordData, sizeof(OldPasswordData) );
  2430. RtlSecureZeroMemory( &OldPasswordDataSecond, sizeof(OldPasswordDataSecond) );
  2431. RtlSecureZeroMemory( &(Passwords.NtOwfPassword), sizeof(Passwords.NtOwfPassword) );
  2432. if (GroupMembership.SidAndAttributes != NULL)
  2433. {
  2434. I_SamIFreeSidAndAttributesList(&GroupMembership);
  2435. }
  2436. if ( UserAllInfo != NULL ) {
  2437. I_SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
  2438. }
  2439. if ( UserHandle != NULL ) {
  2440. I_SamrCloseHandle( &UserHandle );
  2441. }
  2442. return fMatchesOld;
  2443. }
  2444. NTSTATUS
  2445. MsvSamValidate (
  2446. IN SAM_HANDLE DomainHandle,
  2447. IN BOOLEAN UasCompatibilityRequired,
  2448. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  2449. IN PUNICODE_STRING LogonServer,
  2450. IN PUNICODE_STRING LogonDomainName,
  2451. IN PSID LogonDomainId,
  2452. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2453. IN PVOID LogonInformation,
  2454. IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
  2455. OUT PVOID * ValidationInformation,
  2456. OUT PBOOLEAN Authoritative,
  2457. OUT PBOOLEAN BadPasswordCountZeroed,
  2458. IN DWORD AccountsToTry
  2459. )
  2460. /*++
  2461. Routine Description:
  2462. Process an interactive, network, or session logon. It calls
  2463. SamIUserValidation, validates the passed in credentials, updates the logon
  2464. statistics and packages the result for return to the caller.
  2465. This routine is called directly from the MSV Authentication package
  2466. if the account is defined locally. This routine is called
  2467. from the Netlogon Service otherwise.
  2468. Arguments:
  2469. DomainHandle -- Specifies a handle to the SamDomain to use to
  2470. validate the request.
  2471. UasCompatibilityRequired -- TRUE if UasCompatibilityRequired is on.
  2472. SecureChannelType -- The secure channel type this request was made on.
  2473. LogonServer -- Specifies the server name of the caller.
  2474. LogonDomainName -- Specifies the domain of the caller.
  2475. LogonDomainId -- Specifies the DomainId of the domain of the caller.
  2476. LogonLevel -- Specifies the level of information given in
  2477. LogonInformation.
  2478. LogonInformation -- Specifies the description for the user
  2479. logging on. The LogonDomainName field should be ignored.
  2480. The caller is responsible for validating this field.
  2481. ValidationLevel -- Specifies the level of information returned in
  2482. ValidationInformation. Must be NetlogonValidationSamInfo,
  2483. NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4
  2484. ValidationInformation -- Returns the requested validation
  2485. information. This buffer must be freed user MIDL_user_free.
  2486. Authoritative -- Returns whether the status returned is an
  2487. authoritative status which should be returned to the original
  2488. caller. If not, this logon request may be tried again on another
  2489. domain controller. This parameter is returned regardless of the
  2490. status code.
  2491. BadPasswordCountZeroed - Returns TRUE if we zeroed the BadPasswordCount
  2492. field of this user.
  2493. AccountsToTry -- Specifies whether the username specified in
  2494. LogonInformation is to be used to logon, whether to guest account
  2495. is to be used to logon, or both serially.
  2496. Return Value:
  2497. STATUS_SUCCESS: if there was no error.
  2498. STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
  2499. STATUS_NO_SUCH_USER: The specified user has no account.
  2500. STATUS_WRONG_PASSWORD: The password was invalid.
  2501. Other return codes from SamIUserValidation
  2502. --*/
  2503. {
  2504. NTSTATUS Status;
  2505. NTSTATUS GuestStatus;
  2506. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  2507. //
  2508. // Tracing
  2509. //
  2510. NTLM_TRACE_INFO TraceInfo = {0};
  2511. //
  2512. // Begin tracing a sam validate call for NTLM
  2513. //
  2514. if (NtlmGlobalEventTraceFlag){
  2515. //Header goo
  2516. SET_TRACE_HEADER(TraceInfo,
  2517. NtlmValidateGuid,
  2518. EVENT_TRACE_TYPE_START,
  2519. WNODE_FLAG_TRACED_GUID,
  2520. 0);
  2521. TraceEvent(NtlmGlobalTraceLoggerHandle,
  2522. (PEVENT_TRACE_HEADER)&TraceInfo);
  2523. }
  2524. LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  2525. //
  2526. // if simple file/print option is enabled (applies to SSPI callers),
  2527. // check if ForceGuest should occur.
  2528. //
  2529. if( (LogonInfo->ParameterControl & MSV1_0_ALLOW_FORCE_GUEST) )
  2530. {
  2531. if( NtLmGlobalForceGuest )
  2532. {
  2533. if((LogonInfo->ParameterControl & MSV1_0_DISABLE_PERSONAL_FALLBACK)==0)
  2534. {
  2535. if( (LogonLevel == NetlogonNetworkInformation) &&
  2536. (((NtLmCheckProcessOption( MSV1_0_OPTION_DISABLE_FORCE_GUEST ) & MSV1_0_OPTION_DISABLE_FORCE_GUEST )) == 0)
  2537. )
  2538. {
  2539. AccountsToTry &= ~(MSVSAM_SPECIFIED);
  2540. AccountsToTry |= MSVSAM_GUEST;
  2541. }
  2542. }
  2543. }
  2544. }
  2545. //
  2546. // Validate the specified user.
  2547. //
  2548. *BadPasswordCountZeroed = FALSE;
  2549. if ( AccountsToTry & MSVSAM_SPECIFIED ) {
  2550. //
  2551. // Keep track of the total number of logons attempted.
  2552. //
  2553. I_SamIIncrementPerformanceCounter(
  2554. MsvLogonCounter
  2555. );
  2556. InterlockedIncrement((LPLONG) &NlpLogonAttemptCount);
  2557. Status = MsvpSamValidate( (SAMPR_HANDLE) DomainHandle,
  2558. UasCompatibilityRequired,
  2559. SecureChannelType,
  2560. LogonServer,
  2561. LogonDomainName,
  2562. LogonDomainId,
  2563. LogonLevel,
  2564. LogonInformation,
  2565. 0,
  2566. ValidationLevel,
  2567. ValidationInformation,
  2568. Authoritative,
  2569. BadPasswordCountZeroed );
  2570. //
  2571. // If the SAM database authoritatively handled this logon attempt,
  2572. // just return.
  2573. //
  2574. if ( *Authoritative ) {
  2575. goto Cleanup;
  2576. }
  2577. //
  2578. // If the caller only wants to log on as guest,
  2579. // Pretend the first validation simply didn't find the user.
  2580. //
  2581. } else {
  2582. *Authoritative = FALSE;
  2583. Status = STATUS_NO_SUCH_USER;
  2584. }
  2585. //
  2586. // If guest accounts are not allowed,
  2587. // return now.
  2588. //
  2589. if ( LogonLevel != NetlogonNetworkInformation ||
  2590. SecureChannelType != MsvApSecureChannel ||
  2591. ( LogonInfo->ParameterControl & MSV1_0_DONT_TRY_GUEST_ACCOUNT ) ||
  2592. (AccountsToTry & MSVSAM_GUEST) == 0 ) {
  2593. goto Cleanup;
  2594. //return Status;
  2595. }
  2596. //
  2597. // Try the Guest Account.
  2598. //
  2599. GuestStatus = MsvpSamValidate( (SAMPR_HANDLE) DomainHandle,
  2600. UasCompatibilityRequired,
  2601. SecureChannelType,
  2602. LogonServer,
  2603. LogonDomainName,
  2604. LogonDomainId,
  2605. LogonLevel,
  2606. LogonInformation,
  2607. DOMAIN_USER_RID_GUEST,
  2608. ValidationLevel,
  2609. ValidationInformation,
  2610. Authoritative,
  2611. BadPasswordCountZeroed );
  2612. if ( NT_SUCCESS(GuestStatus) ) {
  2613. PNETLOGON_VALIDATION_SAM_INFO4 ValidationInfo;
  2614. ASSERT ((ValidationLevel == NetlogonValidationSamInfo) ||
  2615. (ValidationLevel == NetlogonValidationSamInfo2) ||
  2616. (ValidationLevel == NetlogonValidationSamInfo4) );
  2617. ValidationInfo =
  2618. (PNETLOGON_VALIDATION_SAM_INFO4) *ValidationInformation;
  2619. ValidationInfo->UserFlags |= LOGON_GUEST;
  2620. Status = GuestStatus;
  2621. goto Cleanup;
  2622. //return GuestStatus;
  2623. }
  2624. //
  2625. // Failed Guest logon attempts are never authoritative and the status from
  2626. // the original logon attempt is more significant than the Guest logon
  2627. // status.
  2628. //
  2629. *Authoritative = FALSE;
  2630. Cleanup:
  2631. //
  2632. // Trace the end of this call
  2633. //
  2634. if (NtlmGlobalEventTraceFlag){
  2635. UINT32 Success;
  2636. PNETLOGON_LOGON_IDENTITY_INFO LogonInfo =
  2637. (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
  2638. SET_TRACE_HEADER(TraceInfo,
  2639. NtlmValidateGuid,
  2640. EVENT_TRACE_TYPE_END,
  2641. WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
  2642. 9);
  2643. //
  2644. // Build the "success" trace state bit mask
  2645. // 1bit - Success
  2646. // 2bit - Authoritative
  2647. //
  2648. Success = (Status == STATUS_SUCCESS)?1:0;
  2649. Success |= (*Authoritative)?2:0;
  2650. SET_TRACE_DATA(TraceInfo,
  2651. TRACE_VALIDATE_SUCCESS,
  2652. Success);
  2653. SET_TRACE_USTRING(TraceInfo,
  2654. TRACE_VALIDATE_SERVER,
  2655. (*LogonServer));
  2656. SET_TRACE_USTRING(TraceInfo,
  2657. TRACE_VALIDATE_DOMAIN,
  2658. (*LogonDomainName));
  2659. SET_TRACE_USTRING(TraceInfo,
  2660. TRACE_VALIDATE_USERNAME,
  2661. LogonInfo->UserName);
  2662. SET_TRACE_USTRING(TraceInfo,
  2663. TRACE_VALIDATE_WORKSTATION,
  2664. LogonInfo->Workstation);
  2665. TraceEvent(
  2666. NtlmGlobalTraceLoggerHandle,
  2667. (PEVENT_TRACE_HEADER)&TraceInfo
  2668. );
  2669. }
  2670. return Status;
  2671. }
  2672. NTSTATUS
  2673. MsvSamLogoff (
  2674. IN SAM_HANDLE DomainHandle,
  2675. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  2676. IN PVOID LogonInformation
  2677. )
  2678. /*++
  2679. Routine Description:
  2680. Process an interactive, network, or session logoff. It simply updates
  2681. the logon statistics for the user account.
  2682. This routine is called directly from the MSV Authentication package
  2683. if the user was logged on not using the Netlogon service. This routine
  2684. is called from the Netlogon Service otherwise.
  2685. Arguments:
  2686. DomainHandle -- Specifies a handle to the SamDomain containing
  2687. the user to logoff.
  2688. LogonLevel -- Specifies the level of information given in
  2689. LogonInformation.
  2690. LogonInformation -- Specifies the description for the user
  2691. logging on. The LogonDomainName field should be ignored.
  2692. The caller is responsible for validating this field.
  2693. Return Value:
  2694. STATUS_SUCCESS: if there was no error.
  2695. STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
  2696. STATUS_NO_SUCH_USER: The specified user has no account.
  2697. STATUS_WRONG_PASSWORD: The password was invalid.
  2698. Other return codes from SamIUserValidation
  2699. --*/
  2700. {
  2701. return(STATUS_SUCCESS);
  2702. UNREFERENCED_PARAMETER( DomainHandle );
  2703. UNREFERENCED_PARAMETER( LogonLevel );
  2704. UNREFERENCED_PARAMETER( LogonInformation );
  2705. }
  2706. ULONG
  2707. MsvGetLogonAttemptCount (
  2708. VOID
  2709. )
  2710. /*++
  2711. Routine Description:
  2712. Return the number of logon attempts since the last reboot.
  2713. Arguments:
  2714. NONE
  2715. Return Value:
  2716. Returns the number of logon attempts since the last reboot.
  2717. --*/
  2718. {
  2719. //
  2720. // Keep track of the total number of logons attempted.
  2721. //
  2722. return NlpLogonAttemptCount;
  2723. }