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.

558 lines
20 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1999
  3. Module Name:
  4. mschapp - MS-CHAP Password Change API
  5. Abstract:
  6. These APIs correspond to the MS-CHAP RFC -2433 sections 9 and 10. In order
  7. to develop an MS-CHAP RAS server that works with an NT domain, these APIs
  8. are required.
  9. The MS-CHAP change password APIs are exposed through a DLL that is obtained
  10. from PSS. This DLL is not distributed with NT4.0 or Win2000. It is up to
  11. the ISV to install this with their product. The DLL name is MSCHAPP.DLL.
  12. Only wide (Unicode) versions of these apis will be available. These are the
  13. 2 callable APIs:
  14. * MSChapSrvChangePassword
  15. * MsChapSrvChangePassword2
  16. --*/
  17. #define UNICODE
  18. #define _UNICODE
  19. #ifndef WIN32_LEAN_AND_MEAN
  20. #define WIN32_LEAN_AND_MEAN
  21. #endif
  22. #include <nt.h>
  23. #include <ntrtl.h>
  24. #include <nturtl.h>
  25. #include <windows.h>
  26. #include <ntsam.h>
  27. #include <ntsamp.h>
  28. #include <ntlsa.h>
  29. #include <mschapp.h>
  30. //////////////////////////////////////////////////////////////
  31. // //
  32. // //
  33. // Exported MSChap change password Functions //
  34. // //
  35. // //
  36. //////////////////////////////////////////////////////////////
  37. //critical section for MSChap change password functions
  38. CRITICAL_SECTION MSChapChangePassword;
  39. //function pointers for MSChap Functions
  40. HINSTANCE hSamlib = NULL;
  41. typedef NTSTATUS(* FNSAMCONNECT)(PUNICODE_STRING,
  42. PSAM_HANDLE,
  43. ACCESS_MASK,
  44. POBJECT_ATTRIBUTES);
  45. typedef NTSTATUS(* FNSAMOPENDOMAIN)(SAM_HANDLE,
  46. ACCESS_MASK,
  47. PSID,
  48. PSAM_HANDLE);
  49. typedef NTSTATUS(* FNSAMLOOKUPNAMESINDOMAIN)(SAM_HANDLE,ULONG,PUNICODE_STRING,
  50. PULONG*,PSID_NAME_USE *);
  51. typedef NTSTATUS(* FNSAMOPENUSER)(SAM_HANDLE,ACCESS_MASK,ULONG,PSAM_HANDLE);
  52. typedef NTSTATUS(* FNSAMICHANGEPASSWORDUSER)(SAM_HANDLE,BOOLEAN,PLM_OWF_PASSWORD,PLM_OWF_PASSWORD,
  53. BOOLEAN,PNT_OWF_PASSWORD,PNT_OWF_PASSWORD);
  54. typedef NTSTATUS(* FNSAMICHANGEPASSWORDUSER2)(PUNICODE_STRING,
  55. PUNICODE_STRING,
  56. PSAMPR_ENCRYPTED_USER_PASSWORD,
  57. PENCRYPTED_NT_OWF_PASSWORD,
  58. BOOLEAN,PSAMPR_ENCRYPTED_USER_PASSWORD,
  59. PENCRYPTED_LM_OWF_PASSWORD);
  60. typedef NTSTATUS(* FNSAMCLOSEHANDLE)(SAM_HANDLE);
  61. typedef NTSTATUS(* FNSAMFREEMEMORY)(PVOID);
  62. FNSAMCONNECT FnSamConnect = NULL;
  63. FNSAMOPENDOMAIN FnSamOpenDomain = NULL;
  64. FNSAMLOOKUPNAMESINDOMAIN FnSamLookupNamesInDomain = NULL;
  65. FNSAMOPENUSER FnSamOpenUser = NULL;
  66. FNSAMICHANGEPASSWORDUSER FnSamiChangePasswordUser = NULL;
  67. FNSAMICHANGEPASSWORDUSER2 FnSamiChangePasswordUser2 = NULL;
  68. FNSAMCLOSEHANDLE FnSamCloseHandle = NULL;
  69. FNSAMFREEMEMORY FnSamFreeMemory = NULL;
  70. /*++
  71. MSChapSrvChangePassword:
  72. Changes the password of a user account. Password will be set to
  73. NewPassword only if OldPassword matches the current user password for this
  74. user and there are no restrictions on using the new password. This call
  75. allows users to change their own password if they have access
  76. USER_CHANGE_PASSWORD. Password update restrictions apply.
  77. Arguments:
  78. ServerName - The server to operate on, or NULL for this machine.
  79. UserName - Name of user whose password is to be changed
  80. LMOldPresent - TRUE if the LmOldOwfPassword is valid. This should only be
  81. FALSE if the old password is too long to be represented by a LM
  82. password (Complex NT password). Note the LMNewOwfPassword must always
  83. be valid. If the new password is complex, the LMNewOwfPassword should
  84. be the well-known LM OWF of a NULL password.
  85. LmOldOwfPassword - One-way-function of the current LM password for the
  86. user. Ignored if LmOldPresent == FALSE
  87. LmNewOwfPassword - One-way-function of the new LM password for the user.
  88. NtOldOwfPassword - One-way-function of the current NT password for the
  89. user.
  90. NtNewOwfPassword - One-way-function of the new NT password for the user.
  91. Return Value:
  92. STATUS_SUCCESS - The Service completed successfully.
  93. STATUS_ACCESS_DENIED - Caller does not have the appropriate access to
  94. complete the operation.
  95. STATUS_INVALID_HANDLE - The supplied server or username was not valid.
  96. STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
  97. contains characters that can't be entered from the keyboard, etc.
  98. STATUS_PASSWORD_RESTRICTION - A restriction prevents the password from
  99. being changed. This may be for a number of reasons, including time
  100. restrictions on how often a password may be changed or length
  101. restrictions on the provided password. This error might also be
  102. returned if the new password matched a password in the recent history
  103. log for the account. Security administrators indicate how many of the
  104. most recently used passwords may not be re-used. These are kept in
  105. the password recent history log.
  106. STATUS_WRONG_PASSWORD - OldPassword does not contain the user's current
  107. password.
  108. STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
  109. state (disabled or enabled) to perform the requested operation. The
  110. domain server must be enabled for this operation
  111. STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
  112. role (primary or backup) to perform the requested operation.
  113. STATUS_INVALID_PARAMETER_MIX - LmOldPresent or NtPresent or both must be
  114. TRUE.
  115. --*/
  116. WINADVAPI DWORD WINAPI
  117. MSChapSrvChangePassword(
  118. IN LPWSTR ServerName,
  119. IN LPWSTR UserName,
  120. IN BOOLEAN LmOldPresent,
  121. IN PLM_OWF_PASSWORD LmOldOwfPassword,
  122. IN PLM_OWF_PASSWORD LmNewOwfPassword,
  123. IN PNT_OWF_PASSWORD NtOldOwfPassword,
  124. IN PNT_OWF_PASSWORD NtNewOwfPassword)
  125. {
  126. NTSTATUS Status=STATUS_SUCCESS;
  127. DWORD WinErr=ERROR_SUCCESS;
  128. OBJECT_ATTRIBUTES oa;
  129. UNICODE_STRING UnicodeName;
  130. SAM_HANDLE SamHandle = NULL;
  131. SAM_HANDLE DomainHandle = NULL;
  132. SAM_HANDLE UserHandle = NULL;
  133. LSA_HANDLE LsaHandle = NULL;
  134. PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
  135. PULONG RelativeIds = NULL;
  136. PSID_NAME_USE Use = NULL;
  137. if (NULL == UserName || NULL == LmOldOwfPassword || NULL == LmNewOwfPassword ||
  138. NULL == NtOldOwfPassword || NULL == NtNewOwfPassword) {
  139. WinErr = ERROR_INVALID_PARAMETER;
  140. goto Cleanup;
  141. }
  142. //
  143. // Initialization.
  144. //
  145. if ( hSamlib == NULL )
  146. {
  147. RtlEnterCriticalSection( &MSChapChangePassword );
  148. if ( hSamlib == NULL )
  149. {
  150. hSamlib = LoadLibrary(L"samlib.dll");
  151. WinErr = GetLastError();
  152. if (ERROR_SUCCESS != WinErr) {
  153. goto Cleanup;
  154. }
  155. if (hSamlib != NULL) {
  156. FnSamConnect = (FNSAMCONNECT) GetProcAddress(hSamlib,
  157. "SamConnect");
  158. WinErr = GetLastError();
  159. if (ERROR_SUCCESS != WinErr) {
  160. goto Cleanup;
  161. }
  162. FnSamOpenDomain = (FNSAMOPENDOMAIN) GetProcAddress(hSamlib,
  163. "SamOpenDomain");
  164. WinErr = GetLastError();
  165. if (ERROR_SUCCESS != WinErr) {
  166. goto Cleanup;
  167. }
  168. FnSamLookupNamesInDomain = (FNSAMLOOKUPNAMESINDOMAIN) GetProcAddress(hSamlib,
  169. "SamLookupNamesInDomain");
  170. WinErr = GetLastError();
  171. if (ERROR_SUCCESS != WinErr) {
  172. goto Cleanup;
  173. }
  174. FnSamOpenUser = (FNSAMOPENUSER) GetProcAddress(hSamlib,
  175. "SamOpenUser");
  176. WinErr = GetLastError();
  177. if (ERROR_SUCCESS != WinErr) {
  178. goto Cleanup;
  179. }
  180. FnSamCloseHandle = (FNSAMCLOSEHANDLE) GetProcAddress(hSamlib,
  181. "SamCloseHandle");
  182. WinErr = GetLastError();
  183. if (ERROR_SUCCESS != WinErr) {
  184. goto Cleanup;
  185. }
  186. FnSamFreeMemory = (FNSAMFREEMEMORY) GetProcAddress(hSamlib,
  187. "SamFreeMemory");
  188. WinErr = GetLastError();
  189. if (ERROR_SUCCESS != WinErr) {
  190. goto Cleanup;
  191. }
  192. FnSamiChangePasswordUser = (FNSAMICHANGEPASSWORDUSER) GetProcAddress(hSamlib,
  193. "SamiChangePasswordUser");
  194. WinErr = GetLastError();
  195. if (ERROR_SUCCESS != WinErr) {
  196. goto Cleanup;
  197. }
  198. FnSamiChangePasswordUser2 = (FNSAMICHANGEPASSWORDUSER2) GetProcAddress(hSamlib,
  199. "SamiChangePasswordUser2");
  200. WinErr = GetLastError();
  201. if (ERROR_SUCCESS != WinErr) {
  202. goto Cleanup;
  203. }
  204. }
  205. }
  206. RtlLeaveCriticalSection( &MSChapChangePassword );
  207. }
  208. RtlInitUnicodeString(&UnicodeName, ServerName);
  209. InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
  210. //
  211. // Connect to the LSA on the server
  212. //
  213. Status = LsaOpenPolicy(
  214. &UnicodeName,
  215. &oa,
  216. POLICY_VIEW_LOCAL_INFORMATION,
  217. &LsaHandle);
  218. if (!NT_SUCCESS(Status))
  219. {
  220. goto Cleanup;
  221. }
  222. Status = LsaQueryInformationPolicy(
  223. LsaHandle,
  224. PolicyAccountDomainInformation,
  225. (PVOID *)&DomainInfo);
  226. if (!NT_SUCCESS(Status))
  227. {
  228. goto Cleanup;
  229. }
  230. Status = FnSamConnect(
  231. &UnicodeName,
  232. &SamHandle,
  233. SAM_SERVER_LOOKUP_DOMAIN,
  234. &oa);
  235. if (!NT_SUCCESS(Status))
  236. {
  237. goto Cleanup;
  238. }
  239. Status = FnSamOpenDomain(
  240. SamHandle,
  241. DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS | DOMAIN_READ_PASSWORD_PARAMETERS,
  242. DomainInfo->DomainSid,
  243. &DomainHandle);
  244. if (!NT_SUCCESS(Status))
  245. {
  246. goto Cleanup;
  247. }
  248. RtlInitUnicodeString(
  249. &UnicodeName,
  250. UserName);
  251. Status = FnSamLookupNamesInDomain(
  252. DomainHandle,
  253. 1,
  254. &UnicodeName,
  255. &RelativeIds,
  256. &Use);
  257. if (!NT_SUCCESS(Status))
  258. {
  259. goto Cleanup;
  260. }
  261. if (Use[0] != SidTypeUser)
  262. {
  263. WinErr = ERROR_INVALID_SID;
  264. goto Cleanup;
  265. }
  266. Status = FnSamOpenUser(
  267. DomainHandle,
  268. USER_CHANGE_PASSWORD,
  269. RelativeIds[0],
  270. &UserHandle);
  271. if (!NT_SUCCESS(Status))
  272. {
  273. goto Cleanup;
  274. }
  275. Status = FnSamiChangePasswordUser(
  276. UserHandle,
  277. LmOldPresent, // Only false if Old password too complex
  278. LmOldOwfPassword,
  279. LmNewOwfPassword,
  280. TRUE, // NT password present
  281. NtOldOwfPassword,
  282. NtNewOwfPassword);
  283. if (!NT_SUCCESS(Status))
  284. {
  285. goto Cleanup;
  286. }
  287. Cleanup:
  288. if (LsaHandle != NULL)
  289. {
  290. LsaClose(LsaHandle);
  291. }
  292. if (UserHandle != NULL)
  293. {
  294. FnSamCloseHandle(UserHandle);
  295. }
  296. if (DomainHandle != NULL)
  297. {
  298. FnSamCloseHandle(DomainHandle);
  299. }
  300. if (SamHandle != NULL)
  301. {
  302. FnSamCloseHandle(SamHandle);
  303. }
  304. if (DomainInfo != NULL)
  305. {
  306. LsaFreeMemory(DomainInfo);
  307. }
  308. if (RelativeIds != NULL)
  309. {
  310. FnSamFreeMemory(RelativeIds);
  311. }
  312. if (Use != NULL)
  313. {
  314. FnSamFreeMemory(Use);
  315. }
  316. if (ERROR_SUCCESS != WinErr) {
  317. return WinErr;
  318. }
  319. return RtlNtStatusToDosError(Status);
  320. }
  321. /*++
  322. MSChapSrvChangePassword2:
  323. Changes the password of a user account. Password will be set to
  324. NewPassword only if OldPassword matches the current user password for this
  325. user and there are no restrictions on using the new password. This call
  326. allows users to change their own password if they have access
  327. USER_CHANGE_PASSWORD. Password update restrictions apply.
  328. Arguments:
  329. ServerName - The server to operate on, or NULL for this machine.
  330. UserName - Name of user whose password is to be changed
  331. NewPasswordEncryptedWithOldNt - The new cleartext password encrypted with
  332. the old NT OWF password.
  333. OldNtOwfPasswordEncryptedWithNewNt - The old NT OWF password encrypted
  334. with the new NT OWF password.
  335. LmPresent - If TRUE, indicates that the following two last parameter was
  336. encrypted with the LM OWF password not the NT OWF password.
  337. NewPasswordEncryptedWithOldLm - The new cleartext password encrypted with
  338. the old LM OWF password.
  339. OldLmOwfPasswordEncryptedWithNewLmOrNt - The old LM OWF password encrypted
  340. with the new LM OWF password.
  341. Return Value:
  342. STATUS_SUCCESS - The Service completed successfully.
  343. STATUS_ACCESS_DENIED - Caller does not have the appropriate access to
  344. complete the operation.
  345. STATUS_INVALID_HANDLE - The supplied server or username was not valid.
  346. STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
  347. contains characters that can't be entered from the keyboard, etc.
  348. STATUS_PASSWORD_RESTRICTION - A restriction prevents the password from
  349. being changed. This may be for a number of reasons, including time
  350. restrictions on how often a password may be changed or length
  351. restrictions on the provided password. This error might also be
  352. returned if the new password matched a password in the recent history
  353. log for the account. Security administrators indicate how many of the
  354. most recently used passwords may not be re-used. These are kept in
  355. the password recent history log.
  356. STATUS_WRONG_PASSWORD - OldPassword does not contain the user's current
  357. password.
  358. STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct
  359. state (disabled or enabled) to perform the requested operation. The
  360. domain server must be enabled for this operation.
  361. STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect
  362. role (primary or backup) to perform the requested operation.
  363. --*/
  364. WINADVAPI DWORD WINAPI
  365. MSChapSrvChangePassword2(
  366. IN LPWSTR ServerName,
  367. IN LPWSTR UserName,
  368. IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt,
  369. IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt,
  370. IN BOOLEAN LmPresent,
  371. IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
  372. IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLmOrNt)
  373. {
  374. UNICODE_STRING UnicodeServer;
  375. UNICODE_STRING UnicodeUser;
  376. DWORD WinErr = ERROR_SUCCESS;
  377. if (NULL == UserName || NULL == NewPasswordEncryptedWithOldNt ||
  378. NULL == NewPasswordEncryptedWithOldLm || NULL ==OldNtOwfPasswordEncryptedWithNewNt ||
  379. NULL == OldLmOwfPasswordEncryptedWithNewLmOrNt) {
  380. WinErr = ERROR_INVALID_PARAMETER;
  381. goto Cleanup;
  382. }
  383. //
  384. // Initialization.
  385. //
  386. if ( hSamlib == NULL )
  387. {
  388. RtlEnterCriticalSection( &MSChapChangePassword );
  389. if ( hSamlib == NULL )
  390. {
  391. hSamlib = LoadLibrary(L"samlib.dll");
  392. WinErr = GetLastError();
  393. if (ERROR_SUCCESS != WinErr) {
  394. goto Cleanup;
  395. }
  396. if (hSamlib != NULL) {
  397. FnSamConnect = (FNSAMCONNECT) GetProcAddress(hSamlib,
  398. "SamConnect");
  399. WinErr = GetLastError();
  400. if (ERROR_SUCCESS != WinErr) {
  401. goto Cleanup;
  402. }
  403. FnSamOpenDomain = (FNSAMOPENDOMAIN) GetProcAddress(hSamlib,
  404. "SamOpenDomain");
  405. WinErr = GetLastError();
  406. if (ERROR_SUCCESS != WinErr) {
  407. goto Cleanup;
  408. }
  409. FnSamLookupNamesInDomain = (FNSAMLOOKUPNAMESINDOMAIN) GetProcAddress(hSamlib,
  410. "SamLookupNamesInDomain");
  411. WinErr = GetLastError();
  412. if (ERROR_SUCCESS != WinErr) {
  413. goto Cleanup;
  414. }
  415. FnSamOpenUser = (FNSAMOPENUSER) GetProcAddress(hSamlib,
  416. "SamOpenUser");
  417. WinErr = GetLastError();
  418. if (ERROR_SUCCESS != WinErr) {
  419. goto Cleanup;
  420. }
  421. FnSamCloseHandle = (FNSAMCLOSEHANDLE) GetProcAddress(hSamlib,
  422. "SamCloseHandle");
  423. WinErr = GetLastError();
  424. if (ERROR_SUCCESS != WinErr) {
  425. goto Cleanup;
  426. }
  427. FnSamFreeMemory = (FNSAMFREEMEMORY) GetProcAddress(hSamlib,
  428. "SamFreeMemory");
  429. WinErr = GetLastError();
  430. if (ERROR_SUCCESS != WinErr) {
  431. goto Cleanup;
  432. }
  433. FnSamiChangePasswordUser = (FNSAMICHANGEPASSWORDUSER) GetProcAddress(hSamlib,
  434. "SamiChangePasswordUser");
  435. WinErr = GetLastError();
  436. if (ERROR_SUCCESS != WinErr) {
  437. goto Cleanup;
  438. }
  439. FnSamiChangePasswordUser2 = (FNSAMICHANGEPASSWORDUSER2) GetProcAddress(hSamlib,
  440. "SamiChangePasswordUser2");
  441. WinErr = GetLastError();
  442. if (ERROR_SUCCESS != WinErr) {
  443. goto Cleanup;
  444. }
  445. }
  446. }
  447. RtlLeaveCriticalSection( &MSChapChangePassword );
  448. }
  449. RtlInitUnicodeString(&UnicodeServer, ServerName);
  450. RtlInitUnicodeString(&UnicodeUser, UserName);
  451. return RtlNtStatusToDosError(FnSamiChangePasswordUser2(&UnicodeServer,
  452. &UnicodeUser,
  453. NewPasswordEncryptedWithOldNt,
  454. OldNtOwfPasswordEncryptedWithNewNt,
  455. LmPresent,
  456. NewPasswordEncryptedWithOldLm,
  457. OldLmOwfPasswordEncryptedWithNewLmOrNt));
  458. Cleanup:
  459. return WinErr;
  460. }