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.

669 lines
15 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 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) &&
  219. (
  220. (thePrivateKey != NULL && thePrivateKey->Length == 0) ||
  221. (thePrivateKey == NULL)
  222. )
  223. )
  224. )
  225. {
  226. // If it doesn't exist, create a new one.
  227. status = IASCreatePrivateKey(
  228. hPolicy
  229. );
  230. }
  231. else if (!NT_SUCCESS(status))
  232. {
  233. status = RtlNtStatusToDosError(status);
  234. }
  235. if (status != NO_ERROR) { goto close_policy; }
  236. /////////
  237. // Derive a crypto key from the fixed key.
  238. /////////
  239. if (!CryptCreateHash(
  240. theContext,
  241. CALG_MD5,
  242. 0,
  243. 0,
  244. &hHash
  245. ))
  246. {
  247. status = GetLastError();
  248. goto close_policy;
  249. }
  250. if (!CryptHashData(
  251. hHash,
  252. FIXED_KEY,
  253. sizeof(FIXED_KEY),
  254. 0
  255. ))
  256. {
  257. status = GetLastError();
  258. goto destroy_hash;
  259. }
  260. if (!CryptDeriveKey(
  261. theContext,
  262. CALG_RC4,
  263. hHash,
  264. CRYPT_EXPORTABLE,
  265. &hKey
  266. ))
  267. {
  268. status = GetLastError();
  269. goto destroy_hash;
  270. }
  271. /////////
  272. // Decrypt the private key.
  273. /////////
  274. nbyte = thePrivateKey->Length;
  275. if (!CryptDecrypt(
  276. hKey,
  277. 0,
  278. TRUE,
  279. 0,
  280. (PBYTE)thePrivateKey->Buffer,
  281. &nbyte
  282. ))
  283. {
  284. status = GetLastError();
  285. goto destroy_key;
  286. }
  287. thePrivateKey->Length = (USHORT)nbyte;
  288. destroy_key:
  289. CryptDestroyKey(hKey);
  290. destroy_hash:
  291. CryptDestroyHash(hHash);
  292. close_policy:
  293. LsaClose(hPolicy);
  294. exit:
  295. if (status == NO_ERROR)
  296. {
  297. // We succeeded, so set theInitFlag.
  298. theInitFlag = TRUE;
  299. }
  300. else
  301. {
  302. // We failed, so clean up.
  303. if (thePrivateKey)
  304. {
  305. LsaFreeMemory(thePrivateKey);
  306. thePrivateKey = NULL;
  307. }
  308. if (theContext)
  309. {
  310. CryptReleaseContext(theContext, 0);
  311. theContext = 0;
  312. }
  313. }
  314. API_UNLOCK();
  315. return status;
  316. }
  317. DWORD
  318. WINAPI
  319. IASParmsClearUserPassword(
  320. IN PCWSTR szUserParms,
  321. OUT PWSTR *pszNewUserParms
  322. )
  323. {
  324. DWORD status;
  325. UNICODE_STRING property;
  326. PWSTR tempUserParms;
  327. BOOL updateKey, updatePwd;
  328. // Check the in parameters.
  329. if (pszNewUserParms == NULL) { return ERROR_INVALID_PARAMETER; }
  330. /////////
  331. // Write a null string to the relevant properties.
  332. /////////
  333. memset(&property, 0, sizeof(property));
  334. status = NetpParmsSetUserProperty(
  335. (PWSTR)szUserParms,
  336. PROPERTY_PASSWORD,
  337. property,
  338. 0,
  339. &tempUserParms,
  340. &updatePwd
  341. );
  342. if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
  343. status = NetpParmsSetUserProperty(
  344. tempUserParms,
  345. PROPERTY_USER_KEY,
  346. property,
  347. 0,
  348. pszNewUserParms,
  349. &updateKey
  350. );
  351. NetpParmsUserPropertyFree(tempUserParms);
  352. if (NT_SUCCESS(status))
  353. {
  354. if (!updatePwd && !updateKey)
  355. {
  356. // Nothing changed so don't return the NewUserParms.
  357. NetpParmsUserPropertyFree(*pszNewUserParms);
  358. *pszNewUserParms = NULL;
  359. }
  360. return NO_ERROR;
  361. }
  362. return RtlNtStatusToDosError(status);
  363. }
  364. DWORD
  365. WINAPI
  366. IASParmsGetUserPassword(
  367. IN PCWSTR szUserParms,
  368. OUT PWSTR *pszPassword
  369. )
  370. {
  371. DWORD status, nbyte;
  372. UNICODE_STRING userKey, encryptedPwd;
  373. WCHAR propFlag;
  374. HCRYPTKEY hKey;
  375. // Check the in parameters.
  376. if (pszPassword == NULL) { return ERROR_INVALID_PARAMETER; }
  377. // Make sure we're initialized.
  378. CHECK_INIT();
  379. // Read the user key.
  380. status = NetpParmsQueryUserProperty(
  381. (PWSTR)szUserParms,
  382. PROPERTY_USER_KEY,
  383. &propFlag,
  384. &userKey
  385. );
  386. if (!NT_SUCCESS(status))
  387. {
  388. status = RtlNtStatusToDosError(status);
  389. goto exit;
  390. }
  391. // Read the encrypted password.
  392. status = NetpParmsQueryUserProperty(
  393. (PWSTR)szUserParms,
  394. PROPERTY_PASSWORD,
  395. &propFlag,
  396. &encryptedPwd
  397. );
  398. if (!NT_SUCCESS(status))
  399. {
  400. status = RtlNtStatusToDosError(status);
  401. goto free_key;
  402. }
  403. // If they're both NULL, it's not an error. It just means the cleartext
  404. // password was never set.
  405. if (userKey.Buffer == NULL && encryptedPwd.Buffer == NULL)
  406. {
  407. *pszPassword = NULL;
  408. goto exit;
  409. }
  410. // Make sure the user key is the correct length.
  411. if (userKey.Length != USER_KEY_LENGTH)
  412. {
  413. status = ERROR_INVALID_DATA;
  414. goto free_password;
  415. }
  416. // Convert the user key to a cryptographic key.
  417. if (!IASDeriveUserCryptKey(
  418. (PBYTE)userKey.Buffer,
  419. &hKey
  420. ))
  421. {
  422. status = GetLastError();
  423. goto free_password;
  424. }
  425. // Decrypt the password.
  426. nbyte = encryptedPwd.Length;
  427. if (!CryptDecrypt(
  428. hKey,
  429. 0,
  430. TRUE,
  431. 0,
  432. (PBYTE)encryptedPwd.Buffer,
  433. &nbyte
  434. ))
  435. {
  436. status = GetLastError();
  437. goto destroy_key;
  438. }
  439. // We encrypted the terminating null, so it should still be there.
  440. if (encryptedPwd.Buffer[nbyte / sizeof(WCHAR) - 1] != L'\0')
  441. {
  442. status = ERROR_INVALID_DATA;
  443. goto destroy_key;
  444. }
  445. // Return the cleartext password to the caller.
  446. *pszPassword = encryptedPwd.Buffer;
  447. encryptedPwd.Buffer = NULL;
  448. destroy_key:
  449. CryptDestroyKey(hKey);
  450. free_password:
  451. LocalFree(encryptedPwd.Buffer);
  452. free_key:
  453. LocalFree(userKey.Buffer);
  454. exit:
  455. return status;
  456. }
  457. DWORD
  458. WINAPI
  459. IASParmsSetUserPassword(
  460. IN PCWSTR szUserParms,
  461. IN PCWSTR szPassword,
  462. OUT PWSTR *pszNewUserParms
  463. )
  464. {
  465. DWORD status;
  466. BYTE userKey[USER_KEY_LENGTH];
  467. HCRYPTKEY hKey;
  468. DWORD nbyte;
  469. PBYTE encryptedPwd;
  470. UNICODE_STRING property;
  471. PWSTR tempUserParms;
  472. BOOL update;
  473. // Check the in parameters.
  474. if (szPassword == NULL) { return ERROR_INVALID_PARAMETER; }
  475. // Make sure we're initialized.
  476. CHECK_INIT();
  477. // Generate a user key.
  478. if (!CryptGenRandom(
  479. theContext,
  480. USER_KEY_LENGTH,
  481. userKey
  482. ))
  483. {
  484. status = GetLastError();
  485. goto exit;
  486. }
  487. // Convert the user key to a cryptographic key.
  488. if (!IASDeriveUserCryptKey(
  489. userKey,
  490. &hKey
  491. ))
  492. {
  493. status = GetLastError();
  494. goto exit;
  495. }
  496. // Allocate a buffer for the encrypted password.
  497. nbyte = sizeof(WCHAR) * (lstrlenW(szPassword) + 1);
  498. encryptedPwd = RtlAllocateHeap(
  499. RasSfmHeap(),
  500. 0,
  501. nbyte
  502. );
  503. if (encryptedPwd == NULL)
  504. {
  505. status = ERROR_NOT_ENOUGH_MEMORY;
  506. goto destroy_key;
  507. }
  508. memcpy(encryptedPwd, szPassword, nbyte);
  509. // Encrypt the password.
  510. if (!CryptEncrypt(
  511. hKey,
  512. 0,
  513. TRUE,
  514. 0,
  515. encryptedPwd,
  516. &nbyte,
  517. nbyte
  518. ))
  519. {
  520. status = GetLastError();
  521. goto free_encrypted_password;
  522. }
  523. /////////
  524. // Store the encrypted password.
  525. /////////
  526. property.Buffer = (PWCHAR)encryptedPwd;
  527. property.Length = (USHORT)nbyte;
  528. property.MaximumLength = (USHORT)nbyte;
  529. status = NetpParmsSetUserProperty(
  530. (PWSTR)szUserParms,
  531. PROPERTY_PASSWORD,
  532. property,
  533. 0,
  534. &tempUserParms,
  535. &update
  536. );
  537. if (!NT_SUCCESS(status))
  538. {
  539. status = RtlNtStatusToDosError(status);
  540. goto free_encrypted_password;
  541. }
  542. /////////
  543. // Store the user key.
  544. /////////
  545. property.Buffer = (PWSTR)userKey;
  546. property.Length = USER_KEY_LENGTH;
  547. property.MaximumLength = USER_KEY_LENGTH;
  548. status = NetpParmsSetUserProperty(
  549. tempUserParms,
  550. PROPERTY_USER_KEY,
  551. property,
  552. 0,
  553. pszNewUserParms,
  554. &update
  555. );
  556. if (!NT_SUCCESS(status)) { status = RtlNtStatusToDosError(status); }
  557. NetpParmsUserPropertyFree(tempUserParms);
  558. free_encrypted_password:
  559. RtlFreeHeap(RasSfmHeap(), 0, encryptedPwd);
  560. destroy_key:
  561. CryptDestroyKey(hKey);
  562. exit:
  563. return status;
  564. }
  565. VOID
  566. WINAPI
  567. IASParmsFreeUserParms(
  568. IN LPWSTR szNewUserParms
  569. )
  570. {
  571. NetpParmsUserPropertyFree(szNewUserParms);
  572. }