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.

523 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 2002.
  5. //
  6. // File: conversion.cxx
  7. //
  8. // Contents: data conversion functions
  9. //
  10. // History: 11-Nov-02 Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include "..\pch\headers.hxx"
  14. #include "common.hxx"
  15. #include "security.hxx"
  16. #include "statsync.hxx"
  17. extern CStaticCritSec gcsSSCritSection;
  18. #define SCH_DATA_VERSION L"DataVersion"
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Function: CheckDataVersion
  22. //
  23. // Synopsis: Check for registry key that will indicate
  24. // the version of the scheduler data.
  25. //
  26. // Arguments: None
  27. //
  28. // Returns: DWORD - indicates data version
  29. //
  30. //----------------------------------------------------------------------------
  31. DWORD CheckDataVersion(void)
  32. {
  33. DWORD dwDataVersion = 0;
  34. HKEY hSchedKey = NULL;
  35. long lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  36. SCH_AGENT_KEY,
  37. 0,
  38. KEY_QUERY_VALUE | KEY_SET_VALUE,
  39. &hSchedKey);
  40. if (lErr == ERROR_SUCCESS)
  41. {
  42. DWORD dwType;
  43. DWORD cb = sizeof(dwDataVersion);
  44. lErr = RegQueryValueEx(hSchedKey,
  45. SCH_DATA_VERSION,
  46. NULL,
  47. &dwType,
  48. (LPBYTE) &dwDataVersion,
  49. &cb);
  50. if (lErr != ERROR_SUCCESS || dwType != REG_DWORD)
  51. {
  52. dwDataVersion = 0;
  53. }
  54. }
  55. if (hSchedKey != NULL)
  56. {
  57. RegCloseKey(hSchedKey);
  58. }
  59. return dwDataVersion;
  60. }
  61. //+---------------------------------------------------------------------------
  62. //
  63. // Function: RecordDataVersion
  64. //
  65. // Synopsis: Update registry key to record current data version
  66. //
  67. // Arguments: dwDataVersion -- version value to store
  68. //
  69. // Returns: HRESULT
  70. //
  71. //----------------------------------------------------------------------------
  72. HRESULT RecordDataVersion(DWORD dwDataVersion)
  73. {
  74. HRESULT hr = S_OK;
  75. HKEY hSchedKey = NULL;
  76. long lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  77. SCH_AGENT_KEY,
  78. 0,
  79. KEY_QUERY_VALUE | KEY_SET_VALUE,
  80. &hSchedKey);
  81. if (lErr == ERROR_SUCCESS)
  82. {
  83. DWORD cb = sizeof(dwDataVersion);
  84. RegSetValueEx(hSchedKey,
  85. SCH_DATA_VERSION,
  86. NULL,
  87. REG_DWORD,
  88. (CONST BYTE *)&dwDataVersion,
  89. cb);
  90. if (lErr != ERROR_SUCCESS)
  91. {
  92. schDebugOut((DEB_ERROR, "RegSetValueEx of RecordDataVersion value failed %ld\n", lErr));
  93. hr = E_FAIL;
  94. }
  95. }
  96. if (hSchedKey != NULL)
  97. {
  98. RegCloseKey(hSchedKey);
  99. }
  100. return hr;
  101. }
  102. //+---------------------------------------------------------------------------
  103. //
  104. // Function: ConvertJobIdentityHashMethod
  105. //
  106. // Synopsis: Create new identity entries for each job file in the SAI
  107. // utilizing the new hash method.
  108. //
  109. // Arguments: None
  110. //
  111. // Returns: HRESULT
  112. //
  113. //----------------------------------------------------------------------------
  114. HRESULT ConvertJobIdentityHashMethod(void)
  115. {
  116. HCRYPTPROV hCSP = NULL;
  117. WCHAR* pwszTasksFolder = NULL;
  118. HANDLE hFileEnum = INVALID_HANDLE_VALUE;
  119. DWORD cbSAI = 0;
  120. DWORD cbSAC = 0;
  121. BYTE* pbSAI = NULL;
  122. BYTE* pbSAC = NULL;
  123. BYTE* pbIdentitySet = NULL;
  124. //
  125. // Obtain a provider handle to the CSP (for use with Crypto API).
  126. //
  127. HRESULT hr = GetCSPHandle(&hCSP);
  128. if (FAILED(hr))
  129. {
  130. return(hr);
  131. }
  132. //
  133. // Guard SA security database access.
  134. //
  135. EnterCriticalSection(&gcsSSCritSection);
  136. //
  137. // Read the security database so we can do the lookups
  138. //
  139. hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
  140. if (SUCCEEDED(hr))
  141. {
  142. if (cbSAI <= SAI_HEADER_SIZE)
  143. {
  144. //
  145. // Database empty, nothing to do.
  146. //
  147. }
  148. else
  149. {
  150. //
  151. // Enumerate job objects in the task's folder directory.
  152. //
  153. hr = GetTasksFolder(&pwszTasksFolder);
  154. if (SUCCEEDED(hr))
  155. {
  156. WCHAR wszSearchPath[MAX_PATH + 1];
  157. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  158. if (SUCCEEDED(hr))
  159. {
  160. hr = StringCchCat(wszSearchPath, MAX_PATH + 1, EXTENSION_WILDCARD TSZ_JOB);
  161. if (SUCCEEDED(hr))
  162. {
  163. WIN32_FIND_DATA fd;
  164. ZeroMemory(&fd, sizeof(fd));
  165. DWORD dwRet = 0;
  166. if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
  167. {
  168. //
  169. // Either no jobs (this is OK), or an error occurred.
  170. //
  171. dwRet = GetLastError();
  172. if (dwRet != ERROR_FILE_NOT_FOUND)
  173. {
  174. hr = _HRESULT_FROM_WIN32(dwRet);
  175. }
  176. }
  177. else
  178. {
  179. //
  180. // Must concatenate the filename returned from the enumeration onto the folder path
  181. // before computing the hash. Prepare for doing that repeatedly by taking the path,
  182. // adding a slash, and remembering the next character position.
  183. //
  184. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
  185. if (SUCCEEDED(hr))
  186. {
  187. DWORD iConcatenation = lstrlenW(pwszTasksFolder);
  188. wszSearchPath[iConcatenation++] = L'\\';
  189. //
  190. // Process each found file
  191. //
  192. BYTE rgbIdentity[HASH_DATA_SIZE];
  193. DWORD dwCredentialIndex = 0;
  194. BOOL bIsPasswordNull = FALSE;
  195. BYTE* pbFoundIdentity = NULL;
  196. DWORD cbFileName = 0;
  197. while (dwRet != ERROR_NO_MORE_FILES)
  198. {
  199. //
  200. // Truncate the existing name after the folder path,
  201. // then concatenate the new filename onto it.
  202. //
  203. wszSearchPath[iConcatenation] = L'\0';
  204. if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
  205. {
  206. //
  207. // Hash the job into a unique identity using the old method
  208. //
  209. if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity, 0)))
  210. {
  211. //
  212. // Find the identity in the SAI for this job
  213. //
  214. hr = SAIFindIdentity(rgbIdentity,
  215. cbSAI,
  216. pbSAI,
  217. &dwCredentialIndex,
  218. &bIsPasswordNull,
  219. &pbFoundIdentity,
  220. NULL,
  221. NULL);
  222. //
  223. // S_OK means the identity was found; S_FALSE means it wasn't
  224. // Other codes are errors. We process only the S_OKs, of course.
  225. //
  226. if (S_OK == hr)
  227. {
  228. //
  229. // Hash the job into a unique identity using the new method
  230. //
  231. if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity)))
  232. {
  233. //
  234. // Store a NULL password by flipping the last bit of the hash data.
  235. //
  236. if (bIsPasswordNull)
  237. {
  238. LAST_HASH_BYTE(rgbIdentity) ^= 1;
  239. }
  240. //
  241. // Update the old identity in place with the new one
  242. //
  243. hr = SAIUpdateIdentity(rgbIdentity,
  244. pbFoundIdentity,
  245. cbSAI,
  246. pbSAI);
  247. if (FAILED(hr))
  248. {
  249. break;
  250. }
  251. }
  252. else
  253. {
  254. //
  255. // we failed to produce a new hash for this file;
  256. // while unexpected, it shouldn't be fatal to the conversion process;
  257. // we just won't be able to convert this job; go on to the next job
  258. //
  259. }
  260. }
  261. else
  262. {
  263. //
  264. // we failed to find the original job identity;
  265. // perhaps its credentials have already been lost;
  266. // there's nothing we can do; go on to the next job
  267. //
  268. hr = S_OK;
  269. }
  270. }
  271. }
  272. if (!FindNextFile(hFileEnum, &fd))
  273. {
  274. dwRet = GetLastError();
  275. if (dwRet != ERROR_NO_MORE_FILES)
  276. {
  277. hr = _HRESULT_FROM_WIN32(dwRet);
  278. }
  279. break;
  280. }
  281. }
  282. if (SUCCEEDED(hr))
  283. {
  284. //
  285. // Update security database with the new identities
  286. //
  287. hr = WriteSecurityDBase(cbSAI, pbSAI, cbSAC, pbSAC);
  288. }
  289. }
  290. }
  291. }
  292. }
  293. }
  294. }
  295. }
  296. if (hFileEnum != INVALID_HANDLE_VALUE)
  297. {
  298. FindClose(hFileEnum);
  299. }
  300. if (pwszTasksFolder)
  301. {
  302. delete [] pwszTasksFolder;
  303. }
  304. if (pbSAI)
  305. {
  306. LocalFree(pbSAI);
  307. }
  308. if (pbSAC)
  309. {
  310. LocalFree(pbSAC);
  311. }
  312. if (hCSP)
  313. {
  314. CloseCSPHandle(hCSP);
  315. }
  316. LeaveCriticalSection(&gcsSSCritSection);
  317. return hr;
  318. }
  319. //+---------------------------------------------------------------------------
  320. //
  321. // Function: ConvertNetScheduleJobSignatures
  322. //
  323. // Synopsis: Netschedule jobs (AT jobs) are signed internally with a hash so
  324. // that the service can validate their authenticity at run time.
  325. // Since we are changing how the hash is created, in order to be
  326. // runnable again, the AT jobs must be signed again with the new hash.
  327. // Prior to re-signing, we will verify the jobs using the old hash.
  328. //
  329. // Arguments: None
  330. //
  331. // Returns: HRESULT
  332. //
  333. //----------------------------------------------------------------------------
  334. HRESULT ConvertNetScheduleJobSignatures(void)
  335. {
  336. HRESULT hr = S_OK;
  337. HANDLE hFileEnum = INVALID_HANDLE_VALUE;
  338. WCHAR wszSearchPath[MAX_PATH + 1];
  339. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  340. if (SUCCEEDED(hr))
  341. {
  342. hr = StringCchCat(wszSearchPath, MAX_PATH + 1, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
  343. if (SUCCEEDED(hr))
  344. {
  345. WIN32_FIND_DATA fd;
  346. ZeroMemory(&fd, sizeof(fd));
  347. DWORD dwRet = 0;
  348. if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
  349. {
  350. //
  351. // Either no jobs (this is OK), or an error occurred.
  352. //
  353. dwRet = GetLastError();
  354. if (dwRet != ERROR_FILE_NOT_FOUND)
  355. {
  356. hr = _HRESULT_FROM_WIN32(dwRet);
  357. }
  358. }
  359. else
  360. {
  361. //
  362. // Must concatenate the filename returned from the enumeration onto the folder path
  363. // before loading the job. Prepare for doing that repeatedly by taking the path,
  364. // adding a slash, and remembering the next character position.
  365. //
  366. hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  367. if (SUCCEEDED(hr))
  368. {
  369. DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
  370. wszSearchPath[iConcatenation++] = L'\\';
  371. //
  372. // Process each found file
  373. //
  374. while (dwRet != ERROR_NO_MORE_FILES)
  375. {
  376. //
  377. // Truncate the existing name after the folder path,
  378. // then concatenate the new filename onto it.
  379. //
  380. wszSearchPath[iConcatenation] = L'\0';
  381. if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
  382. {
  383. //
  384. // Load, verify, sign, save, and release the job
  385. //
  386. CJob* pJob = CJob::Create();
  387. if (!pJob)
  388. {
  389. hr = E_OUTOFMEMORY;
  390. break;
  391. }
  392. if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
  393. {
  394. //
  395. // verify using the old hash
  396. //
  397. if (pJob->VerifySignature(0))
  398. {
  399. //
  400. // sign using the new hash
  401. //
  402. if (SUCCEEDED(pJob->Sign()))
  403. {
  404. pJob->SaveWithRetry(pJob->GetFileName(),
  405. FALSE,
  406. SAVEP_VARIABLE_LENGTH_DATA |
  407. SAVEP_PRESERVE_NET_SCHEDULE);
  408. }
  409. }
  410. }
  411. pJob->Release();
  412. }
  413. if (!FindNextFile(hFileEnum, &fd))
  414. {
  415. dwRet = GetLastError();
  416. if (dwRet != ERROR_NO_MORE_FILES)
  417. {
  418. hr = _HRESULT_FROM_WIN32(dwRet);
  419. }
  420. break;
  421. }
  422. }
  423. }
  424. }
  425. if (hFileEnum != INVALID_HANDLE_VALUE)
  426. {
  427. FindClose(hFileEnum);
  428. }
  429. }
  430. }
  431. return hr;
  432. }
  433. //+---------------------------------------------------------------------------
  434. //
  435. // Function: PerformDataConversions
  436. //
  437. // Synopsis: Perform any data conversions that may be required as a result
  438. // of changes to task scheduler. This function is intended to
  439. // allow potential future conversions to be added easily, and to
  440. // allow conversions of data that may have already had earlier
  441. // conversions applied.
  442. //
  443. // Arguments: None
  444. //
  445. // Returns: HRESULT
  446. //
  447. //----------------------------------------------------------------------------
  448. HRESULT PerformDataConversions(void)
  449. {
  450. HRESULT hr = S_OK;
  451. switch (CheckDataVersion())
  452. {
  453. case 0:
  454. hr = ConvertJobIdentityHashMethod();
  455. if (SUCCEEDED(hr))
  456. {
  457. hr = RecordDataVersion(1);
  458. }
  459. // fall through to handle next higher conversion
  460. case 1:
  461. if (SUCCEEDED(hr))
  462. {
  463. hr = ConvertNetScheduleJobSignatures();
  464. if (SUCCEEDED(hr))
  465. {
  466. hr = RecordDataVersion(2);
  467. }
  468. }
  469. // fall through to handle next higher conversion
  470. case 2:
  471. // nothing to do for now
  472. // version 2 is currently the highest and already has all changes applied
  473. break;
  474. default:
  475. // if the value is not listed above, then someone has altered the registry;
  476. // the version value is meaningless to us, so it is safest to not do anything,
  477. // but reflect the unusual situation in the error code in case we want to log it
  478. hr = S_FALSE;
  479. break;
  480. }
  481. return hr;
  482. }