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.

663 lines
14 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // cleartxt.c
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines functions for storing and retrieving cleartext passwords.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 08/31/1998 Original version.
  16. //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <ntlsa.h>
  22. #include <windows.h>
  23. #include <wincrypt.h>
  24. #include <rassfmhp.h>
  25. #include <usrprop.h>
  26. #include <cleartxt.h>
  27. // Name of the private key stored as LSA private data.
  28. UNICODE_STRING PRIVATE_KEY_NAME = { 34, 36, L"G$MSRADIUSPRIVKEY" };
  29. // Length of the private key.
  30. #define PRIVATE_KEY_LENGTH 256
  31. // Length of the user-specific key.
  32. #define USER_KEY_LENGTH 16
  33. // Properties stored in UserParameters.
  34. #define PROPERTY_USER_KEY L"G$RADIUSCHAPKEY"
  35. #define PROPERTY_PASSWORD L"G$RADIUSCHAP"
  36. // Fixed key used for decrypting the private key.
  37. BYTE FIXED_KEY[] =
  38. {
  39. 0x05, 0x56, 0xF6, 0x07, 0xC6, 0x56, 0x02, 0x94, 0x02,
  40. 0xC6, 0xF6, 0x67, 0x56, 0x02, 0xC6, 0x96, 0xB6, 0x56,
  41. 0x02, 0x34, 0x86, 0x16, 0xE6, 0x46, 0x27, 0x16, 0xC2,
  42. 0x02, 0x14, 0x46, 0x96, 0x47, 0x96, 0xC2, 0x02, 0x74,
  43. 0x27, 0x56, 0x47, 0x16, 0x02, 0x16, 0x27, 0x56, 0x02,
  44. 0x47, 0x86, 0x56, 0x02, 0x07, 0x56, 0xF6, 0x07, 0xC6,
  45. 0x56, 0x02, 0x94, 0x02, 0x47, 0x27, 0x57, 0x37, 0x47
  46. };
  47. // Shared handle to the cryptographic provider.
  48. HCRYPTPROV theContext;
  49. // Private key used for encrypting/decrypting cleartext passwords.
  50. PLSA_UNICODE_STRING thePrivateKey;
  51. // TRUE if this API has been successfully initialized.
  52. static BOOL theInitFlag;
  53. // Non-zero if the API is locked.
  54. static LONG theLock;
  55. //////////
  56. // Macros to lock/unlock the API during intialization.
  57. //////////
  58. #define API_LOCK() \
  59. while (InterlockedExchange(&theLock, 1)) Sleep(5)
  60. #define API_UNLOCK() \
  61. InterlockedExchange(&theLock, 0)
  62. //////////
  63. // Macro that ensures the API has been initialized and bails on failure.
  64. //////////
  65. #define CHECK_INIT() \
  66. if (!theInitFlag) { \
  67. status = IASParmsInitialize(); \
  68. if (status != NO_ERROR) { return status; } \
  69. }
  70. //////////
  71. // Creates the private key. Should only be called if the key doesn't exist.
  72. //////////
  73. DWORD
  74. WINAPI
  75. IASCreatePrivateKey(
  76. IN LSA_HANDLE hPolicy
  77. )
  78. {
  79. DWORD status;
  80. BYTE newKey[PRIVATE_KEY_LENGTH];
  81. LSA_UNICODE_STRING privateData;
  82. //////////
  83. // Generate a random key.
  84. //////////
  85. if (!CryptGenRandom(
  86. theContext,
  87. sizeof(newKey),
  88. newKey
  89. ))
  90. { return GetLastError(); }
  91. //////////
  92. // Store it as LSA private data.
  93. //////////
  94. privateData.Length = sizeof(newKey);
  95. privateData.MaximumLength = sizeof(newKey);
  96. privateData.Buffer = (PWSTR)newKey;
  97. status = LsaStorePrivateData(
  98. hPolicy,
  99. &PRIVATE_KEY_NAME,
  100. &privateData
  101. );
  102. if (NT_SUCCESS(status))
  103. {
  104. status = LsaRetrievePrivateData(
  105. hPolicy,
  106. &PRIVATE_KEY_NAME,
  107. &thePrivateKey
  108. );
  109. }
  110. return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
  111. }
  112. //////////
  113. // Derives a cryptographic key from an octet string.
  114. //////////
  115. BOOL
  116. WINAPI
  117. IASDeriveUserCryptKey(
  118. IN PBYTE pbUserKey,
  119. OUT HCRYPTKEY *phKey
  120. )
  121. {
  122. BOOL success;
  123. HCRYPTHASH hHash;
  124. success = CryptCreateHash(
  125. theContext,
  126. CALG_MD5,
  127. 0,
  128. 0,
  129. &hHash
  130. );
  131. if (!success) { goto exit; }
  132. success = CryptHashData(
  133. hHash,
  134. (PBYTE)thePrivateKey->Buffer,
  135. thePrivateKey->Length,
  136. 0
  137. );
  138. if (!success) { goto destroy_hash; }
  139. success = CryptHashData(
  140. hHash,
  141. pbUserKey,
  142. USER_KEY_LENGTH,
  143. 0
  144. );
  145. if (!success) { goto destroy_hash; }
  146. success = CryptDeriveKey(
  147. theContext,
  148. CALG_RC4,
  149. hHash,
  150. CRYPT_EXPORTABLE,
  151. phKey
  152. );
  153. destroy_hash:
  154. CryptDestroyHash(hHash);
  155. exit:
  156. return success;
  157. }
  158. DWORD
  159. WINAPI
  160. IASParmsInitialize( VOID )
  161. {
  162. DWORD status, nbyte;
  163. OBJECT_ATTRIBUTES objAttribs;
  164. LSA_HANDLE hPolicy;
  165. HCRYPTHASH hHash;
  166. HCRYPTKEY hKey;
  167. API_LOCK();
  168. // If we've already been initialized, there's nothing to do.
  169. if (theInitFlag)
  170. {
  171. status = NO_ERROR;
  172. goto exit;
  173. }
  174. /////////
  175. // Acquire a cryptographic context.
  176. /////////
  177. if (!CryptAcquireContext(
  178. &theContext,
  179. NULL,
  180. NULL,
  181. PROV_RSA_FULL,
  182. CRYPT_VERIFYCONTEXT
  183. ))
  184. {
  185. status = GetLastError();
  186. goto exit;
  187. }
  188. /////////
  189. // Open a handle to the LSA.
  190. /////////
  191. InitializeObjectAttributes(
  192. &objAttribs,
  193. NULL,
  194. 0,
  195. NULL,
  196. NULL
  197. );
  198. status = LsaOpenPolicy(
  199. NULL,
  200. &objAttribs,
  201. POLICY_ALL_ACCESS,
  202. &hPolicy
  203. );
  204. if (!NT_SUCCESS(status))
  205. {
  206. status = RtlNtStatusToDosError(status);
  207. goto exit;
  208. }
  209. /////////
  210. // Retrieve the private key.
  211. /////////
  212. status = LsaRetrievePrivateData(
  213. hPolicy,
  214. &PRIVATE_KEY_NAME,
  215. &thePrivateKey
  216. );
  217. if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
  218. (NT_SUCCESS(status) && thePrivateKey->Length == 0))
  219. {
  220. // If it doesn't exist, create a new one.
  221. status = IASCreatePrivateKey(
  222. hPolicy
  223. );
  224. }
  225. else if (!NT_SUCCESS(status))
  226. {
  227. status = RtlNtStatusToDosError(status);
  228. }
  229. if (status != NO_ERROR) { goto close_policy; }
  230. /////////
  231. // Derive a crypto key from the fixed key.
  232. /////////
  233. if (!CryptCreateHash(
  234. theContext,
  235. CALG_MD5,
  236. 0,
  237. 0,
  238. &hHash
  239. ))
  240. {
  241. status = GetLastError();
  242. goto close_policy;
  243. }
  244. if (!CryptHashData(
  245. hHash,
  246. FIXED_KEY,
  247. sizeof(FIXED_KEY),
  248. 0
  249. ))
  250. {
  251. status = GetLastError();
  252. goto destroy_hash;
  253. }
  254. if (!CryptDeriveKey(
  255. theContext,
  256. CALG_RC4,
  257. hHash,
  258. CRYPT_EXPORTABLE,
  259. &hKey
  260. ))
  261. {
  262. status = GetLastError();
  263. goto destroy_hash;
  264. }
  265. /////////
  266. // Decrypt the private key.
  267. /////////
  268. nbyte = thePrivateKey->Length;
  269. if (!CryptDecrypt(
  270. hKey,
  271. 0,
  272. TRUE,
  273. 0,
  274. (PBYTE)thePrivateKey->Buffer,
  275. &nbyte
  276. ))
  277. {
  278. status = GetLastError();
  279. goto destroy_key;
  280. }
  281. thePrivateKey->Length = (USHORT)nbyte;
  282. destroy_key:
  283. CryptDestroyKey(hKey);
  284. destroy_hash:
  285. CryptDestroyHash(hHash);
  286. close_policy:
  287. LsaClose(hPolicy);
  288. exit:
  289. if (status == NO_ERROR)
  290. {
  291. // We succeeded, so set theInitFlag.
  292. theInitFlag = TRUE;
  293. }
  294. else
  295. {
  296. // We failed, so clean up.
  297. if (thePrivateKey)
  298. {
  299. LsaFreeMemory(thePrivateKey);
  300. thePrivateKey = NULL;
  301. }
  302. if (theContext)
  303. {
  304. CryptReleaseContext(theContext, 0);
  305. theContext = 0;
  306. }
  307. }
  308. API_UNLOCK();
  309. return status;
  310. }
  311. DWORD
  312. WINAPI
  313. IASParmsClearUserPassword(
  314. IN PCWSTR szUserParms,
  315. OUT PWSTR *pszNewUserParms
  316. )
  317. {
  318. DWORD status;
  319. UNICODE_STRING property;
  320. PWSTR tempUserParms;
  321. BOOL updateKey, updatePwd;
  322. // Check the in parameters.
  323. if (pszNewUserParms == NULL) { return ERROR_INVALID_PARAMETER; }
  324. /////////
  325. // Write a null string to the relevant properties.
  326. /////////
  327. memset(&property, 0, sizeof(property));
  328. status = NetpParmsSetUserProperty(
  329. (PWSTR)szUserParms,
  330. PROPERTY_PASSWORD,
  331. property,
  332. 0,
  333. &tempUserParms,
  334. &updatePwd
  335. );
  336. if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
  337. status = NetpParmsSetUserProperty(
  338. tempUserParms,
  339. PROPERTY_USER_KEY,
  340. property,
  341. 0,
  342. pszNewUserParms,
  343. &updateKey
  344. );
  345. NetpParmsUserPropertyFree(tempUserParms);
  346. if (NT_SUCCESS(status))
  347. {
  348. if (!updatePwd && !updateKey)
  349. {
  350. // Nothing changed so don't return the NewUserParms.
  351. NetpParmsUserPropertyFree(*pszNewUserParms);
  352. *pszNewUserParms = NULL;
  353. }
  354. return NO_ERROR;
  355. }
  356. return RtlNtStatusToDosError(status);
  357. }
  358. DWORD
  359. WINAPI
  360. IASParmsGetUserPassword(
  361. IN PCWSTR szUserParms,
  362. OUT PWSTR *pszPassword
  363. )
  364. {
  365. DWORD status, nbyte;
  366. UNICODE_STRING userKey, encryptedPwd;
  367. WCHAR propFlag;
  368. HCRYPTKEY hKey;
  369. // Check the in parameters.
  370. if (pszPassword == NULL) { return ERROR_INVALID_PARAMETER; }
  371. // Make sure we're initialized.
  372. CHECK_INIT();
  373. // Read the user key.
  374. status = NetpParmsQueryUserProperty(
  375. (PWSTR)szUserParms,
  376. PROPERTY_USER_KEY,
  377. &propFlag,
  378. &userKey
  379. );
  380. if (!NT_SUCCESS(status))
  381. {
  382. status = RtlNtStatusToDosError(status);
  383. goto exit;
  384. }
  385. // Read the encrypted password.
  386. status = NetpParmsQueryUserProperty(
  387. (PWSTR)szUserParms,
  388. PROPERTY_PASSWORD,
  389. &propFlag,
  390. &encryptedPwd
  391. );
  392. if (!NT_SUCCESS(status))
  393. {
  394. status = RtlNtStatusToDosError(status);
  395. goto free_key;
  396. }
  397. // If they're both NULL, it's not an error. It just means the cleartext
  398. // password was never set.
  399. if (userKey.Buffer == NULL && encryptedPwd.Buffer == NULL)
  400. {
  401. *pszPassword = NULL;
  402. goto exit;
  403. }
  404. // Make sure the user key is the correct length.
  405. if (userKey.Length != USER_KEY_LENGTH)
  406. {
  407. status = ERROR_INVALID_DATA;
  408. goto free_password;
  409. }
  410. // Convert the user key to a cryptographic key.
  411. if (!IASDeriveUserCryptKey(
  412. (PBYTE)userKey.Buffer,
  413. &hKey
  414. ))
  415. {
  416. status = GetLastError();
  417. goto free_password;
  418. }
  419. // Decrypt the password.
  420. nbyte = encryptedPwd.Length;
  421. if (!CryptDecrypt(
  422. hKey,
  423. 0,
  424. TRUE,
  425. 0,
  426. (PBYTE)encryptedPwd.Buffer,
  427. &nbyte
  428. ))
  429. {
  430. status = GetLastError();
  431. goto destroy_key;
  432. }
  433. // We encrypted the terminating null, so it should still be there.
  434. if (encryptedPwd.Buffer[nbyte / sizeof(WCHAR) - 1] != L'\0')
  435. {
  436. status = ERROR_INVALID_DATA;
  437. goto destroy_key;
  438. }
  439. // Return the cleartext password to the caller.
  440. *pszPassword = encryptedPwd.Buffer;
  441. encryptedPwd.Buffer = NULL;
  442. destroy_key:
  443. CryptDestroyKey(hKey);
  444. free_password:
  445. LocalFree(encryptedPwd.Buffer);
  446. free_key:
  447. LocalFree(userKey.Buffer);
  448. exit:
  449. return status;
  450. }
  451. DWORD
  452. WINAPI
  453. IASParmsSetUserPassword(
  454. IN PCWSTR szUserParms,
  455. IN PCWSTR szPassword,
  456. OUT PWSTR *pszNewUserParms
  457. )
  458. {
  459. DWORD status;
  460. BYTE userKey[USER_KEY_LENGTH];
  461. HCRYPTKEY hKey;
  462. DWORD nbyte;
  463. PBYTE encryptedPwd;
  464. UNICODE_STRING property;
  465. PWSTR tempUserParms;
  466. BOOL update;
  467. // Check the in parameters.
  468. if (szPassword == NULL) { return ERROR_INVALID_PARAMETER; }
  469. // Make sure we're initialized.
  470. CHECK_INIT();
  471. // Generate a user key.
  472. if (!CryptGenRandom(
  473. theContext,
  474. USER_KEY_LENGTH,
  475. userKey
  476. ))
  477. {
  478. status = GetLastError();
  479. goto exit;
  480. }
  481. // Convert the user key to a cryptographic key.
  482. if (!IASDeriveUserCryptKey(
  483. userKey,
  484. &hKey
  485. ))
  486. {
  487. status = GetLastError();
  488. goto exit;
  489. }
  490. // Allocate a buffer for the encrypted password.
  491. nbyte = sizeof(WCHAR) * (lstrlenW(szPassword) + 1);
  492. encryptedPwd = RtlAllocateHeap(
  493. RasSfmHeap(),
  494. 0,
  495. nbyte
  496. );
  497. if (encryptedPwd == NULL)
  498. {
  499. status = ERROR_NOT_ENOUGH_MEMORY;
  500. goto destroy_key;
  501. }
  502. memcpy(encryptedPwd, szPassword, nbyte);
  503. // Encrypt the password.
  504. if (!CryptEncrypt(
  505. hKey,
  506. 0,
  507. TRUE,
  508. 0,
  509. encryptedPwd,
  510. &nbyte,
  511. nbyte
  512. ))
  513. {
  514. status = GetLastError();
  515. goto free_encrypted_password;
  516. }
  517. /////////
  518. // Store the encrypted password.
  519. /////////
  520. property.Buffer = (PWCHAR)encryptedPwd;
  521. property.Length = (USHORT)nbyte;
  522. property.MaximumLength = (USHORT)nbyte;
  523. status = NetpParmsSetUserProperty(
  524. (PWSTR)szUserParms,
  525. PROPERTY_PASSWORD,
  526. property,
  527. 0,
  528. &tempUserParms,
  529. &update
  530. );
  531. if (!NT_SUCCESS(status))
  532. {
  533. status = RtlNtStatusToDosError(status);
  534. goto free_encrypted_password;
  535. }
  536. /////////
  537. // Store the user key.
  538. /////////
  539. property.Buffer = (PWSTR)userKey;
  540. property.Length = USER_KEY_LENGTH;
  541. property.MaximumLength = USER_KEY_LENGTH;
  542. status = NetpParmsSetUserProperty(
  543. tempUserParms,
  544. PROPERTY_USER_KEY,
  545. property,
  546. 0,
  547. pszNewUserParms,
  548. &update
  549. );
  550. if (!NT_SUCCESS(status)) { status = RtlNtStatusToDosError(status); }
  551. NetpParmsUserPropertyFree(tempUserParms);
  552. free_encrypted_password:
  553. RtlFreeHeap(RasSfmHeap(), 0, encryptedPwd);
  554. destroy_key:
  555. CryptDestroyKey(hKey);
  556. exit:
  557. return status;
  558. }
  559. VOID
  560. WINAPI
  561. IASParmsFreeUserParms(
  562. IN LPWSTR szNewUserParms
  563. )
  564. {
  565. NetpParmsUserPropertyFree(szNewUserParms);
  566. }