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.

724 lines
21 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. ChangePw.c
  5. Abstract:
  6. This module implements password change from downlevel clients.
  7. XsChangePasswordSam is called by XsNetUserPasswordSet2 in
  8. apiuser.c. I've put this in a seperate file because it #includes
  9. a private SAM header.
  10. Author:
  11. Dave Hart (davehart) 31-Apr-1992
  12. Revision History:
  13. --*/
  14. #include "xactsrvp.h"
  15. #include <ntlsa.h>
  16. #include <ntsam.h>
  17. #include <ntsamp.h>
  18. #include <crypt.h>
  19. #include <lmcons.h>
  20. #include "changepw.h"
  21. #include <netlibnt.h>
  22. #include <smbgtpt.h>
  23. /////////////////////////////////////////////////////////////////////////////
  24. // //
  25. // Internal function prototyptes. //
  26. // //
  27. /////////////////////////////////////////////////////////////////////////////
  28. NTSTATUS
  29. RtlGetPrimaryDomain(
  30. IN ULONG SidLength,
  31. OUT PBOOLEAN PrimaryDomainPresent,
  32. OUT PUNICODE_STRING PrimaryDomainName,
  33. OUT PUSHORT RequiredNameLength,
  34. OUT PSID PrimaryDomainSid OPTIONAL,
  35. OUT PULONG RequiredSidLength
  36. );
  37. /////////////////////////////////////////////////////////////////////////////
  38. // //
  39. // Exported functions. //
  40. // //
  41. /////////////////////////////////////////////////////////////////////////////
  42. NET_API_STATUS
  43. XsChangePasswordSam (
  44. IN PUNICODE_STRING UserName,
  45. IN PVOID OldPassword,
  46. IN PVOID NewPassword,
  47. IN BOOLEAN Encrypted
  48. )
  49. /*++
  50. Routine Description:
  51. This routine is called by XsNetUserPasswordSet2 to change the password
  52. on a Windows NT machine. The code is based on
  53. lsa\msv1_0\nlmain.c MspChangePasswordSam.
  54. Arguments:
  55. UserName - Name of the user to change password for.
  56. OldPassword - Old password encrypted using new password as key.
  57. NewPassword - New password encrypted using old password as key.
  58. Return Value:
  59. --*/
  60. {
  61. NTSTATUS Status;
  62. NT_PRODUCT_TYPE NtProductType;
  63. UNICODE_STRING DomainName;
  64. LPWSTR serverName = NULL;
  65. BOOLEAN DomainNameAllocated;
  66. BOOLEAN PrimaryDomainPresent;
  67. USHORT RequiredDomainNameLength;
  68. ULONG RequiredDomainSidLength;
  69. OBJECT_ATTRIBUTES ObjectAttributes;
  70. SECURITY_QUALITY_OF_SERVICE SecurityQos;
  71. PSID DomainSid = NULL;
  72. PULONG UserId = NULL;
  73. PSID_NAME_USE NameUse = NULL;
  74. SAM_HANDLE SamHandle = NULL;
  75. SAM_HANDLE DomainHandle = NULL;
  76. SAM_HANDLE UserHandle = NULL;
  77. HANDLE OpenedToken;
  78. //
  79. // We're going to _open the local account domain. The name of this
  80. // domain is "Account" on a WinNT machine, or the name of the
  81. // primary domain on a LanManNT machine. Figure out the product
  82. // type, assuming WinNT if RtlGetNtProductType fails.
  83. //
  84. DomainName.MaximumLength = 0;
  85. DomainName.Buffer = NULL;
  86. DomainNameAllocated = FALSE;
  87. NtProductType = NtProductWinNt;
  88. RtlGetNtProductType(
  89. &NtProductType
  90. );
  91. if (NtProductLanManNt != NtProductType) {
  92. NET_API_STATUS error;
  93. //
  94. // The server name is the database name.
  95. //
  96. error = NetpGetComputerName( &serverName );
  97. if ( error != NO_ERROR ) {
  98. return(error);
  99. }
  100. RtlInitUnicodeString(
  101. &DomainName,
  102. serverName
  103. );
  104. } else {
  105. //
  106. // This is a LanManNT machine, so we need to find out the
  107. // name of the primary domain. First get the length of the
  108. // domain name, then make room for it and retrieve it.
  109. //
  110. Status = RtlGetPrimaryDomain(
  111. 0,
  112. &PrimaryDomainPresent,
  113. &DomainName,
  114. &RequiredDomainNameLength,
  115. NULL,
  116. &RequiredDomainSidLength
  117. );
  118. if (STATUS_BUFFER_TOO_SMALL != Status && !NT_SUCCESS(Status)) {
  119. KdPrint(("XsChangePasswordSam: Unable to size primary "
  120. " domain name buffer, %8.8x\n", Status));
  121. goto Cleanup;
  122. }
  123. DomainName.Buffer = RtlAllocateHeap(
  124. RtlProcessHeap(), 0,
  125. DomainName.MaximumLength = RequiredDomainNameLength
  126. );
  127. DomainNameAllocated = TRUE;
  128. DomainSid = RtlAllocateHeap(
  129. RtlProcessHeap(), 0,
  130. RequiredDomainSidLength
  131. );
  132. if (!DomainName.Buffer || !DomainSid) {
  133. KdPrint(("XsChangePasswordSam: Out of memory allocating %d and %d bytes.",
  134. RequiredDomainNameLength, RequiredDomainSidLength));
  135. Status = STATUS_NO_MEMORY;
  136. goto Cleanup;
  137. }
  138. Status = RtlGetPrimaryDomain(
  139. RequiredDomainSidLength,
  140. &PrimaryDomainPresent,
  141. &DomainName,
  142. &RequiredDomainNameLength,
  143. DomainSid,
  144. &RequiredDomainSidLength
  145. );
  146. RtlFreeHeap(RtlProcessHeap(), 0, DomainSid);
  147. DomainSid = NULL;
  148. if (!NT_SUCCESS(Status)) {
  149. KdPrint(("XsChangePasswordSam: Unable to retrieve domain "
  150. "name, %8.8x\n", Status));
  151. goto Cleanup;
  152. }
  153. ASSERT(PrimaryDomainPresent);
  154. }
  155. //
  156. // Wrap an exception handler around this entire function,
  157. // since RPC raises exceptions to return errors.
  158. //
  159. try {
  160. //
  161. // Connect to local SAM.
  162. //
  163. InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
  164. ObjectAttributes.SecurityQualityOfService = &SecurityQos;
  165. SecurityQos.Length = sizeof(SecurityQos);
  166. SecurityQos.ImpersonationLevel = SecurityIdentification;
  167. SecurityQos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
  168. SecurityQos.EffectiveOnly = FALSE;
  169. Status = SamConnect(
  170. NULL,
  171. &SamHandle,
  172. GENERIC_EXECUTE,
  173. &ObjectAttributes
  174. );
  175. if ( !NT_SUCCESS(Status) ) {
  176. KdPrint(("XsChangePasswordSam: SamConnect failed, status %8.8x\n",
  177. Status));
  178. goto Cleanup;
  179. }
  180. //
  181. // Lookup the Domain SID.
  182. //
  183. Status = SamLookupDomainInSamServer(
  184. SamHandle,
  185. &DomainName,
  186. &DomainSid
  187. );
  188. if ( !NT_SUCCESS(Status) ) {
  189. KdPrint(("XsChangePasswordSam: Cannot find domain %wZ, "
  190. "status %8.8x\n", &DomainName, Status));
  191. Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
  192. goto Cleanup;
  193. }
  194. //
  195. // Revert to Local System
  196. //
  197. Status = NtOpenThreadToken(
  198. NtCurrentThread(),
  199. MAXIMUM_ALLOWED,
  200. TRUE,
  201. &OpenedToken
  202. );
  203. if( !NT_SUCCESS( Status ) ) {
  204. goto Cleanup;
  205. }
  206. RevertToSelf();
  207. //
  208. // Open the account domain.
  209. //
  210. Status = SamOpenDomain(
  211. SamHandle,
  212. GENERIC_EXECUTE,
  213. DomainSid,
  214. &DomainHandle
  215. );
  216. if ( !NT_SUCCESS(Status) ) {
  217. #if DBG
  218. UNICODE_STRING UnicodeSid;
  219. RtlConvertSidToUnicodeString(
  220. &UnicodeSid,
  221. DomainSid,
  222. TRUE
  223. );
  224. KdPrint(("XsChangePasswordSam: Cannot open domain %wZ, status %8.8x, SAM handle %8.8x, Domain SID %wZ\n",
  225. &DomainName, Status, SamHandle, UnicodeSid));
  226. RtlFreeUnicodeString(&UnicodeSid);
  227. #endif
  228. Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
  229. goto Cleanup;
  230. }
  231. //
  232. // Find the ID for this username.
  233. //
  234. Status = SamLookupNamesInDomain(
  235. DomainHandle,
  236. 1,
  237. UserName,
  238. &UserId,
  239. &NameUse
  240. );
  241. if ( !NT_SUCCESS(Status) ) {
  242. KdPrint(("XsChangePasswordSam: Cannot lookup user %wZ, "
  243. "status %8.8x\n", UserName, Status));
  244. if (STATUS_NONE_MAPPED == Status) {
  245. Status = STATUS_NO_SUCH_USER;
  246. }
  247. goto Cleanup;
  248. }
  249. //
  250. // Re-impersonate the client
  251. //
  252. Status = NtSetInformationThread(
  253. NtCurrentThread(),
  254. ThreadImpersonationToken,
  255. &OpenedToken,
  256. sizeof( OpenedToken )
  257. );
  258. if( !NT_SUCCESS( Status ) ) {
  259. goto Cleanup;
  260. }
  261. //
  262. // Open the user object.
  263. //
  264. Status = SamOpenUser(
  265. DomainHandle,
  266. USER_CHANGE_PASSWORD,
  267. *UserId,
  268. &UserHandle
  269. );
  270. if ( !NT_SUCCESS(Status) ) {
  271. KdPrint(("XsChangePasswordSam: Cannot open user %wZ, "
  272. "status %8.8x\n", UserName, Status));
  273. goto Cleanup;
  274. }
  275. if (Encrypted) {
  276. //
  277. // The client is Windows for Workgroups, OS/2, or DOS running
  278. // the ENCRYPT service. Pass the cross-encrypted passwords
  279. // to SamiLmChangePasswordUser.
  280. //
  281. Status = SamiLmChangePasswordUser(
  282. UserHandle,
  283. OldPassword,
  284. NewPassword
  285. );
  286. } else {
  287. //
  288. // The client is DOS not running the ENCRYPT service, and so
  289. // sent plaintext. Calculate the one-way functions and call
  290. // SamiChangePasswordUser.
  291. //
  292. LM_OWF_PASSWORD OldLmOwfPassword, NewLmOwfPassword;
  293. Status = RtlCalculateLmOwfPassword(
  294. OldPassword,
  295. &OldLmOwfPassword
  296. );
  297. if (NT_SUCCESS(Status)) {
  298. Status = RtlCalculateLmOwfPassword(
  299. NewPassword,
  300. &NewLmOwfPassword
  301. );
  302. }
  303. if (!NT_SUCCESS(Status)) {
  304. KdPrint(("XsChangePasswordSam: Unable to generate OWF "
  305. "passwords, %8.8x\n", Status));
  306. goto Cleanup;
  307. }
  308. //
  309. // Ask SAM to change the LM password and not store a new
  310. // NT password.
  311. //
  312. Status = SamiChangePasswordUser(
  313. UserHandle,
  314. TRUE,
  315. &OldLmOwfPassword,
  316. &NewLmOwfPassword,
  317. FALSE,
  318. NULL,
  319. NULL
  320. );
  321. }
  322. if ( !NT_SUCCESS(Status) ) {
  323. KdPrint(("XsChangePasswordSam: Cannot change password "
  324. "for %wZ, status %8.8x\n", UserName, Status));
  325. goto Cleanup;
  326. }
  327. } except (Status = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER) {
  328. KdPrint(("XsChangePasswordSam: caught exception 0x%8.8x\n", Status));
  329. if (RPC_S_SERVER_UNAVAILABLE == Status) {
  330. Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
  331. }
  332. }
  333. Cleanup:
  334. NetApiBufferFree( serverName );
  335. if (DomainNameAllocated && DomainName.Buffer) {
  336. RtlFreeHeap(RtlProcessHeap(), 0, DomainName.Buffer);
  337. }
  338. if (DomainSid) {
  339. SamFreeMemory(DomainSid);
  340. }
  341. if (UserId) {
  342. SamFreeMemory(UserId);
  343. }
  344. if (NameUse) {
  345. SamFreeMemory(NameUse);
  346. }
  347. if (UserHandle) {
  348. SamCloseHandle(UserHandle);
  349. }
  350. if (DomainHandle) {
  351. SamCloseHandle(DomainHandle);
  352. }
  353. if (SamHandle) {
  354. SamCloseHandle(SamHandle);
  355. }
  356. return RtlNtStatusToDosError(Status);
  357. }
  358. NTSTATUS
  359. XsSamOEMChangePasswordUser2_P (
  360. API_HANDLER_PARAMETERS
  361. )
  362. /*++
  363. Routine Description:
  364. This routine handles a call to SamrOemChangePasswordUser2 coming in
  365. from Win 95 clients
  366. Arguments:
  367. API_HANDLER_PARAMETERS - information about the API call. See
  368. XsTypes.h for details.
  369. Return Value:
  370. NTSTATUS - STATUS_SUCCESS or reason for failure.
  371. --*/
  372. {
  373. PXS_SAMOEMCHGPASSWORDUSER2_P parameters = Parameters;
  374. STRING UserName;
  375. SAMPR_ENCRYPTED_USER_PASSWORD EncryptedUserPassword;
  376. ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
  377. NTSTATUS ntstatus;
  378. API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings
  379. try {
  380. if( SmbGetUshort( &parameters->BufLen ) !=
  381. sizeof( EncryptedUserPassword) + sizeof( EncryptedOwfPassword ) ) {
  382. Header->Status = ERROR_INVALID_PARAMETER;
  383. return STATUS_SUCCESS;
  384. }
  385. RtlCopyMemory( &EncryptedUserPassword,
  386. parameters->Buffer,
  387. sizeof( EncryptedUserPassword ) );
  388. RtlCopyMemory( &EncryptedOwfPassword,
  389. parameters->Buffer + sizeof( EncryptedUserPassword ),
  390. sizeof( EncryptedOwfPassword ) );
  391. UserName.Buffer = parameters->UserName;
  392. UserName.Length = (USHORT) strlen( UserName.Buffer );
  393. UserName.MaximumLength = UserName.Length;
  394. ntstatus = SamiOemChangePasswordUser2(
  395. NULL,
  396. &UserName,
  397. &EncryptedUserPassword,
  398. &EncryptedOwfPassword );
  399. if( ntstatus == STATUS_NOT_SUPPORTED ) {
  400. Header->Status = NERR_InvalidAPI;
  401. } else {
  402. Header->Status = (WORD)NetpNtStatusToApiStatus( ntstatus );
  403. }
  404. } except( EXCEPTION_EXECUTE_HANDLER ) {
  405. Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() );
  406. }
  407. return STATUS_SUCCESS;
  408. } // XsSamOEMChangePasswordUser2_P
  409. /////////////////////////////////////////////////////////////////////////////
  410. // //
  411. // Internal function implementation. //
  412. // //
  413. /////////////////////////////////////////////////////////////////////////////
  414. //
  415. // Copied from ntos\dll\seurtl.c, where it is disabled. Remove if
  416. // it is enabled in ntdll.
  417. //
  418. NTSTATUS
  419. RtlGetPrimaryDomain(
  420. IN ULONG SidLength,
  421. OUT PBOOLEAN PrimaryDomainPresent,
  422. OUT PUNICODE_STRING PrimaryDomainName,
  423. OUT PUSHORT RequiredNameLength,
  424. OUT PSID PrimaryDomainSid OPTIONAL,
  425. OUT PULONG RequiredSidLength
  426. )
  427. /*++
  428. Routine Description:
  429. This procedure opens the LSA policy object and retrieves
  430. the primary domain information for this machine.
  431. Arguments:
  432. SidLength - Specifies the length of the PrimaryDomainSid
  433. parameter.
  434. PrimaryDomainPresent - Receives a boolean value indicating
  435. whether this machine has a primary domain or not. TRUE
  436. indicates the machine does have a primary domain. FALSE
  437. indicates the machine does not.
  438. PrimaryDomainName - Points to the unicode string to receive
  439. the primary domain name. This parameter will only be
  440. used if there is a primary domain.
  441. RequiredNameLength - Recevies the length of the primary
  442. domain name (in bytes). This parameter will only be
  443. used if there is a primary domain.
  444. PrimaryDomainSid - This optional parameter, if present,
  445. points to a buffer to receive the primary domain's
  446. SID. This parameter will only be used if there is a
  447. primary domain.
  448. RequiredSidLength - Recevies the length of the primary
  449. domain SID (in bytes). This parameter will only be
  450. used if there is a primary domain.
  451. Return Value:
  452. STATUS_SUCCESS - The requested information has been retrieved.
  453. STATUS_BUFFER_TOO_SMALL - One of the return buffers was not
  454. large enough to receive the corresponding information.
  455. The RequiredNameLength and RequiredSidLength parameter
  456. values have been set to indicate the needed length.
  457. Other status values as may be returned by:
  458. LsaOpenPolicy()
  459. LsaQueryInformationPolicy()
  460. RtlCopySid()
  461. --*/
  462. {
  463. NTSTATUS Status, IgnoreStatus;
  464. OBJECT_ATTRIBUTES ObjectAttributes;
  465. LSA_HANDLE LsaHandle;
  466. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  467. PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
  468. //
  469. // Set up the Security Quality Of Service
  470. //
  471. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  472. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  473. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  474. SecurityQualityOfService.EffectiveOnly = FALSE;
  475. //
  476. // Set up the object attributes to open the Lsa policy object
  477. //
  478. InitializeObjectAttributes(&ObjectAttributes,
  479. NULL,
  480. 0L,
  481. (HANDLE)NULL,
  482. NULL);
  483. ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
  484. //
  485. // Open the local LSA policy object
  486. //
  487. Status = LsaOpenPolicy( NULL,
  488. &ObjectAttributes,
  489. POLICY_VIEW_LOCAL_INFORMATION,
  490. &LsaHandle
  491. );
  492. if (NT_SUCCESS(Status)) {
  493. //
  494. // Get the primary domain info
  495. //
  496. Status = LsaQueryInformationPolicy(LsaHandle,
  497. PolicyPrimaryDomainInformation,
  498. (PVOID *)&PrimaryDomainInfo);
  499. IgnoreStatus = LsaClose(LsaHandle);
  500. ASSERT(NT_SUCCESS(IgnoreStatus));
  501. }
  502. if (NT_SUCCESS(Status)) {
  503. //
  504. // Is there a primary domain?
  505. //
  506. if (PrimaryDomainInfo->Sid != NULL) {
  507. //
  508. // Yes
  509. //
  510. (*PrimaryDomainPresent) = TRUE;
  511. (*RequiredNameLength) = PrimaryDomainInfo->Name.Length;
  512. (*RequiredSidLength) = RtlLengthSid(PrimaryDomainInfo->Sid);
  513. //
  514. // Copy the name
  515. //
  516. if (PrimaryDomainName->MaximumLength >=
  517. PrimaryDomainInfo->Name.Length) {
  518. RtlCopyUnicodeString(
  519. PrimaryDomainName,
  520. &PrimaryDomainInfo->Name
  521. );
  522. } else {
  523. Status = STATUS_BUFFER_TOO_SMALL;
  524. }
  525. //
  526. // Copy the SID (if appropriate)
  527. //
  528. if (PrimaryDomainSid != NULL && NT_SUCCESS(Status)) {
  529. Status = RtlCopySid(SidLength,
  530. PrimaryDomainSid,
  531. PrimaryDomainInfo->Sid
  532. );
  533. }
  534. } else {
  535. (*PrimaryDomainPresent) = FALSE;
  536. }
  537. //
  538. // We're finished with the buffer returned by LSA
  539. //
  540. IgnoreStatus = LsaFreeMemory(PrimaryDomainInfo);
  541. ASSERT(NT_SUCCESS(IgnoreStatus));
  542. }
  543. return(Status);
  544. }