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.

2988 lines
75 KiB

  1. /*++
  2. Copyright (c) 1996, 1997 Microsoft Corporation
  3. Module Name:
  4. keybckup.cpp
  5. Abstract:
  6. This module contains routines associated with server side Key Backup
  7. operations.
  8. User sends data D2 to remote agent (remote agent is this code)
  9. Agent uses secret monster key K, random R2, HMACs to derive SymKeyK.
  10. Use SymKeyK to encrypt {userid, D2}
  11. Agent returns recovery field E{userid, D2}, R2 to User
  12. User stores recovery field E{userid, D2}, R2
  13. Author:
  14. Scott Field (sfield) 16-Aug-97
  15. --*/
  16. #include <pch.cpp>
  17. #pragma hdrstop
  18. #include <ntlsa.h>
  19. //
  20. // functions to backup and restore to/from recoverable blob
  21. //
  22. BOOL
  23. BackupToRecoverableBlobW2K(
  24. IN HANDLE hToken, // client access token opened for TOKEN_QUERY
  25. IN BYTE *pDataIn,
  26. IN DWORD cbDataIn,
  27. IN OUT BYTE **ppDataOut,
  28. IN OUT DWORD *pcbDataOut
  29. );
  30. BOOL
  31. RestoreFromRecoverableBlob(
  32. IN HANDLE hToken,
  33. IN BOOL fWin2kDataOut,
  34. IN BYTE * pDataIn,
  35. IN DWORD cbDataIn,
  36. IN OUT BYTE ** ppDataOut,
  37. IN OUT DWORD * pcbDataOut
  38. );
  39. BOOL
  40. RestoreFromRecoverableBlobW2K(
  41. IN HANDLE hToken, // client access token opened for TOKEN_QUERY
  42. IN BYTE *pDataIn,
  43. IN DWORD cbDataIn,
  44. IN OUT BYTE **ppDataOut,
  45. IN OUT DWORD *pcbDataOut
  46. );
  47. BOOL ConvertRecoveredBlobToW2KBlob(
  48. IN BYTE *pbMasterKey,
  49. IN DWORD cbMaserKey,
  50. IN PBYTE pbLocalKey,
  51. IN DWORD cbLocalKey,
  52. IN PSID pSidCandidate,
  53. IN OUT BYTE **ppbDataOut,
  54. IN OUT DWORD *pcbDataOut);
  55. //
  56. // functions to get/create/set keys to persistent storage.
  57. //
  58. BOOL
  59. GetBackupKey(
  60. IN GUID *pguidKey,
  61. OUT PBYTE *ppbKey,
  62. OUT DWORD *pcbKey,
  63. OUT HCRYPTPROV *phCryptProv,
  64. OUT HCRYPTKEY *phCryptKey
  65. );
  66. BOOL
  67. CreateBackupKeyW2K(
  68. IN DWORD dwKeyVersion,
  69. IN OUT GUID *pguidKey,
  70. OUT PBYTE *ppbKey,
  71. OUT DWORD *pcbKey);
  72. BOOL
  73. CreateBackupKey(
  74. IN DWORD dwKeyVersion,
  75. IN OUT GUID *pguidKey,
  76. OUT PBYTE *ppbKey,
  77. OUT DWORD *pcbKey,
  78. OUT HCRYPTPROV *phCryptProv,
  79. OUT HCRYPTKEY *phCryptKey
  80. );
  81. BOOL
  82. SaveBackupKey(
  83. IN GUID *pguidKey,
  84. IN BYTE *pbKey,
  85. IN DWORD cbKey
  86. );
  87. BOOL
  88. DestroyBackupKey(
  89. IN GUID guidKey
  90. );
  91. //
  92. // functions to get/create/set the preferred backup key.
  93. //
  94. BOOL
  95. SetupPreferredBackupKeys(
  96. VOID
  97. );
  98. BOOL
  99. FreePreferredBackupKey(
  100. VOID
  101. );
  102. //
  103. // helper functions to set/get the GUID associated with the preferred
  104. // backup key.
  105. //
  106. BOOL
  107. GetPreferredBackupKeyGuid(
  108. IN DWORD dwVersion,
  109. IN OUT GUID *pguidKey
  110. );
  111. BOOL
  112. SetPreferredBackupKeyGuid(
  113. IN DWORD dwVersion,
  114. IN GUID *pguidKey
  115. );
  116. //
  117. // helper functions for managing SYSTEM credentials.
  118. //
  119. BOOL
  120. CreateSystemCredentials(
  121. VOID
  122. );
  123. DWORD
  124. QuerySystemCredentials(
  125. IN OUT BYTE rgbSystemCredMachine[ A_SHA_DIGEST_LEN ],
  126. IN OUT BYTE rgbSystemCredUser [ A_SHA_DIGEST_LEN ]
  127. );
  128. BOOL
  129. FreeSystemCredentials(
  130. VOID
  131. );
  132. BOOL GeneratePublicKeyCert(HCRYPTPROV hCryptProv,
  133. HCRYPTKEY hCryptKey,
  134. GUID *pguidKey,
  135. DWORD *pcbPublicExportLength,
  136. PBYTE *ppbPublicExportData);
  137. //
  138. // utility functions for interacting with LSA, etc.
  139. //
  140. NTSTATUS
  141. OpenPolicy(
  142. LPWSTR ServerName, // machine to open policy on (Unicode)
  143. DWORD DesiredAccess, // desired access to policy
  144. PLSA_HANDLE PolicyHandle // resultant policy handle
  145. );
  146. BOOL
  147. WaitOnSAMDatabase(
  148. VOID
  149. );
  150. #define FILETIME_TICKS_PER_SECOND 10000000
  151. #define BACKUPKEY_LIFETIME (60*60*24*365) // 1 Year
  152. //
  153. // private defines and prototypes for the backup and restore
  154. // blob operations.
  155. //
  156. #define BACKUPKEY_VERSION_W2K 1 // legacy version of monster key material
  157. #define BACKUPKEY_MATERIAL_SIZE (256) // monster key material size, excluding version, etc.
  158. #define BACKUPKEY_VERSION 2 // legacy version of monster key material
  159. //
  160. // LSA secret key name prefix, textual GUID key ID follows
  161. //
  162. #define BACKUPKEY_NAME_PREFIX L"G$BCKUPKEY_"
  163. //
  164. // LSA secret key name - identifies GUID of legacy preferred key
  165. //
  166. #define BACKUPKEY_PREFERRED_W2K L"G$BCKUPKEY_P"
  167. //
  168. // LSA secret key name - identifies GUID of preferred key
  169. //
  170. #define BACKUPKEY_PREFERRED L"G$BCKUPKEY_PREFERRED"
  171. //
  172. // exposed Random R2 used to derive symetric key from monster key
  173. // BACKUPKEY_R2_LEN makes BACKUPKEY_RECOVERY_BLOB size mod 32.
  174. //
  175. #define BACKUPKEY_R2_LEN (68) // length of random HMAC data
  176. //
  177. // size of inner Random R3 used to derive MAC key.
  178. //
  179. #define BACKUPKEY_R3_LEN (32)
  180. typedef struct {
  181. DWORD dwVersion; // version of structure (BACKUPKEY_RECOVERY_BLOB_VERSION)
  182. DWORD cbClearData; // quantity of clear data, not including Sid
  183. DWORD cbCipherData; // quantity of cipher data following structure
  184. GUID guidKey; // guid identifying backup key used
  185. BYTE R2[BACKUPKEY_R2_LEN]; // random data used during HMAC to derive symetric key
  186. } BACKUPKEY_RECOVERY_BLOB_W2K,
  187. *PBACKUPKEY_RECOVERY_BLOB_W2K,
  188. *LPBACKUPKEY_RECOVERY_BLOB_W2K;
  189. //
  190. // when dwOuterVersion is 1,
  191. // BYTE bCipherData[cbCipherData] follows
  192. //
  193. // in the clear, bCipherData is
  194. // struct BACKUPKEY_INNER_BLOB
  195. // BYTE bUserClearData[cbClearData]
  196. // SID data follows bUserClearData[cbClearData]
  197. // GetLengthSid() yields sid data length, IsValidSid() used to validate
  198. // structural integrity of data. Further authentication of requesting user
  199. // done when restore requested.
  200. //
  201. typedef struct {
  202. BYTE R3[BACKUPKEY_R3_LEN]; // random data used to derive MAC key
  203. BYTE MAC[A_SHA_DIGEST_LEN]; // HMAC(R3, pUserSid | pbClearUserData)
  204. } BACKUPKEY_INNER_BLOB_W2K,
  205. *PBACKUPKEY_INNER_BLOB_W2K,
  206. *LPBACKUPKEY_INNER_BLOB_W2K;
  207. //
  208. // definitions to support credentials for the SYSTEM account.
  209. // this includes two scenarios:
  210. // 1. Calls originating from the local system account security context.
  211. // 2. Calls with the LOCAL_MACHINE disposition.
  212. //
  213. #define SYSTEM_CREDENTIALS_VERSION 1
  214. #define SYSTEM_CREDENTIALS_SECRET L"DPAPI_SYSTEM"
  215. typedef struct {
  216. DWORD dwVersion;
  217. BYTE rgbSystemCredMachine[ A_SHA_DIGEST_LEN ];
  218. BYTE rgbSystemCredUser[ A_SHA_DIGEST_LEN ];
  219. } SYSTEM_CREDENTIALS, *PSYSTEM_CREDENTIALS, *LPSYSTEM_CREDENTIALS;
  220. //
  221. // Counter value and name for memory mapped file.
  222. // See timer.exe...
  223. //
  224. #ifdef DCSTRESS
  225. LPVOID g_pCounter = NULL;
  226. #define WSZ_MAP_OBJECT L"rpcnt"
  227. #endif // DCSTRESS
  228. BOOL g_fBackupKeyServerStarted = FALSE;
  229. //
  230. // Legacy system preferred backup key
  231. //
  232. GUID g_guidW2KPreferredKey;
  233. PBYTE g_pbW2KPreferredKey = NULL;
  234. DWORD g_cbW2KPreferredKey = 0;
  235. // Public/Private style preferred key
  236. GUID g_guidPreferredKey;
  237. PBYTE g_pbPreferredKey = NULL;
  238. DWORD g_cbPreferredKey = 0;
  239. HCRYPTPROV g_hProvPreferredKey = NULL;
  240. HCRYPTKEY g_hKeyPreferredKey = NULL;
  241. RTL_CRITICAL_SECTION g_csInitialization;
  242. BOOL g_fSetupPreferredAttempted = FALSE;
  243. //
  244. // global SYSTEM credentials:
  245. // One is for calls originating from the Local System account security context
  246. // at per-user disposition;
  247. // The other key is for calls originating from any account at the per-machine
  248. // disposition.
  249. //
  250. BOOL g_fSystemCredsInitialized = FALSE;
  251. BYTE g_rgbSystemCredMachine[ A_SHA_DIGEST_LEN ];
  252. BYTE g_rgbSystemCredUser[ A_SHA_DIGEST_LEN ];
  253. DWORD
  254. s_BackuprKey(
  255. /* [in] */ handle_t h,
  256. /* [in] */ GUID __RPC_FAR *pguidActionAgent,
  257. /* [in] */ BYTE __RPC_FAR *pDataIn,
  258. /* [in] */ DWORD cbDataIn,
  259. /* [size_is][size_is][out] */ BYTE __RPC_FAR *__RPC_FAR *ppDataOut,
  260. /* [out] */ DWORD __RPC_FAR *pcbDataOut,
  261. /* [in] */ DWORD dwParam
  262. )
  263. /*++
  264. Server side implemention of the BackupKey() interface.
  265. --*/
  266. {
  267. static const GUID guidBackup = BACKUPKEY_BACKUP_GUID;
  268. static const GUID guidRestoreW2K = BACKUPKEY_RESTORE_GUID_W2K;
  269. static const GUID guidRestore = BACKUPKEY_RESTORE_GUID;
  270. static const GUID guidRetrieve = BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID;
  271. HANDLE hToken = NULL;
  272. PBYTE pTempDataOut;
  273. BOOL fEncrypt;
  274. BOOL fSuccess;
  275. DWORD dwLastError = ERROR_SUCCESS;
  276. DWORD rc;
  277. if( !g_fBackupKeyServerStarted )
  278. return ERROR_INVALID_PARAMETER;
  279. __try {
  280. //
  281. // insure the preferred key is setup.
  282. //
  283. if(!SetupPreferredBackupKeys())
  284. return ERROR_INVALID_PARAMETER;
  285. //
  286. // pickup a copy of an access token representing the client.
  287. //
  288. dwLastError = RpcImpersonateClient( h );
  289. if(dwLastError != RPC_S_OK)
  290. goto cleanup;
  291. fSuccess = OpenThreadToken(
  292. GetCurrentThread(),
  293. TOKEN_QUERY,
  294. FALSE,
  295. &hToken
  296. );
  297. if(!fSuccess)
  298. dwLastError = GetLastError();
  299. if ((RPC_S_OK != (rc = RpcRevertToSelf())) && fSuccess)
  300. {
  301. dwLastError = rc;
  302. goto cleanup;
  303. }
  304. if(!fSuccess)
  305. goto cleanup;
  306. if(memcmp(pguidActionAgent, &guidRestore, sizeof(GUID)) == 0)
  307. {
  308. if(cbDataIn < sizeof(DWORD))
  309. {
  310. // Not enough room for a version
  311. dwLastError = ERROR_INVALID_PARAMETER;
  312. goto cleanup;
  313. }
  314. if(BACKUPKEY_RECOVERY_BLOB_VERSION_W2K == ((DWORD *)pDataIn)[0])
  315. {
  316. // The recovery blob is of the legacy style, so simply
  317. // restore it the old way.
  318. fSuccess = RestoreFromRecoverableBlobW2K(
  319. hToken,
  320. pDataIn,
  321. cbDataIn,
  322. &pTempDataOut,
  323. pcbDataOut
  324. );
  325. }
  326. else if(BACKUPKEY_RECOVERY_BLOB_VERSION == ((DWORD *)pDataIn)[0])
  327. {
  328. fSuccess = RestoreFromRecoverableBlob(
  329. hToken,
  330. FALSE,
  331. pDataIn,
  332. cbDataIn,
  333. &pTempDataOut,
  334. pcbDataOut
  335. );
  336. }
  337. else
  338. {
  339. dwLastError = ERROR_INVALID_PARAMETER;
  340. goto cleanup;
  341. }
  342. }
  343. else if(memcmp(pguidActionAgent, &guidBackup, sizeof(GUID)) == 0)
  344. {
  345. // We only use the legacy mechanism for backup when the backup
  346. // method is called. The real mechanism of backup
  347. // requires backup on the client machine alone.
  348. fSuccess = BackupToRecoverableBlobW2K(
  349. hToken,
  350. pDataIn,
  351. cbDataIn,
  352. &pTempDataOut,
  353. pcbDataOut
  354. );
  355. }
  356. else if(memcmp(pguidActionAgent, &guidRestoreW2K, sizeof(GUID)) == 0)
  357. {
  358. //
  359. // A legacy client is calling, and always expects a legacy
  360. // pbBK style return blob.
  361. if(cbDataIn < sizeof(DWORD))
  362. {
  363. // Not enough room for a version
  364. dwLastError = ERROR_INVALID_PARAMETER;
  365. goto cleanup;
  366. }
  367. if(BACKUPKEY_RECOVERY_BLOB_VERSION_W2K == ((DWORD *)pDataIn)[0])
  368. {
  369. // The recovery blob is of the legacy style, so simply
  370. // restore it the old way.
  371. fSuccess = RestoreFromRecoverableBlobW2K(
  372. hToken,
  373. pDataIn,
  374. cbDataIn,
  375. &pTempDataOut,
  376. pcbDataOut
  377. );
  378. }
  379. else if(BACKUPKEY_RECOVERY_BLOB_VERSION == ((DWORD *)pDataIn)[0])
  380. {
  381. // This is a current recovery blob, so restore it the
  382. // current way
  383. fSuccess = RestoreFromRecoverableBlob(
  384. hToken,
  385. TRUE,
  386. pDataIn,
  387. cbDataIn,
  388. &pTempDataOut,
  389. pcbDataOut
  390. );
  391. }
  392. else
  393. {
  394. dwLastError = ERROR_INVALID_PARAMETER;
  395. goto cleanup;
  396. }
  397. }
  398. else if(memcmp(pguidActionAgent, &guidRetrieve, sizeof(GUID)) == 0)
  399. {
  400. //
  401. // The client is asking for a copy of the domain backup public key.
  402. //
  403. if(FIsLegacyCompliant())
  404. {
  405. dwLastError = ERROR_NOT_SUPPORTED;
  406. goto cleanup;
  407. }
  408. if(!FDistributeDomainBackupKey())
  409. {
  410. dwLastError = ERROR_NOT_SUPPORTED;
  411. goto cleanup;
  412. }
  413. if((g_cbPreferredKey < 3*sizeof(DWORD)) ||
  414. (((DWORD *)g_pbPreferredKey)[0] != BACKUPKEY_VERSION) ||
  415. (((DWORD *)g_pbPreferredKey)[1] +
  416. ((DWORD *)g_pbPreferredKey)[2] + 3*sizeof(DWORD) != g_cbPreferredKey))
  417. {
  418. dwLastError = ERROR_INVALID_PARAMETER;
  419. goto cleanup;
  420. }
  421. pTempDataOut = (PBYTE)SSAlloc(((DWORD *)g_pbPreferredKey)[2]);
  422. if(NULL == pTempDataOut)
  423. {
  424. dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  425. goto cleanup;
  426. }
  427. CopyMemory(pTempDataOut,
  428. g_pbPreferredKey +
  429. 3*sizeof(DWORD) +
  430. ((DWORD *)g_pbPreferredKey)[1],
  431. ((DWORD *)g_pbPreferredKey)[2]);
  432. *pcbDataOut = ((DWORD *)g_pbPreferredKey)[2];
  433. fSuccess = TRUE;
  434. }
  435. else
  436. {
  437. dwLastError = ERROR_INVALID_PARAMETER;
  438. goto cleanup;
  439. }
  440. if( fSuccess ) {
  441. //
  442. // everything went as planned: tell caller about buffer
  443. //
  444. *ppDataOut = pTempDataOut;
  445. #ifdef DCSTRESS
  446. //
  447. // Increment RPC counter for timer.exe, if timer
  448. // is running.
  449. //
  450. if (g_pCounter)
  451. (*(DWORD*)g_pCounter)++;
  452. #endif // DCSTRESS
  453. } else {
  454. dwLastError = GetLastError();
  455. if(dwLastError == ERROR_SUCCESS) {
  456. dwLastError = ERROR_FILE_NOT_FOUND;
  457. }
  458. }
  459. } __except (EXCEPTION_EXECUTE_HANDLER) {
  460. // TODO: convert to Win Error
  461. dwLastError = GetExceptionCode();
  462. }
  463. cleanup:
  464. if(hToken)
  465. CloseHandle(hToken);
  466. return dwLastError;
  467. }
  468. ///////////////////////////////////////////////////////////////////
  469. // BackupToRecoverableBlobW2K
  470. //
  471. // This functionality is requested by W2K legacy clients, which
  472. // are passing up the pbBK Backup key to be encrypted into a
  473. // pbBBK.
  474. //
  475. // We encrypt this verbatim, using a version of
  476. // BACKUPKEY_RECOVERY_BLOB_BK, indicating that
  477. // BACKUPKEY_RESTORE_GUID_W2K must be used
  478. // to recover this blob, not BACKUPKEY_RESTORE_GUID.
  479. //
  480. // Post W2K Recovery blobs are created on the clients, so we don't
  481. // need server code to create them.
  482. //
  483. // The format of the output data is as follows:
  484. //
  485. // typedef struct {
  486. // DWORD dwVersion; // version of structure
  487. // DWORD cbClearData; // length of input data
  488. // DWORD cbCipherData; // quantity of cipher data following structure
  489. // GUID guidKey; // guid identifying backup key used
  490. // BYTE R2[BACKUPKEY_R2_LEN]; // random data used during HMAC to derive symetric key
  491. // } BACKUPKEY_RECOVERY_BLOB_W2K;
  492. //
  493. // typedef struct {
  494. // BYTE R3[BACKUPKEY_R3_LEN]; // random data used to derive MAC key
  495. // BYTE MAC[A_SHA_DIGEST_LEN]; // HMAC(R3, pUserSid | pbClearUserData)
  496. // } BACKUPKEY_INNER_BLOB_W2K;
  497. //
  498. // <user sid>
  499. // <input data>
  500. //
  501. // The BACKUPKEY_INNER_BLOB_W2K structure and the data following
  502. // it are encrypted.
  503. //
  504. ///////////////////////////////////////////////////////////////////
  505. BOOL
  506. BackupToRecoverableBlobW2K(
  507. IN HANDLE hToken,
  508. IN BYTE *pDataIn,
  509. IN DWORD cbDataIn,
  510. IN OUT BYTE **ppDataOut,
  511. IN OUT DWORD *pcbDataOut
  512. )
  513. {
  514. PSID pSidUser = NULL;
  515. DWORD cbSidUser;
  516. PBACKUPKEY_RECOVERY_BLOB_W2K pRecoveryBlob;
  517. DWORD cbRecoveryBlob;
  518. PBACKUPKEY_INNER_BLOB_W2K pInnerBlob;
  519. DWORD cbInnerBlob;
  520. PBYTE pbCipherBegin;
  521. BYTE rgbSymKey[A_SHA_DIGEST_LEN];
  522. BYTE rgbMacKey[A_SHA_DIGEST_LEN];
  523. RC4_KEYSTRUCT sRC4Key;
  524. DWORD dwLastError = ERROR_SUCCESS;
  525. BOOL fSuccess = FALSE;
  526. if( pDataIn == NULL || cbDataIn == 0 ||
  527. ppDataOut == NULL || pcbDataOut == NULL ) {
  528. SetLastError(ERROR_INVALID_PARAMETER);
  529. return FALSE;
  530. }
  531. *ppDataOut = NULL;
  532. //
  533. // get Sid associated with client user.
  534. //
  535. if(!GetTokenUserSid( hToken, &pSidUser ))
  536. return FALSE;
  537. cbSidUser = GetLengthSid( pSidUser );
  538. //
  539. // Calculate the size of the inner blob
  540. //
  541. cbInnerBlob = sizeof(BACKUPKEY_INNER_BLOB_W2K) +
  542. cbSidUser +
  543. cbDataIn;
  544. //
  545. // Estimate the size of the encrypted data buffer
  546. //
  547. //
  548. // allocate buffer to contain results
  549. // RECOVERABLE_BLOB struct + Sid + cbDataIn
  550. // note that cbDataIn works because we use a stream cipher.
  551. //
  552. *pcbDataOut = sizeof(BACKUPKEY_RECOVERY_BLOB_W2K) +
  553. sizeof(BACKUPKEY_INNER_BLOB_W2K) +
  554. cbSidUser +
  555. cbDataIn ;
  556. *ppDataOut = (LPBYTE)SSAlloc( *pcbDataOut );
  557. if(*ppDataOut == NULL) {
  558. dwLastError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  559. goto cleanup;
  560. }
  561. pRecoveryBlob = (PBACKUPKEY_RECOVERY_BLOB_W2K)*ppDataOut;
  562. pRecoveryBlob->dwVersion = BACKUPKEY_RECOVERY_BLOB_VERSION_W2K;
  563. pRecoveryBlob->cbClearData = cbDataIn; // does not include Sid since not handed back on restore
  564. pRecoveryBlob->cbCipherData = sizeof(BACKUPKEY_INNER_BLOB_W2K) + cbSidUser + cbDataIn;
  565. CopyMemory( &(pRecoveryBlob->guidKey), &g_guidW2KPreferredKey, sizeof(GUID));
  566. pInnerBlob = (PBACKUPKEY_INNER_BLOB_W2K)(pRecoveryBlob+1);
  567. //
  568. // generate random R2 for SymKey
  569. //
  570. if(!RtlGenRandom(pRecoveryBlob->R2, BACKUPKEY_R2_LEN))
  571. goto cleanup;
  572. //
  573. // generate random R3 for MAC
  574. //
  575. if(!RtlGenRandom(pInnerBlob->R3, BACKUPKEY_R3_LEN))
  576. goto cleanup;
  577. //
  578. // check that we are dealing with a persisted key version we
  579. // understand.
  580. //
  581. if( ((DWORD*)g_pbW2KPreferredKey)[0] != BACKUPKEY_VERSION_W2K)
  582. goto cleanup;
  583. //
  584. // derive symetric key via HMAC from preferred backup key and
  585. // random R2.
  586. //
  587. if(!FMyPrimitiveHMACParam(
  588. (LPBYTE)g_pbW2KPreferredKey + sizeof(DWORD),
  589. g_cbW2KPreferredKey - sizeof(DWORD),
  590. pRecoveryBlob->R2,
  591. BACKUPKEY_R2_LEN,
  592. rgbSymKey
  593. ))
  594. goto cleanup;
  595. //
  596. // derive MAC key via HMAC from preferred backup key and
  597. // random R3.
  598. //
  599. if(!FMyPrimitiveHMACParam(
  600. (LPBYTE)g_pbW2KPreferredKey + sizeof(DWORD),
  601. g_cbW2KPreferredKey - sizeof(DWORD),
  602. pInnerBlob->R3,
  603. BACKUPKEY_R3_LEN,
  604. rgbMacKey // resultant MAC key
  605. ))
  606. goto cleanup;
  607. //
  608. // copy pSidUser and pDataIn following inner MAC'ish blob.
  609. //
  610. pbCipherBegin = (PBYTE)(pInnerBlob+1);
  611. CopyMemory( pbCipherBegin, pSidUser, cbSidUser );
  612. CopyMemory( pbCipherBegin+cbSidUser, pDataIn, cbDataIn );
  613. //
  614. // use MAC key to derive result from pSidUser and pDataIn
  615. //
  616. if(!FMyPrimitiveHMACParam(
  617. rgbMacKey,
  618. sizeof(rgbMacKey),
  619. pbCipherBegin,
  620. cbSidUser + cbDataIn,
  621. pInnerBlob->MAC // resultant MAC for verification.
  622. ))
  623. goto cleanup;
  624. //
  625. // adjust cipher start point to include R3 and MAC.
  626. //
  627. pbCipherBegin = (PBYTE)(pRecoveryBlob+1);
  628. //
  629. // initialize rc4 key
  630. //
  631. rc4_key(&sRC4Key, sizeof(rgbSymKey), rgbSymKey);
  632. //
  633. // encrypt data R3, MAC, pSidUser, pDataIn beyond recovery blob
  634. //
  635. rc4(&sRC4Key, pRecoveryBlob->cbCipherData, pbCipherBegin);
  636. fSuccess = TRUE;
  637. cleanup:
  638. RtlSecureZeroMemory( &sRC4Key, sizeof(sRC4Key) );
  639. RtlSecureZeroMemory( rgbSymKey, sizeof(rgbSymKey) );
  640. if(pSidUser)
  641. SSFree(pSidUser);
  642. if(!fSuccess) {
  643. if(*ppDataOut) {
  644. SSFree(*ppDataOut);
  645. *ppDataOut = NULL;
  646. }
  647. if( dwLastError == ERROR_SUCCESS )
  648. dwLastError = ERROR_INVALID_DATA;
  649. SetLastError( dwLastError );
  650. }
  651. return fSuccess;
  652. }
  653. BOOL
  654. RestoreFromRecoverableBlobW2K(
  655. IN HANDLE hToken,
  656. IN BYTE *pDataIn,
  657. IN DWORD cbDataIn,
  658. IN OUT BYTE **ppDataOut,
  659. IN OUT DWORD *pcbDataOut
  660. )
  661. {
  662. PSID pSidCandidate;
  663. DWORD cbSidCandidate;
  664. BOOL fIsMember;
  665. PBACKUPKEY_RECOVERY_BLOB_W2K pRecoveryBlob;
  666. PBACKUPKEY_INNER_BLOB_W2K pInnerBlob;
  667. PBYTE pbCipherBegin;
  668. BYTE rgbSymKey[A_SHA_DIGEST_LEN];
  669. BYTE rgbMacKey[A_SHA_DIGEST_LEN];
  670. BYTE rgbMacCandidate[A_SHA_DIGEST_LEN];
  671. RC4_KEYSTRUCT sRC4Key;
  672. PBYTE pbPersistedKey = NULL;
  673. DWORD cbPersistedKey = 0;
  674. BOOL fUsedPreferredKey = TRUE; // did we use preferred backup key?
  675. DWORD dwLastError = ERROR_SUCCESS;
  676. BOOL fSuccess = FALSE;
  677. if( pDataIn == NULL || cbDataIn == 0 ||
  678. ppDataOut == NULL || pcbDataOut == NULL ) {
  679. SetLastError(ERROR_INVALID_PARAMETER);
  680. return FALSE;
  681. }
  682. *ppDataOut = NULL;
  683. pRecoveryBlob = (PBACKUPKEY_RECOVERY_BLOB_W2K)pDataIn;
  684. //
  685. // check for invalid recovery blob version.
  686. // also check that input and output size fields aren't out of bounds
  687. // for a stream cipher (v1 blob).
  688. // TODO: further size validation against cbClearData and cbCipherData.
  689. //
  690. if(
  691. cbDataIn < (sizeof(BACKUPKEY_RECOVERY_BLOB_W2K) + sizeof(BACKUPKEY_INNER_BLOB_W2K)) ||
  692. pRecoveryBlob->dwVersion != BACKUPKEY_RECOVERY_BLOB_VERSION_W2K ||
  693. pRecoveryBlob->cbCipherData != (cbDataIn - sizeof(BACKUPKEY_RECOVERY_BLOB_W2K)) ||
  694. pRecoveryBlob->cbClearData > pRecoveryBlob->cbCipherData
  695. ) {
  696. SetLastError(ERROR_INVALID_PARAMETER);
  697. return FALSE;
  698. }
  699. //
  700. // determine if we use the preferred key, or some other key.
  701. // if the specified key is not the preferred one, fetch the
  702. // proper key.
  703. //
  704. if(memcmp(&g_guidW2KPreferredKey, &(pRecoveryBlob->guidKey), sizeof(GUID)) == 0) {
  705. pbPersistedKey = g_pbW2KPreferredKey;
  706. cbPersistedKey = g_cbW2KPreferredKey;
  707. fUsedPreferredKey = TRUE;
  708. } else {
  709. if(!GetBackupKey(
  710. &(pRecoveryBlob->guidKey),
  711. &pbPersistedKey,
  712. &cbPersistedKey,
  713. NULL,
  714. NULL
  715. ))
  716. goto cleanup;
  717. fUsedPreferredKey = FALSE;
  718. }
  719. //
  720. // check that we are dealing with a persisted key version we
  721. // understand.
  722. //
  723. if(((DWORD*)pbPersistedKey)[0] != BACKUPKEY_VERSION_W2K)
  724. goto cleanup;
  725. //
  726. // derive symetric key via HMAC from backup key and random R2.
  727. //
  728. if(!FMyPrimitiveHMACParam(
  729. (LPBYTE)pbPersistedKey + sizeof(DWORD),
  730. cbPersistedKey - sizeof(DWORD),
  731. pRecoveryBlob->R2,
  732. BACKUPKEY_R2_LEN,
  733. rgbSymKey
  734. ))
  735. goto cleanup;
  736. //
  737. // initialize rc4 key
  738. //
  739. rc4_key(&sRC4Key, sizeof(rgbSymKey), rgbSymKey);
  740. //
  741. // decrypt data R3, MAC, pSidUser, pDataIn beyond recovery blob
  742. //
  743. pbCipherBegin = (PBYTE)(pRecoveryBlob+1);
  744. rc4(&sRC4Key, pRecoveryBlob->cbCipherData, pbCipherBegin);
  745. pInnerBlob = (PBACKUPKEY_INNER_BLOB_W2K)(pRecoveryBlob+1);
  746. //
  747. // derive MAC key via HMAC from backup key and random R3.
  748. //
  749. if(!FMyPrimitiveHMACParam(
  750. (LPBYTE)pbPersistedKey + sizeof(DWORD),
  751. cbPersistedKey - sizeof(DWORD),
  752. pInnerBlob->R3,
  753. BACKUPKEY_R3_LEN,
  754. rgbMacKey // resultant MAC key
  755. ))
  756. goto cleanup;
  757. //
  758. // adjust pbCipherBegin to only include decrypted pUserSid, and pDataIn
  759. //
  760. pbCipherBegin = (PBYTE)(pInnerBlob+1);
  761. //
  762. // validate user Sid: compare client user to that embedded in
  763. // decrypted recovery blob.
  764. //
  765. pSidCandidate = (PSID)pbCipherBegin;
  766. if(!IsValidSid(pSidCandidate)) {
  767. dwLastError = ERROR_INVALID_SID;
  768. goto cleanup;
  769. }
  770. cbSidCandidate = GetLengthSid(pSidCandidate);
  771. //
  772. // use MAC key to derive result from pSidUser and pDataIn
  773. //
  774. if(!FMyPrimitiveHMACParam(
  775. rgbMacKey,
  776. sizeof(rgbMacKey),
  777. pbCipherBegin,
  778. pRecoveryBlob->cbCipherData - sizeof(BACKUPKEY_INNER_BLOB_W2K),
  779. rgbMacCandidate // resultant MAC for verification.
  780. ))
  781. goto cleanup;
  782. //
  783. // verify MAC equality
  784. //
  785. if(memcmp(pInnerBlob->MAC, rgbMacCandidate, A_SHA_DIGEST_LEN) != 0) {
  786. dwLastError = ERROR_INVALID_ACCESS;
  787. goto cleanup;
  788. }
  789. //
  790. // check if client passes accesscheck against embedded Sid.
  791. // TODO: see if we expand to check for ADMINS ?
  792. //
  793. if(!CheckTokenMembership( hToken, pSidCandidate, &fIsMember )) {
  794. dwLastError = GetLastError();
  795. goto cleanup;
  796. }
  797. if( !fIsMember ) {
  798. dwLastError = ERROR_INVALID_ACCESS;
  799. goto cleanup;
  800. }
  801. //
  802. // validation against cbClearData for good measure.
  803. //
  804. if( pRecoveryBlob->cbClearData != (cbDataIn -
  805. sizeof(BACKUPKEY_RECOVERY_BLOB_W2K) -
  806. sizeof(BACKUPKEY_INNER_BLOB_W2K) -
  807. cbSidCandidate)
  808. ) {
  809. dwLastError = ERROR_INVALID_PARAMETER;
  810. goto cleanup;
  811. }
  812. //
  813. // allocate buffer to contain results
  814. //
  815. *pcbDataOut = pRecoveryBlob->cbClearData;
  816. *ppDataOut = (LPBYTE)SSAlloc( *pcbDataOut );
  817. if(*ppDataOut == NULL) {
  818. *pcbDataOut = 0;
  819. dwLastError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  820. goto cleanup;
  821. }
  822. //
  823. // advance past decrypted Sid and copy results to caller.
  824. //
  825. CopyMemory(*ppDataOut, pbCipherBegin+cbSidCandidate, *pcbDataOut);
  826. fSuccess = TRUE;
  827. cleanup:
  828. RtlSecureZeroMemory( &sRC4Key, sizeof(sRC4Key) );
  829. RtlSecureZeroMemory( rgbSymKey, sizeof(rgbSymKey) );
  830. RtlSecureZeroMemory( pDataIn, cbDataIn );
  831. //
  832. // free the fetched key if it wasn't the preferred one.
  833. //
  834. if(!fUsedPreferredKey && pbPersistedKey) {
  835. RtlSecureZeroMemory(pbPersistedKey, cbPersistedKey);
  836. SSFree(pbPersistedKey);
  837. }
  838. if(!fSuccess) {
  839. if(*ppDataOut) {
  840. SSFree(*ppDataOut);
  841. *ppDataOut = NULL;
  842. }
  843. if( dwLastError == ERROR_SUCCESS )
  844. dwLastError = ERROR_INVALID_DATA;
  845. SetLastError( dwLastError );
  846. }
  847. return fSuccess;
  848. }
  849. BOOL
  850. RestoreFromRecoverableBlob(
  851. IN HANDLE hToken,
  852. IN BOOL fWin2kDataOut,
  853. IN BYTE * pDataIn,
  854. IN DWORD cbDataIn,
  855. IN OUT BYTE ** ppDataOut,
  856. IN OUT DWORD * pcbDataOut
  857. )
  858. {
  859. PSID pSidCandidate;
  860. DWORD cbSidCandidate;
  861. BOOL fIsMember;
  862. PBACKUPKEY_RECOVERY_BLOB pRecoveryBlob;
  863. PBACKUPKEY_KEY_BLOB pKeyBlob;
  864. PBACKUPKEY_INNER_BLOB pInnerBlob;
  865. DWORD cbKeyBlob = 0;
  866. PBYTE pbMasterKey = NULL;
  867. PBYTE pbPayloadKey = NULL;
  868. PBYTE pbPersistedKey = NULL;
  869. DWORD cbPersistedKey = 0;
  870. HCRYPTPROV hProv = NULL;
  871. HCRYPTKEY hKey = NULL;
  872. BYTE rgbPayloadMAC[A_SHA_DIGEST_LEN];
  873. BOOL fUsedPreferredKey = TRUE; // did we use preferred backup key?
  874. DWORD dwLastError = ERROR_SUCCESS;
  875. BOOL fSuccess = FALSE;
  876. if( pDataIn == NULL || cbDataIn == 0 ||
  877. ppDataOut == NULL || pcbDataOut == NULL ) {
  878. SetLastError(ERROR_INVALID_PARAMETER);
  879. return FALSE;
  880. }
  881. *ppDataOut = NULL;
  882. //
  883. // Make a copy of pDataIn, so we can decrypt the copy
  884. // and then destroy it
  885. //
  886. pRecoveryBlob = (PBACKUPKEY_RECOVERY_BLOB)SSAlloc(cbDataIn);
  887. if(NULL == pRecoveryBlob)
  888. {
  889. SetLastError(ERROR_NOT_ENOUGH_SERVER_MEMORY);
  890. return FALSE;
  891. }
  892. CopyMemory((PBYTE)pRecoveryBlob,
  893. pDataIn,
  894. cbDataIn);
  895. //
  896. // check for invalid recovery blob version.
  897. // also check that input and output size fields aren't out of bounds
  898. // for a stream cipher (v1 blob).
  899. // TODO: further size validation against cbClearData and cbCipherData.
  900. //
  901. if(
  902. (cbDataIn < sizeof(BACKUPKEY_RECOVERY_BLOB)) ||
  903. (cbDataIn < (sizeof(BACKUPKEY_RECOVERY_BLOB) + pRecoveryBlob->cbEncryptedMasterKey + pRecoveryBlob->cbEncryptedPayload)) ||
  904. (pRecoveryBlob->dwVersion != BACKUPKEY_RECOVERY_BLOB_VERSION)
  905. ) {
  906. SetLastError(ERROR_INVALID_PARAMETER);
  907. return FALSE;
  908. }
  909. //
  910. // determine if we use the preferred key, or some other key.
  911. // if the specified key is not the preferred one, fetch the
  912. // proper key.
  913. //
  914. if(memcmp(&g_guidPreferredKey, &(pRecoveryBlob->guidKey), sizeof(GUID)) == 0) {
  915. pbPersistedKey = g_pbPreferredKey;
  916. cbPersistedKey = g_cbPreferredKey;
  917. hProv = g_hProvPreferredKey;
  918. hKey = g_hKeyPreferredKey;
  919. fUsedPreferredKey = TRUE;
  920. } else {
  921. if(!GetBackupKey(
  922. &(pRecoveryBlob->guidKey),
  923. &pbPersistedKey,
  924. &cbPersistedKey,
  925. &hProv,
  926. &hKey
  927. ))
  928. {
  929. dwLastError = GetLastError();
  930. goto cleanup;
  931. }
  932. fUsedPreferredKey = FALSE;
  933. }
  934. //
  935. // check that we are dealing with a persisted key version we
  936. // understand.
  937. //
  938. if(((DWORD*)pbPersistedKey)[0] != BACKUPKEY_VERSION)
  939. {
  940. dwLastError = NTE_BAD_KEY;
  941. goto cleanup;
  942. }
  943. pKeyBlob = (PBACKUPKEY_KEY_BLOB)(pRecoveryBlob+1);
  944. pInnerBlob = (PBACKUPKEY_INNER_BLOB)((PBYTE)pKeyBlob + pRecoveryBlob->cbEncryptedMasterKey);
  945. cbKeyBlob = pRecoveryBlob->cbEncryptedMasterKey;
  946. //
  947. // Decrypt the master key and payload key
  948. //
  949. if(!CryptDecrypt(hKey,
  950. NULL,
  951. TRUE,
  952. 0, //CRYPT_OAEP,
  953. (PBYTE)pKeyBlob,
  954. &cbKeyBlob))
  955. {
  956. dwLastError = GetLastError();
  957. goto cleanup;
  958. }
  959. //
  960. // Use the payload key to decrypt the payload
  961. //
  962. if(pKeyBlob->cbPayloadKey != DES3_KEYSIZE + DES_BLOCKLEN)
  963. {
  964. dwLastError = ERROR_INVALID_DATA;
  965. goto cleanup;
  966. }
  967. pbMasterKey= (PBYTE)(pKeyBlob+1);
  968. pbPayloadKey = pbMasterKey + pKeyBlob->cbMasterKey;
  969. if(pRecoveryBlob->cbEncryptedPayload < A_SHA_DIGEST_LEN + sizeof(BACKUPKEY_INNER_BLOB))
  970. {
  971. dwLastError = ERROR_INVALID_DATA;
  972. goto cleanup;
  973. }
  974. {
  975. DES3TABLE s3DESKey;
  976. BYTE InputBlock[DES_BLOCKLEN];
  977. DWORD iBlock;
  978. DWORD cBlocks = pRecoveryBlob->cbEncryptedPayload/DES_BLOCKLEN;
  979. BYTE feedback[ DES_BLOCKLEN ];
  980. // initialize 3des key
  981. //
  982. if(cBlocks*DES_BLOCKLEN != pRecoveryBlob->cbEncryptedPayload)
  983. {
  984. // Master key must be a multiple of DES_BLOCKLEN
  985. goto cleanup;
  986. }
  987. tripledes3key(&s3DESKey, pbPayloadKey);
  988. //
  989. // IV is derived from the DES_BLOCKLEN bytes of the calculated
  990. // rgbSymKey, after the 3des key
  991. CopyMemory(feedback, pbPayloadKey + DES3_KEYSIZE, DES_BLOCKLEN);
  992. for(iBlock=0; iBlock < cBlocks; iBlock++)
  993. {
  994. CopyMemory(InputBlock,
  995. ((PBYTE)pInnerBlob)+iBlock*DES_BLOCKLEN,
  996. DES_BLOCKLEN);
  997. CBC(tripledes,
  998. DES_BLOCKLEN,
  999. ((PBYTE)pInnerBlob)+iBlock*DES_BLOCKLEN,
  1000. InputBlock,
  1001. &s3DESKey,
  1002. DECRYPT,
  1003. feedback);
  1004. }
  1005. }
  1006. //
  1007. // Check the MAC
  1008. //
  1009. // Generate the payload MAC
  1010. FMyPrimitiveSHA( (PBYTE)pInnerBlob,
  1011. pRecoveryBlob->cbEncryptedPayload - A_SHA_DIGEST_LEN,
  1012. rgbPayloadMAC);
  1013. if(0 != memcmp(rgbPayloadMAC,
  1014. (PBYTE)pInnerBlob + pRecoveryBlob->cbEncryptedPayload - A_SHA_DIGEST_LEN,
  1015. A_SHA_DIGEST_LEN))
  1016. {
  1017. dwLastError = ERROR_INVALID_DATA;
  1018. goto cleanup;
  1019. }
  1020. if(pInnerBlob->dwPayloadVersion != BACKUPKEY_PAYLOAD_VERSION)
  1021. {
  1022. dwLastError = ERROR_INVALID_DATA;
  1023. goto cleanup;
  1024. }
  1025. pSidCandidate = (PBYTE)(pInnerBlob+1) + pInnerBlob->cbLocalKey;
  1026. //
  1027. // validate user Sid: compare client user to that embedded in
  1028. // decrypted recovery blob.
  1029. //
  1030. if(!IsValidSid(pSidCandidate)) {
  1031. dwLastError = ERROR_INVALID_SID;
  1032. goto cleanup;
  1033. }
  1034. cbSidCandidate = GetLengthSid(pSidCandidate);
  1035. if(cbSidCandidate +
  1036. pInnerBlob->cbLocalKey +
  1037. sizeof(BACKUPKEY_INNER_BLOB) +
  1038. A_SHA_DIGEST_LEN > pRecoveryBlob->cbEncryptedPayload)
  1039. {
  1040. dwLastError = ERROR_INVALID_DATA;
  1041. goto cleanup;
  1042. }
  1043. //
  1044. // check if client passes accesscheck against embedded Sid.
  1045. // TODO: see if we expand to check for ADMINS ?
  1046. //
  1047. if(!CheckTokenMembership( hToken, pSidCandidate, &fIsMember )) {
  1048. dwLastError = GetLastError();
  1049. goto cleanup;
  1050. }
  1051. if( !fIsMember ) {
  1052. dwLastError = ERROR_INVALID_ACCESS;
  1053. goto cleanup;
  1054. }
  1055. if(fWin2kDataOut)
  1056. {
  1057. if(!ConvertRecoveredBlobToW2KBlob(
  1058. pbMasterKey,
  1059. pKeyBlob->cbMasterKey,
  1060. (PBYTE)(pInnerBlob+1),
  1061. pInnerBlob->cbLocalKey,
  1062. pSidCandidate,
  1063. ppDataOut,
  1064. pcbDataOut))
  1065. {
  1066. dwLastError = GetLastError();
  1067. goto cleanup;
  1068. }
  1069. }
  1070. else
  1071. {
  1072. *pcbDataOut = sizeof(DWORD) + pKeyBlob->cbMasterKey;
  1073. *ppDataOut = (LPBYTE)SSAlloc( *pcbDataOut );
  1074. if(*ppDataOut == NULL) {
  1075. *pcbDataOut = 0;
  1076. dwLastError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  1077. goto cleanup;
  1078. }
  1079. *((DWORD *)*ppDataOut) = MASTERKEY_BLOB_RAW_VERSION;
  1080. CopyMemory((*ppDataOut) + sizeof(DWORD), pbMasterKey, (*pcbDataOut) - sizeof(DWORD));
  1081. }
  1082. fSuccess = TRUE;
  1083. cleanup:
  1084. RtlSecureZeroMemory( pDataIn, cbDataIn );
  1085. //
  1086. // free the fetched key if it wasn't the preferred one.
  1087. //
  1088. if(!fUsedPreferredKey && pbPersistedKey) {
  1089. RtlSecureZeroMemory(pbPersistedKey, cbPersistedKey);
  1090. SSFree(pbPersistedKey);
  1091. if(hKey)
  1092. {
  1093. CryptDestroyKey(hKey);
  1094. }
  1095. if(hProv)
  1096. {
  1097. CryptReleaseContext(hProv, 0);
  1098. }
  1099. }
  1100. if(!fSuccess) {
  1101. if(*ppDataOut) {
  1102. SSFree(*ppDataOut);
  1103. *ppDataOut = NULL;
  1104. }
  1105. if( dwLastError == ERROR_SUCCESS )
  1106. dwLastError = ERROR_INVALID_DATA;
  1107. SetLastError( dwLastError );
  1108. }
  1109. if(pRecoveryBlob)
  1110. {
  1111. RtlSecureZeroMemory(pRecoveryBlob, cbDataIn);
  1112. SSFree(pRecoveryBlob);
  1113. }
  1114. return fSuccess;
  1115. }
  1116. RPC_STATUS
  1117. RPC_ENTRY
  1118. BackupCallback(
  1119. RPC_IF_HANDLE idIF,
  1120. PVOID pCtx)
  1121. {
  1122. RPC_STATUS Status;
  1123. PWSTR pBinding = NULL;
  1124. PWSTR pProtSeq = NULL;
  1125. Status = RpcBindingToStringBinding(pCtx, &pBinding);
  1126. if(Status != RPC_S_OK)
  1127. {
  1128. goto cleanup;
  1129. }
  1130. Status = RpcStringBindingParse(pBinding,
  1131. NULL,
  1132. &pProtSeq,
  1133. NULL,
  1134. NULL,
  1135. NULL);
  1136. if(Status != RPC_S_OK)
  1137. {
  1138. goto cleanup;
  1139. }
  1140. // Make sure caller is using a supported protocol
  1141. if((CompareString(LOCALE_INVARIANT,
  1142. NORM_IGNORECASE,
  1143. pProtSeq,
  1144. -1,
  1145. DPAPI_LOCAL_PROT_SEQ,
  1146. -1) != CSTR_EQUAL) &&
  1147. (CompareString(LOCALE_INVARIANT,
  1148. NORM_IGNORECASE,
  1149. pProtSeq,
  1150. -1,
  1151. DPAPI_BACKUP_PROT_SEQ,
  1152. -1) != CSTR_EQUAL) &&
  1153. (CompareString(LOCALE_INVARIANT,
  1154. NORM_IGNORECASE,
  1155. pProtSeq,
  1156. -1,
  1157. DPAPI_LEGACY_BACKUP_PROT_SEQ,
  1158. -1) != CSTR_EQUAL))
  1159. {
  1160. Status = ERROR_ACCESS_DENIED;
  1161. goto cleanup;
  1162. }
  1163. Status = RPC_S_OK;
  1164. cleanup:
  1165. if(pProtSeq)
  1166. {
  1167. RpcStringFree(&pProtSeq);
  1168. }
  1169. if(pBinding)
  1170. {
  1171. RpcStringFree(&pBinding);
  1172. }
  1173. return Status;
  1174. }
  1175. DWORD
  1176. InitializeBackupKeyServer(VOID)
  1177. {
  1178. RPC_STATUS Status;
  1179. LPWSTR pszPrincipalName = NULL;
  1180. //
  1181. // enable SNEGO authentication
  1182. //
  1183. Status = RpcServerInqDefaultPrincName(RPC_C_AUTHN_GSS_NEGOTIATE,
  1184. &pszPrincipalName);
  1185. if (RPC_S_OK != Status)
  1186. {
  1187. return Status;
  1188. }
  1189. SS_ASSERT(0 != wcslen(pszPrincipalName));
  1190. Status = RpcServerRegisterAuthInfoW(
  1191. pszPrincipalName,
  1192. RPC_C_AUTHN_GSS_NEGOTIATE,
  1193. 0,
  1194. 0
  1195. );
  1196. RpcStringFree(&pszPrincipalName);
  1197. pszPrincipalName = NULL;
  1198. if( Status )
  1199. {
  1200. return Status;
  1201. }
  1202. Status = RpcServerRegisterIfEx(s_BackupKey_v1_0_s_ifspec,
  1203. NULL,
  1204. NULL,
  1205. RPC_IF_AUTOLISTEN,
  1206. RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
  1207. BackupCallback);
  1208. if( Status )
  1209. {
  1210. return Status;
  1211. }
  1212. g_fBackupKeyServerStarted = TRUE;
  1213. return ERROR_SUCCESS;
  1214. }
  1215. DWORD
  1216. WINAPI
  1217. QueueInitBackupKeyServerThreadFunc(
  1218. IN LPVOID lpThreadArgument
  1219. )
  1220. {
  1221. RPC_STATUS Status;
  1222. ULONG i;
  1223. UNREFERENCED_PARAMETER(lpThreadArgument);
  1224. // Loop for 10 minutes, then give up.
  1225. for(i = 0; i < 40; i++)
  1226. {
  1227. // Sleep for 15 seconds.
  1228. Sleep(15000);
  1229. Status = InitializeBackupKeyServer();
  1230. if(Status == RPC_S_OK)
  1231. {
  1232. return RPC_S_OK;
  1233. }
  1234. }
  1235. DebugLog((DEB_ERROR, "InitializeBackupKeyServer failed forever: 0x%x\n", Status));
  1236. return Status;
  1237. }
  1238. DWORD
  1239. StartBackupKeyServer(
  1240. VOID
  1241. )
  1242. {
  1243. NTSTATUS Status;
  1244. //
  1245. // initialize critical section that prevents race condition for
  1246. // deferred intitialization activities.
  1247. //
  1248. Status = RtlInitializeCriticalSection( &g_csInitialization );
  1249. if(!NT_SUCCESS(Status))
  1250. {
  1251. return Status;
  1252. }
  1253. //
  1254. // if we aren't a domain controller, don't do anything.
  1255. //
  1256. if(!IsDomainController())
  1257. {
  1258. return ERROR_SUCCESS;
  1259. }
  1260. //
  1261. // Initialize domain recovery rpc endpoints.
  1262. //
  1263. Status = InitializeBackupKeyServer();
  1264. if(Status != RPC_S_OK)
  1265. {
  1266. DebugLog((DEB_WARN, "InitializeBackupKeyServer failed on first attempt: 0x%x\n", Status));
  1267. // First attempt failed, so queue up a worker thread to retry this operation periodically.
  1268. if(!QueueUserWorkItem(
  1269. QueueInitBackupKeyServerThreadFunc,
  1270. NULL,
  1271. WT_EXECUTELONGFUNCTION))
  1272. {
  1273. Status = GetLastError();
  1274. DebugLog((DEB_ERROR, "Unable to start InitializeBackupKeyServer worker thread: 0x%x\n", Status));
  1275. return Status;
  1276. }
  1277. }
  1278. return ERROR_SUCCESS;
  1279. }
  1280. DWORD
  1281. StopBackupKeyServer(
  1282. VOID
  1283. )
  1284. {
  1285. RPC_STATUS status;
  1286. RtlDeleteCriticalSection( &g_csInitialization );
  1287. //
  1288. // only do something if the server started.
  1289. //
  1290. if(!g_fBackupKeyServerStarted)
  1291. return ERROR_SUCCESS;
  1292. status = RpcServerUnregisterIf(s_BackupKey_v1_0_s_ifspec, 0, 0);
  1293. FreePreferredBackupKey();
  1294. FreeSystemCredentials();
  1295. g_fBackupKeyServerStarted = FALSE;
  1296. return status;
  1297. }
  1298. BOOL
  1299. GetBackupKey(
  1300. IN GUID *pguidKey,
  1301. OUT PBYTE *ppbKey,
  1302. OUT DWORD *pcbKey,
  1303. OUT HCRYPTPROV *phCryptProv,
  1304. OUT HCRYPTKEY *phCryptKey
  1305. )
  1306. {
  1307. LSA_HANDLE PolicyHandle;
  1308. UNICODE_STRING SecretKeyName;
  1309. UNICODE_STRING *pSecretData;
  1310. WCHAR wszKeyGuid[ (sizeof(BACKUPKEY_NAME_PREFIX) / sizeof(WCHAR)) + MAX_GUID_SZ_CHARS ];
  1311. NTSTATUS Status;
  1312. BOOL fSuccess;
  1313. if(pguidKey == NULL || ppbKey == NULL || pcbKey == NULL)
  1314. return FALSE;
  1315. //
  1316. // setup the UNICODE_STRINGs for the call.
  1317. //
  1318. CopyMemory(wszKeyGuid, BACKUPKEY_NAME_PREFIX, sizeof(BACKUPKEY_NAME_PREFIX));
  1319. if(MyGuidToStringW(pguidKey,
  1320. (LPWSTR)( (LPBYTE)wszKeyGuid + sizeof(BACKUPKEY_NAME_PREFIX) - sizeof(WCHAR) )
  1321. ) != 0) return FALSE;
  1322. InitLsaString(&SecretKeyName, wszKeyGuid);
  1323. Status = OpenPolicy(NULL, POLICY_GET_PRIVATE_INFORMATION, &PolicyHandle);
  1324. if(!NT_SUCCESS(Status))
  1325. return FALSE;
  1326. Status = LsaRetrievePrivateData(
  1327. PolicyHandle,
  1328. &SecretKeyName,
  1329. &pSecretData
  1330. );
  1331. LsaClose(PolicyHandle);
  1332. if(!NT_SUCCESS(Status) || pSecretData == NULL)
  1333. return FALSE;
  1334. *ppbKey = (LPBYTE)SSAlloc( pSecretData->Length );
  1335. if(*ppbKey) {
  1336. *pcbKey = pSecretData->Length;
  1337. CopyMemory( *ppbKey, pSecretData->Buffer, pSecretData->Length );
  1338. fSuccess = TRUE;
  1339. } else {
  1340. fSuccess = FALSE;
  1341. }
  1342. if(fSuccess && (NULL != phCryptProv))
  1343. {
  1344. if((*pcbKey >= sizeof(DWORD)) && // prefix bug 170438
  1345. (*((DWORD *)*ppbKey) == BACKUPKEY_VERSION))
  1346. {
  1347. if(!CryptAcquireContext(phCryptProv,
  1348. NULL,
  1349. NULL,
  1350. PROV_RSA_FULL,
  1351. CRYPT_VERIFYCONTEXT))
  1352. {
  1353. fSuccess = FALSE;
  1354. }
  1355. else
  1356. {
  1357. if(phCryptKey)
  1358. {
  1359. if(!CryptImportKey(*phCryptProv,
  1360. (*ppbKey) + 3*sizeof(DWORD),
  1361. ((DWORD *)*ppbKey)[1],
  1362. NULL,
  1363. 0,
  1364. phCryptKey))
  1365. {
  1366. fSuccess = FALSE;
  1367. CryptReleaseContext(*phCryptProv, 0);
  1368. *phCryptProv = NULL;
  1369. }
  1370. }
  1371. }
  1372. }
  1373. else
  1374. {
  1375. *phCryptProv = NULL;
  1376. }
  1377. }
  1378. RtlSecureZeroMemory( pSecretData->Buffer, pSecretData->Length );
  1379. LsaFreeMemory( pSecretData );
  1380. return fSuccess;
  1381. }
  1382. BOOL
  1383. CreateBackupKeyW2K(
  1384. IN OUT GUID *pguidKey,
  1385. OUT PBYTE *ppbKey,
  1386. OUT DWORD *pcbKey
  1387. )
  1388. /*++
  1389. This routine creates a new backup key and an identifier for that key.
  1390. The key is then stored as an global LSA secret.
  1391. --*/
  1392. {
  1393. DWORD RpcStatus;
  1394. BOOL fSuccess = FALSE;;
  1395. if(pguidKey == NULL || ppbKey == NULL || pcbKey == NULL)
  1396. return FALSE;
  1397. *ppbKey = NULL;
  1398. //
  1399. // generate new Guid representing key
  1400. //
  1401. RpcStatus = UuidCreate( pguidKey );
  1402. if( RpcStatus != RPC_S_OK && RpcStatus != RPC_S_UUID_LOCAL_ONLY )
  1403. return FALSE;
  1404. *pcbKey = BACKUPKEY_MATERIAL_SIZE + sizeof(DWORD);
  1405. *ppbKey = (LPBYTE)SSAlloc( *pcbKey );
  1406. if(*ppbKey == NULL)
  1407. return FALSE;
  1408. //
  1409. // generate random key material.
  1410. //
  1411. fSuccess = RtlGenRandom(*ppbKey, *pcbKey);
  1412. if(fSuccess) {
  1413. //
  1414. // version the key material.
  1415. //
  1416. ((DWORD *)*ppbKey)[0] = BACKUPKEY_VERSION_W2K;
  1417. fSuccess = SaveBackupKey(pguidKey, *ppbKey, *pcbKey);
  1418. } else {
  1419. SSFree( *ppbKey );
  1420. *ppbKey = NULL;
  1421. }
  1422. return fSuccess;
  1423. }
  1424. BOOL
  1425. CreateBackupKey(
  1426. IN OUT GUID *pguidKey,
  1427. OUT PBYTE *ppbKey,
  1428. OUT DWORD *pcbKey,
  1429. OUT HCRYPTPROV *phCryptProv,
  1430. OUT HCRYPTKEY *phCryptKey
  1431. )
  1432. /*++
  1433. This routine creates a new backup key and an identifier for that key.
  1434. The key is then stored as an global LSA secret.
  1435. --*/
  1436. {
  1437. DWORD RpcStatus;
  1438. BOOL fSuccess = FALSE;;
  1439. HCRYPTPROV hCryptProv = NULL;
  1440. HCRYPTKEY hCryptKey = NULL;
  1441. DWORD dwDefaultKeySize = 2048;
  1442. PBYTE pbPublicExportData = NULL;
  1443. if(pguidKey == NULL || ppbKey == NULL || pcbKey == NULL)
  1444. return FALSE;
  1445. *ppbKey = NULL;
  1446. //
  1447. // generate new Guid representing key
  1448. //
  1449. RpcStatus = UuidCreate( pguidKey );
  1450. if( RpcStatus != RPC_S_OK && RpcStatus != RPC_S_UUID_LOCAL_ONLY )
  1451. return FALSE;
  1452. DWORD cbPrivateExportLength = 0;
  1453. DWORD cbPublicExportLength = 0;
  1454. if(!CryptAcquireContext(&hCryptProv,
  1455. NULL,
  1456. NULL,
  1457. PROV_RSA_FULL,
  1458. CRYPT_VERIFYCONTEXT))
  1459. {
  1460. goto error;
  1461. }
  1462. if(!CryptGenKey(hCryptProv,
  1463. AT_KEYEXCHANGE,
  1464. CRYPT_EXPORTABLE | dwDefaultKeySize << 16, // 2048 bit
  1465. &hCryptKey))
  1466. {
  1467. goto error;
  1468. }
  1469. //
  1470. // Get the private key size
  1471. //
  1472. if(!CryptExportKey(hCryptKey,
  1473. NULL,
  1474. PRIVATEKEYBLOB,
  1475. 0,
  1476. NULL,
  1477. &cbPrivateExportLength))
  1478. {
  1479. goto error;
  1480. }
  1481. if(!GeneratePublicKeyCert(hCryptProv,
  1482. hCryptKey,
  1483. pguidKey,
  1484. &cbPublicExportLength,
  1485. &pbPublicExportData))
  1486. {
  1487. goto error;
  1488. }
  1489. *pcbKey = sizeof(DWORD) + // version
  1490. sizeof(DWORD) + // cbPrivateExportLength
  1491. sizeof(DWORD) + // cbPublicExportLength
  1492. cbPrivateExportLength +
  1493. cbPublicExportLength;
  1494. *ppbKey = (LPBYTE)SSAlloc( *pcbKey );
  1495. if(*ppbKey == NULL)
  1496. goto error;
  1497. ((DWORD *)*ppbKey)[0] = BACKUPKEY_VERSION;
  1498. ((DWORD *)*ppbKey)[1] = cbPrivateExportLength;
  1499. ((DWORD *)*ppbKey)[2] = cbPublicExportLength;
  1500. if(!CryptExportKey(hCryptKey,
  1501. NULL,
  1502. PRIVATEKEYBLOB,
  1503. 0,
  1504. (*ppbKey) + 3*sizeof(DWORD),
  1505. &cbPrivateExportLength))
  1506. {
  1507. goto error;
  1508. }
  1509. CopyMemory((*ppbKey) + 3*sizeof(DWORD) + cbPrivateExportLength,
  1510. pbPublicExportData,
  1511. cbPublicExportLength);
  1512. *phCryptProv = hCryptProv;
  1513. hCryptProv = NULL;
  1514. *phCryptKey = hCryptKey;
  1515. hCryptKey = NULL;
  1516. fSuccess = SaveBackupKey(pguidKey, *ppbKey, *pcbKey);
  1517. error:
  1518. if(hCryptKey)
  1519. {
  1520. }
  1521. if(hCryptProv)
  1522. {
  1523. CryptReleaseContext(hCryptProv,
  1524. 0);
  1525. }
  1526. if(pbPublicExportData)
  1527. {
  1528. SSFree(pbPublicExportData);
  1529. }
  1530. return fSuccess;
  1531. }
  1532. BOOL
  1533. SaveBackupKey(
  1534. IN GUID *pguidKey,
  1535. IN BYTE *pbKey,
  1536. IN DWORD cbKey // size of pbKey material, not greater than 0xffff
  1537. )
  1538. /*++
  1539. Persist the specified key to a global LSA secret.
  1540. --*/
  1541. {
  1542. LSA_HANDLE PolicyHandle;
  1543. UNICODE_STRING SecretKeyName;
  1544. UNICODE_STRING SecretData;
  1545. WCHAR wszKeyGuid[ (sizeof(BACKUPKEY_NAME_PREFIX) / sizeof(WCHAR)) + MAX_GUID_SZ_CHARS ];
  1546. NTSTATUS Status;
  1547. if(pguidKey == NULL || pbKey == NULL || cbKey > 0xffff)
  1548. return FALSE;
  1549. //
  1550. // setup the UNICODE_STRINGs for the call.
  1551. //
  1552. CopyMemory(wszKeyGuid, BACKUPKEY_NAME_PREFIX, sizeof(BACKUPKEY_NAME_PREFIX));
  1553. if(MyGuidToStringW(pguidKey,
  1554. (LPWSTR)( (LPBYTE)wszKeyGuid + sizeof(BACKUPKEY_NAME_PREFIX) - sizeof(WCHAR) )
  1555. ) != 0) return FALSE;
  1556. InitLsaString(&SecretKeyName, wszKeyGuid);
  1557. SecretData.Buffer = (LPWSTR)pbKey;
  1558. SecretData.Length = (USHORT)cbKey;
  1559. SecretData.MaximumLength = (USHORT)cbKey;
  1560. Status = OpenPolicy(NULL, POLICY_CREATE_SECRET, &PolicyHandle);
  1561. if(!NT_SUCCESS(Status))
  1562. return FALSE;
  1563. Status = LsaStorePrivateData(
  1564. PolicyHandle,
  1565. &SecretKeyName,
  1566. &SecretData
  1567. );
  1568. LsaClose(PolicyHandle);
  1569. if(!NT_SUCCESS(Status))
  1570. return FALSE;
  1571. return TRUE;
  1572. }
  1573. BOOL
  1574. DestroyBackupKey(
  1575. IN GUID guidKey
  1576. )
  1577. {
  1578. //
  1579. // Delete the LSA secret containing the specified key.
  1580. //
  1581. return FALSE;
  1582. }
  1583. BOOL
  1584. SetupPreferredBackupKeys(
  1585. VOID
  1586. )
  1587. {
  1588. static BOOL fSetupStatus = FALSE;
  1589. BOOL fLocalStatus = FALSE;
  1590. if( g_fSetupPreferredAttempted )
  1591. return fSetupStatus;
  1592. RtlEnterCriticalSection( &g_csInitialization );
  1593. if( !g_fSetupPreferredAttempted ) {
  1594. //
  1595. // Wait on LSA/SAM to be available.
  1596. //
  1597. fSetupStatus = WaitOnSAMDatabase();
  1598. if( fSetupStatus ) {
  1599. fLocalStatus = FALSE;
  1600. //
  1601. // get the preferred backup key.
  1602. // TODO: if this fails (unlikely), we should probably log an event!
  1603. // check outcome of StartBackupKeyServer() in the main service code
  1604. //
  1605. //
  1606. // Get the legacy backup key
  1607. //
  1608. if(GetPreferredBackupKeyGuid(BACKUPKEY_VERSION_W2K, &g_guidW2KPreferredKey)) {
  1609. //
  1610. // now, pickup the specified key
  1611. //
  1612. fLocalStatus = GetBackupKey(&g_guidW2KPreferredKey,
  1613. &g_pbW2KPreferredKey,
  1614. &g_cbW2KPreferredKey,
  1615. NULL,
  1616. NULL);
  1617. }
  1618. if(!fLocalStatus)
  1619. {
  1620. //
  1621. // no preferred backup key specified, or we couldn't read one
  1622. // create a new one.
  1623. //
  1624. if(CreateBackupKeyW2K(&g_guidW2KPreferredKey,
  1625. &g_pbW2KPreferredKey,
  1626. &g_cbW2KPreferredKey))
  1627. fLocalStatus = SetPreferredBackupKeyGuid(BACKUPKEY_VERSION_W2K,
  1628. &g_guidW2KPreferredKey);
  1629. else
  1630. fLocalStatus = FALSE;
  1631. }
  1632. fSetupStatus = fLocalStatus;
  1633. fLocalStatus = FALSE;
  1634. //
  1635. // Get the current backup key
  1636. //
  1637. if(GetPreferredBackupKeyGuid(BACKUPKEY_VERSION, &g_guidPreferredKey)) {
  1638. //
  1639. // now, pickup the specified key
  1640. //
  1641. fLocalStatus = GetBackupKey(&g_guidPreferredKey,
  1642. &g_pbPreferredKey,
  1643. &g_cbPreferredKey,
  1644. &g_hProvPreferredKey,
  1645. &g_hKeyPreferredKey);
  1646. }
  1647. if(!fLocalStatus)
  1648. {
  1649. //
  1650. // no preferred backup key specified. create one and specify it
  1651. // as being the preferred one.
  1652. //
  1653. if(CreateBackupKey(&g_guidPreferredKey,
  1654. &g_pbPreferredKey,
  1655. &g_cbPreferredKey,
  1656. &g_hProvPreferredKey,
  1657. &g_hKeyPreferredKey))
  1658. fLocalStatus = SetPreferredBackupKeyGuid(BACKUPKEY_VERSION,
  1659. &g_guidPreferredKey);
  1660. else
  1661. fLocalStatus = FALSE;
  1662. }
  1663. }
  1664. if(!fLocalStatus)
  1665. {
  1666. fSetupStatus = FALSE;
  1667. }
  1668. g_fSetupPreferredAttempted = TRUE;
  1669. }
  1670. RtlLeaveCriticalSection( &g_csInitialization );
  1671. return fSetupStatus;
  1672. }
  1673. BOOL
  1674. GetPreferredBackupKeyGuid(
  1675. IN DWORD dwVersion,
  1676. IN OUT GUID *pguidKey
  1677. )
  1678. /*++
  1679. Get the GUID value associated with the key which has been set to be preferred.
  1680. The return value is TRUE, if successful. The GUID value is copied into the
  1681. buffer specified by the pguidKey parameter.
  1682. The return value is FALSE on failure; if the GUID does not exist, or the
  1683. data could not be retrieved, for instance.
  1684. --*/
  1685. {
  1686. LSA_HANDLE PolicyHandle;
  1687. UNICODE_STRING SecretKeyName;
  1688. UNICODE_STRING *pSecretData;
  1689. USHORT cbData;
  1690. NTSTATUS Status;
  1691. BOOL fSuccess;
  1692. if(pguidKey == NULL)
  1693. return FALSE;
  1694. //
  1695. // setup the UNICODE_STRINGs for the call.
  1696. //
  1697. InitLsaString(&SecretKeyName,
  1698. (dwVersion == BACKUPKEY_VERSION_W2K)?BACKUPKEY_PREFERRED_W2K:BACKUPKEY_PREFERRED);
  1699. Status = OpenPolicy(NULL, POLICY_GET_PRIVATE_INFORMATION, &PolicyHandle);
  1700. if(!NT_SUCCESS(Status))
  1701. return FALSE;
  1702. Status = LsaRetrievePrivateData(
  1703. PolicyHandle,
  1704. &SecretKeyName,
  1705. &pSecretData
  1706. );
  1707. LsaClose(PolicyHandle);
  1708. if(!NT_SUCCESS(Status) || pSecretData == NULL)
  1709. return FALSE;
  1710. if(pSecretData->Length == sizeof(GUID)) {
  1711. CopyMemory(pguidKey, pSecretData->Buffer, sizeof(GUID));
  1712. fSuccess = TRUE;
  1713. } else {
  1714. fSuccess = FALSE;
  1715. }
  1716. RtlSecureZeroMemory(pSecretData->Buffer, pSecretData->Length);
  1717. LsaFreeMemory(pSecretData);
  1718. return fSuccess;
  1719. }
  1720. BOOL
  1721. SetPreferredBackupKeyGuid(
  1722. IN DWORD dwVersion,
  1723. IN GUID *pguidKey
  1724. )
  1725. /*++
  1726. Sets the specified GUID as being the preferred backup key, by reference
  1727. from the GUID to key mapping.
  1728. --*/
  1729. {
  1730. LSA_HANDLE PolicyHandle;
  1731. UNICODE_STRING SecretKeyName;
  1732. UNICODE_STRING SecretData;
  1733. NTSTATUS Status;
  1734. if(pguidKey == NULL)
  1735. return FALSE;
  1736. //
  1737. // setup the UNICODE_STRINGs for the call.
  1738. //
  1739. InitLsaString(&SecretKeyName,
  1740. (dwVersion == BACKUPKEY_VERSION_W2K)?BACKUPKEY_PREFERRED_W2K:BACKUPKEY_PREFERRED);
  1741. SecretData.Buffer = (LPWSTR)pguidKey;
  1742. SecretData.Length = sizeof(GUID);
  1743. SecretData.MaximumLength = sizeof(GUID);
  1744. Status = OpenPolicy(NULL, POLICY_CREATE_SECRET, &PolicyHandle);
  1745. if(!NT_SUCCESS(Status))
  1746. return FALSE;
  1747. Status = LsaStorePrivateData(
  1748. PolicyHandle,
  1749. &SecretKeyName,
  1750. &SecretData
  1751. );
  1752. LsaClose(PolicyHandle);
  1753. if(!NT_SUCCESS(Status))
  1754. return FALSE;
  1755. return TRUE;
  1756. }
  1757. BOOL
  1758. FreePreferredBackupKey(
  1759. VOID
  1760. )
  1761. {
  1762. g_fSetupPreferredAttempted = FALSE;
  1763. //
  1764. // free allocated key pair.
  1765. //
  1766. if(g_pbPreferredKey) {
  1767. RtlSecureZeroMemory(g_pbPreferredKey, g_cbPreferredKey);
  1768. SSFree(g_pbPreferredKey);
  1769. g_pbPreferredKey = NULL;
  1770. }
  1771. return TRUE;
  1772. }
  1773. NTSTATUS
  1774. OpenPolicy(
  1775. LPWSTR ServerName,
  1776. DWORD DesiredAccess,
  1777. PLSA_HANDLE PolicyHandle
  1778. )
  1779. {
  1780. LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  1781. LSA_UNICODE_STRING ServerString;
  1782. PLSA_UNICODE_STRING Server;
  1783. //
  1784. // Always initialize the object attributes to all zeroes.
  1785. //
  1786. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
  1787. if (ServerName != NULL) {
  1788. //
  1789. // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
  1790. //
  1791. InitLsaString(&ServerString, ServerName);
  1792. Server = &ServerString;
  1793. } else {
  1794. Server = NULL;
  1795. }
  1796. //
  1797. // Attempt to open the policy.
  1798. //
  1799. return LsaOpenPolicy(
  1800. Server,
  1801. &ObjectAttributes,
  1802. DesiredAccess,
  1803. PolicyHandle
  1804. );
  1805. }
  1806. BOOL
  1807. WaitOnSAMDatabase(
  1808. VOID
  1809. )
  1810. {
  1811. NTSTATUS Status;
  1812. LSA_UNICODE_STRING EventName;
  1813. OBJECT_ATTRIBUTES EventAttributes;
  1814. HANDLE hEvent;
  1815. BOOL fSuccess = FALSE;
  1816. InitLsaString( &EventName, L"\\SAM_SERVICE_STARTED" );
  1817. InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
  1818. Status = NtOpenEvent( &hEvent,
  1819. SYNCHRONIZE|EVENT_MODIFY_STATE,
  1820. &EventAttributes );
  1821. if ( !NT_SUCCESS(Status))
  1822. {
  1823. if( Status == STATUS_OBJECT_NAME_NOT_FOUND )
  1824. {
  1825. //
  1826. // SAM hasn't created this event yet, let us create it now.
  1827. // SAM opens this event to set it.
  1828. //
  1829. Status = NtCreateEvent(
  1830. &hEvent,
  1831. SYNCHRONIZE|EVENT_MODIFY_STATE,
  1832. &EventAttributes,
  1833. NotificationEvent,
  1834. FALSE // The event is initially not signaled
  1835. );
  1836. if( Status == STATUS_OBJECT_NAME_EXISTS ||
  1837. Status == STATUS_OBJECT_NAME_COLLISION )
  1838. {
  1839. //
  1840. // second chance, if the SAM created the event before we
  1841. // do.
  1842. //
  1843. Status = NtOpenEvent( &hEvent,
  1844. SYNCHRONIZE|EVENT_MODIFY_STATE,
  1845. &EventAttributes );
  1846. }
  1847. }
  1848. if ( !NT_SUCCESS(Status))
  1849. {
  1850. //
  1851. // could not make the event handle
  1852. //
  1853. return FALSE;
  1854. }
  1855. }
  1856. if( WAIT_OBJECT_0 == WaitForSingleObject( hEvent, INFINITE ) )
  1857. fSuccess = TRUE;
  1858. CloseHandle( hEvent );
  1859. return fSuccess;
  1860. }
  1861. DWORD
  1862. GetSystemCredential(
  1863. IN BOOL fLocalMachine,
  1864. IN OUT BYTE rgbCredential[ A_SHA_DIGEST_LEN ]
  1865. )
  1866. /*++
  1867. This routines returns the credential associated with the SYSTEM account
  1868. based on the fLocalMachine parameter.
  1869. If fLocalMachine is TRUE, the credential returned is suitable for use
  1870. at the LOCAL_MACHINE storage disposition.
  1871. Otherwise, the credential returned is suitable for use when the calling
  1872. user security context is the Local System account.
  1873. --*/
  1874. {
  1875. PBYTE Credential;
  1876. if(!g_fSystemCredsInitialized) {
  1877. DWORD dwLastError;
  1878. RtlEnterCriticalSection( &g_csInitialization );
  1879. if(!g_fSystemCredsInitialized) {
  1880. dwLastError = QuerySystemCredentials( g_rgbSystemCredMachine, g_rgbSystemCredUser );
  1881. if( dwLastError == ERROR_FILE_NOT_FOUND ) {
  1882. if( CreateSystemCredentials() )
  1883. dwLastError = QuerySystemCredentials( g_rgbSystemCredMachine, g_rgbSystemCredUser );
  1884. }
  1885. if( dwLastError == ERROR_SUCCESS )
  1886. g_fSystemCredsInitialized = TRUE;
  1887. } else {
  1888. dwLastError = ERROR_SUCCESS;
  1889. }
  1890. RtlLeaveCriticalSection( &g_csInitialization );
  1891. if( dwLastError != ERROR_SUCCESS )
  1892. return dwLastError;
  1893. }
  1894. if( fLocalMachine )
  1895. Credential = g_rgbSystemCredMachine;
  1896. else
  1897. Credential = g_rgbSystemCredUser;
  1898. CopyMemory( rgbCredential, Credential, A_SHA_DIGEST_LEN );
  1899. return ERROR_SUCCESS;
  1900. }
  1901. BOOL
  1902. UpdateSystemCredentials(
  1903. VOID
  1904. )
  1905. {
  1906. BOOL fSuccess;
  1907. RtlEnterCriticalSection( &g_csInitialization );
  1908. g_fSystemCredsInitialized = FALSE;
  1909. fSuccess = CreateSystemCredentials();
  1910. RtlLeaveCriticalSection( &g_csInitialization );
  1911. return fSuccess;
  1912. }
  1913. BOOL
  1914. CreateSystemCredentials(
  1915. VOID
  1916. )
  1917. {
  1918. SYSTEM_CREDENTIALS SystemCredentials;
  1919. LSA_HANDLE PolicyHandle;
  1920. UNICODE_STRING SecretKeyName;
  1921. UNICODE_STRING SecretData;
  1922. NTSTATUS Status;
  1923. //
  1924. // create random key material.
  1925. //
  1926. if(!RtlGenRandom( (PBYTE)&SystemCredentials, sizeof(SystemCredentials) ))
  1927. {
  1928. return FALSE;
  1929. }
  1930. SystemCredentials.dwVersion = SYSTEM_CREDENTIALS_VERSION;
  1931. //
  1932. // setup the UNICODE_STRINGs for the call.
  1933. //
  1934. InitLsaString(&SecretKeyName, SYSTEM_CREDENTIALS_SECRET);
  1935. SecretData.Buffer = (LPWSTR)&SystemCredentials;
  1936. SecretData.Length = sizeof( SystemCredentials );
  1937. SecretData.MaximumLength = sizeof( SystemCredentials );
  1938. Status = OpenPolicy(NULL, POLICY_CREATE_SECRET, &PolicyHandle);
  1939. if(!NT_SUCCESS(Status))
  1940. return FALSE;
  1941. Status = LsaStorePrivateData(
  1942. PolicyHandle,
  1943. &SecretKeyName,
  1944. &SecretData
  1945. );
  1946. LsaClose(PolicyHandle);
  1947. if(!NT_SUCCESS(Status))
  1948. return FALSE;
  1949. return TRUE;
  1950. }
  1951. DWORD
  1952. QuerySystemCredentials(
  1953. IN OUT BYTE rgbSystemCredMachine[ A_SHA_DIGEST_LEN ],
  1954. IN OUT BYTE rgbSystemCredUser [ A_SHA_DIGEST_LEN ]
  1955. )
  1956. {
  1957. LSA_HANDLE PolicyHandle;
  1958. UNICODE_STRING SecretKeyName;
  1959. UNICODE_STRING *pSecretData;
  1960. USHORT cbData;
  1961. NTSTATUS Status;
  1962. DWORD dwLastError = ERROR_INVALID_PARAMETER;
  1963. BOOL fSuccess;
  1964. if( !WaitOnSAMDatabase() )
  1965. return WAIT_TIMEOUT;
  1966. //
  1967. // setup the UNICODE_STRINGs for the call.
  1968. //
  1969. InitLsaString(&SecretKeyName, SYSTEM_CREDENTIALS_SECRET);
  1970. Status = OpenPolicy(NULL, POLICY_GET_PRIVATE_INFORMATION, &PolicyHandle);
  1971. if(!NT_SUCCESS(Status))
  1972. return dwLastError;
  1973. Status = LsaRetrievePrivateData(
  1974. PolicyHandle,
  1975. &SecretKeyName,
  1976. &pSecretData
  1977. );
  1978. LsaClose(PolicyHandle);
  1979. if(Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1980. return ERROR_FILE_NOT_FOUND;
  1981. if(!NT_SUCCESS(Status) || pSecretData == NULL)
  1982. return dwLastError;
  1983. if( pSecretData->Length == 0)
  1984. return ERROR_FILE_NOT_FOUND;
  1985. if( pSecretData->Length == sizeof(SYSTEM_CREDENTIALS) ) {
  1986. PSYSTEM_CREDENTIALS pSystemCredentials = (PSYSTEM_CREDENTIALS)pSecretData->Buffer;
  1987. if( pSystemCredentials->dwVersion == SYSTEM_CREDENTIALS_VERSION ) {
  1988. CopyMemory( rgbSystemCredMachine, pSystemCredentials->rgbSystemCredMachine, A_SHA_DIGEST_LEN );
  1989. CopyMemory( rgbSystemCredUser, pSystemCredentials->rgbSystemCredUser, A_SHA_DIGEST_LEN );
  1990. dwLastError = ERROR_SUCCESS;
  1991. }
  1992. }
  1993. RtlSecureZeroMemory(pSecretData->Buffer, pSecretData->Length);
  1994. LsaFreeMemory(pSecretData);
  1995. return dwLastError;
  1996. }
  1997. BOOL
  1998. FreeSystemCredentials(
  1999. VOID
  2000. )
  2001. {
  2002. RtlSecureZeroMemory( g_rgbSystemCredMachine, sizeof(g_rgbSystemCredMachine) );
  2003. RtlSecureZeroMemory( g_rgbSystemCredUser, sizeof(g_rgbSystemCredUser) );
  2004. g_fSystemCredsInitialized = FALSE;
  2005. return TRUE;
  2006. }
  2007. BOOL GeneratePublicKeyCert(HCRYPTPROV hCryptProv,
  2008. HCRYPTKEY hCryptKey,
  2009. GUID *pguidKey,
  2010. DWORD *pcbPublicExportLength,
  2011. PBYTE *ppbPublicExportData)
  2012. {
  2013. BOOL fRet = FALSE;
  2014. CERT_INFO CertInfo;
  2015. CERT_PUBLIC_KEY_INFO *pKeyInfo = NULL;
  2016. DWORD cbKeyInfo = 0;
  2017. CERT_NAME_BLOB CertName;
  2018. CERT_RDN_ATTR RDNAttributes[1];
  2019. CERT_RDN CertRDN[] = {1, RDNAttributes} ;
  2020. CERT_NAME_INFO NameInfo = {1, CertRDN};
  2021. CertName.pbData = NULL;
  2022. CertName.cbData = 0;
  2023. RDNAttributes[0].Value.pbData = NULL;
  2024. RDNAttributes[0].Value.cbData = 0;
  2025. DWORD cbCertSize = 0;
  2026. PBYTE pbCert = NULL;
  2027. DWORD cSize = 0;
  2028. // Generate a self-signed cert structure
  2029. RDNAttributes[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
  2030. RDNAttributes[0].pszObjId = szOID_COMMON_NAME;
  2031. if(!GetComputerNameEx(ComputerNameDnsDomain,
  2032. NULL,
  2033. &cSize))
  2034. {
  2035. DWORD dwError = GetLastError();
  2036. if((dwError != ERROR_MORE_DATA) &&
  2037. (dwError != ERROR_BUFFER_OVERFLOW))
  2038. {
  2039. goto error;
  2040. }
  2041. }
  2042. RDNAttributes[0].Value.cbData = cSize * sizeof(WCHAR);
  2043. RDNAttributes[0].Value.pbData = (PBYTE)SSAlloc(RDNAttributes[0].Value.cbData);
  2044. if(NULL == RDNAttributes[0].Value.pbData)
  2045. {
  2046. goto error;
  2047. }
  2048. if(!GetComputerNameEx(ComputerNameDnsDomain,
  2049. (LPWSTR)RDNAttributes[0].Value.pbData,
  2050. &cSize))
  2051. {
  2052. goto error;
  2053. }
  2054. //
  2055. // Get the actual public key info from the key
  2056. //
  2057. if(!CryptExportPublicKeyInfo(hCryptProv,
  2058. AT_KEYEXCHANGE,
  2059. X509_ASN_ENCODING,
  2060. NULL,
  2061. &cbKeyInfo))
  2062. {
  2063. goto error;
  2064. }
  2065. pKeyInfo = (CERT_PUBLIC_KEY_INFO *)SSAlloc(cbKeyInfo);
  2066. if(NULL == pKeyInfo)
  2067. {
  2068. goto error;
  2069. }
  2070. if(!CryptExportPublicKeyInfo(hCryptProv,
  2071. AT_KEYEXCHANGE,
  2072. X509_ASN_ENCODING,
  2073. pKeyInfo,
  2074. &cbKeyInfo))
  2075. {
  2076. goto error;
  2077. }
  2078. //
  2079. // Generate the certificate name
  2080. //
  2081. if(!CryptEncodeObject(X509_ASN_ENCODING,
  2082. X509_NAME,
  2083. &NameInfo,
  2084. NULL,
  2085. &CertName.cbData))
  2086. {
  2087. goto error;
  2088. }
  2089. CertName.pbData = (PBYTE)SSAlloc(CertName.cbData);
  2090. if(NULL == CertName.pbData)
  2091. {
  2092. goto error;
  2093. }
  2094. if(!CryptEncodeObject(X509_ASN_ENCODING,
  2095. X509_NAME,
  2096. &NameInfo,
  2097. CertName.pbData,
  2098. &CertName.cbData))
  2099. {
  2100. goto error;
  2101. }
  2102. CertInfo.dwVersion = CERT_V3;
  2103. CertInfo.SerialNumber.pbData = (PBYTE)pguidKey;
  2104. CertInfo.SerialNumber.cbData = sizeof(GUID);
  2105. CertInfo.SignatureAlgorithm.pszObjId = szOID_OIWSEC_sha1RSASign;
  2106. CertInfo.SignatureAlgorithm.Parameters.cbData = 0;
  2107. CertInfo.SignatureAlgorithm.Parameters.pbData = NULL;
  2108. CertInfo.Issuer.pbData = CertName.pbData;
  2109. CertInfo.Issuer.cbData = CertName.cbData;
  2110. GetSystemTimeAsFileTime(&CertInfo.NotBefore);
  2111. CertInfo.NotAfter = CertInfo.NotBefore;
  2112. ((LARGE_INTEGER * )&CertInfo.NotAfter)->QuadPart +=
  2113. Int32x32To64(FILETIME_TICKS_PER_SECOND, BACKUPKEY_LIFETIME);
  2114. CertInfo.Subject.pbData = CertName.pbData;
  2115. CertInfo.Subject.cbData = CertName.cbData;
  2116. CertInfo.SubjectPublicKeyInfo = *pKeyInfo;
  2117. CertInfo.SubjectUniqueId.pbData = (PBYTE)pguidKey;
  2118. CertInfo.SubjectUniqueId.cbData = sizeof(GUID);
  2119. CertInfo.SubjectUniqueId.cUnusedBits = 0;
  2120. CertInfo.IssuerUniqueId.pbData = (PBYTE)pguidKey;
  2121. CertInfo.IssuerUniqueId.cbData = sizeof(GUID);
  2122. CertInfo.IssuerUniqueId.cUnusedBits = 0;
  2123. CertInfo.cExtension = 0;
  2124. CertInfo.rgExtension = NULL;
  2125. if(!CryptSignAndEncodeCertificate(hCryptProv,
  2126. AT_KEYEXCHANGE,
  2127. X509_ASN_ENCODING,
  2128. X509_CERT_TO_BE_SIGNED,
  2129. &CertInfo,
  2130. &CertInfo.SignatureAlgorithm,
  2131. NULL,
  2132. NULL,
  2133. &cbCertSize))
  2134. {
  2135. goto error;
  2136. }
  2137. pbCert = (PBYTE)SSAlloc(cbCertSize);
  2138. if(NULL == pbCert)
  2139. {
  2140. goto error;
  2141. }
  2142. if(!CryptSignAndEncodeCertificate(hCryptProv,
  2143. AT_KEYEXCHANGE,
  2144. X509_ASN_ENCODING,
  2145. X509_CERT_TO_BE_SIGNED,
  2146. &CertInfo,
  2147. &CertInfo.SignatureAlgorithm,
  2148. NULL,
  2149. pbCert,
  2150. &cbCertSize))
  2151. {
  2152. goto error;
  2153. }
  2154. *pcbPublicExportLength = cbCertSize;
  2155. *ppbPublicExportData = pbCert;
  2156. if(!CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCertSize))
  2157. {
  2158. GetLastError();
  2159. }
  2160. pbCert = NULL;
  2161. fRet = TRUE;
  2162. error:
  2163. if(pbCert)
  2164. {
  2165. SSFree(pbCert);
  2166. }
  2167. if(pKeyInfo)
  2168. {
  2169. SSFree(pKeyInfo);
  2170. }
  2171. if(CertName.pbData)
  2172. {
  2173. SSFree(CertName.pbData);
  2174. }
  2175. if(RDNAttributes[0].Value.pbData)
  2176. {
  2177. SSFree(RDNAttributes[0].Value.pbData);
  2178. }
  2179. return fRet;
  2180. }
  2181. BOOL ConvertRecoveredBlobToW2KBlob(
  2182. IN BYTE *pbMasterKey,
  2183. IN DWORD cbMasterKey,
  2184. IN PBYTE pbLocalKey,
  2185. IN DWORD cbLocalKey,
  2186. IN PSID pSidCandidate,
  2187. IN OUT BYTE **ppbDataOut,
  2188. IN OUT DWORD *pcbDataOut)
  2189. {
  2190. BYTE rgbBKEncryptionKey[ A_SHA_DIGEST_LEN ];
  2191. DWORD cbSidCandidate=0;
  2192. PMASTERKEY_BLOB_W2K pMasterKeyBlob = NULL;
  2193. DWORD cbMasterKeyBlob = 0;
  2194. DWORD cbMasterInnerKeyBlob;
  2195. PMASTERKEY_INNER_BLOB_W2K pMasterKeyInnerBlob = NULL;
  2196. PBYTE pbCipherBegin;
  2197. RC4_KEYSTRUCT sRC4Key;
  2198. BYTE rgbMacKey[A_SHA_DIGEST_LEN];
  2199. DWORD dwLastError = (DWORD)NTE_BAD_KEY;
  2200. BYTE rgbSymKey[A_SHA_DIGEST_LEN*2]; // big enough to handle 3des keys
  2201. if(!IsValidSid(pSidCandidate)) {
  2202. goto cleanup;
  2203. }
  2204. cbSidCandidate = GetLengthSid(pSidCandidate);
  2205. //
  2206. // derive BK encryption key from decrypted Local Key.
  2207. //
  2208. FMyPrimitiveSHA( pbLocalKey, cbLocalKey, rgbBKEncryptionKey );
  2209. cbMasterInnerKeyBlob = sizeof(MASTERKEY_INNER_BLOB_W2K) +
  2210. cbMasterKey ;
  2211. cbMasterKeyBlob = sizeof(MASTERKEY_BLOB_W2K) +
  2212. cbMasterInnerKeyBlob;
  2213. pMasterKeyBlob = (PMASTERKEY_BLOB_W2K)SSAlloc( cbMasterKeyBlob );
  2214. if(pMasterKeyBlob == NULL)
  2215. {
  2216. dwLastError = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  2217. goto cleanup;
  2218. }
  2219. pMasterKeyBlob->dwVersion = MASTERKEY_BLOB_VERSION_W2K;
  2220. pMasterKeyInnerBlob =
  2221. (PMASTERKEY_INNER_BLOB_W2K)(pMasterKeyBlob + 1);
  2222. //
  2223. // generate random R2 for SymKey
  2224. //
  2225. if(!RtlGenRandom(pMasterKeyBlob->R2, MASTERKEY_R2_LEN_W2K))
  2226. goto cleanup;
  2227. //
  2228. // generate random R3 for MAC
  2229. //
  2230. if(!RtlGenRandom(pMasterKeyInnerBlob->R3, MASTERKEY_R3_LEN_W2K))
  2231. goto cleanup;
  2232. //
  2233. // derive symetric key via rgbMKEncryptionKey and random R2
  2234. //
  2235. if(!FMyPrimitiveHMACParam(
  2236. rgbBKEncryptionKey,
  2237. A_SHA_DIGEST_LEN,
  2238. pMasterKeyBlob->R2,
  2239. MASTERKEY_R2_LEN_W2K,
  2240. rgbSymKey
  2241. ))
  2242. goto cleanup;
  2243. //
  2244. // derive MAC key via HMAC from rgbMKEncryptionKey and random R3.
  2245. //
  2246. if(!FMyPrimitiveHMACParam(
  2247. rgbBKEncryptionKey,
  2248. A_SHA_DIGEST_LEN,
  2249. pMasterKeyInnerBlob->R3,
  2250. MASTERKEY_R3_LEN_W2K,
  2251. rgbMacKey // resultant MAC key
  2252. ))
  2253. goto cleanup;
  2254. pbCipherBegin = (PBYTE)(pMasterKeyInnerBlob+1);
  2255. //
  2256. // copy pbMasterKey following inner MAC'ish blob.
  2257. //
  2258. CopyMemory( pbCipherBegin, pbMasterKey, cbMasterKey );
  2259. //
  2260. // use MAC key to derive result from pbMasterKey
  2261. //
  2262. if(!FMyPrimitiveHMACParam(
  2263. rgbMacKey,
  2264. sizeof(rgbMacKey),
  2265. pbMasterKey,
  2266. cbMasterKey,
  2267. pMasterKeyInnerBlob->MAC // resultant MAC for verification.
  2268. ))
  2269. goto cleanup;
  2270. rc4_key(&sRC4Key, A_SHA_DIGEST_LEN, rgbSymKey);
  2271. rc4(&sRC4Key,
  2272. cbMasterInnerKeyBlob,
  2273. (PBYTE)pMasterKeyInnerBlob);
  2274. *ppbDataOut = (PBYTE)pMasterKeyBlob;
  2275. *pcbDataOut = cbMasterKeyBlob;
  2276. pMasterKeyBlob = NULL; // prevent free of blob on success (caller does it).
  2277. dwLastError = ERROR_SUCCESS;
  2278. cleanup:
  2279. if(pMasterKeyBlob) {
  2280. RtlSecureZeroMemory(pMasterKeyBlob, cbMasterKeyBlob);
  2281. SSFree(pMasterKeyBlob);
  2282. }
  2283. SetLastError(dwLastError);
  2284. return (dwLastError == ERROR_SUCCESS);
  2285. }