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.

516 lines
15 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. crypto.c
  5. Abstract:
  6. routines for encrypting/decrypting random data blob. heavily modelled
  7. after service\cp\crypto.c
  8. Author:
  9. Charlie Wickham (charlwi) 14-Feb-2001
  10. Environment:
  11. User Mode
  12. Revision History:
  13. --*/
  14. #include "clusres.h"
  15. #include "clusrtl.h"
  16. #include "netname.h"
  17. #include <wincrypt.h>
  18. #include <lm.h>
  19. //
  20. // header for encrypted data.
  21. //
  22. #define SALT_SIZE 16
  23. #define IV_SIZE 8
  24. typedef struct _NETNAME_ENCRYPTED_DATA {
  25. DWORD Version;
  26. struct _NETNAME_ENCRYPTION_INITIALIZATION_DATA {
  27. BYTE IV[IV_SIZE];
  28. BYTE Salt[SALT_SIZE];
  29. } InitData;
  30. BYTE Data[0];
  31. } NETNAME_ENCRYPTED_DATA, *PNETNAME_ENCRYPTED_DATA;
  32. // current version for the CRYPTO_KEY_INFO struct
  33. #define NETNAME_ENCRYPTED_DATA_VERSION 1
  34. DWORD
  35. GenSymKey(
  36. IN HCRYPTPROV hProv,
  37. IN BYTE *pbSalt,
  38. IN BYTE *pbIV,
  39. OUT HCRYPTKEY *phSymKey
  40. )
  41. /*++
  42. Routine Description:
  43. Generate a session key based on the specified Salt and IV.
  44. Arguments:
  45. hProv - Handle to the crypto provider (key container)
  46. pbSalt - Salt value
  47. pbIV - IV value
  48. phSymKey - Resulting symmetric key (CALG_RC2)
  49. Return Value:
  50. ERROR_SUCCESS if successful
  51. Win32 error code otherwise
  52. --*/
  53. {
  54. HCRYPTHASH hHash = 0;
  55. DWORD cbPassword = 0;
  56. DWORD Status;
  57. if (!CryptCreateHash(hProv,
  58. CALG_SHA1,
  59. 0,
  60. 0,
  61. &hHash))
  62. {
  63. Status = GetLastError();
  64. goto Ret;
  65. }
  66. if (!CryptHashData(hHash,
  67. pbSalt,
  68. SALT_SIZE,
  69. 0))
  70. {
  71. Status = GetLastError();
  72. goto Ret;
  73. }
  74. // derive the key from the hash
  75. if (!CryptDeriveKey(hProv,
  76. CALG_RC2,
  77. hHash,
  78. 0,
  79. phSymKey))
  80. {
  81. Status = GetLastError();
  82. goto Ret;
  83. }
  84. // set the IV on the key
  85. if (!CryptSetKeyParam(*phSymKey,
  86. KP_IV,
  87. pbIV,
  88. 0))
  89. {
  90. Status = GetLastError();
  91. goto Ret;
  92. }
  93. Status = ERROR_SUCCESS;
  94. Ret:
  95. if (hHash)
  96. CryptDestroyHash(hHash);
  97. return (Status);
  98. } // GenSymKey
  99. DWORD
  100. EncryptNetNameData(
  101. RESOURCE_HANDLE ResourceHandle,
  102. LPWSTR MachinePwd,
  103. PBYTE * EncryptedInfo,
  104. PDWORD EncryptedInfoLength,
  105. HKEY Key
  106. )
  107. /*++
  108. Routine Description:
  109. encrypt the password, set a pointer to the encrypted data and store it in
  110. the registry.
  111. There is a chicken/egg problem of sorts in that we have to generate the
  112. key before we can use it to encrypt data. This requires having a spot to
  113. store the Salt and IV. Since we have to hold all the info in one buffer
  114. for the registry write, we tempoarily use stack-based buffer
  115. (keyGenBuffer) for the header info. Once we know the size of the encrypted
  116. data, we can allocate the proper sized buffer (encryptedInfo), copy
  117. everything into it, and write the registry. It is this buffer that is
  118. handed back to the caller via the EncryptedInfo argument.
  119. Arguments:
  120. ResourceHandle - for logging to the cluster log
  121. MachinePwd - pointer to unicode string password
  122. EncryptedInfo - address of a pointer that receives a pointer to the encrypted blob
  123. EncryptedInfoLength - pointer to a DWORD that receives the length of the blob
  124. Key - handle to netname parameters key where the data is stored
  125. Return Value:
  126. ERROR_SUCCESS, otherwise Win32 error
  127. --*/
  128. {
  129. DWORD status;
  130. DWORD encInfoLength;
  131. DWORD encDataLength = 0;
  132. BOOL success;
  133. DWORD pwdLength = ( wcslen( MachinePwd ) + 1 ) * sizeof( WCHAR );
  134. HCRYPTPROV cryptoProvider = 0;
  135. HCRYPTKEY sessionKey = 0;
  136. NETNAME_ENCRYPTED_DATA keyGenBuffer; // temp header buffer to generate key
  137. PNETNAME_ENCRYPTED_DATA encryptedInfo = NULL; // final data area
  138. //
  139. // get a handle to the full RSA provider
  140. //
  141. if ( !CryptAcquireContext(&cryptoProvider,
  142. NULL,
  143. NULL,
  144. PROV_RSA_FULL,
  145. CRYPT_MACHINE_KEYSET | CRYPT_SILENT))
  146. {
  147. status = GetLastError();
  148. if ( status == NTE_BAD_KEYSET ) {
  149. success = CryptAcquireContext(&cryptoProvider,
  150. NULL,
  151. NULL,
  152. PROV_RSA_FULL,
  153. CRYPT_MACHINE_KEYSET |
  154. CRYPT_SILENT |
  155. CRYPT_NEWKEYSET);
  156. status = success ? ERROR_SUCCESS : GetLastError();
  157. }
  158. if ( status != ERROR_SUCCESS ) {
  159. (NetNameLogEvent)(ResourceHandle,
  160. LOG_ERROR,
  161. L"Can't acquire crypto context for encrypt. status %1!u!.\n",
  162. status);
  163. return status;
  164. }
  165. }
  166. //
  167. // generate the session key.
  168. //
  169. if ( !CryptGenRandom(cryptoProvider,
  170. sizeof(struct _NETNAME_ENCRYPTION_INITIALIZATION_DATA),
  171. (PBYTE)&keyGenBuffer.InitData))
  172. {
  173. status = GetLastError();
  174. (NetNameLogEvent)(ResourceHandle,
  175. LOG_ERROR,
  176. L"Can't generate seed data. status %1!u!.\n",
  177. status);
  178. goto cleanup;
  179. }
  180. keyGenBuffer.Version = NETNAME_ENCRYPTED_DATA_VERSION;
  181. status = GenSymKey(cryptoProvider,
  182. keyGenBuffer.InitData.Salt,
  183. keyGenBuffer.InitData.IV,
  184. &sessionKey);
  185. if ( status != ERROR_SUCCESS ) {
  186. (NetNameLogEvent)(ResourceHandle,
  187. LOG_ERROR,
  188. L"Can't generate session key for encrypt. status %1!u!.\n",
  189. status);
  190. goto cleanup;
  191. }
  192. //
  193. // find the size we need for the buffer to receive the encrypted data
  194. //
  195. encDataLength = pwdLength;
  196. if ( CryptEncrypt(sessionKey,
  197. 0,
  198. TRUE,
  199. 0,
  200. NULL,
  201. &encDataLength,
  202. 0))
  203. {
  204. //
  205. // alloc a buffer large enough to hold the data and copy the password into it.
  206. //
  207. ASSERT( encDataLength >= pwdLength );
  208. encInfoLength = sizeof( NETNAME_ENCRYPTED_DATA ) + encDataLength;
  209. encryptedInfo = HeapAlloc( GetProcessHeap(), 0, encInfoLength );
  210. if ( encryptedInfo != NULL ) {
  211. wcscpy( (PWCHAR)encryptedInfo->Data, MachinePwd );
  212. if ( CryptEncrypt(sessionKey,
  213. 0,
  214. TRUE,
  215. 0,
  216. encryptedInfo->Data,
  217. &pwdLength,
  218. encDataLength))
  219. {
  220. *encryptedInfo = keyGenBuffer;
  221. status = ResUtilSetBinaryValue(Key,
  222. PARAM_NAME__RANDOM,
  223. (const LPBYTE)encryptedInfo,
  224. encInfoLength,
  225. NULL,
  226. NULL);
  227. if ( status != ERROR_SUCCESS ) {
  228. (NetNameLogEvent)(ResourceHandle,
  229. LOG_ERROR,
  230. L"Can't write %1!u! bytes of data to registry. status %2!u!.\n",
  231. encInfoLength,
  232. status);
  233. goto cleanup;
  234. }
  235. }
  236. else {
  237. status = GetLastError();
  238. (NetNameLogEvent)(ResourceHandle,
  239. LOG_ERROR,
  240. L"Can't encrypt %1!u! bytes. status %2!u!.\n",
  241. pwdLength,
  242. status);
  243. goto cleanup;
  244. }
  245. }
  246. else {
  247. status = ERROR_NOT_ENOUGH_MEMORY;
  248. (NetNameLogEvent)(ResourceHandle,
  249. LOG_ERROR,
  250. L"Can't allocate %1!u! bytes for encrypted data. status %2!u!.\n",
  251. encInfoLength,
  252. status);
  253. goto cleanup;
  254. }
  255. }
  256. else {
  257. status = GetLastError();
  258. (NetNameLogEvent)(ResourceHandle,
  259. LOG_ERROR,
  260. L"Can't determine size of encrypted data buffer for %1!u! bytes of data. status %2!u!.\n",
  261. pwdLength,
  262. status);
  263. goto cleanup;
  264. }
  265. *EncryptedInfoLength = encInfoLength;
  266. *EncryptedInfo = (PBYTE)encryptedInfo;
  267. cleanup:
  268. if ( status != ERROR_SUCCESS && encryptedInfo != NULL ) {
  269. HeapFree( GetProcessHeap(), 0, encryptedInfo );
  270. }
  271. if ( sessionKey != 0 ) {
  272. if ( !CryptDestroyKey( sessionKey )) {
  273. (NetNameLogEvent)(ResourceHandle,
  274. LOG_WARNING,
  275. L"Couldn't destory session key. status %1!u!.\n",
  276. GetLastError());
  277. }
  278. }
  279. if ( !CryptReleaseContext( cryptoProvider, 0 )) {
  280. (NetNameLogEvent)(ResourceHandle,
  281. LOG_WARNING,
  282. L"Can't release crypto context. status %1!u!.\n",
  283. GetLastError());
  284. }
  285. return status;
  286. } // EncryptNetNameData
  287. DWORD
  288. DecryptNetNameData(
  289. RESOURCE_HANDLE ResourceHandle,
  290. PBYTE EncryptedInfo,
  291. DWORD EncryptedInfoLength,
  292. LPWSTR MachinePwd
  293. )
  294. /*++
  295. Routine Description:
  296. Reverse of encrypt routine - decrypt random blob and hand back the
  297. password
  298. Arguments:
  299. ResourceHandle - used to log into the cluster log
  300. EncryptedInfo - pointer to encrypted info header and data
  301. EncryptedInfoLength - # of bytes in EncryptedInfo
  302. MachinePwd - pointer to buffer that receives the unicode password
  303. Return Value:
  304. ERROR_SUCCESS, otherwise Win32 error
  305. --*/
  306. {
  307. DWORD status;
  308. DWORD encDataLength = EncryptedInfoLength - sizeof( NETNAME_ENCRYPTED_DATA );
  309. BOOL success;
  310. DWORD pwdByteLength;
  311. DWORD pwdBufferSize;
  312. PWCHAR machinePwd = NULL;
  313. HCRYPTPROV cryptoProvider = 0;
  314. HCRYPTKEY sessionKey = 0;
  315. PNETNAME_ENCRYPTED_DATA encryptedInfo = (PNETNAME_ENCRYPTED_DATA)EncryptedInfo;
  316. //
  317. // get a handle to the full RSA provider
  318. //
  319. if ( !CryptAcquireContext(&cryptoProvider,
  320. NULL,
  321. NULL,
  322. PROV_RSA_FULL,
  323. CRYPT_MACHINE_KEYSET | CRYPT_SILENT))
  324. {
  325. status = GetLastError();
  326. if ( status == NTE_BAD_KEYSET ) {
  327. success = CryptAcquireContext(&cryptoProvider,
  328. NULL,
  329. NULL,
  330. PROV_RSA_FULL,
  331. CRYPT_MACHINE_KEYSET |
  332. CRYPT_SILENT |
  333. CRYPT_NEWKEYSET);
  334. status = success ? ERROR_SUCCESS : GetLastError();
  335. }
  336. if ( status != ERROR_SUCCESS ) {
  337. (NetNameLogEvent)(ResourceHandle,
  338. LOG_ERROR,
  339. L"Can't acquire crypto context for decrypt. status %1!u!.\n",
  340. status);
  341. return status;
  342. }
  343. }
  344. status = GenSymKey(cryptoProvider,
  345. encryptedInfo->InitData.Salt,
  346. encryptedInfo->InitData.IV,
  347. &sessionKey);
  348. if ( status != ERROR_SUCCESS ) {
  349. (NetNameLogEvent)(ResourceHandle,
  350. LOG_ERROR,
  351. L"Can't generate session key for decrypt. status %1!u!.\n",
  352. status);
  353. goto cleanup;
  354. }
  355. //
  356. // CryptDecrypt writes the decrypted data back into the buffer that was
  357. // holding the encrypted data. For this reason, allocate a new buffer that
  358. // will eventually contain the password.
  359. //
  360. pwdByteLength = ( LM20_PWLEN + 1 ) * sizeof( WCHAR );
  361. pwdBufferSize = ( pwdByteLength > encDataLength ? pwdByteLength : encDataLength );
  362. machinePwd = HeapAlloc( GetProcessHeap(), 0, pwdBufferSize );
  363. if ( machinePwd != NULL ) {
  364. RtlCopyMemory( machinePwd, encryptedInfo->Data, encDataLength );
  365. if ( CryptDecrypt(sessionKey,
  366. 0,
  367. TRUE,
  368. 0,
  369. (PBYTE)machinePwd,
  370. &encDataLength))
  371. {
  372. ASSERT( pwdByteLength == encDataLength );
  373. wcscpy( MachinePwd, machinePwd );
  374. }
  375. else {
  376. status = GetLastError();
  377. (NetNameLogEvent)(ResourceHandle,
  378. LOG_ERROR,
  379. L"Can't decrypt %1!u! bytes of data. status %2!u!.\n",
  380. encDataLength,
  381. status);
  382. goto cleanup;
  383. }
  384. }
  385. else {
  386. status = ERROR_NOT_ENOUGH_MEMORY;
  387. (NetNameLogEvent)(ResourceHandle,
  388. LOG_ERROR,
  389. L"Can't allocate %1!u! bytes for decrypt. status %2!u!.\n",
  390. pwdBufferSize,
  391. status);
  392. goto cleanup;
  393. }
  394. cleanup:
  395. if ( machinePwd != NULL) {
  396. HeapFree( GetProcessHeap(), 0, machinePwd );
  397. }
  398. if ( sessionKey != 0 ) {
  399. if ( !CryptDestroyKey( sessionKey )) {
  400. (NetNameLogEvent)(ResourceHandle,
  401. LOG_WARNING,
  402. L"Couldn't destory session key. status %1!u!.\n",
  403. GetLastError());
  404. }
  405. }
  406. if ( !CryptReleaseContext( cryptoProvider, 0 )) {
  407. (NetNameLogEvent)(ResourceHandle,
  408. LOG_WARNING,
  409. L"Can't release crypto context. status %1!u!.\n",
  410. GetLastError());
  411. }
  412. return status;
  413. } // DecryptNetNameData
  414. /* end crypto.c */