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.

1376 lines
50 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright(C) 2002 Microsoft Corporation
  5. //
  6. // File: sysprep.cxx
  7. //
  8. //----------------------------------------------------------------------------
  9. #include "..\pch\headers.hxx"
  10. #include "security.hxx"
  11. #include "sysprep.hxx"
  12. static WCHAR gwszSysprepKey[] = L"TSSK"; // Task Scheduler Sysprep Key
  13. static WCHAR gwszSysprepIdentity[] = L"TSSI"; // Task Scheduler Sysprep Identity Data
  14. // needed by ScavengeSASecurityDBase
  15. extern SERVICE_STATUS g_SvcStatus;
  16. #define SERVICE_RUNNING 0x00000004
  17. //+---------------------------------------------------------------------------
  18. //
  19. // Function: GetUniqueSPSName
  20. //
  21. // Synopsis: calls NewWorkItem a few times trying to get a unique file name out of it
  22. //
  23. // Arguments: ITaskScheduler *pITaskScheduler, IUnknown** pITask
  24. // WCHAR* pwszTaskName, to receive the name that was actually
  25. // used in the creation of the task
  26. //
  27. // Returns: Various HRESULTs
  28. //
  29. // Notes: None.
  30. //
  31. //----------------------------------------------------------------------------
  32. HRESULT GetUniqueSPSName(ITaskScheduler* pITaskScheduler, ITask** ppITask, WCHAR* pwszTaskName)
  33. {
  34. HRESULT hr = E_FAIL;
  35. // okay, so we're not distinguishing between errors, code's simpler
  36. // and the only expectable error is "already exists"
  37. for (int i = 0; (i < 16) && FAILED(hr); i++)
  38. {
  39. if (FAILED(StringCchPrintf(pwszTaskName, 20, L"$~$Sys%X$", i)))
  40. break;
  41. hr = pITaskScheduler->NewWorkItem(pwszTaskName,
  42. CLSID_CTask,
  43. IID_ITask,
  44. (IUnknown**)ppITask);
  45. }
  46. return hr;
  47. }
  48. //+---------------------------------------------------------------------------
  49. //
  50. // Function: PrepSysPrepTask
  51. //
  52. // Synopsis: Creates a task to be run which will call run the sysprep
  53. // code in the local system account
  54. //
  55. // Arguments: Task** ppITaskToRun, to receive pointer to ITask interface
  56. // task is to activated by calling the Run() method
  57. // WCHAR* pwszTaskName, to receive the name that was actually
  58. // used in the creation of the task
  59. //
  60. // Returns: Various HRESULTs
  61. //
  62. // Notes: None.
  63. //
  64. //----------------------------------------------------------------------------
  65. HRESULT PrepSysPrepTask(ITask** ppITaskToRun, WCHAR* pwszTaskName)
  66. {
  67. HRESULT hr = E_FAIL;
  68. WCHAR applicationName[MAX_PATH +1];
  69. WCHAR argument[MAX_PATH +20];
  70. DWORD expandedSize;
  71. expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\rundll32.exe\"", applicationName, MAX_PATH +1);
  72. if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
  73. return HRESULT_FROM_WIN32(GetLastError());
  74. expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\SchedSvc.dll\",SysPrepCallback", argument, MAX_PATH +20);
  75. if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
  76. return HRESULT_FROM_WIN32(GetLastError());
  77. TASK_TRIGGER tigger;
  78. tigger.wStartHour = 10;
  79. tigger.wStartMinute = 20;
  80. tigger.wBeginYear = 1957;
  81. tigger.wBeginMonth = 6;
  82. tigger.wBeginDay = 14;
  83. tigger.wEndYear = 2001;
  84. tigger.wEndMonth = 10;
  85. tigger.wEndDay = 8 ;
  86. tigger.MinutesDuration = 0;
  87. tigger.MinutesInterval = 0;
  88. tigger.rgFlags = 0;
  89. tigger.TriggerType = TASK_TIME_TRIGGER_ONCE;
  90. tigger.Reserved2 = 0;
  91. tigger.wRandomMinutesInterval = 0;
  92. tigger.cbTriggerSize = sizeof(TASK_TRIGGER);
  93. tigger.Reserved1 = 0;
  94. ITaskScheduler *pITaskScheduler = NULL;
  95. ITask *pITask = NULL;
  96. IPersistFile *pIPersistFile = NULL;
  97. ITaskTrigger* pTrigger = NULL;
  98. WORD idontcare;
  99. hr = CoCreateInstance( CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void**)&pITaskScheduler);
  100. if (SUCCEEDED(hr) &&
  101. SUCCEEDED(hr = GetUniqueSPSName(pITaskScheduler, &pITask, pwszTaskName)) &&
  102. SUCCEEDED(hr = pITask->SetApplicationName(applicationName)) &&
  103. SUCCEEDED(hr = pITask->SetParameters(argument)) &&
  104. SUCCEEDED(hr = pITask->SetFlags(TASK_FLAG_DELETE_WHEN_DONE | TASK_FLAG_DISABLED)) &&
  105. SUCCEEDED(hr = pITask->SetAccountInformation(L"", NULL)) &&
  106. SUCCEEDED(hr = pITask->CreateTrigger(&idontcare, &pTrigger)) &&
  107. SUCCEEDED(hr = pTrigger->SetTrigger(&tigger)) &&
  108. SUCCEEDED(hr = pITask->QueryInterface(IID_IPersistFile, (void **)&pIPersistFile)) &&
  109. SUCCEEDED(hr = pIPersistFile->Save(NULL, TRUE)))
  110. {
  111. // return it to the caller
  112. *ppITaskToRun = pITask;
  113. }
  114. else
  115. if (pITask)
  116. pITask->Release();
  117. if (pITaskScheduler)
  118. pITaskScheduler->Release();
  119. if (pIPersistFile)
  120. pIPersistFile->Release();
  121. if (pTrigger)
  122. pTrigger->Release();
  123. return hr;
  124. }
  125. //+---------------------------------------------------------------------------
  126. //
  127. // Function: SaveSysprepInfo
  128. //
  129. // Synopsis: Saves job identity and credential key information prior to Sysprep
  130. // so that it can be used to convert old data after Sysprep.
  131. //
  132. // Arguments: None.
  133. //
  134. // Returns: HRESULT
  135. //
  136. //----------------------------------------------------------------------------
  137. HRESULT SaveSysprepInfo(void)
  138. {
  139. //
  140. // Initialize security
  141. //
  142. InitSS();
  143. SetMysteryDWORDValue();
  144. HRESULT hr = PreProcessNetScheduleJobs();
  145. if (FAILED(hr))
  146. {
  147. // ignore, we still need to do the other processing
  148. }
  149. //
  150. // Obtain a provider handle to the CSP (for use with Crypto API).
  151. //
  152. HCRYPTPROV hCSP = NULL;
  153. hr = GetCSPHandle(&hCSP);
  154. if (SUCCEEDED(hr))
  155. {
  156. if (hCSP)
  157. {
  158. //
  159. // We've got the handle, now save the important stuff
  160. //
  161. hr = SaveSysprepKeyInfo(hCSP);
  162. if (SUCCEEDED(hr))
  163. {
  164. hr = SaveSysprepIdentityInfo(hCSP);
  165. }
  166. CloseCSPHandle(hCSP);
  167. }
  168. else
  169. {
  170. hr = E_FAIL;
  171. }
  172. }
  173. //
  174. // Done doing security stuff
  175. //
  176. UninitSS();
  177. if (FAILED(hr))
  178. {
  179. CHECK_HRESULT(hr);
  180. }
  181. return hr;
  182. }
  183. //+---------------------------------------------------------------------------
  184. //
  185. // Function: SaveSysprepKeyInfo
  186. //
  187. // Synopsis: Stores the computed CredentialKey as a secret prior to sysprep so that it can be
  188. // retrieved and used to decrypt credentials after sysprep, so those credentials may
  189. // be encrypted again using the post-sysprep key.
  190. //
  191. // Arguments: hCSP - handle to the crypto service provider
  192. //
  193. // Returns: HRESULT
  194. //
  195. //----------------------------------------------------------------------------
  196. HRESULT SaveSysprepKeyInfo(HCRYPTPROV hCSP)
  197. {
  198. //
  199. // Generate the encryption key
  200. //
  201. RC2_KEY_INFO RC2KeyInfo;
  202. HRESULT hr = ComputeCredentialKey(hCSP, &RC2KeyInfo);
  203. if (SUCCEEDED(hr))
  204. {
  205. //
  206. // Write the key to LSA
  207. //
  208. hr = WriteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, sizeof(RC2KeyInfo), (BYTE*)&RC2KeyInfo);
  209. }
  210. return hr;
  211. }
  212. //+---------------------------------------------------------------------------
  213. //
  214. // Function: SaveSysprepIdentityInfo
  215. //
  216. // Synopsis: Obtains the credential index and NULL password setting for each
  217. // existing job identity and stores that information along with the
  218. // associated filename in an LSA secret so that the identities can
  219. // be rehashed and stored in the identity database, postsysprep,
  220. // still associated with the correct credential.
  221. //
  222. // Arguments: hCSP - handle to the crypto service provider
  223. //
  224. // Returns: HRESULT
  225. //
  226. //----------------------------------------------------------------------------
  227. HRESULT SaveSysprepIdentityInfo(HCRYPTPROV hCSP)
  228. {
  229. WCHAR* pwszTasksFolder = NULL;
  230. HANDLE hFileEnum = INVALID_HANDLE_VALUE;
  231. DWORD cbSAI;
  232. DWORD cbSAC;
  233. BYTE* pbSAI = NULL;
  234. BYTE* pbSAC = NULL;
  235. //
  236. // First, read the security database so we can do the lookups
  237. //
  238. HRESULT hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
  239. if (SUCCEEDED(hr))
  240. {
  241. if (cbSAI <= SAI_HEADER_SIZE)
  242. {
  243. //
  244. // Database empty, nothing to do.
  245. //
  246. }
  247. else
  248. {
  249. //
  250. // Enumerate job objects in the task's folder directory.
  251. //
  252. hr = GetTasksFolder(&pwszTasksFolder);
  253. if (SUCCEEDED(hr))
  254. {
  255. WCHAR wszSearchPath[MAX_PATH + 1];
  256. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  257. if (SUCCEEDED(hr))
  258. {
  259. hr = StringCchCat(wszSearchPath, MAX_PATH + 1, EXTENSION_WILDCARD TSZ_JOB);
  260. if (SUCCEEDED(hr))
  261. {
  262. WIN32_FIND_DATA fd;
  263. DWORD dwRet = 0;
  264. if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
  265. {
  266. //
  267. // Either no jobs (this is OK), or an error occurred.
  268. //
  269. dwRet = GetLastError();
  270. if (dwRet != ERROR_FILE_NOT_FOUND)
  271. hr = _HRESULT_FROM_WIN32(dwRet);
  272. }
  273. else
  274. {
  275. //
  276. // Must concatenate the filename returned from the enumeration onto the folder path
  277. // before computing the hash. Prepare for doing that repeatedly by taking the path,
  278. // adding a slash, and remembering the next character position.
  279. //
  280. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  281. if (SUCCEEDED(hr))
  282. {
  283. DWORD iConcatenation = lstrlenW(pwszTasksFolder);
  284. wszSearchPath[iConcatenation++] = L'\\';
  285. //
  286. // Allocate buffer used to collect identity data that will be written to the LSA secret
  287. //
  288. BYTE rgbIdentityData[MAX_SECRET_SIZE];
  289. BYTE* pbCurrentData = (BYTE*)&rgbIdentityData;
  290. DWORD cbIdentityData = 0;
  291. //
  292. // Process each found file
  293. //
  294. BYTE rgbIdentity[HASH_DATA_SIZE];
  295. BOOL bIsPasswordNull;
  296. DWORD dwCredentialIndex;
  297. DWORD cbFileName;
  298. while (dwRet != ERROR_NO_MORE_FILES)
  299. {
  300. //
  301. // Truncate the existing name after the folder path,
  302. // then concatenate the new filename onto it.
  303. //
  304. wszSearchPath[iConcatenation] = L'\0';
  305. if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
  306. {
  307. //
  308. // Hash the job into a unique identity.
  309. //
  310. if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity)))
  311. {
  312. //
  313. // Find the identity in the SAI for this job
  314. //
  315. hr = SAIFindIdentity(rgbIdentity,
  316. cbSAI,
  317. pbSAI,
  318. &dwCredentialIndex,
  319. &bIsPasswordNull,
  320. NULL,
  321. NULL,
  322. NULL);
  323. //
  324. // S_OK means the identity was found; S_FALSE means it wasn't
  325. // Other codes are errors. We process only the S_OKs, of course.
  326. //
  327. if (S_OK == hr)
  328. {
  329. cbFileName = (lstrlenW(fd.cFileName) + 1) * sizeof(WCHAR);
  330. if ((cbIdentityData + cbFileName + sizeof(BOOL) + sizeof(DWORD)) > MAX_SECRET_SIZE)
  331. {
  332. //
  333. // this should _never_ happen, as we shouldn't be able to exceed the size
  334. // given that we only add data for jobs that have already been found in
  335. // the SAI, and we are collecting less data than was stored there
  336. //
  337. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  338. break;
  339. }
  340. CopyMemory(pbCurrentData, fd.cFileName, cbFileName);
  341. pbCurrentData += cbFileName;
  342. CopyMemory(pbCurrentData, &bIsPasswordNull, sizeof(BOOL));
  343. pbCurrentData += sizeof(BOOL);
  344. CopyMemory(pbCurrentData, &dwCredentialIndex, sizeof(DWORD));
  345. pbCurrentData += sizeof(DWORD);
  346. cbIdentityData += (cbFileName + sizeof(BOOL) + sizeof(DWORD));
  347. }
  348. else
  349. {
  350. hr = S_OK; // OK, we failed this one, go on to the next
  351. }
  352. }
  353. }
  354. if (!FindNextFile(hFileEnum, &fd))
  355. {
  356. dwRet = GetLastError();
  357. if (dwRet != ERROR_NO_MORE_FILES)
  358. hr = _HRESULT_FROM_WIN32(dwRet);
  359. break;
  360. }
  361. }
  362. if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
  363. {
  364. hr = WriteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, cbIdentityData, (BYTE*)&rgbIdentityData);
  365. }
  366. }
  367. }
  368. }
  369. }
  370. }
  371. }
  372. }
  373. if (pbSAI) LocalFree(pbSAI);
  374. if (pbSAC) LocalFree(pbSAC);
  375. if (pwszTasksFolder)
  376. delete [] pwszTasksFolder;
  377. if (hFileEnum != INVALID_HANDLE_VALUE)
  378. FindClose(hFileEnum);
  379. return hr;
  380. }
  381. //+---------------------------------------------------------------------------
  382. //
  383. // Function: PreProcessNetScheduleJobs
  384. //
  385. // Synopsis: Netschedule jobs (AT jobs) are signed internally with a hash so
  386. // that the service can validate their authenticity at run time.
  387. // Sysprep changes important data used by the Crypto API such that
  388. // the generated hash will be different and can never match the hash
  389. // stored in the AT job. In order to be runnable again, the AT jobs
  390. // must be signed again with the new hash.
  391. //
  392. // *** WARNING ***
  393. // This means that if someone could drop a bogus AT job into the tasks
  394. // folder, it would automatically get signed as a result of running sysprep.
  395. // In order to prevent this, check all AT jobs prior to sysprep and
  396. // eliminate any that are not valid.
  397. //
  398. // Arguments: None
  399. //
  400. // Returns: HRESULT
  401. //
  402. //----------------------------------------------------------------------------
  403. HRESULT PreProcessNetScheduleJobs(void)
  404. {
  405. HANDLE hFileEnum = INVALID_HANDLE_VALUE;
  406. WCHAR* pwszTasksFolder = NULL;
  407. HRESULT hr = GetTasksFolder(&pwszTasksFolder);
  408. if (SUCCEEDED(hr))
  409. {
  410. WCHAR wszSearchPath[MAX_PATH + 1];
  411. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  412. if (SUCCEEDED(hr))
  413. {
  414. hr = StringCchCat(wszSearchPath, MAX_PATH + 1, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
  415. if (SUCCEEDED(hr))
  416. {
  417. WIN32_FIND_DATA fd;
  418. DWORD dwRet = 0;
  419. if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
  420. {
  421. //
  422. // Either no jobs (this is OK), or an error occurred.
  423. //
  424. dwRet = GetLastError();
  425. if (dwRet != ERROR_FILE_NOT_FOUND)
  426. hr = _HRESULT_FROM_WIN32(dwRet);
  427. }
  428. else
  429. {
  430. //
  431. // Must concatenate the filename returned from the enumeration onto the folder path
  432. // before loading the job. Prepare for doing that repeatedly by taking the path,
  433. // adding a slash, and remembering the next character position.
  434. //
  435. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  436. if (SUCCEEDED(hr))
  437. {
  438. DWORD iConcatenation = lstrlenW(pwszTasksFolder);
  439. wszSearchPath[iConcatenation++] = L'\\';
  440. //
  441. // Process each found file
  442. //
  443. while (dwRet != ERROR_NO_MORE_FILES)
  444. {
  445. //
  446. // Truncate the existing name after the folder path,
  447. // then concatenate the new filename onto it.
  448. //
  449. wszSearchPath[iConcatenation] = L'\0';
  450. if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
  451. {
  452. //
  453. // Load and check signature of each job
  454. //
  455. CJob* pJob = CJob::Create();
  456. if (!pJob)
  457. {
  458. hr = E_OUTOFMEMORY;
  459. break;
  460. }
  461. if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
  462. {
  463. if (!pJob->VerifySignature())
  464. {
  465. if (!DeleteFile(wszSearchPath))
  466. {
  467. dwRet = GetLastError();
  468. // go on anyway
  469. }
  470. }
  471. }
  472. pJob->Release();
  473. }
  474. if (!FindNextFile(hFileEnum, &fd))
  475. {
  476. dwRet = GetLastError();
  477. if (dwRet != ERROR_NO_MORE_FILES)
  478. hr = _HRESULT_FROM_WIN32(dwRet);
  479. break;
  480. }
  481. }
  482. }
  483. }
  484. if (hFileEnum != INVALID_HANDLE_VALUE)
  485. FindClose(hFileEnum);
  486. }
  487. }
  488. }
  489. if (pwszTasksFolder)
  490. delete [] pwszTasksFolder;
  491. return hr;
  492. }
  493. //+---------------------------------------------------------------------------
  494. //
  495. // Function: GetSysprepIdentityInfo
  496. //
  497. // Synopsis: Retrieves the stored job identity data so new hashes can be calculated.
  498. //
  499. // Arguments: pcbIdentityData - pointer to count of bytes in the identity data
  500. // ppIdentityData - pointer to pointer to retrieved identity data
  501. //
  502. // Returns: HRESULT
  503. //
  504. //----------------------------------------------------------------------------
  505. HRESULT GetSysprepIdentityInfo(DWORD* pcbIdentityData, BYTE** ppIdentityData)
  506. {
  507. *ppIdentityData = NULL;
  508. //
  509. // Retrieve job identity data from LSA, if it exists
  510. //
  511. HRESULT hr = ReadLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, pcbIdentityData, ppIdentityData);
  512. if (FAILED(hr))
  513. {
  514. if (*ppIdentityData != NULL)
  515. {
  516. LocalFree(*ppIdentityData);
  517. *ppIdentityData = NULL;
  518. }
  519. }
  520. return hr;
  521. }
  522. //+---------------------------------------------------------------------------
  523. //
  524. // Function: GetSysprepKeyInfo
  525. //
  526. // Synopsis: Retrieves the stored pre-sysprep CredentialKey so that it can be used to decrypt
  527. // credentials after sysprep, so those credentials may be encrypted again using the
  528. // post-sysprep key.
  529. //
  530. // Arguments: pcbRC2KeyInfo - pointer to count of bytes in key data
  531. // ppRC2KeyInfo - pointer to pointer to retrieved key
  532. //
  533. // Returns: HRESULT
  534. //
  535. //----------------------------------------------------------------------------
  536. HRESULT GetSysprepKeyInfo(DWORD* pcbRC2KeyInfo, RC2_KEY_INFO** ppRC2KeyInfo)
  537. {
  538. *ppRC2KeyInfo = NULL;
  539. //
  540. // Get the key from LSA
  541. //
  542. HRESULT hr = ReadLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, pcbRC2KeyInfo, (BYTE**)ppRC2KeyInfo);
  543. if (SUCCEEDED(hr))
  544. {
  545. //
  546. // Check the size. We know exactly how big this should be, so make sure it is.
  547. //
  548. if (*pcbRC2KeyInfo != sizeof(RC2_KEY_INFO))
  549. {
  550. *pcbRC2KeyInfo = 0;
  551. LocalFree(*ppRC2KeyInfo);
  552. *ppRC2KeyInfo = NULL;
  553. hr = E_FAIL;
  554. }
  555. }
  556. else // failed!
  557. {
  558. if (*ppRC2KeyInfo != NULL)
  559. {
  560. LocalFree(*ppRC2KeyInfo);
  561. *ppRC2KeyInfo = NULL;
  562. }
  563. }
  564. return hr;
  565. }
  566. //+---------------------------------------------------------------------------
  567. //
  568. // Function: ConvertSysprepInfo
  569. //
  570. // Synopsis: Converts credentials stored prior to sysprep so they may be decrypted after sysprep.
  571. // It does this by retrieving the pre-sysprep key, decrypting the credentials with it, and then
  572. // encrypting them using the new post-sysprep key.
  573. //
  574. // Arguments: None
  575. //
  576. // Returns: HRESULT
  577. //
  578. //----------------------------------------------------------------------------
  579. HRESULT ConvertSysprepInfo(void)
  580. {
  581. //
  582. // Initialize these up here so they will have values if we exit early
  583. //
  584. HCRYPTPROV hCSP = NULL;
  585. BYTE* pbSAI = NULL;
  586. DWORD cbSAI = 0;
  587. BYTE* pbSAC = NULL;
  588. DWORD cbSAC = 0;
  589. RC2_KEY_INFO* pRC2KeyPreSysprep = NULL;
  590. DWORD cbRC2KeyPreSysprep = 0;
  591. RC2_KEY_INFO RC2KeyPostSysprep;
  592. //
  593. // Initialize security
  594. //
  595. InitSS();
  596. SetMysteryDWORDValue();
  597. //
  598. // Set this as it is relied on by ScavengeSASecurityDBase
  599. //
  600. g_SvcStatus.dwCurrentState = SERVICE_RUNNING;
  601. //
  602. // Get the tasks folder for use by ConvertNetScheduleJobs, ConvertIdentityData, and ScavengeSASecurityDBase
  603. //
  604. HRESULT hr = GetTasksFolder(&g_TasksFolderInfo.ptszPath);
  605. if (FAILED(hr))
  606. {
  607. goto ErrorExit;
  608. }
  609. //
  610. // Obtain a provider handle to the CSP (for use with Crypto API)
  611. //
  612. hr = GetCSPHandle(&hCSP);
  613. if (FAILED(hr))
  614. {
  615. goto ErrorExit;
  616. }
  617. //
  618. // Get the pre-sysprep key from LSA
  619. //
  620. hr = GetSysprepKeyInfo(&cbRC2KeyPreSysprep, &pRC2KeyPreSysprep);
  621. if (FAILED(hr))
  622. {
  623. goto ErrorExit;
  624. }
  625. //
  626. // Generate the post-sysprep key
  627. //
  628. hr = ComputeCredentialKey(hCSP, &RC2KeyPostSysprep);
  629. if (FAILED(hr))
  630. {
  631. goto ErrorExit;
  632. }
  633. //
  634. // The Net Schedule conversions are independent of the rest of the logic,
  635. // so do them first and get them out of the way.
  636. //
  637. hr = ConvertNetScheduleJobs();
  638. if (FAILED(hr))
  639. {
  640. // ignore, we still can do our other conversions
  641. hr = S_OK;
  642. }
  643. hr = ConvertNetScheduleCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep);
  644. if (FAILED(hr))
  645. {
  646. // ignore, we still can do our other conversions
  647. hr = S_OK;
  648. }
  649. //
  650. // Read SAI & SAC databases.
  651. // It is not necessary to guard security db access with a critsec as elsewhere in the code,
  652. // as the service will not be running during MiniSetup and there will be no other threads accessing this data.
  653. //
  654. hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
  655. if (FAILED(hr))
  656. {
  657. goto ErrorExit;
  658. }
  659. //
  660. // Do some validations on the database
  661. //
  662. if (cbSAI <= SAI_HEADER_SIZE || pbSAI == NULL ||
  663. cbSAC <= SAC_HEADER_SIZE || pbSAC == NULL)
  664. {
  665. goto ErrorExit;
  666. }
  667. //
  668. // Place updated entries into SAI for all jobs
  669. //
  670. hr = ConvertIdentityData(hCSP, &cbSAI, &pbSAI, &cbSAC, &pbSAC);
  671. if (FAILED(hr))
  672. {
  673. goto ErrorExit;
  674. }
  675. //
  676. // Place updated entries into SAC for all credentials
  677. //
  678. hr = ConvertCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep, &cbSAI, &pbSAI, &cbSAC, &pbSAC);
  679. //
  680. // Clearing key content at earliest opportunity
  681. //
  682. SecureZeroMemory(&RC2KeyPostSysprep, sizeof(RC2KeyPostSysprep));
  683. if (SUCCEEDED(hr))
  684. {
  685. //
  686. // Update security database with new identities and converted credentials
  687. //
  688. hr = WriteSecurityDBase(cbSAI, pbSAI, cbSAC, pbSAC);
  689. if (SUCCEEDED(hr))
  690. {
  691. //
  692. // Allow scavenger cleanup to delete old identities
  693. //
  694. ScavengeSASecurityDBase();
  695. }
  696. }
  697. ErrorExit:
  698. //
  699. // Delete the secrets
  700. //
  701. DeleteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey);
  702. DeleteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity);
  703. //
  704. // Clean up
  705. //
  706. if (g_TasksFolderInfo.ptszPath)
  707. delete [] g_TasksFolderInfo.ptszPath;
  708. if (hCSP) CloseCSPHandle(hCSP);
  709. if (pbSAI) LocalFree(pbSAI);
  710. if (pbSAC) LocalFree(pbSAC);
  711. if (pRC2KeyPreSysprep) LocalFree(pRC2KeyPreSysprep);
  712. //
  713. // Log an error & reset the SA security dbases SAI & SAC if corruption is detected.
  714. //
  715. if (hr == SCHED_E_ACCOUNT_DBASE_CORRUPT)
  716. {
  717. //
  718. // Reset SAI & SAC by writing four bytes of zeros into each.
  719. // Ignore the return code. No recourse if this fails.
  720. //
  721. DWORD dwZero = 0;
  722. WriteSecurityDBase(sizeof(dwZero), (BYTE*)&dwZero, sizeof(dwZero), (BYTE*)&dwZero);
  723. }
  724. //
  725. // Done doing security stuff
  726. //
  727. UninitSS();
  728. return(hr);
  729. }
  730. //+---------------------------------------------------------------------------
  731. //
  732. // Function: ConvertNetScheduleJobs
  733. //
  734. // Synopsis: Netschedule jobs (AT jobs) are signed internally with a hash so
  735. // that the service can validate their authenticity at run time.
  736. // Sysprep changes important data used by the Crypto API such that
  737. // the generated hash will be different and can never match the hash
  738. // stored in the AT job. In order to be runnable again, the AT jobs
  739. // must be signed again with the new hash.
  740. //
  741. // *** WARNING ***
  742. // This means that if someone could drop a bogus AT job into the tasks
  743. // folder, it would automatically get signed as a result of running sysprep.
  744. // In order to prevent this, PreProcessNetScheduleJobs has already checked
  745. // all AT jobs prior to sysprep and eliminated any that were not valid.
  746. // Arguments: None
  747. //
  748. // Returns: HRESULT
  749. //
  750. //----------------------------------------------------------------------------
  751. HRESULT ConvertNetScheduleJobs(void)
  752. {
  753. HRESULT hr = S_OK;
  754. HANDLE hFileEnum = INVALID_HANDLE_VALUE;
  755. WCHAR wszSearchPath[MAX_PATH + 1];
  756. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  757. if (SUCCEEDED(hr))
  758. {
  759. hr = StringCchCat(wszSearchPath, MAX_PATH + 1, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
  760. if (SUCCEEDED(hr))
  761. {
  762. WIN32_FIND_DATA fd;
  763. DWORD dwRet = 0;
  764. if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
  765. {
  766. //
  767. // Either no jobs (this is OK), or an error occurred.
  768. //
  769. dwRet = GetLastError();
  770. if (dwRet != ERROR_FILE_NOT_FOUND)
  771. hr = _HRESULT_FROM_WIN32(dwRet);
  772. }
  773. else
  774. {
  775. //
  776. // Must concatenate the filename returned from the enumeration onto the folder path
  777. // before loading the job. Prepare for doing that repeatedly by taking the path,
  778. // adding a slash, and remembering the next character position.
  779. //
  780. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  781. if (SUCCEEDED(hr))
  782. {
  783. DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
  784. wszSearchPath[iConcatenation++] = L'\\';
  785. //
  786. // Process each found file
  787. //
  788. while (dwRet != ERROR_NO_MORE_FILES)
  789. {
  790. //
  791. // Truncate the existing name after the folder path,
  792. // then concatenate the new filename onto it.
  793. //
  794. wszSearchPath[iConcatenation] = L'\0';
  795. if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
  796. {
  797. //
  798. // Load, sign, save, and release the job
  799. //
  800. CJob* pJob = CJob::Create();
  801. if (!pJob)
  802. {
  803. hr = E_OUTOFMEMORY;
  804. break;
  805. }
  806. if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
  807. {
  808. if (SUCCEEDED(pJob->Sign()))
  809. {
  810. pJob->SaveWithRetry(pJob->GetFileName(),
  811. FALSE,
  812. SAVEP_VARIABLE_LENGTH_DATA |
  813. SAVEP_PRESERVE_NET_SCHEDULE);
  814. }
  815. }
  816. pJob->Release();
  817. }
  818. if (!FindNextFile(hFileEnum, &fd))
  819. {
  820. dwRet = GetLastError();
  821. if (dwRet != ERROR_NO_MORE_FILES)
  822. hr = _HRESULT_FROM_WIN32(dwRet);
  823. break;
  824. }
  825. }
  826. }
  827. }
  828. if (hFileEnum != INVALID_HANDLE_VALUE)
  829. FindClose(hFileEnum);
  830. }
  831. }
  832. return hr;
  833. }
  834. //+---------------------------------------------------------------------------
  835. //
  836. // Function: ConvertIdentityData
  837. //
  838. // Synopsis: Create new job identity entries in SAI representing the existing
  839. // jobs on the system. It does this by retrieving information stored
  840. // pre-sysprep associating each existing job with its credential, then
  841. // creating and storing a new identity to reference that credential.
  842. //
  843. // Arguments: hCSP - handle to crypto service provider
  844. // pcbSAI - pointer to dword containing count of bytes in SAI
  845. // ppbSAI - pointer to pointer to SAI data
  846. // pcbSAC - pointer to dword containing count of bytes in SAC
  847. // ppbSAC - pointer to pointer to SAC data
  848. //
  849. // Returns: HRESULT
  850. //
  851. //----------------------------------------------------------------------------
  852. HRESULT ConvertIdentityData(HCRYPTPROV hCSP, DWORD* pcbSAI, BYTE** ppbSAI, DWORD* pcbSAC, BYTE** ppbSAC)
  853. {
  854. //
  855. // Get the job identity data from LSA
  856. //
  857. BYTE* pbIdentityData = NULL;
  858. DWORD cbIdentityData = 0;
  859. HRESULT hr = GetSysprepIdentityInfo(&cbIdentityData, &pbIdentityData);
  860. if (FAILED(hr))
  861. {
  862. goto ErrorExit;
  863. }
  864. //
  865. // Insert updated hashes into SAI for pre-sysprep jobs
  866. //
  867. if (pbIdentityData)
  868. {
  869. //
  870. // Get the tasks folder and prepare buffer for use in producing job path
  871. //
  872. WCHAR wszJobPath[MAX_PATH + 1];
  873. hr = StringCchCopy(wszJobPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  874. if (SUCCEEDED(hr))
  875. {
  876. DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
  877. wszJobPath[iConcatenation++] = L'\\';
  878. //
  879. // Process each stored task
  880. //
  881. BYTE* pbCurrentData = pbIdentityData;
  882. BYTE* pbLastByte = (BYTE*)(pbIdentityData + cbIdentityData - 1);
  883. WCHAR* pwszFileName = NULL;
  884. BOOL bIsPasswordNull;
  885. DWORD dwCredentialIndex;
  886. BYTE rgbIdentity[HASH_DATA_SIZE];
  887. BYTE* pbIdentitySet = NULL;
  888. while (pbCurrentData <= pbLastByte)
  889. {
  890. pwszFileName = (WCHAR*) pbCurrentData;
  891. pbCurrentData += ((lstrlenW(pwszFileName) + 1) * sizeof(WCHAR));
  892. CopyMemory(&bIsPasswordNull, pbCurrentData, sizeof(BOOL));
  893. pbCurrentData += sizeof(BOOL);
  894. CopyMemory(&dwCredentialIndex, pbCurrentData, sizeof(DWORD));
  895. if ((pbCurrentData + sizeof(DWORD)) > (pbLastByte + 1))
  896. {
  897. //
  898. // the first DWORD after pbCurrentData is beyond the first BYTE after pbLastByte;
  899. // this means that the DWORD pointed to by pbCurrentData is partially or wholly
  900. // beyond the end of the data -- something is corrupt
  901. //
  902. hr = E_FAIL;
  903. goto ErrorExit;
  904. }
  905. //
  906. // Truncate the existing name after the folder path,
  907. // then concatenate the new filename onto it.
  908. //
  909. wszJobPath[iConcatenation] = L'\0';
  910. if (SUCCEEDED(StringCchCat(wszJobPath, MAX_PATH + 1, pwszFileName)))
  911. {
  912. //
  913. // Hash the job into a unique identity.
  914. //
  915. if (SUCCEEDED(HashJobIdentity(hCSP, (LPCWSTR)&wszJobPath, rgbIdentity)))
  916. {
  917. //
  918. // Store a NULL password by flipping the last bit of the hash data.
  919. //
  920. if (bIsPasswordNull)
  921. {
  922. LAST_HASH_BYTE(rgbIdentity) ^= 1;
  923. }
  924. //
  925. // Insert the job identity into the SAI identity set associated
  926. // with this credential.
  927. //
  928. hr = SAIIndexIdentity(*pcbSAI,
  929. *ppbSAI,
  930. dwCredentialIndex,
  931. 0,
  932. NULL,
  933. NULL,
  934. &pbIdentitySet);
  935. if (hr == S_FALSE)
  936. {
  937. //
  938. // The SAC & SAI databases are out of sync. Should *never* occur.
  939. //
  940. ASSERT_SECURITY_DBASE_CORRUPT();
  941. hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
  942. goto ErrorExit;
  943. }
  944. else if (SUCCEEDED(hr))
  945. {
  946. hr = SAIInsertIdentity(rgbIdentity,
  947. pbIdentitySet,
  948. pcbSAI,
  949. ppbSAI);
  950. }
  951. else
  952. {
  953. hr = S_OK; // OK, we failed this one, go on to the next
  954. }
  955. }
  956. }
  957. pbCurrentData += sizeof(DWORD);
  958. }
  959. }
  960. }
  961. ErrorExit:
  962. if (pbIdentityData)
  963. LocalFree(pbIdentityData);
  964. return(hr);
  965. }
  966. //+---------------------------------------------------------------------------
  967. //
  968. // Function: ConvertCredentialData
  969. //
  970. // Synopsis: Converts credentials stored prior to sysprep so they may be decrypted after sysprep.
  971. // It does this by retrieving the pre-sysprep key, decrypting the credentials with it, and then
  972. // encrypting them using the new post-sysprep key.
  973. //
  974. // Arguments: pRC2KeyPreSysprep - pointer to presysprep key
  975. // pRC2KeyPostSysprep - pointer to postsysprep key
  976. // pcbSAI - pointer to dword containing count of bytes in SAI
  977. // ppbSAI - pointer to pointer to SAI data
  978. // pcbSAC - pointer to dword containing count of bytes in SAC
  979. // ppbSAC - pointer to pointer to SAC data
  980. //
  981. // Returns: HRESULT
  982. //
  983. //----------------------------------------------------------------------------
  984. HRESULT ConvertCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep,
  985. RC2_KEY_INFO* pRC2KeyPostSysprep,
  986. DWORD* pcbSAI,
  987. BYTE** ppbSAI,
  988. DWORD* pcbSAC,
  989. BYTE** ppbSAC)
  990. {
  991. HRESULT hr = S_OK;
  992. HRESULT hRes2 = S_OK; // for hresults that we don't want to affect our return code
  993. BYTE* pbEncryptedData = NULL;
  994. DWORD cbEncryptedData = 0;
  995. BYTE* pbSACEnd = *ppbSAC + *pcbSAC;
  996. BYTE* pbCredential = *ppbSAC + USN_SIZE; // Advance past USN.
  997. DWORD cbCredential = 0;
  998. //
  999. // Read credential count
  1000. //
  1001. DWORD dwCredentialCount = 0;
  1002. CopyMemory(&dwCredentialCount, pbCredential, sizeof(dwCredentialCount));
  1003. pbCredential += sizeof(dwCredentialCount);
  1004. //
  1005. // Loop through all credentials, decrypting, encrypting, and updating each one
  1006. //
  1007. JOB_CREDENTIALS jc;
  1008. RC2_KEY_INFO RC2KeyPreSysprepCopy;
  1009. RC2_KEY_INFO RC2KeyPostSysprepCopy;
  1010. for (DWORD dwCredentialIndex = 0;
  1011. (dwCredentialIndex < dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) < *pcbSAC);
  1012. dwCredentialIndex++
  1013. )
  1014. {
  1015. //
  1016. // Ensure sufficient space remains in the buffer.
  1017. //
  1018. if ((pbCredential + sizeof(cbCredential)) > pbSACEnd)
  1019. {
  1020. ASSERT_SECURITY_DBASE_CORRUPT();
  1021. hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
  1022. goto ErrorExit;
  1023. }
  1024. CopyMemory(&cbCredential, pbCredential, sizeof(cbCredential));
  1025. pbCredential += sizeof(cbCredential);
  1026. //
  1027. // Check remaining buffer size again
  1028. //
  1029. if ((pbCredential + HASH_DATA_SIZE) > pbSACEnd)
  1030. {
  1031. ASSERT_SECURITY_DBASE_CORRUPT();
  1032. hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
  1033. goto ErrorExit;
  1034. }
  1035. //
  1036. // Check remaining buffer size yet again
  1037. //
  1038. if ((pbCredential + cbCredential) > pbSACEnd)
  1039. {
  1040. ASSERT_SECURITY_DBASE_CORRUPT();
  1041. hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
  1042. goto ErrorExit;
  1043. }
  1044. //
  1045. // The start of the credential refers to the credential identity.
  1046. // Skip over this to refer to the encrypted bits. Copy the pbCredential
  1047. // and cbCredential for this dwCredentialIndex so that it can be
  1048. // manipulated without altering the original buffer.
  1049. //
  1050. cbEncryptedData = cbCredential - HASH_DATA_SIZE;
  1051. pbEncryptedData = new BYTE[cbEncryptedData];
  1052. CopyMemory(pbEncryptedData, pbCredential + HASH_DATA_SIZE, cbEncryptedData);
  1053. //
  1054. // The decryption process (CBC call) clobbers the key, so make a fresh
  1055. // copy from the source each time through the loop
  1056. //
  1057. CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));
  1058. //
  1059. // Decrypt credential using the pre-sysprep key
  1060. //
  1061. // *** Important ***
  1062. //
  1063. // The encrypted credentials passed are decrypted *in-place*.
  1064. // Therefore, buffer content has been compromised;
  1065. // plus, the decrypted data must be zeroed immediately
  1066. // following decryption (even in a failure case).
  1067. //
  1068. hRes2 = DecryptCredentials(RC2KeyPreSysprepCopy, cbEncryptedData, pbEncryptedData, &jc);
  1069. //
  1070. // Don't leave the plain-text password on the heap.
  1071. //
  1072. SecureZeroMemory(pbEncryptedData, cbEncryptedData);
  1073. if (SUCCEEDED(hRes2))
  1074. {
  1075. //
  1076. // The encryption process (CBC call) clobbers the key, so make a fresh
  1077. // copy from the source each time through the loop
  1078. //
  1079. CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));
  1080. //
  1081. // Encrypt credential using the post-sysprep key
  1082. //
  1083. hRes2 = EncryptCredentials(RC2KeyPostSysprepCopy,
  1084. jc.wszAccount,
  1085. jc.wszDomain,
  1086. jc.wszPassword,
  1087. NULL,
  1088. &cbEncryptedData,
  1089. &pbEncryptedData);
  1090. //
  1091. // Don't leave the plain-text password on the stack
  1092. //
  1093. ZERO_PASSWORD(jc.wszPassword);
  1094. jc.ccPassword = 0;
  1095. if (SUCCEEDED(hRes2))
  1096. {
  1097. //
  1098. // Update the old encrypted credential with the newly encrypted credential
  1099. //
  1100. hr = SACUpdateCredential(cbEncryptedData,
  1101. pbEncryptedData,
  1102. cbCredential,
  1103. pbCredential,
  1104. pcbSAC,
  1105. ppbSAC);
  1106. if (FAILED(hr))
  1107. {
  1108. goto ErrorExit; // a failure to update leaves the SAC data
  1109. // in an unknown state, so we need to bail
  1110. }
  1111. }
  1112. }
  1113. //
  1114. // Clean up
  1115. //
  1116. delete pbEncryptedData;
  1117. pbEncryptedData = NULL;
  1118. //
  1119. // Advance to next credential.
  1120. //
  1121. pbCredential += (HASH_DATA_SIZE + cbEncryptedData);
  1122. }
  1123. //
  1124. // Still more integrity checking.
  1125. // Did we reach the end of the buffer exactly when we thought we should?
  1126. // If not, something is wrong somewhere.
  1127. //
  1128. if ((dwCredentialIndex == dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) != *pcbSAC) ||
  1129. (dwCredentialIndex != dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) > *pcbSAC))
  1130. {
  1131. //
  1132. // The database appears to be truncated.
  1133. //
  1134. ASSERT_SECURITY_DBASE_CORRUPT();
  1135. hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
  1136. goto ErrorExit;
  1137. }
  1138. ErrorExit:
  1139. if (pbEncryptedData) LocalFree(pbEncryptedData);
  1140. return(hr);
  1141. }
  1142. //+---------------------------------------------------------------------------
  1143. //
  1144. // Function: ConvertNetScheduleCredentialData
  1145. //
  1146. // Synopsis: Converts Net Schedule credential stored prior to sysprep so it may be decrypted after sysprep.
  1147. // It does this by using the pre-sysprep key to decrypt the credential with it, and then
  1148. // encrypting it using the new post-sysprep key.
  1149. //
  1150. // Arguments: pRC2KeyPreSysprep - pointer to presysprep key
  1151. // pRC2KeyPostSysprep - pointer to postsysprep key
  1152. //
  1153. // Returns: HRESULT
  1154. //
  1155. //----------------------------------------------------------------------------
  1156. HRESULT ConvertNetScheduleCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep, RC2_KEY_INFO* pRC2KeyPostSysprep)
  1157. {
  1158. BYTE* pbEncryptedData = NULL;
  1159. DWORD cbEncryptedData = 0;
  1160. //
  1161. // Read the Net Schedule account from LSA
  1162. //
  1163. HRESULT hr = ReadLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, &cbEncryptedData, &pbEncryptedData);
  1164. if (FAILED(hr))
  1165. {
  1166. goto ErrorExit;
  1167. }
  1168. if (hr == S_FALSE || cbEncryptedData <= sizeof(DWORD))
  1169. {
  1170. //
  1171. // The information was specified previously but has been reset since.
  1172. // NOTE: This will be the case if the value has been reset back to LocalSystem,
  1173. // as it merely stores a dword = 0x00000000 in that case
  1174. //
  1175. //
  1176. // Do nothing -- there is no data to convert
  1177. //
  1178. }
  1179. else
  1180. {
  1181. //
  1182. // The decryption process (CBC call) clobbers the key, so make a copy for use
  1183. //
  1184. RC2_KEY_INFO RC2KeyPreSysprepCopy;
  1185. CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));
  1186. JOB_CREDENTIALS jc;
  1187. hr = DecryptCredentials(RC2KeyPreSysprepCopy,
  1188. cbEncryptedData,
  1189. pbEncryptedData,
  1190. &jc);
  1191. SecureZeroMemory(&RC2KeyPreSysprepCopy, sizeof(RC2_KEY_INFO));
  1192. if (FAILED(hr))
  1193. {
  1194. goto ErrorExit;
  1195. }
  1196. //
  1197. // Don't leave the plain-text password on the heap.
  1198. //
  1199. SecureZeroMemory(pbEncryptedData, cbEncryptedData);
  1200. //
  1201. // The encryption process (CBC call) clobbers the key, so make a copy for use
  1202. //
  1203. RC2_KEY_INFO RC2KeyPostSysprepCopy;
  1204. CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));
  1205. //
  1206. // Encrypt credential using the post-sysprep key
  1207. //
  1208. hr = EncryptCredentials(RC2KeyPostSysprepCopy,
  1209. jc.wszAccount,
  1210. jc.wszDomain,
  1211. jc.wszPassword,
  1212. NULL,
  1213. &cbEncryptedData,
  1214. &pbEncryptedData);
  1215. SecureZeroMemory(&RC2KeyPostSysprepCopy, sizeof(RC2_KEY_INFO));
  1216. if (FAILED(hr))
  1217. {
  1218. goto ErrorExit;
  1219. }
  1220. //
  1221. // Don't leave the plain-text password on the stack
  1222. //
  1223. ZERO_PASSWORD(jc.wszPassword);
  1224. hr = WriteLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, cbEncryptedData, pbEncryptedData);
  1225. }
  1226. ErrorExit:
  1227. if (pbEncryptedData != NULL)
  1228. LocalFree(pbEncryptedData);
  1229. return(hr);
  1230. }