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.

2249 lines
54 KiB

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