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

1352 lines
39 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. logon.c
  5. Abstract:
  6. This module contains the routines called by MSV1_0 authentication package.
  7. Author:
  8. Yi-Hsin Sung (yihsins)
  9. Andy Herron (andyhe) 06-Jun-1994 Added support for MSV1_0 subauthority
  10. Revision History:
  11. Andy Herron (andyhe) 15-Aug-1994 Pulled out (older) unused MSV1_0
  12. subauthority routines.
  13. --*/
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <ntsam.h>
  18. #include <windows.h>
  19. #include <ntmsv1_0.h>
  20. #include <crypt.h>
  21. #include <fpnwcomm.h>
  22. #include <usrprop.h>
  23. #include <samrpc.h>
  24. #include <samisrv.h>
  25. #include <ntlsa.h>
  26. #include <lsarpc.h>
  27. #include <lsaisrv.h>
  28. #include <lmcons.h>
  29. #include <logonmsv.h>
  30. #define RESPONSE_SIZE 8
  31. #define WKSTA_ADDRESS_SIZE 20
  32. #define NET_ADDRESS_SIZE 8
  33. #define NODE_ADDRESS_SIZE 12
  34. #define MSV1_0_PASSTHRU 0x01
  35. #define MSV1_0_GUEST_LOGON 0x02
  36. #ifndef LOGON_SUBAUTH_SESSION_KEY
  37. #define LOGON_SUBAUTH_SESSION_KEY 0x40
  38. #endif
  39. typedef NTSTATUS (*PF_SamIConnect)(
  40. IN PSAMPR_SERVER_NAME ServerName,
  41. OUT SAMPR_HANDLE *ServerHandle,
  42. IN ACCESS_MASK DesiredAccess,
  43. IN BOOLEAN TrustedClient
  44. );
  45. typedef NTSTATUS (*PF_SamrOpenUser)(
  46. SAMPR_HANDLE DomainHandle,
  47. ACCESS_MASK DesiredAccess,
  48. ULONG UserId,
  49. SAMPR_HANDLE __RPC_FAR *UserHandle);
  50. typedef NTSTATUS (*PF_SamrCloseHandle)(
  51. SAMPR_HANDLE __RPC_FAR *SamHandle);
  52. typedef NTSTATUS (*PF_SamrQueryInformationDomain)(
  53. SAMPR_HANDLE DomainHandle,
  54. DOMAIN_INFORMATION_CLASS DomainInformationClass,
  55. PSAMPR_DOMAIN_INFO_BUFFER __RPC_FAR *Buffer);
  56. typedef NTSTATUS (*PF_SamrOpenDomain)(
  57. SAMPR_HANDLE ServerHandle,
  58. ACCESS_MASK DesiredAccess,
  59. PRPC_SID DomainId,
  60. SAMPR_HANDLE __RPC_FAR *DomainHandle);
  61. typedef NTSTATUS (*PF_SamIAccountRestrictions)(
  62. IN SAM_HANDLE UserHandle,
  63. IN PUNICODE_STRING LogonWorkstation,
  64. IN PUNICODE_STRING Workstations,
  65. IN PLOGON_HOURS LogonHours,
  66. OUT PLARGE_INTEGER LogoffTime,
  67. OUT PLARGE_INTEGER KickoffTime
  68. );
  69. typedef VOID (*PF_SamIFree_SAMPR_DOMAIN_INFO_BUFFER )(
  70. PSAMPR_DOMAIN_INFO_BUFFER Source,
  71. DOMAIN_INFORMATION_CLASS Branch
  72. );
  73. typedef NTSTATUS (NTAPI *PF_LsaIQueryInformationPolicyTrusted)(
  74. IN POLICY_INFORMATION_CLASS InformationClass,
  75. OUT PLSAPR_POLICY_INFORMATION *Buffer
  76. );
  77. typedef VOID (NTAPI *PF_LsaIFree_LSAPR_POLICY_INFORMATION )(
  78. IN POLICY_INFORMATION_CLASS InformationClass,
  79. IN PLSAPR_POLICY_INFORMATION PolicyInformation
  80. );
  81. typedef NTSTATUS (NTAPI *PF_LsaIOpenPolicyTrusted)(
  82. OUT PLSAPR_HANDLE PolicyHandle
  83. );
  84. typedef NTSTATUS (*PF_LsarQueryInformationPolicy)(
  85. LSAPR_HANDLE PolicyHandle,
  86. POLICY_INFORMATION_CLASS InformationClass,
  87. PLSAPR_POLICY_INFORMATION __RPC_FAR *PolicyInformation);
  88. PF_SamIConnect pfSamIConnect = NULL;
  89. PF_SamrOpenUser pfSamrOpenUser = NULL;
  90. PF_SamrCloseHandle pfSamrCloseHandle = NULL;
  91. PF_SamrQueryInformationDomain pfSamrQueryInformationDomain = NULL;
  92. PF_SamrOpenDomain pfSamrOpenDomain = NULL;
  93. PF_SamIAccountRestrictions pfSamIAccountRestrictions = NULL;
  94. PF_SamIFree_SAMPR_DOMAIN_INFO_BUFFER pfSamIFree_SAMPR_DOMAIN_INFO_BUFFER = NULL;
  95. PF_LsaIQueryInformationPolicyTrusted pfLsaIQueryInformationPolicyTrusted = NULL;
  96. PF_LsaIFree_LSAPR_POLICY_INFORMATION pfLsaIFree_LSAPR_POLICY_INFORMATION = NULL;
  97. PF_LsaIOpenPolicyTrusted pfLsaIOpenPolicyTrusted = NULL;
  98. PF_LsarQueryInformationPolicy pfLsarQueryInformationPolicy = NULL;
  99. NTSTATUS LoadSamAndLsa(
  100. VOID
  101. ) ;
  102. NTSTATUS LoadSamAndLsa(
  103. VOID
  104. )
  105. /*++
  106. Routine Description:
  107. This routine loads the SAM/LSA dlls and resolves the entry points we
  108. need, to avoid staticly linking to those DLLs which do not expect to
  109. be loaded by proceses other than LSA.
  110. Arguments:
  111. None
  112. Return Value:
  113. STATUS_SUCCESS: if there was no error.
  114. STATUS_DLL_NOT_FOUND: cannot load either DLL
  115. STATUS_ENTRYPOINT_NOT_FOUND: cannot get proc address for any of entry points
  116. --*/
  117. {
  118. static HMODULE hDllSam = NULL ;
  119. static HMODULE hDllLsa = NULL ;
  120. static NTSTATUS lastStatus = STATUS_SUCCESS ;
  121. if (hDllLsa && hDllSam) {
  122. return lastStatus ;
  123. }
  124. if (!(hDllSam = LoadLibrary(L"SAMSRV"))) {
  125. return STATUS_DLL_NOT_FOUND ;
  126. }
  127. if (!(hDllLsa = LoadLibrary(L"LSASRV"))) {
  128. (void) FreeLibrary(hDllSam) ;
  129. hDllSam = NULL ;
  130. return STATUS_DLL_NOT_FOUND ;
  131. }
  132. pfSamIConnect = (PF_SamIConnect)
  133. GetProcAddress(hDllSam,"SamIConnect");
  134. pfSamrOpenUser = (PF_SamrOpenUser)
  135. GetProcAddress(hDllSam,"SamrOpenUser");
  136. pfSamrCloseHandle = (PF_SamrCloseHandle)
  137. GetProcAddress(hDllSam,"SamrCloseHandle");
  138. pfSamrQueryInformationDomain = (PF_SamrQueryInformationDomain)
  139. GetProcAddress(hDllSam,"SamrQueryInformationDomain") ;
  140. pfSamrOpenDomain = (PF_SamrOpenDomain)
  141. GetProcAddress(hDllSam,"SamrOpenDomain");
  142. pfSamIAccountRestrictions = (PF_SamIAccountRestrictions)
  143. GetProcAddress(hDllSam,"SamIAccountRestrictions");
  144. pfSamIFree_SAMPR_DOMAIN_INFO_BUFFER = (PF_SamIFree_SAMPR_DOMAIN_INFO_BUFFER)
  145. GetProcAddress(hDllSam,"SamIFree_SAMPR_DOMAIN_INFO_BUFFER");
  146. pfLsaIQueryInformationPolicyTrusted = (PF_LsaIQueryInformationPolicyTrusted)
  147. GetProcAddress(hDllLsa,"LsaIQueryInformationPolicyTrusted");
  148. pfLsaIFree_LSAPR_POLICY_INFORMATION = (PF_LsaIFree_LSAPR_POLICY_INFORMATION)
  149. GetProcAddress(hDllLsa,"LsaIFree_LSAPR_POLICY_INFORMATION");
  150. pfLsaIOpenPolicyTrusted = (PF_LsaIOpenPolicyTrusted)
  151. GetProcAddress(hDllLsa,"LsaIOpenPolicyTrusted");
  152. pfLsarQueryInformationPolicy = (PF_LsarQueryInformationPolicy)
  153. GetProcAddress(hDllLsa,"LsarQueryInformationPolicy");
  154. if (!( pfSamIConnect &&
  155. pfSamrOpenUser &&
  156. pfSamrCloseHandle &&
  157. pfSamrQueryInformationDomain &&
  158. pfSamrOpenDomain &&
  159. pfSamIAccountRestrictions &&
  160. pfSamIFree_SAMPR_DOMAIN_INFO_BUFFER &&
  161. pfLsaIQueryInformationPolicyTrusted &&
  162. pfLsaIFree_LSAPR_POLICY_INFORMATION &&
  163. pfLsaIOpenPolicyTrusted &&
  164. pfLsarQueryInformationPolicy) ) {
  165. //
  166. // cannot find at least one
  167. //
  168. lastStatus = STATUS_ENTRYPOINT_NOT_FOUND ;
  169. (void) FreeLibrary(hDllSam) ;
  170. hDllSam = NULL ;
  171. (void) FreeLibrary(hDllLsa) ;
  172. hDllLsa = NULL ;
  173. }
  174. else {
  175. lastStatus = STATUS_SUCCESS ;
  176. }
  177. return lastStatus ;
  178. }
  179. ///////////////////////////////////////////////////////////////////////////////
  180. ULONG
  181. MapRidToObjectId(
  182. DWORD dwRid,
  183. LPWSTR pszUserName,
  184. BOOL fNTAS,
  185. BOOL fBuiltin );
  186. //
  187. // These are never closed once they're opened. This is similar to how
  188. // msv1_0 does it. Since there's no callback at shutdown time, we have no
  189. // way of knowing when to close them.
  190. //
  191. HANDLE SamDomainHandle = NULL;
  192. SAMPR_HANDLE SamConnectHandle = NULL;
  193. LSA_HANDLE LsaPolicyHandle = NULL;
  194. //
  195. // This is where we store out LSA Secret
  196. //
  197. BOOLEAN GotSecret = FALSE;
  198. UCHAR LsaSecretBuffer[USER_SESSION_KEY_LENGTH + 1];
  199. //
  200. // forward declare
  201. //
  202. BOOLEAN
  203. MNSWorkstationValidate (
  204. IN PUNICODE_STRING Workstation,
  205. IN PUNICODE_STRING UserParameters
  206. );
  207. BOOL
  208. GetPasswordExpired(
  209. IN LARGE_INTEGER PasswordLastSet,
  210. IN LARGE_INTEGER MaxPasswordAge
  211. );
  212. NTSTATUS
  213. QueryDomainPasswordInfo (
  214. PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
  215. );
  216. VOID
  217. Shuffle(
  218. UCHAR *achObjectId,
  219. UCHAR *szUpperPassword,
  220. int iPasswordLen,
  221. UCHAR *achOutputBuffer
  222. );
  223. NTSTATUS GetNcpSecretKey( CHAR *pchNWSecretKey );
  224. NTSTATUS
  225. Msv1_0SubAuthenticationRoutine2 (
  226. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  227. IN PVOID LogonInformation,
  228. IN ULONG Flags,
  229. IN PUSER_ALL_INFORMATION UserAll,
  230. OUT PULONG WhichFields,
  231. OUT PULONG UserFlags,
  232. OUT PBOOLEAN Authoritative,
  233. OUT PLARGE_INTEGER LogoffTime,
  234. OUT PLARGE_INTEGER KickoffTime,
  235. OUT PUSER_SESSION_KEY UserSessionKey OPTIONAL
  236. )
  237. /*++
  238. Routine Description:
  239. The subauthentication routine does cient/server specific authentication
  240. of a user. The credentials of the user are passed in addition to all the
  241. information from SAM defining the user. This routine decides whether to
  242. let the user logon.
  243. Arguments:
  244. LogonLevel -- Specifies the level of information given in
  245. LogonInformation.
  246. LogonInformation -- Specifies the description for the user
  247. logging on. The LogonDomainName field should be ignored.
  248. Flags - Flags describing the circumstances of the logon.
  249. MSV1_0_PASSTHRU -- This is a PassThru authenication. (i.e., the
  250. user isn't connecting to this machine.)
  251. MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
  252. user account.
  253. UserAll -- The description of the user as returned from SAM.
  254. WhichFields -- Returns which fields from UserAllInfo are to be written
  255. back to SAM. The fields will only be written if MSV returns success
  256. to it's caller. Only the following bits are valid.
  257. USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM. If
  258. the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
  259. must delete the old buffer using MIDL_user_free() and reallocate the
  260. buffer using MIDL_user_allocate().
  261. UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
  262. LogonProfile. The following bits are currently defined:
  263. LOGON_GUEST -- This was a guest logon
  264. LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials
  265. LOGON_GRACE_LOGON -- The caller's password has expired but logon
  266. was allowed during a grace period following the expiration.
  267. LOGON_SUBAUTH_SESSION_KEY - a session key was returned from this
  268. logon
  269. SubAuthentication packages should restrict themselves to returning
  270. bits in the high order byte of UserFlags. However, this convention
  271. isn't enforced giving the SubAuthentication package more flexibility.
  272. Authoritative -- Returns whether the status returned is an
  273. authoritative status which should be returned to the original
  274. caller. If not, this logon request may be tried again on another
  275. domain controller. This parameter is returned regardless of the
  276. status code.
  277. LogoffTime - Receives the time at which the user should logoff the
  278. system. This time is specified as a GMT relative NT system time.
  279. KickoffTime - Receives the time at which the user should be kicked
  280. off the system. This time is specified as a GMT relative NT system
  281. time. Specify, a full scale positive number if the user isn't to
  282. be kicked off.
  283. UserSessionKey - If non-null, recives a session key for this logon
  284. session.
  285. Return Value:
  286. STATUS_SUCCESS: if there was no error.
  287. STATUS_NO_SUCH_USER: The specified user has no account.
  288. STATUS_WRONG_PASSWORD: The password was invalid.
  289. STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
  290. STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
  291. STATUS_ACCOUNT_DISABLED: The account is disabled
  292. STATUS_ACCOUNT_EXPIRED: The account has expired.
  293. STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
  294. on next logon.
  295. STATUS_PASSWORD_EXPIRED: The Password is expired.
  296. STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at
  297. this time.
  298. STATUS_INVALID_WORKSTATION - The user is not authorized to logon to
  299. the specified workstation.
  300. --*/
  301. {
  302. NTSTATUS status;
  303. ULONG UserAccountControl;
  304. LARGE_INTEGER LogonTime;
  305. WCHAR PropertyFlag;
  306. NT_OWF_PASSWORD DecryptedPassword;
  307. UCHAR Response[RESPONSE_SIZE];
  308. UNICODE_STRING EncryptedPassword;
  309. UNICODE_STRING PasswordDateSet;
  310. UNICODE_STRING GraceLoginRemaining;
  311. SAMPR_HANDLE UserHandle;
  312. LARGE_INTEGER pwSetTime;
  313. PSAMPR_DOMAIN_INFO_BUFFER DomainInfo;
  314. PSAMPR_USER_INFO_BUFFER userControlInfo;
  315. LPWSTR pNewUserParams;
  316. int index;
  317. UCHAR achK[32];
  318. PNETLOGON_NETWORK_INFO LogonNetworkInfo;
  319. PCHAR challenge;
  320. BOOLEAN authoritative = TRUE; // important default!
  321. ULONG userFlags = 0; // important default!
  322. ULONG whichFields = 0; // important default!
  323. LARGE_INTEGER logoffTime;
  324. LARGE_INTEGER kickoffTime;
  325. pNewUserParams = NULL;
  326. DomainInfo = NULL;
  327. GraceLoginRemaining.Buffer = NULL;
  328. PasswordDateSet.Buffer = NULL;
  329. EncryptedPassword.Buffer = NULL;
  330. userControlInfo = NULL;
  331. logoffTime.HighPart = 0x7FFFFFFF; // default to no kickoff and
  332. logoffTime.LowPart = 0xFFFFFFFF; // no forced logoff
  333. kickoffTime.HighPart = 0x7FFFFFFF;
  334. kickoffTime.LowPart = 0xFFFFFFFF;
  335. status = LoadSamAndLsa() ;
  336. if ( !NT_SUCCESS( status )) {
  337. return status ;
  338. }
  339. (VOID) NtQuerySystemTime( &LogonTime );
  340. //
  341. // Check whether the SubAuthentication package supports this type
  342. // of logon.
  343. //
  344. if ( LogonLevel != NetlogonNetworkInformation ) {
  345. //
  346. // This SubAuthentication package only supports network logons.
  347. //
  348. status = STATUS_INVALID_INFO_CLASS;
  349. goto CleanUp;
  350. }
  351. //
  352. // This SubAuthentication package doesn't support access via machine
  353. // accounts.
  354. //
  355. UserAccountControl = USER_NORMAL_ACCOUNT;
  356. //
  357. // Local user (Temp Duplicate) accounts are only used on the machine
  358. // being directly logged onto.
  359. // (Nor are interactive or service logons allowed to them.)
  360. //
  361. if ( (Flags & MSV1_0_PASSTHRU) == 0 ) {
  362. UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
  363. }
  364. LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;
  365. //
  366. // If the account type isn't allowed,
  367. // Treat this as though the User Account doesn't exist.
  368. //
  369. // This SubAuthentication package doesn't allow guest logons.
  370. //
  371. if ( ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) ||
  372. ( Flags & MSV1_0_GUEST_LOGON ) ) {
  373. authoritative = FALSE;
  374. status = STATUS_NO_SUCH_USER;
  375. goto CleanUp;
  376. }
  377. //
  378. // Ensure the account isn't locked out.
  379. //
  380. if ( UserAll->UserId != DOMAIN_USER_RID_ADMIN &&
  381. (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) ) {
  382. //
  383. // Since the UI strongly encourages admins to disable user
  384. // accounts rather than delete them. Treat disabled acccount as
  385. // non-authoritative allowing the search to continue for other
  386. // accounts by the same name.
  387. //
  388. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  389. authoritative = FALSE;
  390. }
  391. status = STATUS_ACCOUNT_LOCKED_OUT;
  392. goto CleanUp;
  393. }
  394. //
  395. // Get the encrypted password from the user parms field
  396. //
  397. status = NetpParmsQueryUserPropertyWithLength( &UserAll->Parameters,
  398. NWPASSWORD,
  399. &PropertyFlag,
  400. &EncryptedPassword );
  401. if ( !NT_SUCCESS( status )) {
  402. goto CleanUp;
  403. }
  404. //
  405. // If the user does not have a netware password, fail the login
  406. //
  407. if ( EncryptedPassword.Length == 0 ) {
  408. status = STATUS_NO_SUCH_USER;
  409. goto CleanUp;
  410. }
  411. //
  412. // Read our LSA secret if we haven't already
  413. //
  414. if (! GotSecret) {
  415. status = GetNcpSecretKey( &LsaSecretBuffer[0] );
  416. if (! NT_SUCCESS(status)) {
  417. goto CleanUp;
  418. }
  419. GotSecret = TRUE;
  420. }
  421. //
  422. // Decrypt the password with NetwareLsaSecret to get the one way form
  423. //
  424. status = RtlDecryptNtOwfPwdWithUserKey(
  425. (PENCRYPTED_NT_OWF_PASSWORD) EncryptedPassword.Buffer,
  426. (PUSER_SESSION_KEY) &LsaSecretBuffer[0],
  427. &DecryptedPassword );
  428. if ( !NT_SUCCESS( status )) {
  429. goto CleanUp;
  430. }
  431. //
  432. // Get the response to challenge. We do this by finishing off the
  433. // password encryption algorithm here.
  434. //
  435. challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
  436. Shuffle( challenge, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
  437. Shuffle( challenge+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
  438. for (index = 0; index < 16; index++) {
  439. achK[index] ^= achK[31-index];
  440. }
  441. for (index = 0; index < RESPONSE_SIZE; index++) {
  442. Response[index] = achK[index] ^ achK[15-index];
  443. }
  444. if ( memcmp( Response,
  445. (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
  446. RESPONSE_SIZE ) != 0 ) {
  447. //
  448. // Since the UI strongly encourages admins to disable user
  449. // accounts rather than delete them. Treat disabled acccount as
  450. // non-authoritative allowing the search to continue for other
  451. // accounts by the same name.
  452. //
  453. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  454. authoritative = FALSE;
  455. }
  456. //
  457. // if the user tried to use a NULL password, don't note this as
  458. // a bad password attempt since LOGON.EXE does this by default.
  459. // Instead, map it to STATUS_LOGON_FAILURE.
  460. //
  461. {
  462. UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
  463. DWORD chObjectId;
  464. NT_PRODUCT_TYPE ProductType;
  465. DWORD dwUserId;
  466. //
  467. // first we calculate what the user's Object ID is...
  468. //
  469. RtlGetNtProductType( &ProductType );
  470. dwUserId = MapRidToObjectId(
  471. UserAll->UserId,
  472. UserAll->UserName.Buffer,
  473. ProductType == NtProductLanManNt,
  474. FALSE );
  475. chObjectId = SWAP_OBJECT_ID (dwUserId);
  476. //
  477. // then we calculate the user's password residue with a null
  478. // password
  479. //
  480. RtlZeroMemory( &pszShuffledNWPassword, NT_OWF_PASSWORD_LENGTH * 2 );
  481. Shuffle( (UCHAR *) &chObjectId, NULL, 0, pszShuffledNWPassword );
  482. //
  483. // we then finish off the encryption as we did above for the
  484. // password in the user's record.
  485. //
  486. challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
  487. Shuffle( challenge, pszShuffledNWPassword, 16, &achK[0] );
  488. Shuffle( challenge+4, pszShuffledNWPassword, 16, &achK[16] );
  489. for (index = 0; index < 16; index++) {
  490. achK[index] ^= achK[31-index];
  491. }
  492. for (index = 0; index < RESPONSE_SIZE; index++) {
  493. Response[index] = achK[index] ^ achK[15-index];
  494. }
  495. //
  496. // now if the password that the user sent in matches the encrypted
  497. // form of the null password, we exit with a generic return code
  498. // that won't cause the user's record to be updated. This will
  499. // also cause LSA to not wait for 3 seconds to return the error
  500. // (which is a good thing in this case).
  501. //
  502. if ( memcmp( Response,
  503. (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
  504. RESPONSE_SIZE ) == 0 ) {
  505. status = STATUS_LOGON_FAILURE;
  506. } else {
  507. status = STATUS_WRONG_PASSWORD;
  508. }
  509. }
  510. goto CleanUp;
  511. }
  512. //
  513. // Prevent rest of things from effecting the Administrator user
  514. //
  515. if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {
  516. status = STATUS_SUCCESS;
  517. goto CleanUp;
  518. }
  519. //
  520. // Check if the account is disabled.
  521. //
  522. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
  523. //
  524. // Since the UI strongly encourages admins to disable user
  525. // accounts rather than delete them. Treat disabled acccount as
  526. // non-authoritative allowing the search to continue for other
  527. // accounts by the same name.
  528. //
  529. authoritative = FALSE;
  530. status = STATUS_ACCOUNT_DISABLED;
  531. goto CleanUp;
  532. }
  533. //
  534. // Check if the account has expired.
  535. //
  536. if (UserAll->AccountExpires.QuadPart > 0 &&
  537. LogonTime.QuadPart >= UserAll->AccountExpires.QuadPart ) {
  538. status = STATUS_ACCOUNT_EXPIRED;
  539. goto CleanUp;
  540. }
  541. status = QueryDomainPasswordInfo( &DomainInfo );
  542. if ( !NT_SUCCESS( status )) {
  543. goto CleanUp;
  544. }
  545. //
  546. // Response is correct. So, check if the password has expired or not
  547. //
  548. if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
  549. status = NetpParmsQueryUserPropertyWithLength( &UserAll->Parameters,
  550. NWTIMEPASSWORDSET,
  551. &PropertyFlag,
  552. &PasswordDateSet );
  553. if ( !NT_SUCCESS( status ) ||
  554. PasswordDateSet.Length < sizeof(LARGE_INTEGER) ) {
  555. // date last password was set was not present.... hmmm.
  556. // we won't update anything here but let someone know all
  557. // is not kosher by making this a grace login.
  558. userFlags = LOGON_GRACE_LOGON;
  559. } else {
  560. pwSetTime = *((PLARGE_INTEGER)(PasswordDateSet.Buffer));
  561. if ( (pwSetTime.HighPart == 0xFFFF &&
  562. pwSetTime.LowPart == 0xFFFF ) ||
  563. GetPasswordExpired( pwSetTime,
  564. DomainInfo->Password.MaxPasswordAge )) {
  565. //
  566. // if we're on a bdc, just exit with invalid password, then
  567. // we'll go try it on the PDC.
  568. //
  569. POLICY_LSA_SERVER_ROLE_INFO *LsaServerRole;
  570. PLSAPR_POLICY_INFORMATION LsaPolicyBuffer = NULL;
  571. status = (*pfLsaIQueryInformationPolicyTrusted)(
  572. PolicyLsaServerRoleInformation,
  573. &LsaPolicyBuffer );
  574. if ( NT_SUCCESS( status ) && (LsaPolicyBuffer != NULL)) {
  575. LsaServerRole = (POLICY_LSA_SERVER_ROLE_INFO *) LsaPolicyBuffer;
  576. if (LsaServerRole->LsaServerRole == PolicyServerRoleBackup) {
  577. LsaFreeMemory( LsaServerRole );
  578. status = STATUS_PASSWORD_EXPIRED;
  579. goto CleanUp;
  580. }
  581. LsaFreeMemory( LsaServerRole );
  582. }
  583. //
  584. // Password has expired, so check if grace login is still allowed
  585. //
  586. userFlags = LOGON_GRACE_LOGON;
  587. //
  588. // if this is a password validate rather than an
  589. // actual login, don't update/check grace logins.
  590. //
  591. if ( LogonNetworkInfo->Identity.Workstation.Length > 0 ) {
  592. status = NetpParmsQueryUserPropertyWithLength( &UserAll->Parameters,
  593. GRACELOGINREMAINING,
  594. &PropertyFlag,
  595. &GraceLoginRemaining );
  596. if ( ! NT_SUCCESS( status ) ) {
  597. //
  598. // The grace login value cannot be determined.
  599. //
  600. goto CleanUp;
  601. } else if ( ( GraceLoginRemaining.Length != 0 ) &&
  602. ( *(GraceLoginRemaining.Buffer) > 0 ) ) {
  603. //
  604. // Password has expired and grace login is available.
  605. // So, return success and decrease the grace login remaining
  606. // in the user parms field.
  607. //
  608. BOOL fUpdate;
  609. (*(GraceLoginRemaining.Buffer))--;
  610. status = NetpParmsSetUserProperty( UserAll->Parameters.Buffer,
  611. GRACELOGINREMAINING,
  612. GraceLoginRemaining,
  613. USER_PROPERTY_TYPE_ITEM,
  614. &pNewUserParams,
  615. &fUpdate );
  616. if ( NT_SUCCESS( status) &&
  617. fUpdate ) {
  618. //
  619. // if we actually updated the parms, mark as such.
  620. //
  621. whichFields = USER_ALL_PARAMETERS;
  622. //
  623. // The length of the parameters didn't grow... we
  624. // know that because we started with a value and
  625. // ended with the same value - 1 ( same length )
  626. //
  627. memcpy( UserAll->Parameters.Buffer,
  628. pNewUserParams,
  629. UserAll->Parameters.Length );
  630. }
  631. status = STATUS_SUCCESS;
  632. } else {
  633. status = STATUS_PASSWORD_EXPIRED;
  634. goto CleanUp;
  635. }
  636. }
  637. }
  638. }
  639. }
  640. //
  641. // To validate the user's logon hours, we must have a handle to the user.
  642. // We'll open the user here.
  643. //
  644. UserHandle = NULL;
  645. status = (*pfSamrOpenUser)( SamDomainHandle,
  646. USER_READ_ACCOUNT,
  647. UserAll->UserId,
  648. &UserHandle );
  649. if ( !NT_SUCCESS(status) ) {
  650. KdPrint(( "FPNWCLNT: Cannot SamrOpenUser %lX\n", status));
  651. goto CleanUp;
  652. }
  653. //
  654. // Validate the user's logon hours.
  655. //
  656. status = (*pfSamIAccountRestrictions)( UserHandle,
  657. NULL, // workstation id
  658. NULL, // workstation list
  659. &UserAll->LogonHours,
  660. &logoffTime,
  661. &kickoffTime
  662. );
  663. (*pfSamrCloseHandle)( &UserHandle );
  664. if ( ! NT_SUCCESS( status )) {
  665. goto CleanUp;
  666. }
  667. //
  668. // Validate if the user can logon from this workstation.
  669. // (Supply subauthentication package specific code here.)
  670. if ( ! MNSWorkstationValidate( &LogonNetworkInfo->Identity.Workstation,
  671. &UserAll->Parameters ) ) {
  672. status = STATUS_INVALID_WORKSTATION;
  673. goto CleanUp;
  674. }
  675. //
  676. // The user is valid. CleanUp up before returning.
  677. //
  678. CleanUp:
  679. //
  680. // If we succeeded, create a session key. The session key is created
  681. // by taking the decrypted password (a hash of the object id and
  682. // cleartext password) and adding the index of each byte to each byte
  683. // modulo 255, and using that to create a new challenge response from
  684. // the old challenge response.
  685. //
  686. if (NT_SUCCESS(status) && (UserSessionKey != NULL)) {
  687. UCHAR ChallengeResponse[NT_CHALLENGE_LENGTH];
  688. PUCHAR Password = (PUCHAR) &DecryptedPassword.data;
  689. PUCHAR SessionKey = (PUCHAR) UserSessionKey;
  690. ASSERT(RESPONSE_SIZE >= NT_CHALLENGE_LENGTH);
  691. RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
  692. RtlCopyMemory(
  693. ChallengeResponse,
  694. Response,
  695. NT_CHALLENGE_LENGTH );
  696. //
  697. // Create the new password
  698. //
  699. for (index = 0; index < sizeof(DecryptedPassword) ; index++ ) {
  700. Password[index] = Password[index] + (UCHAR) index;
  701. }
  702. //
  703. // Use it to make a normal challenge response using the old challenge
  704. // response
  705. //
  706. Shuffle( ChallengeResponse, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
  707. Shuffle( ChallengeResponse+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
  708. for (index = 0; index < 16; index++) {
  709. achK[index] ^= achK[31-index];
  710. }
  711. for (index = 0; index < RESPONSE_SIZE; index++) {
  712. SessionKey[index] = achK[index] ^ achK[15-index];
  713. }
  714. userFlags |= LOGON_SUBAUTH_SESSION_KEY;
  715. }
  716. if (DomainInfo != NULL) {
  717. (*pfSamIFree_SAMPR_DOMAIN_INFO_BUFFER)( DomainInfo, DomainPasswordInformation );
  718. }
  719. if (EncryptedPassword.Buffer == NULL) {
  720. LocalFree( EncryptedPassword.Buffer );
  721. }
  722. if (PasswordDateSet.Buffer != NULL) {
  723. LocalFree( PasswordDateSet.Buffer );
  724. }
  725. if (GraceLoginRemaining.Buffer != NULL) {
  726. LocalFree( GraceLoginRemaining.Buffer );
  727. }
  728. if (pNewUserParams != NULL) {
  729. NetpParmsUserPropertyFree( pNewUserParams );
  730. }
  731. *Authoritative = authoritative;
  732. *UserFlags = userFlags;
  733. *WhichFields = whichFields;
  734. LogoffTime->HighPart = logoffTime.HighPart;
  735. LogoffTime->LowPart = logoffTime.LowPart;
  736. KickoffTime->HighPart = kickoffTime.HighPart;
  737. KickoffTime->LowPart = kickoffTime.LowPart;
  738. return status;
  739. } // Msv1_0SubAuthenticationRoutine
  740. NTSTATUS
  741. Msv1_0SubAuthenticationRoutine (
  742. IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
  743. IN PVOID LogonInformation,
  744. IN ULONG Flags,
  745. IN PUSER_ALL_INFORMATION UserAll,
  746. OUT PULONG WhichFields,
  747. OUT PULONG UserFlags,
  748. OUT PBOOLEAN Authoritative,
  749. OUT PLARGE_INTEGER LogoffTime,
  750. OUT PLARGE_INTEGER KickoffTime
  751. )
  752. /*++
  753. Routine Description:
  754. Compatibility wrapper for Msv1_0SubAuthenticationRoutine2.
  755. Arguments:
  756. Same as Msv1_0SubAuthenticationRoutine2
  757. Return Value:
  758. Same as Msv1_0SubAuthenticationRoutine2
  759. --*/
  760. {
  761. return(Msv1_0SubAuthenticationRoutine2(
  762. LogonLevel,
  763. LogonInformation,
  764. Flags,
  765. UserAll,
  766. WhichFields,
  767. UserFlags,
  768. Authoritative,
  769. LogoffTime,
  770. KickoffTime,
  771. NULL // session key
  772. ) );
  773. }
  774. BOOLEAN
  775. MNSWorkstationValidate (
  776. IN PUNICODE_STRING Workstation,
  777. IN PUNICODE_STRING UserParameters
  778. )
  779. {
  780. NTSTATUS status;
  781. WCHAR PropertyFlag;
  782. UNICODE_STRING LogonWorkstations;
  783. INT cbRequired;
  784. INT cb;
  785. LPWSTR pszTmp;
  786. if ( Workstation->Length < (NET_ADDRESS_SIZE * sizeof(WCHAR)) ) {
  787. //
  788. // Zero is used when simply verifying a password.
  789. //
  790. // We also check that the length is enough so we dont
  791. // blow up later. If for some reason a bad string is
  792. // supplied, we pass it. This should never happen. Not a
  793. // security hole as the user has no control over the string.
  794. //
  795. return(TRUE);
  796. }
  797. status = NetpParmsQueryUserPropertyWithLength( UserParameters,
  798. NWLOGONFROM,
  799. &PropertyFlag,
  800. &LogonWorkstations );
  801. if ( !NT_SUCCESS( status) || LogonWorkstations.Length == 0 ) {
  802. return TRUE;
  803. }
  804. cbRequired = (LogonWorkstations.Length + 1) * sizeof(WCHAR);
  805. pszTmp = LocalAlloc( LMEM_ZEROINIT, cbRequired);
  806. if ( pszTmp == NULL ) {
  807. //
  808. // Not enough memory to allocate the buffer. Just
  809. // let the user logon.
  810. //
  811. LocalFree( LogonWorkstations.Buffer );
  812. return TRUE;
  813. }
  814. cb = MultiByteToWideChar( CP_ACP,
  815. MB_PRECOMPOSED,
  816. (const CHAR *) LogonWorkstations.Buffer,
  817. LogonWorkstations.Length,
  818. pszTmp,
  819. cbRequired );
  820. LocalFree( LogonWorkstations.Buffer ); // Don't need it any more
  821. if ( cb > 1 )
  822. {
  823. USHORT TotalEntries = LogonWorkstations.Length/WKSTA_ADDRESS_SIZE;
  824. WCHAR *pszEntry = pszTmp;
  825. WCHAR *pszWksta = Workstation->Buffer ;
  826. _wcsupr(pszEntry) ;
  827. _wcsupr(pszWksta) ;
  828. while ( TotalEntries > 0 )
  829. {
  830. //
  831. // if net # is not wildcard, check for match
  832. //
  833. if (wcsncmp(L"FFFFFFFF", pszEntry, NET_ADDRESS_SIZE)!=0)
  834. {
  835. if (wcsncmp(pszWksta, pszEntry, NET_ADDRESS_SIZE)!=0)
  836. {
  837. //
  838. // if no match, goto next entry
  839. //
  840. pszEntry += WKSTA_ADDRESS_SIZE;
  841. TotalEntries--;
  842. continue ;
  843. }
  844. }
  845. //
  846. // from above, net number passes. check node number.
  847. // again, look for wildcard first.
  848. //
  849. if (wcsncmp(L"FFFFFFFFFFFF", pszEntry+NET_ADDRESS_SIZE,
  850. NODE_ADDRESS_SIZE)!=0)
  851. {
  852. if (wcsncmp(pszEntry+NET_ADDRESS_SIZE,
  853. pszWksta+NET_ADDRESS_SIZE,
  854. NODE_ADDRESS_SIZE)!=0)
  855. {
  856. //
  857. // if no match, goto next entry
  858. //
  859. pszEntry += WKSTA_ADDRESS_SIZE;
  860. TotalEntries--;
  861. continue ;
  862. }
  863. }
  864. //
  865. // found a match. return it.
  866. //
  867. LocalFree( pszTmp );
  868. return TRUE;
  869. }
  870. } else {
  871. //
  872. // MultiByteToWideChar failed or empty string (ie. 1 char).
  873. // Just let the user logon
  874. //
  875. LocalFree( pszTmp );
  876. return TRUE;
  877. }
  878. LocalFree( pszTmp );
  879. return FALSE;
  880. }
  881. BOOL
  882. GetPasswordExpired(
  883. IN LARGE_INTEGER PasswordLastSet,
  884. IN LARGE_INTEGER MaxPasswordAge
  885. )
  886. /*++
  887. Routine Description:
  888. This routine returns true if the password is expired, false otherwise.
  889. Arguments:
  890. PasswordLastSet - Time when the password was last set for this user.
  891. MaxPasswordAge - Maximum password age for any password in the domain.
  892. Return Value:
  893. Returns true if password is expired. False if not expired.
  894. --*/
  895. {
  896. LARGE_INTEGER PasswordMustChange;
  897. NTSTATUS status;
  898. BOOLEAN rc;
  899. LARGE_INTEGER TimeNow;
  900. //
  901. // Compute the expiration time as the time the password was
  902. // last set plus the maximum age.
  903. //
  904. if (PasswordLastSet.QuadPart < 0 ||
  905. MaxPasswordAge.QuadPart > 0 ) {
  906. rc = TRUE; // default for invalid times is that it is expired.
  907. } else {
  908. try {
  909. PasswordMustChange.QuadPart = PasswordLastSet.QuadPart -
  910. MaxPasswordAge.QuadPart;
  911. //
  912. // Limit the resultant time to the maximum valid absolute time
  913. //
  914. if ( PasswordMustChange.QuadPart < 0 ) {
  915. rc = FALSE;
  916. } else {
  917. status = NtQuerySystemTime( &TimeNow );
  918. if (NT_SUCCESS(status)) {
  919. if ( TimeNow.QuadPart >= PasswordMustChange.QuadPart ) {
  920. rc = TRUE;
  921. } else {
  922. rc = FALSE;
  923. }
  924. } else {
  925. rc = FALSE; // won't fail if NtQuerySystemTime failed.
  926. }
  927. }
  928. } except(EXCEPTION_EXECUTE_HANDLER) {
  929. rc = TRUE;
  930. }
  931. }
  932. return rc;
  933. }
  934. NTSTATUS
  935. QueryDomainPasswordInfo (
  936. PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
  937. )
  938. /*++
  939. This routine opens a handle to sam so that we can get the max password
  940. age.
  941. --*/
  942. {
  943. NTSTATUS status;
  944. OBJECT_ATTRIBUTES PolicyObjectAttributes;
  945. PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
  946. //
  947. // if we don't yet have a domain handle, open domain handle so that
  948. // we can query the domain's password expiration time.
  949. //
  950. status = LoadSamAndLsa() ;
  951. if ( !NT_SUCCESS( status )) {
  952. return status ;
  953. }
  954. if (SamDomainHandle == NULL) {
  955. //
  956. // Determine the DomainName and DomainId of the Account Database
  957. //
  958. if (LsaPolicyHandle == NULL) {
  959. InitializeObjectAttributes( &PolicyObjectAttributes,
  960. NULL, // Name
  961. 0, // Attributes
  962. NULL, // Root
  963. NULL ); // Security Descriptor
  964. status = (*pfLsaIOpenPolicyTrusted)(&LsaPolicyHandle);
  965. if ( !NT_SUCCESS(status) ) {
  966. LsaPolicyHandle = NULL;
  967. KdPrint(( "FPNWCLNT: Cannot LsaIOpenPolicyTrusted 0x%x\n", status));
  968. goto CleanUp;
  969. }
  970. }
  971. status = (*pfLsarQueryInformationPolicy)( LsaPolicyHandle,
  972. PolicyAccountDomainInformation,
  973. &PolicyAccountDomainInfo );
  974. if ( !NT_SUCCESS(status) ) {
  975. KdPrint(( "FPNWCLNT: Cannot LsarQueryInformationPolicy 0x%x\n", status));
  976. goto CleanUp;
  977. }
  978. if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
  979. status = STATUS_NO_SUCH_DOMAIN;
  980. KdPrint(( "FPNWCLNT: Domain Sid is null 0x%x\n", status));
  981. goto CleanUp;
  982. }
  983. //
  984. // Open our connection with SAM
  985. //
  986. if (SamConnectHandle == NULL) {
  987. status = (*pfSamIConnect)( NULL, // No server name
  988. &SamConnectHandle,
  989. SAM_SERVER_CONNECT,
  990. (BOOLEAN) TRUE ); // Indicate we are privileged
  991. if ( !NT_SUCCESS(status) ) {
  992. SamConnectHandle = NULL;
  993. KdPrint(( "FPNWCLNT: Cannot SamIConnect 0x%x\n", status));
  994. goto CleanUp;
  995. }
  996. }
  997. //
  998. // Open the domain.
  999. //
  1000. status = (*pfSamrOpenDomain)( SamConnectHandle,
  1001. DOMAIN_READ_OTHER_PARAMETERS,
  1002. (RPC_SID *) PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
  1003. &SamDomainHandle );
  1004. if ( !NT_SUCCESS(status) ) {
  1005. SamDomainHandle = NULL;
  1006. KdPrint(( "FPNWCLNT: Cannot SamrOpenDomain 0x%x\n", status));
  1007. goto CleanUp;
  1008. }
  1009. }
  1010. status = (*pfSamrQueryInformationDomain)( SamDomainHandle,
  1011. DomainPasswordInformation,
  1012. DomainInfo );
  1013. if ( !NT_SUCCESS(status) ) {
  1014. KdPrint(( "FPNWCLNT: Cannot SamrQueryInformationDomain %lX\n", status));
  1015. goto CleanUp;
  1016. }
  1017. CleanUp:
  1018. if (PolicyAccountDomainInfo != NULL) {
  1019. (*pfLsaIFree_LSAPR_POLICY_INFORMATION)( PolicyAccountDomainInformation,
  1020. PolicyAccountDomainInfo );
  1021. }
  1022. return(status);
  1023. } // QueryDomainPasswordInfo
  1024. // logon.c eof.