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.

2109 lines
51 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // SYNOPSIS
  6. //
  7. // Implements the IAS API into the NT LSA.
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include <nt.h>
  11. #include <ntrtl.h>
  12. #include <nturtl.h>
  13. #include <ntlsa.h>
  14. #include <windows.h>
  15. #include <align.h>
  16. #include <ntsam.h>
  17. #include <ntsamp.h>
  18. #include <dsgetdc.h>
  19. #include <lm.h>
  20. #include <crypt.h>
  21. #include <sha.h>
  22. #include <rasfmsub.h>
  23. #include <oaidl.h>
  24. #include <iaspolcy.h>
  25. #include <iastrace.h>
  26. #define IASSAMAPI
  27. #include <statinfo.h>
  28. #include <ezsam.h>
  29. #include <dyninfo.h>
  30. #include <ezlogon.h>
  31. #include <iaslsa.h>
  32. #include <iasntds.h>
  33. #include <iasparms.h>
  34. #define STRSAFE_NO_DEPRECATE
  35. #include <strsafe.h>
  36. #include <raserror.h>
  37. extern CRITICAL_SECTION critSec;
  38. #define DEFAULT_PARAMETER_CONTROL \
  39. (MSV1_0_DONT_TRY_GUEST_ACCOUNT | MSV1_0_TRY_SPECIFIED_DOMAIN_ONLY | MSV1_0_DISABLE_PERSONAL_FALLBACK)
  40. //////////
  41. // Make sure that the defines in iaslsa.h match the actual NT defines.
  42. //////////
  43. #if _MSV1_0_CHALLENGE_LENGTH != MSV1_0_CHALLENGE_LENGTH
  44. #error _MSV1_0_CHALLENGE_LENGTH != MSV1_0_CHALLENGE_LENGTH
  45. #endif
  46. #if _NT_RESPONSE_LENGTH != NT_RESPONSE_LENGTH
  47. #error _NT_RESPONSE_LENGTH != NT_RESPONSE_LENGTH
  48. #endif
  49. #if _LM_RESPONSE_LENGTH != LM_RESPONSE_LENGTH
  50. #error _LM_RESPONSE_LENGTH != LM_RESPONSE_LENGTH
  51. #endif
  52. #if _MSV1_0_USER_SESSION_KEY_LENGTH != MSV1_0_USER_SESSION_KEY_LENGTH
  53. #error _MSV1_0_USER_SESSION_KEY_LENGTH != MSV1_0_USER_SESSION_KEY_LENGTH
  54. #endif
  55. #if _MSV1_0_LANMAN_SESSION_KEY_LENGTH != MSV1_0_LANMAN_SESSION_KEY_LENGTH
  56. #error _MSV1_0_LANMAN_SESSION_KEY_LENGTH != MSV1_0_LANMAN_SESSION_KEY_LENGTH
  57. #endif
  58. #if _ENCRYPTED_LM_OWF_PASSWORD_LENGTH != ENCRYPTED_LM_OWF_PASSWORD_LENGTH
  59. #error _ENCRYPTED_LM_OWF_PASSWORD_LENGTH != ENCRYPTED_LM_OWF_PASSWORD_LENGTH
  60. #endif
  61. #if _ENCRYPTED_NT_OWF_PASSWORD_LENGTH != ENCRYPTED_NT_OWF_PASSWORD_LENGTH
  62. #error _ENCRYPTED_NT_OWF_PASSWORD_LENGTH != ENCRYPTED_NT_OWF_PASSWORD_LENGTH
  63. #endif
  64. #if _MAX_ARAP_USER_NAMELEN != MAX_ARAP_USER_NAMELEN
  65. #error _MAX_ARAP_USER_NAMELEN != MAX_ARAP_USER_NAMELEN
  66. #endif
  67. #if _AUTHENTICATOR_RESPONSE_LENGTH > A_SHA_DIGEST_LEN
  68. #error _AUTHENTICATOR_RESPONSE_LENGTH > A_SHA_DIGEST_LEN
  69. #endif
  70. #if _CHAP_RESPONSE_SIZE != CHAP_RESPONSE_SIZE
  71. #error _CHAP_RESPONSE_SIZE != CHAP_RESPONSE_SIZE
  72. #endif
  73. //////////
  74. // Reference count for API initialization.
  75. //////////
  76. LONG theRefCount;
  77. //////////
  78. // Lock variable -- non-zero if API is locked.
  79. //////////
  80. LONG theLsaLock;
  81. //////////
  82. // SID lengths for the local domains.
  83. //////////
  84. ULONG theAccountSidLen, theBuiltinSidLen;
  85. //////////
  86. // Macros to lock/unlock the LSA API during intialization and shutdown.
  87. //////////
  88. #define LSA_LOCK() \
  89. while (InterlockedExchange(&theLsaLock, 1)) Sleep(5)
  90. #define LSA_UNLOCK() \
  91. InterlockedExchange(&theLsaLock, 0)
  92. ///////////////////////////////////////////////////////////////////////////////
  93. //
  94. // FUNCTION
  95. //
  96. // IASLsaInitialize
  97. //
  98. // DESCRIPTION
  99. //
  100. // Initializes the LSA API.
  101. //
  102. ///////////////////////////////////////////////////////////////////////////////
  103. DWORD
  104. WINAPI
  105. IASLsaInitialize( VOID )
  106. {
  107. DWORD status;
  108. LSA_LOCK();
  109. if (theRefCount == 0)
  110. {
  111. IASTraceString("Initializing LSA/SAM sub-system.");
  112. status = IASStaticInfoInitialize();
  113. if (status != NO_ERROR) { goto exit; }
  114. status = IASSamInitialize();
  115. if (status != NO_ERROR) { goto shutdown_static; }
  116. status = IASDynamicInfoInitialize();
  117. if (status != NO_ERROR) { goto shutdown_sam; }
  118. status = IASLogonInitialize();
  119. if (status != NO_ERROR) { goto shutdown_dynamic; }
  120. theAccountSidLen = IASLengthRequiredChildSid(theAccountDomainSid);
  121. theBuiltinSidLen = IASLengthRequiredChildSid(theBuiltinDomainSid);
  122. IASTraceString("LSA/SAM sub-system initialized successfully.");
  123. }
  124. else
  125. {
  126. // We're already initialized.
  127. status = NO_ERROR;
  128. }
  129. ++theRefCount;
  130. goto exit;
  131. shutdown_dynamic:
  132. IASDynamicInfoShutdown();
  133. shutdown_sam:
  134. IASSamShutdown();
  135. shutdown_static:
  136. IASStaticInfoShutdown();
  137. IASTraceFailure("LSA/SAM initialization", status);
  138. exit:
  139. LSA_UNLOCK();
  140. return status;
  141. }
  142. ///////////////////////////////////////////////////////////////////////////////
  143. //
  144. // FUNCTION
  145. //
  146. // IASLsaUninitialize
  147. //
  148. // DESCRIPTION
  149. //
  150. // Uninitializes the LSA API.
  151. //
  152. ///////////////////////////////////////////////////////////////////////////////
  153. VOID
  154. WINAPI
  155. IASLsaUninitialize( VOID )
  156. {
  157. LSA_LOCK();
  158. --theRefCount;
  159. if (theRefCount == 0)
  160. {
  161. IASLogonShutdown();
  162. IASDynamicInfoShutdown();
  163. IASSamShutdown();
  164. IASStaticInfoShutdown();
  165. }
  166. LSA_UNLOCK();
  167. }
  168. ///////////////////////////////////////////////////////////////////////////////
  169. //
  170. // FUNCTION
  171. //
  172. // IASLogonPAP
  173. //
  174. // DESCRIPTION
  175. //
  176. // Performs PAP authentication against the NT SAM database.
  177. //
  178. ///////////////////////////////////////////////////////////////////////////////
  179. DWORD
  180. WINAPI
  181. IASLogonPAP(
  182. PCWSTR UserName,
  183. PCWSTR Domain,
  184. PCSTR Password,
  185. PHANDLE Token,
  186. PIAS_PAP_PROFILE Profile
  187. )
  188. {
  189. DWORD status;
  190. ULONG authLength;
  191. PMSV1_0_LM20_LOGON authInfo;
  192. PBYTE data;
  193. PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
  194. ProfileBuffer = NULL;
  195. // Calculate the length of the authentication info.
  196. authLength = (ULONG)(sizeof(MSV1_0_LM20_LOGON) +
  197. (ALIGN_WCHAR - 1) +
  198. (wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
  199. strlen(Password) * (sizeof(WCHAR) + sizeof(CHAR)));
  200. // Allocate a buffer on the stack.
  201. // Need extra room for the RtlCopyAnsiStringToUnicode conversion.
  202. authInfo = (PMSV1_0_LM20_LOGON)_alloca(authLength + 2 * sizeof(WCHAR));
  203. // Initialize the struct.
  204. IASInitAuthInfo(
  205. authInfo,
  206. sizeof(MSV1_0_LM20_LOGON),
  207. UserName,
  208. Domain,
  209. &data
  210. );
  211. // Copy in the ANSI password.
  212. IASInitAnsiString(
  213. authInfo->CaseInsensitiveChallengeResponse,
  214. data,
  215. Password
  216. );
  217. // Make sure that the Unicode string is properly aligned.
  218. data = ROUND_UP_POINTER(data, ALIGN_WCHAR);
  219. // Copy in the Unicode password. We have to force a UNICODE_STRING into
  220. // an ANSI_STRING struct.
  221. IASInitUnicodeStringFromAnsi(
  222. *(PUNICODE_STRING)&authInfo->CaseSensitiveChallengeResponse,
  223. data,
  224. authInfo->CaseInsensitiveChallengeResponse
  225. );
  226. // Set the parameters.
  227. authInfo->ParameterControl = DEFAULT_PARAMETER_CONTROL |
  228. MSV1_0_CLEARTEXT_PASSWORD_ALLOWED |
  229. MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED;
  230. status = IASLogonUser(
  231. authInfo,
  232. authLength,
  233. &ProfileBuffer,
  234. Token
  235. );
  236. if (status == NO_ERROR)
  237. {
  238. Profile->KickOffTime.QuadPart = ProfileBuffer->KickOffTime.QuadPart;
  239. // free it.
  240. LsaFreeReturnBuffer(ProfileBuffer);
  241. }
  242. else
  243. {
  244. Profile->KickOffTime.QuadPart = MAXLONGLONG;
  245. }
  246. return status;
  247. }
  248. ///////////////////////////////////////////////////////////////////////////////
  249. //
  250. // FUNCTION
  251. //
  252. // IASLogonCHAP
  253. //
  254. // DESCRIPTION
  255. //
  256. // Performs MD5-CHAP authentication against the NT SAM database.
  257. //
  258. ///////////////////////////////////////////////////////////////////////////////
  259. DWORD
  260. WINAPI
  261. IASLogonCHAP(
  262. PCWSTR UserName,
  263. PCWSTR Domain,
  264. BYTE ChallengeID,
  265. PBYTE Challenge,
  266. DWORD ChallengeLength,
  267. PBYTE Response,
  268. PHANDLE Token,
  269. PIAS_CHAP_PROFILE Profile
  270. )
  271. {
  272. DWORD status;
  273. ULONG authLength, rasAuthLength, md5AuthLength;
  274. PMSV1_0_SUBAUTH_LOGON authInfo;
  275. PBYTE data;
  276. RAS_SUBAUTH_INFO* ras;
  277. MD5CHAP_SUBAUTH_INFO* md5;
  278. MD5CHAP_EX_SUBAUTH_INFO* md5ex;
  279. PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
  280. ProfileBuffer = NULL;
  281. // Calculate the length of the MD5 subauth info.
  282. if (ChallengeLength == 16)
  283. {
  284. md5AuthLength = sizeof(MD5CHAP_SUBAUTH_INFO);
  285. }
  286. else
  287. {
  288. md5AuthLength = sizeof(MD5CHAP_EX_SUBAUTH_INFO) + ChallengeLength - 1;
  289. }
  290. // Calculate the length of the RAS subauth info.
  291. rasAuthLength = sizeof(RAS_SUBAUTH_INFO) + md5AuthLength;
  292. // Calculate the length of all the subauth info.
  293. authLength = (ULONG)(sizeof(MSV1_0_LM20_LOGON) +
  294. (ALIGN_WORST - 1) +
  295. (wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
  296. rasAuthLength);
  297. // Allocate a buffer on the stack.
  298. authInfo = (PMSV1_0_SUBAUTH_LOGON)_alloca(authLength);
  299. // Initialize the struct.
  300. IASInitAuthInfo(
  301. authInfo,
  302. sizeof(MSV1_0_LM20_LOGON),
  303. UserName,
  304. Domain,
  305. &data
  306. );
  307. //////////
  308. // Set the RAS_SUBAUTH_INFO.
  309. //////////
  310. // Make sure the struct is properly aligned.
  311. data = ROUND_UP_POINTER(data, ALIGN_WORST);
  312. authInfo->AuthenticationInfo1.Length = (USHORT)rasAuthLength;
  313. authInfo->AuthenticationInfo1.MaximumLength = (USHORT)rasAuthLength;
  314. authInfo->AuthenticationInfo1.Buffer = (PCHAR)data;
  315. ras = (RAS_SUBAUTH_INFO*)data;
  316. ras->DataSize = md5AuthLength;
  317. //////////
  318. // Set the MD5CHAP_SUBAUTH_INFO or MD5CHAP_EX_SUBAUTH_INFO.
  319. //////////
  320. if (ChallengeLength == 16)
  321. {
  322. ras->ProtocolType = RAS_SUBAUTH_PROTO_MD5CHAP;
  323. md5 = (MD5CHAP_SUBAUTH_INFO*)ras->Data;
  324. md5->uchChallengeId = ChallengeID;
  325. IASInitFixedArray(md5->uchChallenge, Challenge);
  326. IASInitFixedArray(md5->uchResponse, Response);
  327. }
  328. else
  329. {
  330. ras->ProtocolType = RAS_SUBAUTH_PROTO_MD5CHAP_EX;
  331. md5ex = (MD5CHAP_EX_SUBAUTH_INFO*)ras->Data;
  332. md5ex->uchChallengeId = ChallengeID;
  333. IASInitFixedArray(md5ex->uchResponse, Response);
  334. memcpy(md5ex->uchChallenge, Challenge, ChallengeLength);
  335. }
  336. // Set the parameters and package ID.
  337. authInfo->ParameterControl =
  338. DEFAULT_PARAMETER_CONTROL |
  339. (MSV1_0_SUBAUTHENTICATION_DLL_RAS << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT) |
  340. MSV1_0_SUBAUTHENTICATION_DLL_EX;
  341. status = IASLogonUser(
  342. authInfo,
  343. authLength,
  344. &ProfileBuffer,
  345. Token
  346. );
  347. if (status == NO_ERROR)
  348. {
  349. Profile->KickOffTime.QuadPart = ProfileBuffer->KickOffTime.QuadPart;
  350. // free it.
  351. LsaFreeReturnBuffer(ProfileBuffer);
  352. }
  353. else
  354. {
  355. Profile->KickOffTime.QuadPart = MAXLONGLONG;
  356. }
  357. return status;
  358. }
  359. ///////////////////////////////////////////////////////////////////////////////
  360. //
  361. // FUNCTION
  362. //
  363. // IASLogonMSCHAP
  364. //
  365. // DESCRIPTION
  366. //
  367. // Performs MS-CHAP authentication against the NT SAM database.
  368. //
  369. ///////////////////////////////////////////////////////////////////////////////
  370. DWORD
  371. WINAPI
  372. IASLogonMSCHAP(
  373. PCWSTR UserName,
  374. PCWSTR Domain,
  375. PBYTE Challenge,
  376. PBYTE NtResponse,
  377. PBYTE LmResponse,
  378. PIAS_MSCHAP_PROFILE Profile,
  379. PHANDLE Token
  380. )
  381. {
  382. DWORD status;
  383. ULONG authLength;
  384. PMSV1_0_LM20_LOGON authInfo;
  385. PBYTE data;
  386. PMSV1_0_LM20_LOGON_PROFILE logonProfile;
  387. DWORD len;
  388. // Calculate the length of the authentication info.
  389. authLength = sizeof(MSV1_0_LM20_LOGON) +
  390. (wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
  391. (LmResponse ? LM_RESPONSE_LENGTH : 0) +
  392. (NtResponse ? NT_RESPONSE_LENGTH : 0);
  393. // Allocate a buffer on the stack.
  394. authInfo = (PMSV1_0_LM20_LOGON)_alloca(authLength);
  395. // Initialize the struct.
  396. IASInitAuthInfo(
  397. authInfo,
  398. sizeof(MSV1_0_LM20_LOGON),
  399. UserName,
  400. Domain,
  401. &data
  402. );
  403. /////////
  404. // Fill in the challenges and responses.
  405. /////////
  406. IASInitFixedArray(
  407. authInfo->ChallengeToClient,
  408. Challenge
  409. );
  410. if (NtResponse)
  411. {
  412. IASInitOctetString(
  413. authInfo->CaseSensitiveChallengeResponse,
  414. data,
  415. NtResponse,
  416. NT_RESPONSE_LENGTH
  417. );
  418. }
  419. else
  420. {
  421. memset(
  422. &authInfo->CaseSensitiveChallengeResponse,
  423. 0,
  424. sizeof(authInfo->CaseSensitiveChallengeResponse)
  425. );
  426. }
  427. if (LmResponse)
  428. {
  429. IASInitOctetString(
  430. authInfo->CaseInsensitiveChallengeResponse,
  431. data,
  432. LmResponse,
  433. LM_RESPONSE_LENGTH
  434. );
  435. }
  436. else
  437. {
  438. memset(
  439. &authInfo->CaseInsensitiveChallengeResponse,
  440. 0,
  441. sizeof(authInfo->CaseInsensitiveChallengeResponse)
  442. );
  443. }
  444. // Set the parameters.
  445. authInfo->ParameterControl = DEFAULT_PARAMETER_CONTROL;
  446. status = IASLogonUser(
  447. authInfo,
  448. authLength,
  449. &logonProfile,
  450. Token
  451. );
  452. if (status == NO_ERROR)
  453. {
  454. Profile->KickOffTime.QuadPart = logonProfile->KickOffTime.QuadPart;
  455. if (logonProfile->LogonDomainName.Buffer)
  456. {
  457. wcsncpy(Profile->LogonDomainName,
  458. logonProfile->LogonDomainName.Buffer,
  459. DNLEN);
  460. }
  461. else
  462. {
  463. memset(Profile->LogonDomainName, 0, sizeof(Profile->LogonDomainName));
  464. }
  465. IASInitFixedArray(
  466. Profile->LanmanSessionKey,
  467. logonProfile->LanmanSessionKey
  468. );
  469. IASInitFixedArray(
  470. Profile->UserSessionKey,
  471. logonProfile->UserSessionKey
  472. );
  473. LsaFreeReturnBuffer(logonProfile);
  474. }
  475. return status;
  476. }
  477. ///////////////////////////////////////////////////////////////////////////////
  478. //
  479. // FUNCTION
  480. //
  481. // IASChangePassword1
  482. //
  483. // DESCRIPTION
  484. //
  485. // Performs V1 password change.
  486. //
  487. ///////////////////////////////////////////////////////////////////////////////
  488. DWORD
  489. WINAPI
  490. IASChangePassword1(
  491. IN PCWSTR UserName,
  492. IN PCWSTR Domain,
  493. IN PBYTE Challenge,
  494. IN PBYTE LmOldPassword,
  495. IN PBYTE LmNewPassword,
  496. IN PBYTE NtOldPassword,
  497. IN PBYTE NtNewPassword,
  498. IN DWORD NewLmPasswordLength,
  499. IN BOOL NtPresent,
  500. OUT PBYTE NewNtResponse,
  501. OUT PBYTE NewLmResponse
  502. )
  503. {
  504. DWORD status;
  505. SAM_HANDLE hUser;
  506. LM_OWF_PASSWORD LmOwfOldPassword;
  507. LM_OWF_PASSWORD LmOwfNewPassword;
  508. NT_OWF_PASSWORD NtOwfOldPassword;
  509. NT_OWF_PASSWORD NtOwfNewPassword;
  510. BOOLEAN fLmOldPresent;
  511. //////////
  512. // Open the user object.
  513. //////////
  514. status = IASSamOpenUser(
  515. Domain,
  516. UserName,
  517. USER_CHANGE_PASSWORD,
  518. DS_WRITABLE_REQUIRED,
  519. NULL,
  520. NULL,
  521. &hUser
  522. );
  523. if (status != NO_ERROR) { return status; }
  524. //////////
  525. // Decrypt the LM passwords.
  526. //////////
  527. RtlDecryptLmOwfPwdWithLmSesKey(
  528. (PENCRYPTED_LM_OWF_PASSWORD)LmOldPassword,
  529. (PLM_SESSION_KEY)Challenge,
  530. &LmOwfOldPassword
  531. );
  532. RtlDecryptLmOwfPwdWithLmSesKey(
  533. (PENCRYPTED_LM_OWF_PASSWORD)LmNewPassword,
  534. (PLM_SESSION_KEY)Challenge,
  535. &LmOwfNewPassword
  536. );
  537. //////////
  538. // Decrypt the NT passwords if present.
  539. //////////
  540. if (NtPresent)
  541. {
  542. RtlDecryptNtOwfPwdWithNtSesKey(
  543. (PENCRYPTED_NT_OWF_PASSWORD)NtOldPassword,
  544. (PNT_SESSION_KEY)Challenge,
  545. &NtOwfOldPassword
  546. );
  547. RtlDecryptNtOwfPwdWithNtSesKey(
  548. (PENCRYPTED_NT_OWF_PASSWORD)NtNewPassword,
  549. (PNT_SESSION_KEY)Challenge,
  550. &NtOwfNewPassword
  551. );
  552. }
  553. //////////
  554. // Change the password for this user
  555. //////////
  556. fLmOldPresent = (NewLmPasswordLength > LM20_PWLEN) ? FALSE : TRUE;
  557. status = SamiChangePasswordUser(
  558. hUser,
  559. fLmOldPresent,
  560. &LmOwfOldPassword,
  561. &LmOwfNewPassword,
  562. (BOOLEAN)NtPresent,
  563. &NtOwfOldPassword,
  564. &NtOwfNewPassword
  565. );
  566. if (NT_SUCCESS(status))
  567. {
  568. //////////
  569. // Calculate the user's reponse with the new password.
  570. //////////
  571. RtlCalculateLmResponse(
  572. (PLM_CHALLENGE)Challenge,
  573. &LmOwfNewPassword,
  574. (PLM_RESPONSE)NewLmResponse
  575. );
  576. RtlCalculateNtResponse(
  577. (PNT_CHALLENGE)Challenge,
  578. &NtOwfNewPassword,
  579. (PNT_RESPONSE)NewNtResponse
  580. );
  581. }
  582. SamCloseHandle(hUser);
  583. return RtlNtStatusToDosError(status);
  584. }
  585. ///////////////////////////////////////////////////////////////////////////////
  586. //
  587. // FUNCTION
  588. //
  589. // IASChangePassword2
  590. //
  591. // DESCRIPTION
  592. //
  593. // Performs V2 password change.
  594. //
  595. ///////////////////////////////////////////////////////////////////////////////
  596. DWORD
  597. WINAPI
  598. IASChangePassword2(
  599. IN PCWSTR UserName,
  600. IN PCWSTR Domain,
  601. IN PBYTE OldNtHash,
  602. IN PBYTE OldLmHash,
  603. IN PBYTE NtEncPassword,
  604. IN PBYTE LmEncPassword,
  605. IN BOOL LmPresent
  606. )
  607. {
  608. DWORD status;
  609. PDOMAIN_CONTROLLER_INFOW dci;
  610. UNICODE_STRING uniServerName, uniUserName;
  611. //////////
  612. // Get the name of the DC to connect to.
  613. //////////
  614. if (_wcsicmp(Domain, theAccountDomain) == 0)
  615. {
  616. //////////
  617. // Local domain, so use theLocalServer.
  618. //////////
  619. dci = NULL;
  620. RtlInitUnicodeString(
  621. &uniServerName,
  622. theLocalServer
  623. );
  624. }
  625. else
  626. {
  627. //////////
  628. // Remote domain, so use IASGetDcName.
  629. //////////
  630. status = IASGetDcName(
  631. Domain,
  632. DS_WRITABLE_REQUIRED,
  633. &dci
  634. );
  635. if (status != NO_ERROR) { goto exit; }
  636. RtlInitUnicodeString(
  637. &uniServerName,
  638. dci->DomainControllerName
  639. );
  640. }
  641. RtlInitUnicodeString(
  642. &uniUserName,
  643. UserName
  644. );
  645. status = SamiChangePasswordUser2(
  646. &uniServerName,
  647. &uniUserName,
  648. (PSAMPR_ENCRYPTED_USER_PASSWORD)NtEncPassword,
  649. (PENCRYPTED_NT_OWF_PASSWORD)OldNtHash,
  650. (BOOLEAN)LmPresent,
  651. (PSAMPR_ENCRYPTED_USER_PASSWORD)LmEncPassword,
  652. (PENCRYPTED_LM_OWF_PASSWORD)OldLmHash
  653. );
  654. status = RtlNtStatusToDosError(status);
  655. if (dci)
  656. {
  657. NetApiBufferFree(dci);
  658. }
  659. exit:
  660. return status;
  661. }
  662. ///////////////////////////////////////////////////////////////////////////////
  663. //
  664. // Various constants used for MS-CHAP v2
  665. //
  666. ///////////////////////////////////////////////////////////////////////////////
  667. UCHAR AuthMagic1[39] =
  668. {
  669. 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
  670. 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
  671. 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
  672. 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
  673. };
  674. UCHAR AuthMagic2[41] =
  675. {
  676. 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
  677. 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
  678. 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
  679. 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
  680. 0x6E
  681. };
  682. UCHAR SHSpad1[40] =
  683. {
  684. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  685. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  686. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  687. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  688. };
  689. UCHAR SHSpad2[40] =
  690. {
  691. 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
  692. 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
  693. 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
  694. 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2
  695. };
  696. UCHAR KeyMagic1[27] =
  697. {
  698. 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
  699. 0x68, 0x65, 0x20, 0x4D, 0x50, 0x50, 0x45, 0x20, 0x4D,
  700. 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4B, 0x65, 0x79
  701. };
  702. UCHAR KeyMagic2[84] =
  703. {
  704. 0x4F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69,
  705. 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2C, 0x20,
  706. 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
  707. 0x65, 0x20, 0x73, 0x65, 0x6E, 0x64, 0x20, 0x6B, 0x65, 0x79,
  708. 0x3B, 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
  709. 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
  710. 0x2C, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
  711. 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
  712. 0x6B, 0x65, 0x79, 0x2E
  713. };
  714. UCHAR KeyMagic3[84] =
  715. {
  716. 0x4F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69,
  717. 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2C, 0x20,
  718. 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
  719. 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
  720. 0x6B, 0x65, 0x79, 0x3B, 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68,
  721. 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
  722. 0x69, 0x64, 0x65, 0x2C, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
  723. 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x64, 0x20,
  724. 0x6B, 0x65, 0x79, 0x2E
  725. };
  726. ///////////////////////////////////////////////////////////////////////////////
  727. //
  728. // FUNCTION
  729. //
  730. // IASLogonMSCHAPv2
  731. //
  732. // DESCRIPTION
  733. //
  734. // Performs MS-CHAP v2 authentication.
  735. //
  736. ///////////////////////////////////////////////////////////////////////////////
  737. DWORD
  738. WINAPI
  739. IASLogonMSCHAPv2(
  740. IN PCWSTR UserName,
  741. IN PCWSTR Domain,
  742. IN PCSTR HashUserName,
  743. IN PBYTE Challenge,
  744. IN DWORD ChallengeLength,
  745. IN PBYTE Response,
  746. IN PBYTE PeerChallenge,
  747. OUT PIAS_MSCHAP_V2_PROFILE Profile,
  748. OUT PHANDLE Token
  749. )
  750. {
  751. A_SHA_CTX context;
  752. BYTE digest[A_SHA_DIGEST_LEN], masterKey[A_SHA_DIGEST_LEN];
  753. BYTE computedChallenge[MSV1_0_CHALLENGE_LENGTH];
  754. IAS_MSCHAP_PROFILE v1profile;
  755. DWORD status;
  756. /////////
  757. // Compute the v2 challenge.
  758. /////////
  759. A_SHAInit(&context);
  760. A_SHAUpdate(&context, PeerChallenge, 16);
  761. A_SHAUpdate(&context, Challenge, ChallengeLength);
  762. A_SHAUpdate(&context, (PBYTE)HashUserName, strlen(HashUserName));
  763. A_SHAFinal(&context, digest);
  764. memcpy(computedChallenge, digest, sizeof(computedChallenge));
  765. /////////
  766. // Authenticate the user.
  767. /////////
  768. status = IASLogonMSCHAP(
  769. UserName,
  770. Domain,
  771. computedChallenge,
  772. Response,
  773. NULL,
  774. &v1profile,
  775. Token
  776. );
  777. if (status != NO_ERROR) { return status; }
  778. /////////
  779. // Generate authenticator response.
  780. /////////
  781. A_SHAInit(&context);
  782. A_SHAUpdate(&context, v1profile.UserSessionKey, 16);
  783. A_SHAUpdate(&context, Response, NT_RESPONSE_LENGTH);
  784. A_SHAUpdate(&context, AuthMagic1, sizeof(AuthMagic1));
  785. A_SHAFinal(&context, digest);
  786. A_SHAInit(&context);
  787. A_SHAUpdate(&context, digest, sizeof(digest));
  788. A_SHAUpdate(&context, computedChallenge, sizeof(computedChallenge));
  789. A_SHAUpdate(&context, AuthMagic2, sizeof(AuthMagic2));
  790. A_SHAFinal(&context, digest);
  791. memcpy(Profile->AuthResponse, digest, _AUTHENTICATOR_RESPONSE_LENGTH);
  792. /////////
  793. // Generate master key.
  794. /////////
  795. A_SHAInit(&context);
  796. A_SHAUpdate(&context, v1profile.UserSessionKey, 16);
  797. A_SHAUpdate(&context, Response, NT_RESPONSE_LENGTH);
  798. A_SHAUpdate(&context, KeyMagic1, sizeof(KeyMagic1));
  799. A_SHAFinal(&context, masterKey);
  800. /////////
  801. // Generate receive key.
  802. /////////
  803. A_SHAInit(&context);
  804. A_SHAUpdate(&context, masterKey, 16);
  805. A_SHAUpdate(&context, SHSpad1, sizeof(SHSpad1));
  806. A_SHAUpdate(&context, KeyMagic2, sizeof(KeyMagic2));
  807. A_SHAUpdate(&context, SHSpad2, sizeof(SHSpad2));
  808. A_SHAFinal(&context, digest);
  809. memcpy(Profile->RecvSessionKey, digest, MSV1_0_USER_SESSION_KEY_LENGTH);
  810. /////////
  811. // Generate send key.
  812. /////////
  813. A_SHAInit(&context);
  814. A_SHAUpdate(&context, masterKey, 16);
  815. A_SHAUpdate(&context, SHSpad1, sizeof(SHSpad1));
  816. A_SHAUpdate(&context, KeyMagic3, sizeof(KeyMagic3));
  817. A_SHAUpdate(&context, SHSpad2, sizeof(SHSpad2));
  818. A_SHAFinal(&context, digest);
  819. memcpy(Profile->SendSessionKey, digest, MSV1_0_USER_SESSION_KEY_LENGTH);
  820. /////////
  821. // Copy the logon domain.
  822. /////////
  823. memcpy(
  824. Profile->LogonDomainName,
  825. v1profile.LogonDomainName,
  826. sizeof(Profile->LogonDomainName)
  827. );
  828. /////////
  829. // Save the KickOffTime.
  830. /////////
  831. Profile->KickOffTime = v1profile.KickOffTime;
  832. return NO_ERROR;
  833. }
  834. ///////////////////////////////////////////////////////////////////////////////
  835. //
  836. // FUNCTION
  837. //
  838. // IASChangePassword3
  839. //
  840. // DESCRIPTION
  841. //
  842. // Performs MS-CHAP v2 change password.
  843. //
  844. ///////////////////////////////////////////////////////////////////////////////
  845. DWORD
  846. WINAPI
  847. IASChangePassword3(
  848. IN PCWSTR UserName,
  849. IN PCWSTR Domain,
  850. IN PBYTE EncHash,
  851. IN PBYTE EncPassword
  852. )
  853. {
  854. return IASChangePassword2(
  855. UserName,
  856. Domain,
  857. EncHash,
  858. NULL,
  859. EncPassword,
  860. NULL,
  861. FALSE
  862. );
  863. }
  864. ///////////////////////////////////////////////////////////////////////////////
  865. //
  866. // FUNCTION
  867. //
  868. // IASGetAliasMembership
  869. //
  870. // DESCRIPTION
  871. //
  872. // Determines alias membership in the local account and built-in domains.
  873. //
  874. ///////////////////////////////////////////////////////////////////////////////
  875. DWORD
  876. WINAPI
  877. IASGetAliasMembership(
  878. IN PSID UserSid,
  879. IN PTOKEN_GROUPS GlobalGroups,
  880. IN PIAS_LSA_ALLOC Allocator,
  881. OUT PTOKEN_GROUPS *Groups,
  882. OUT PDWORD ReturnLength
  883. )
  884. {
  885. DWORD i, status, idx;
  886. ULONG globalSidCount;
  887. PSID *globalSids, sidBuffer;
  888. PULONG accountAliases, builtinAliases;
  889. ULONG accountAliasCount, builtinAliasCount;
  890. ULONG buflen, groupCount;
  891. //////////
  892. // Form an array of the 'global' SIDs (global groups plus user).
  893. //////////
  894. globalSidCount = GlobalGroups->GroupCount + 1;
  895. globalSids = (PSID*)_alloca(globalSidCount * sizeof(PSID));
  896. // First the group SIDs ...
  897. for (i = 0; i < GlobalGroups->GroupCount; ++i)
  898. {
  899. globalSids[i] = GlobalGroups->Groups[i].Sid;
  900. }
  901. // ... then the user SID.
  902. globalSids[i] = UserSid;
  903. //////////
  904. // Lookup aliases in the account and built-in domains.
  905. //////////
  906. status = SamGetAliasMembership(
  907. theAccountDomainHandle,
  908. globalSidCount,
  909. globalSids,
  910. &accountAliasCount,
  911. &accountAliases
  912. );
  913. if (!NT_SUCCESS(status))
  914. {
  915. status = RtlNtStatusToDosError(status);
  916. goto exit;
  917. }
  918. status = SamGetAliasMembership(
  919. theBuiltinDomainHandle,
  920. globalSidCount,
  921. globalSids,
  922. &builtinAliasCount,
  923. &builtinAliases
  924. );
  925. if (!NT_SUCCESS(status))
  926. {
  927. status = RtlNtStatusToDosError(status);
  928. goto free_account_aliases;
  929. }
  930. //////////
  931. // Allocate memory for the TOKEN_GROUPS struct.
  932. //////////
  933. // Space for the struct header.
  934. buflen = FIELD_OFFSET(TOKEN_GROUPS, Groups);
  935. // Space for the global groups.
  936. groupCount = GlobalGroups->GroupCount;
  937. for (i = 0; i < groupCount; ++i)
  938. {
  939. buflen += RtlLengthSid(GlobalGroups->Groups[i].Sid);
  940. }
  941. // Space for the aliases in the account domain.
  942. groupCount += accountAliasCount;
  943. buflen += theAccountSidLen * accountAliasCount;
  944. // Space for the aliases in the builtin domain.
  945. groupCount += builtinAliasCount;
  946. buflen += theBuiltinSidLen * builtinAliasCount;
  947. // Space for the SID_AND_ATTRIBUTES array.
  948. buflen += sizeof(SID_AND_ATTRIBUTES) * groupCount;
  949. *Groups = (PTOKEN_GROUPS)Allocator(buflen);
  950. if (!*Groups)
  951. {
  952. status = ERROR_NOT_ENOUGH_MEMORY;
  953. goto free_builtin_aliases;
  954. }
  955. *ReturnLength = buflen;
  956. //////////
  957. // Fill in the TOKEN_GROUPS struct.
  958. //////////
  959. (*Groups)->GroupCount = groupCount;
  960. sidBuffer = (*Groups)->Groups + groupCount;
  961. RtlCopySidAndAttributesArray(
  962. GlobalGroups->GroupCount,
  963. GlobalGroups->Groups,
  964. buflen,
  965. (*Groups)->Groups,
  966. sidBuffer,
  967. &sidBuffer,
  968. &buflen
  969. );
  970. idx = GlobalGroups->GroupCount;
  971. for (i = 0; i < accountAliasCount; ++i, ++idx)
  972. {
  973. IASInitializeChildSid(
  974. sidBuffer,
  975. theAccountDomainSid,
  976. accountAliases[i]
  977. );
  978. (*Groups)->Groups[idx].Sid = sidBuffer;
  979. (*Groups)->Groups[idx].Attributes = SE_GROUP_ENABLED;
  980. sidBuffer = (PBYTE)sidBuffer + theAccountSidLen;
  981. }
  982. for (i = 0; i < builtinAliasCount; ++i, ++idx)
  983. {
  984. IASInitializeChildSid(
  985. sidBuffer,
  986. theBuiltinDomainSid,
  987. builtinAliases[i]
  988. );
  989. (*Groups)->Groups[idx].Sid = sidBuffer;
  990. (*Groups)->Groups[idx].Attributes = SE_GROUP_ENABLED;
  991. sidBuffer = (PBYTE)sidBuffer + theBuiltinSidLen;
  992. }
  993. free_builtin_aliases:
  994. SamFreeMemory(builtinAliases);
  995. free_account_aliases:
  996. SamFreeMemory(accountAliases);
  997. exit:
  998. return status;
  999. }
  1000. ///////////////////////////////////////////////////////////////////////////////
  1001. //
  1002. // FUNCTION
  1003. //
  1004. // IASGetGroupsForUser
  1005. //
  1006. // DESCRIPTION
  1007. //
  1008. // Allocated and initializes a TOKEN_GROUPS struct for the specified user.
  1009. //
  1010. ///////////////////////////////////////////////////////////////////////////////
  1011. #define REQUIRED_USER_FIELDS \
  1012. ( USER_ALL_USERACCOUNTCONTROL | \
  1013. USER_ALL_ACCOUNTEXPIRES | \
  1014. USER_ALL_PARAMETERS )
  1015. DWORD
  1016. WINAPI
  1017. IASGetGroupsForUser(
  1018. IN PCWSTR UserName,
  1019. IN PCWSTR Domain,
  1020. IN PIAS_LSA_ALLOC Allocator,
  1021. OUT PTOKEN_GROUPS *Groups,
  1022. OUT PDWORD ReturnLength,
  1023. OUT PLARGE_INTEGER SessionTimeout
  1024. )
  1025. {
  1026. DWORD status, i;
  1027. SAM_HANDLE hUser;
  1028. PSID userDomainSid;
  1029. ULONG userRid;
  1030. PUSER_ALL_INFORMATION uai;
  1031. PGROUP_MEMBERSHIP globalGroups;
  1032. ULONG globalGroupCount, globalSidLen;
  1033. PTOKEN_GROUPS tokenGroups;
  1034. PSID sidBuffer;
  1035. _ASSERT(SessionTimeout != NULL);
  1036. //////////
  1037. // Open the user.
  1038. //////////
  1039. status = IASSamOpenUser(
  1040. Domain,
  1041. UserName,
  1042. USER_LIST_GROUPS | USER_READ_ACCOUNT | USER_READ_LOGON,
  1043. 0,
  1044. &userRid,
  1045. &userDomainSid,
  1046. &hUser
  1047. );
  1048. if (status != NO_ERROR) { goto exit; }
  1049. //////////
  1050. // Check the account restrictions.
  1051. //////////
  1052. status = SamQueryInformationUser(
  1053. hUser,
  1054. UserAllInformation,
  1055. (PVOID*)&uai
  1056. );
  1057. if (!NT_SUCCESS(status))
  1058. {
  1059. status = RtlNtStatusToDosError(status);
  1060. goto close_user;
  1061. }
  1062. if ((uai->WhichFields & REQUIRED_USER_FIELDS) != REQUIRED_USER_FIELDS)
  1063. {
  1064. status = ERROR_ACCESS_DENIED;
  1065. goto free_user_info;
  1066. }
  1067. if (uai->UserAccountControl & USER_ACCOUNT_DISABLED)
  1068. {
  1069. status = ERROR_ACCOUNT_DISABLED;
  1070. goto free_user_info;
  1071. }
  1072. if (uai->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
  1073. {
  1074. status = ERROR_ACCOUNT_LOCKED_OUT;
  1075. goto free_user_info;
  1076. }
  1077. status = IASCheckAccountRestrictions(
  1078. &(uai->AccountExpires),
  1079. (PIAS_LOGON_HOURS)&(uai->LogonHours),
  1080. SessionTimeout
  1081. );
  1082. if (status != NO_ERROR) { goto free_user_info; }
  1083. //////////
  1084. // Get the user's global groups.
  1085. //////////
  1086. status = SamGetGroupsForUser(
  1087. hUser,
  1088. &globalGroups,
  1089. &globalGroupCount
  1090. );
  1091. if (!NT_SUCCESS(status))
  1092. {
  1093. status = RtlNtStatusToDosError(status);
  1094. goto close_user;
  1095. }
  1096. //////////
  1097. // Allocate memory for the TOKEN_GROUPS struct plus the user SID.
  1098. //////////
  1099. globalSidLen = IASLengthRequiredChildSid(userDomainSid);
  1100. tokenGroups =
  1101. (PTOKEN_GROUPS)_alloca(
  1102. FIELD_OFFSET(TOKEN_GROUPS, Groups) +
  1103. (sizeof(SID_AND_ATTRIBUTES) + globalSidLen) *
  1104. globalGroupCount +
  1105. globalSidLen
  1106. );
  1107. //////////
  1108. // Fill in the TOKEN_GROUPS struct.
  1109. //////////
  1110. tokenGroups->GroupCount = globalGroupCount;
  1111. sidBuffer = tokenGroups->Groups + globalGroupCount;
  1112. for (i = 0; i < globalGroupCount; ++i)
  1113. {
  1114. IASInitializeChildSid(
  1115. sidBuffer,
  1116. userDomainSid,
  1117. globalGroups[i].RelativeId
  1118. );
  1119. tokenGroups->Groups[i].Sid = sidBuffer;
  1120. tokenGroups->Groups[i].Attributes = globalGroups[i].Attributes;
  1121. sidBuffer = (PBYTE)sidBuffer + globalSidLen;
  1122. }
  1123. ///////
  1124. // Compute the user SID.
  1125. ///////
  1126. IASInitializeChildSid(
  1127. sidBuffer,
  1128. userDomainSid,
  1129. userRid
  1130. );
  1131. ///////
  1132. // Expand the group membership locally.
  1133. ///////
  1134. status = IASGetAliasMembership(
  1135. sidBuffer,
  1136. tokenGroups,
  1137. Allocator,
  1138. Groups,
  1139. ReturnLength
  1140. );
  1141. SamFreeMemory(globalGroups);
  1142. free_user_info:
  1143. SamFreeMemory(uai);
  1144. close_user:
  1145. RtlFreeHeap(RtlProcessHeap(), 0, userDomainSid);
  1146. SamCloseHandle(hUser);
  1147. exit:
  1148. return status;
  1149. }
  1150. ///////////////////////////////////////////////////////////////////////////////
  1151. //
  1152. // FUNCTION
  1153. //
  1154. // GetSamUserParameters
  1155. //
  1156. // DESCRIPTION
  1157. //
  1158. // Retrieves the USER_PARAMETERS_INFORMATION for a user.
  1159. //
  1160. ///////////////////////////////////////////////////////////////////////////////
  1161. DWORD
  1162. WINAPI
  1163. GetSamUserParameters(
  1164. IN PCWSTR UserName,
  1165. IN PCWSTR Domain,
  1166. OUT PUSER_PARAMETERS_INFORMATION *UserParameters
  1167. )
  1168. {
  1169. DWORD status;
  1170. SAM_HANDLE hUser;
  1171. // Initialize the out parameter.
  1172. *UserParameters = NULL;
  1173. // Find the user.
  1174. status = IASSamOpenUser(
  1175. Domain,
  1176. UserName,
  1177. USER_READ_ACCOUNT,
  1178. 0,
  1179. NULL,
  1180. NULL,
  1181. &hUser
  1182. );
  1183. if (status == NO_ERROR)
  1184. {
  1185. // Get the user's parameters.
  1186. status = SamQueryInformationUser(
  1187. hUser,
  1188. UserParametersInformation,
  1189. (PVOID*)UserParameters
  1190. );
  1191. if (!NT_SUCCESS(status)) { status = RtlNtStatusToDosError(status); }
  1192. SamCloseHandle(hUser);
  1193. }
  1194. return status;
  1195. }
  1196. ///////////////////////////////////////////////////////////////////////////////
  1197. //
  1198. // FUNCTION
  1199. //
  1200. // IASGetUserParameters
  1201. //
  1202. // DESCRIPTION
  1203. //
  1204. // Returns the SAM UserParameters for a given user. The returned string
  1205. // must be freed through LocalFree.
  1206. //
  1207. ///////////////////////////////////////////////////////////////////////////////
  1208. DWORD
  1209. WINAPI
  1210. IASGetUserParameters(
  1211. IN PCWSTR UserName,
  1212. IN PCWSTR Domain,
  1213. OUT PWSTR *UserParameters
  1214. )
  1215. {
  1216. DWORD status;
  1217. SAM_HANDLE hUser;
  1218. PUSER_PARAMETERS_INFORMATION upi;
  1219. // Initialize the out parameter.
  1220. *UserParameters = NULL;
  1221. // Get the USER_PARAMETERS_INFORMATION.
  1222. status = GetSamUserParameters(
  1223. UserName,
  1224. Domain,
  1225. &upi
  1226. );
  1227. if (status != NO_ERROR) { return status; }
  1228. *UserParameters = (PWSTR)LocalAlloc(
  1229. LMEM_FIXED,
  1230. upi->Parameters.Length + sizeof(WCHAR)
  1231. );
  1232. if (*UserParameters)
  1233. {
  1234. memcpy(*UserParameters, upi->Parameters.Buffer, upi->Parameters.Length);
  1235. (*UserParameters)[upi->Parameters.Length / sizeof(WCHAR)] = L'\0';
  1236. }
  1237. else
  1238. {
  1239. status = ERROR_NOT_ENOUGH_MEMORY;
  1240. }
  1241. SamFreeMemory(upi);
  1242. return status;
  1243. }
  1244. ///////////////////////////////////////////////////////////////////////////////
  1245. //
  1246. // FUNCTION
  1247. //
  1248. // IASGetRASUserInfo
  1249. //
  1250. // DESCRIPTION
  1251. //
  1252. // Basically a rewrite of RasAdminUserGetInfo.
  1253. //
  1254. ///////////////////////////////////////////////////////////////////////////////
  1255. DWORD
  1256. WINAPI
  1257. IASGetRASUserInfo(
  1258. IN PCWSTR UserName,
  1259. IN PCWSTR Domain,
  1260. OUT PRAS_USER_0 RasUser0
  1261. )
  1262. {
  1263. DWORD status;
  1264. PWSTR userParms;
  1265. status = IASGetUserParameters(
  1266. UserName,
  1267. Domain,
  1268. &userParms
  1269. );
  1270. if (status == NO_ERROR)
  1271. {
  1272. status = IASParmsQueryRasUser0(
  1273. userParms,
  1274. RasUser0
  1275. );
  1276. LocalFree(userParms);
  1277. }
  1278. return status;
  1279. }
  1280. ///////////////////////////////////////////////////////////////////////////////
  1281. //
  1282. // FUNCTION
  1283. //
  1284. // IASValidateUserName
  1285. //
  1286. // DESCRIPTION
  1287. //
  1288. // Verifies that the input parameters represent a valid SAM account.
  1289. //
  1290. ///////////////////////////////////////////////////////////////////////////////
  1291. DWORD
  1292. WINAPI
  1293. IASValidateUserName(
  1294. IN PCWSTR UserName,
  1295. IN PCWSTR Domain
  1296. )
  1297. {
  1298. DWORD status;
  1299. PWCHAR attrs[1];
  1300. PLDAPMessage msg;
  1301. SAM_HANDLE hUser;
  1302. IAS_NTDS_RESULT result = { 0, 0 };
  1303. // For remote domains, we'll try LDAP first since it's faster.
  1304. if (!IASIsDomainLocal(Domain))
  1305. {
  1306. attrs[0] = NULL;
  1307. status = IASNtdsQueryUserAttributes(
  1308. Domain,
  1309. UserName,
  1310. LDAP_SCOPE_SUBTREE,
  1311. attrs,
  1312. &result
  1313. );
  1314. IASNtdsFreeResult(&result);
  1315. if (status != ERROR_DS_NOT_INSTALLED &&
  1316. status != ERROR_INVALID_DOMAIN_ROLE)
  1317. {
  1318. return status;
  1319. }
  1320. }
  1321. // Couldn't use the DS, so try SAM.
  1322. status = IASSamOpenUser(
  1323. Domain,
  1324. UserName,
  1325. USER_READ_ACCOUNT,
  1326. 0,
  1327. NULL,
  1328. NULL,
  1329. &hUser
  1330. );
  1331. if (status == NO_ERROR) { SamCloseHandle(hUser); }
  1332. return status;
  1333. }
  1334. ///////////////////////////////////////////////////////////////////////////////
  1335. //
  1336. // FUNCTION
  1337. //
  1338. // IASGetDefaultDomain
  1339. //
  1340. // DESCRIPTION
  1341. //
  1342. // Returns the default domain. The returned string should be treated as
  1343. // read-only memory.
  1344. //
  1345. ///////////////////////////////////////////////////////////////////////////////
  1346. PCWSTR
  1347. WINAPI
  1348. IASGetDefaultDomain( VOID )
  1349. {
  1350. return theDefaultDomain;
  1351. }
  1352. ///////////////////////////////////////////////////////////////////////////////
  1353. //
  1354. // FUNCTION
  1355. //
  1356. // IASGetDnsDomainName
  1357. //
  1358. // DESCRIPTION
  1359. //
  1360. // Returns the dns name of the domain
  1361. //
  1362. ///////////////////////////////////////////////////////////////////////////////
  1363. DWORD
  1364. WINAPI
  1365. IASGetDnsDomainName(LPWSTR buffer, LPDWORD bufferByteSize)
  1366. {
  1367. DWORD result = NO_ERROR;
  1368. EnterCriticalSection(&critSec);
  1369. // i.e. assert that this function is not called after uninitialize or
  1370. // if initialize failed
  1371. _ASSERT(theDnsDomainName != 0);
  1372. if (bufferByteSize == 0 || buffer == 0)
  1373. {
  1374. result = ERROR_INSUFFICIENT_BUFFER;
  1375. }
  1376. else
  1377. {
  1378. HRESULT hr = StringCbCopyNW(
  1379. buffer,
  1380. *bufferByteSize,
  1381. theDnsDomainName->Buffer,
  1382. theDnsDomainName->Length
  1383. );
  1384. if (FAILED(hr))
  1385. {
  1386. result = HRESULT_CODE(hr);
  1387. }
  1388. }
  1389. // always set the necessary size
  1390. *bufferByteSize = theDnsDomainName->Length + sizeof(wchar_t); // for '\0'
  1391. // Failure could be for any reason: insufficient buffer size or
  1392. // anything else.
  1393. LeaveCriticalSection(&critSec);
  1394. return result;
  1395. }
  1396. ///////////////////////////////////////////////////////////////////////////////
  1397. //
  1398. // FUNCTION
  1399. //
  1400. // IASIsDomainLocal
  1401. //
  1402. // DESCRIPTION
  1403. //
  1404. // Returns TRUE if the specified domain resides on the local machine.
  1405. //
  1406. ///////////////////////////////////////////////////////////////////////////////
  1407. BOOL
  1408. WINAPI
  1409. IASIsDomainLocal(
  1410. IN PCWSTR Domain
  1411. )
  1412. {
  1413. return (_wcsicmp(Domain, theAccountDomain) == 0) ? TRUE : FALSE;
  1414. }
  1415. ///////////////////////////////////////////////////////////////////////////////
  1416. //
  1417. // FUNCTION
  1418. //
  1419. // IASGetRole
  1420. //
  1421. // DESCRIPTION
  1422. //
  1423. // Returns the role of the local computer.
  1424. //
  1425. ///////////////////////////////////////////////////////////////////////////////
  1426. IAS_ROLE
  1427. WINAPI
  1428. IASGetRole( VOID )
  1429. {
  1430. return ourRole;
  1431. }
  1432. ///////////////////////////////////////////////////////////////////////////////
  1433. //
  1434. // FUNCTION
  1435. //
  1436. // IASGetProductType
  1437. //
  1438. // DESCRIPTION
  1439. //
  1440. // Returns the product type of the local computer.
  1441. //
  1442. ///////////////////////////////////////////////////////////////////////////////
  1443. IAS_PRODUCT_TYPE
  1444. WINAPI
  1445. IASGetProductType( VOID )
  1446. {
  1447. return ourProductType;
  1448. }
  1449. ///////////////////////////////////////////////////////////////////////////////
  1450. //
  1451. // FUNCTION
  1452. //
  1453. // IASGetGuestAccountName
  1454. //
  1455. // DESCRIPTION
  1456. //
  1457. // Returns the SAM account name of the guest account for the default
  1458. // domain. GuestAccount must be large enough to hold DNLEN + UNLEN + 2
  1459. // characters.
  1460. //
  1461. ///////////////////////////////////////////////////////////////////////////////
  1462. DWORD
  1463. WINAPI
  1464. IASGetGuestAccountName(
  1465. OUT PWSTR AccountName
  1466. )
  1467. {
  1468. wcscpy(AccountName, theGuestAccount);
  1469. return NO_ERROR;
  1470. }
  1471. ///////////////////////////////////////////////////////////////////////////////
  1472. //
  1473. // FUNCTION
  1474. //
  1475. // IASMapWin32Error
  1476. //
  1477. // DESCRIPTION
  1478. //
  1479. // Maps a Win32 error code to an IAS reason code. If the error can't be
  1480. // mapped, 'hrDefault' is returned. If 'hrDefault' equals -1, then
  1481. // HRESULT_FROM_WIN32 will be used to force a mapping.
  1482. //
  1483. ///////////////////////////////////////////////////////////////////////////////
  1484. HRESULT
  1485. WINAPI
  1486. IASMapWin32Error(
  1487. IN DWORD dwError,
  1488. IN HRESULT hrDefault
  1489. )
  1490. {
  1491. HRESULT hr = hrDefault;
  1492. switch (dwError)
  1493. {
  1494. case NO_ERROR:
  1495. hr = S_OK;
  1496. break;
  1497. case ERROR_ACCESS_DENIED:
  1498. hr = IAS_ACCESS_DENIED;
  1499. break;
  1500. case ERROR_NO_SUCH_DOMAIN:
  1501. hr = IAS_NO_SUCH_DOMAIN;
  1502. break;
  1503. case ERROR_NO_LOGON_SERVERS:
  1504. case RPC_S_SERVER_UNAVAILABLE:
  1505. case RPC_S_SERVER_TOO_BUSY:
  1506. case RPC_S_CALL_FAILED:
  1507. case EPT_S_NOT_REGISTERED:
  1508. hr = IAS_DOMAIN_UNAVAILABLE;
  1509. break;
  1510. case ERROR_INVALID_PASSWORD:
  1511. case ERROR_LOGON_FAILURE:
  1512. hr = IAS_AUTH_FAILURE;
  1513. break;
  1514. case ERROR_INVALID_LOGON_HOURS:
  1515. hr = IAS_INVALID_LOGON_HOURS;
  1516. break;
  1517. case ERROR_PASSWORD_EXPIRED:
  1518. case ERROR_PASSWORD_MUST_CHANGE:
  1519. hr = IAS_PASSWORD_MUST_CHANGE;
  1520. break;
  1521. case ERROR_ACCOUNT_RESTRICTION:
  1522. hr = IAS_ACCOUNT_RESTRICTION;
  1523. break;
  1524. case ERROR_ACCOUNT_DISABLED:
  1525. hr = IAS_ACCOUNT_DISABLED;
  1526. break;
  1527. case ERROR_ACCOUNT_EXPIRED:
  1528. hr = IAS_ACCOUNT_EXPIRED;
  1529. break;
  1530. case ERROR_ACCOUNT_LOCKED_OUT:
  1531. hr = IAS_ACCOUNT_LOCKED_OUT;
  1532. break;
  1533. case ERROR_NO_SUCH_USER:
  1534. case ERROR_NONE_MAPPED:
  1535. case NERR_UserNotFound:
  1536. hr = IAS_NO_SUCH_USER;
  1537. break;
  1538. case ERROR_ILL_FORMED_PASSWORD:
  1539. case ERROR_PASSWORD_RESTRICTION:
  1540. hr = IAS_CHANGE_PASSWORD_FAILURE;
  1541. break;
  1542. case ERROR_DS_NO_ATTRIBUTE_OR_VALUE:
  1543. hr = IAS_NO_CLEARTEXT_PASSWORD;
  1544. break;
  1545. case CRYPT_E_REVOKED:
  1546. hr = IAS_CRYPT_E_REVOKED;
  1547. break;
  1548. case CRYPT_E_NO_REVOCATION_DLL:
  1549. hr = IAS_CRYPT_E_NO_REVOCATION_DLL;
  1550. break;
  1551. case CRYPT_E_NO_REVOCATION_CHECK:
  1552. hr = IAS_CRYPT_E_NO_REVOCATION_CHECK;
  1553. break;
  1554. case CRYPT_E_REVOCATION_OFFLINE:
  1555. hr = IAS_CRYPT_E_REVOCATION_OFFLINE;
  1556. break;
  1557. case SEC_E_MESSAGE_ALTERED:
  1558. hr = IAS_SEC_E_MESSAGE_ALTERED;
  1559. break;
  1560. case SEC_E_NO_AUTHENTICATING_AUTHORITY:
  1561. hr = IAS_SEC_E_NO_AUTHENTICATING_AUTHORITY;
  1562. break;
  1563. case SEC_E_INCOMPLETE_MESSAGE:
  1564. hr = IAS_SEC_E_INCOMPLETE_MESSAGE;
  1565. break;
  1566. case SEC_E_INCOMPLETE_CREDENTIALS:
  1567. hr = IAS_SEC_E_INCOMPLETE_CREDENTIALS;
  1568. break;
  1569. case SEC_E_TIME_SKEW:
  1570. hr = IAS_SEC_E_TIME_SKEW;
  1571. break;
  1572. case SEC_E_UNTRUSTED_ROOT:
  1573. hr = IAS_SEC_E_UNTRUSTED_ROOT;
  1574. break;
  1575. case SEC_E_ILLEGAL_MESSAGE:
  1576. hr = IAS_SEC_E_ILLEGAL_MESSAGE;
  1577. break;
  1578. case SEC_E_CERT_WRONG_USAGE:
  1579. hr = IAS_SEC_E_CERT_WRONG_USAGE;
  1580. break;
  1581. case SEC_E_CERT_EXPIRED:
  1582. hr = IAS_SEC_E_CERT_EXPIRED;
  1583. break;
  1584. case SEC_E_ALGORITHM_MISMATCH:
  1585. hr = IAS_SEC_E_ALGORITHM_MISMATCH;
  1586. break;
  1587. case SEC_E_SMARTCARD_LOGON_REQUIRED:
  1588. hr = IAS_SEC_E_SMARTCARD_LOGON_REQUIRED;
  1589. break;
  1590. case SEC_E_SHUTDOWN_IN_PROGRESS:
  1591. hr = IAS_SEC_E_SHUTDOWN_IN_PROGRESS;
  1592. break;
  1593. case SEC_E_MULTIPLE_ACCOUNTS:
  1594. hr = IAS_SEC_E_MULTIPLE_ACCOUNTS;
  1595. break;
  1596. case TRUST_E_PROVIDER_UNKNOWN:
  1597. hr = IAS_TRUST_E_PROVIDER_UNKNOWN;
  1598. break;
  1599. case TRUST_E_ACTION_UNKNOWN:
  1600. hr = IAS_TRUST_E_ACTION_UNKNOWN;
  1601. break;
  1602. case TRUST_E_SUBJECT_FORM_UNKNOWN:
  1603. hr = IAS_TRUST_E_SUBJECT_FORM_UNKNOWN;
  1604. break;
  1605. case TRUST_E_SUBJECT_NOT_TRUSTED:
  1606. hr = IAS_TRUST_E_SUBJECT_NOT_TRUSTED;
  1607. break;
  1608. case TRUST_E_NOSIGNATURE:
  1609. hr = IAS_TRUST_E_NOSIGNATURE;
  1610. break;
  1611. case CERT_E_EXPIRED:
  1612. hr = IAS_CERT_E_EXPIRED;
  1613. break;
  1614. case CERT_E_VALIDITYPERIODNESTING:
  1615. hr = IAS_CERT_E_VALIDITYPERIODNESTING;
  1616. break;
  1617. case CERT_E_ROLE:
  1618. hr = IAS_CERT_E_ROLE;
  1619. break;
  1620. case CERT_E_PATHLENCONST:
  1621. hr = IAS_CERT_E_PATHLENCONST;
  1622. break;
  1623. case CERT_E_CRITICAL:
  1624. hr = IAS_CERT_E_CRITICAL;
  1625. break;
  1626. case CERT_E_PURPOSE:
  1627. hr = IAS_CERT_E_PURPOSE;
  1628. break;
  1629. case CERT_E_ISSUERCHAINING:
  1630. hr = IAS_CERT_E_ISSUERCHAINING;
  1631. break;
  1632. case CERT_E_MALFORMED:
  1633. hr = IAS_CERT_E_MALFORMED;
  1634. break;
  1635. case CERT_E_UNTRUSTEDROOT:
  1636. hr = IAS_CERT_E_UNTRUSTEDROOT;
  1637. break;
  1638. case CERT_E_CHAINING:
  1639. hr = IAS_CERT_E_CHAINING;
  1640. break;
  1641. case TRUST_E_FAIL:
  1642. hr = IAS_TRUST_E_FAIL;
  1643. break;
  1644. case CERT_E_REVOKED:
  1645. hr = IAS_CERT_E_REVOKED ;
  1646. break;
  1647. case CERT_E_UNTRUSTEDTESTROOT:
  1648. hr = IAS_CERT_E_UNTRUSTEDTESTROOT;
  1649. break;
  1650. case CERT_E_REVOCATION_FAILURE:
  1651. hr = IAS_CERT_E_REVOCATION_FAILURE;
  1652. break;
  1653. case CERT_E_CN_NO_MATCH:
  1654. hr = IAS_CERT_E_CN_NO_MATCH;
  1655. break;
  1656. case CERT_E_WRONG_USAGE:
  1657. hr = IAS_CERT_E_WRONG_USAGE;
  1658. break;
  1659. case TRUST_E_EXPLICIT_DISTRUST:
  1660. hr = IAS_TRUST_E_EXPLICIT_DISTRUST;
  1661. break;
  1662. case CERT_E_UNTRUSTEDCA:
  1663. hr = IAS_CERT_E_UNTRUSTEDCA;
  1664. break;
  1665. case CERT_E_INVALID_POLICY:
  1666. hr = IAS_CERT_E_INVALID_POLICY;
  1667. break;
  1668. case CERT_E_INVALID_NAME:
  1669. hr = IAS_CERT_E_INVALID_NAME;
  1670. break;
  1671. case SEC_E_PKINIT_NAME_MISMATCH:
  1672. hr = IAS_SEC_E_PKINIT_NAME_MISMATCH;
  1673. break;
  1674. case SEC_E_OUT_OF_SEQUENCE:
  1675. hr = IAS_SEC_E_OUT_OF_SEQUENCE;
  1676. break;
  1677. case SEC_E_NO_CREDENTIALS:
  1678. hr = IAS_SEC_E_NO_CREDENTIALS;
  1679. break;
  1680. case NTE_BAD_UID:
  1681. case NTE_BAD_HASH:
  1682. case NTE_BAD_KEY:
  1683. case NTE_BAD_LEN:
  1684. case NTE_BAD_DATA:
  1685. case NTE_BAD_SIGNATURE:
  1686. case NTE_BAD_VER:
  1687. case NTE_BAD_ALGID:
  1688. case NTE_BAD_FLAGS:
  1689. case NTE_BAD_TYPE:
  1690. case NTE_BAD_KEY_STATE:
  1691. case NTE_BAD_HASH_STATE:
  1692. case NTE_NO_KEY:
  1693. case NTE_NO_MEMORY:
  1694. case NTE_EXISTS:
  1695. case NTE_PERM:
  1696. case NTE_NOT_FOUND:
  1697. case NTE_DOUBLE_ENCRYPT:
  1698. case NTE_BAD_PROVIDER:
  1699. case NTE_BAD_PROV_TYPE:
  1700. case NTE_BAD_PUBLIC_KEY:
  1701. case NTE_BAD_KEYSET:
  1702. case NTE_PROV_TYPE_NOT_DEF:
  1703. case NTE_PROV_TYPE_ENTRY_BAD:
  1704. case NTE_KEYSET_NOT_DEF:
  1705. case NTE_KEYSET_ENTRY_BAD:
  1706. case NTE_PROV_TYPE_NO_MATCH:
  1707. case NTE_SIGNATURE_FILE_BAD:
  1708. case SEC_I_CONTINUE_NEEDED:
  1709. case SEC_I_COMPLETE_NEEDED:
  1710. case SEC_I_COMPLETE_AND_CONTINUE:
  1711. case SEC_I_LOCAL_LOGON:
  1712. case SEC_E_BAD_PKGID:
  1713. case SEC_E_CONTEXT_EXPIRED:
  1714. case SEC_I_CONTEXT_EXPIRED:
  1715. case SEC_E_BUFFER_TOO_SMALL:
  1716. case SEC_I_INCOMPLETE_CREDENTIALS:
  1717. case SEC_I_RENEGOTIATE:
  1718. case SEC_E_WRONG_PRINCIPAL:
  1719. case SEC_I_NO_LSA_CONTEXT:
  1720. case SEC_E_ENCRYPT_FAILURE:
  1721. case SEC_E_DECRYPT_FAILURE:
  1722. case SEC_E_SECURITY_QOS_FAILED:
  1723. case SEC_E_UNFINISHED_CONTEXT_DELETED:
  1724. case SEC_E_NO_TGT_REPLY:
  1725. case SEC_E_NO_IP_ADDRESSES:
  1726. case SEC_E_WRONG_CREDENTIAL_HANDLE:
  1727. case SEC_E_CRYPTO_SYSTEM_INVALID:
  1728. case SEC_E_MAX_REFERRALS_EXCEEDED:
  1729. case SEC_E_MUST_BE_KDC:
  1730. case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
  1731. case SEC_E_TOO_MANY_PRINCIPALS:
  1732. case SEC_E_NO_PA_DATA:
  1733. case SEC_E_CERT_UNKNOWN:
  1734. hr = IAS_UNEXPECTED_EAP_ERROR;
  1735. break;
  1736. case ERROR_PROTOCOL_NOT_CONFIGURED:
  1737. hr = IAS_EAP_NEGOTIATION_FAILED;
  1738. break;
  1739. default:
  1740. // Default value already set above.
  1741. break;
  1742. }
  1743. return hr;
  1744. }
  1745. ///////////////////////////////////////////////////////////////////////////////
  1746. //
  1747. // FUNCTION
  1748. //
  1749. // IASGetDcName
  1750. //
  1751. // DESCRIPTION
  1752. //
  1753. // Wrapper around DsGetDcNameW. Tries to do the right thing with regard
  1754. // to NETBIOS and DNS names.
  1755. //
  1756. ///////////////////////////////////////////////////////////////////////////////
  1757. DWORD
  1758. WINAPI
  1759. IASGetDcName(
  1760. IN LPCWSTR DomainName,
  1761. IN ULONG Flags,
  1762. OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
  1763. )
  1764. {
  1765. DWORD status;
  1766. PDOMAIN_CONTROLLER_INFOW dci;
  1767. if (!(Flags & DS_IS_DNS_NAME)) { Flags |= DS_IS_FLAT_NAME; }
  1768. status = DsGetDcNameW(
  1769. NULL,
  1770. DomainName,
  1771. NULL,
  1772. NULL,
  1773. Flags,
  1774. DomainControllerInfo
  1775. );
  1776. if (status == NO_ERROR &&
  1777. !(Flags & DS_IS_DNS_NAME) &&
  1778. ((*DomainControllerInfo)->Flags & DS_DS_FLAG))
  1779. {
  1780. // It's an NT5 DC, so we need the DNS name of the server.
  1781. Flags |= DS_RETURN_DNS_NAME;
  1782. // We always want a cache hit here.
  1783. Flags &= ~(ULONG)DS_FORCE_REDISCOVERY;
  1784. if (!DsGetDcNameW(
  1785. NULL,
  1786. DomainName,
  1787. NULL,
  1788. NULL,
  1789. Flags,
  1790. &dci
  1791. ))
  1792. {
  1793. NetApiBufferFree(*DomainControllerInfo);
  1794. *DomainControllerInfo = dci;
  1795. }
  1796. }
  1797. return status;
  1798. }