main
cpp 2,493 lines 85.5 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be";
6 static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified";
7 static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache";
8 static const DWORD FILE_OPERATION_RETRY_COUNT = 3;
9 static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
10
11 static HRESULT CacheVerifyPayloadSignature(
12 __in BURN_PAYLOAD* pPayload,
13 __in_z LPCWSTR wzUnverifiedPayloadPath,
14 __in HANDLE hFile,
15 __in BURN_CACHE_STEP cacheStep,
16 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
17 __in LPPROGRESS_ROUTINE pfnProgress,
18 __in LPVOID pContext
19 );
20 static HRESULT CalculatePotentialBaseWorkingFolders(
21 __in BURN_CACHE* pCache,
22 __in BURN_ENGINE_COMMAND* pInternalCommand,
23 __in LPCWSTR wzAcquisitionFolder
24 );
25 static HRESULT CalculateWorkingFolders(
26 __in BURN_CACHE* pCache,
27 __in BURN_ENGINE_COMMAND* pInternalCommand
28 );
29 static HRESULT GetLastUsedSourceFolder(
30 __in BURN_VARIABLES* pVariables,
31 __out_z LPWSTR* psczLastSource
32 );
33 static HRESULT SecurePerMachineCacheRoot(
34 __in BURN_CACHE* pCache
35 );
36 static HRESULT CreateCompletedPath(
37 __in BURN_CACHE* pCache,
38 __in BOOL fPerMachine,
39 __in LPCWSTR wzCacheId,
40 __in LPCWSTR wzFilePath,
41 __out_z LPWSTR* psczCachePath
42 );
43 static HRESULT CreateUnverifiedPath(
44 __in BURN_CACHE* pCache,
45 __in BOOL fPerMachine,
46 __in_z LPCWSTR wzPayloadId,
47 __out_z LPWSTR* psczUnverifiedPayloadPath
48 );
49 static HRESULT GetRootPath(
50 __in BURN_CACHE* pCache,
51 __in BOOL fPerMachine,
52 __in BOOL fAllowRedirect,
53 __deref_out_z LPWSTR* psczRootPath
54 );
55 static HRESULT VerifyThenTransferContainer(
56 __in BURN_CONTAINER* pContainer,
57 __in_z LPCWSTR wzCachedPath,
58 __in_z LPCWSTR wzUnverifiedContainerPath,
59 __in BOOL fMove,
60 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
61 __in LPPROGRESS_ROUTINE pfnProgress,
62 __in LPVOID pContext
63 );
64 static HRESULT VerifyThenTransferPayload(
65 __in BURN_PAYLOAD* pPayload,
66 __in_z LPCWSTR wzCachedPath,
67 __in_z LPCWSTR wzUnverifiedPayloadPath,
68 __in BOOL fMove,
69 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
70 __in LPPROGRESS_ROUTINE pfnProgress,
71 __in LPVOID pContext
72 );
73 static HRESULT CacheTransferFileWithRetry(
74 __in_z LPCWSTR wzSourcePath,
75 __in_z LPCWSTR wzDestinationPath,
76 __in BOOL fMove,
77 __in BURN_CACHE_STEP cacheStep,
78 __in DWORD64 qwFileSize,
79 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
80 __in LPPROGRESS_ROUTINE pfnProgress,
81 __in LPVOID pContext
82 );
83 static HRESULT VerifyFileAgainstContainer(
84 __in BURN_CONTAINER* pContainer,
85 __in_z LPCWSTR wzVerifyPath,
86 __in BOOL fAlreadyCached,
87 __in BURN_CACHE_STEP cacheStep,
88 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
89 __in LPPROGRESS_ROUTINE pfnProgress,
90 __in LPVOID pContext
91 );
92 static HRESULT VerifyFileAgainstPayload(
93 __in BURN_PAYLOAD* pPayload,
94 __in_z LPCWSTR wzVerifyPath,
95 __in BOOL fAlreadyCached,
96 __in BURN_CACHE_STEP cacheStep,
97 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
98 __in LPPROGRESS_ROUTINE pfnProgress,
99 __in LPVOID pContext
100 );
101 static HRESULT ResetPathPermissions(
102 __in BOOL fPerMachine,
103 __in_z LPCWSTR wzPath
104 );
105 static HRESULT SecurePath(
106 __in LPCWSTR wzPath
107 );
108 static HRESULT CopyEngineToWorkingFolder(
109 __in BOOL fElevated,
110 __in BURN_CACHE* pCache,
111 __in_z LPCWSTR wzSourcePath,
112 __in_z LPCWSTR wzWorkingFolderName,
113 __in_z LPCWSTR wzExecutableName,
114 __in BURN_SECTION* pSection,
115 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
116 );
117 static HRESULT CopyEngineWithSignatureFixup(
118 __in HANDLE hEngineFile,
119 __in_z LPCWSTR wzEnginePath,
120 __in_z LPCWSTR wzTargetPath,
121 __in BURN_SECTION* pSection
122 );
123 static HRESULT RemoveBundleOrPackage(
124 __in BURN_CACHE* pCache,
125 __in BOOL fBundle,
126 __in BOOL fPerMachine,
127 __in_z LPCWSTR wzBundleOrPackageId,
128 __in_z LPCWSTR wzCacheId
129 );
130 static HRESULT VerifyFileSize(
131 __in HANDLE hFile,
132 __in DWORD64 qwFileSize,
133 __in_z LPCWSTR wzUnverifiedPayloadPath
134 );
135 static HRESULT VerifyHash(
136 __in BYTE* pbHash,
137 __in DWORD cbHash,
138 __in DWORD64 qwFileSize,
139 __in BOOL fVerifyFileSize,
140 __in_z LPCWSTR wzUnverifiedPayloadPath,
141 __in HANDLE hFile,
142 __in BURN_CACHE_STEP cacheStep,
143 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
144 __in LPPROGRESS_ROUTINE pfnProgress,
145 __in LPVOID pContext
146 );
147 static HRESULT VerifyPayloadAgainstCertChain(
148 __in BURN_PAYLOAD* pPayload,
149 __in PCCERT_CHAIN_CONTEXT pChainContext
150 );
151 static HRESULT SendCacheBeginMessage(
152 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
153 __in LPVOID pContext,
154 __in BURN_CACHE_STEP cacheStep
155 );
156 static HRESULT SendCacheSuccessMessage(
157 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
158 __in LPVOID pContext,
159 __in DWORD64 qwFileSize
160 );
161 static HRESULT SendCacheCompleteMessage(
162 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
163 __in LPVOID pContext,
164 __in HRESULT hrStatus
165 );
166 static HRESULT SendCacheFailureMessage(
167 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
168 __in LPVOID pContext,
169 __in BURN_CACHE_STEP cacheStep
170 );
171
172 extern "C" HRESULT CacheInitialize(
173 __in BURN_CACHE* pCache,
174 __in BURN_ENGINE_COMMAND* pInternalCommand
175 )
176 {
177 Assert(!pCache->fInitializedCache);
178
179 HRESULT hr = S_OK;
180 LPWSTR sczAppData = NULL;
181 BOOL fPathEqual = FALSE;
182
183 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
184 // Always construct the default machine package cache path so we can determine if we're redirected.
185 hr = ShelGetFolder(&sczAppData, CSIDL_COMMON_APPDATA);
186 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
187
188 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultMachinePackageCache);
189 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine");
190
191 hr = PathBackslashTerminate(&pCache->sczDefaultMachinePackageCache);
192 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine");
193
194
195 // The machine package cache can be redirected through policy.
196 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &pCache->sczCurrentMachinePackageCache);
197 ExitOnFailure(hr, "Failed to read PackageCache policy directory.");
198
199 if (pCache->sczCurrentMachinePackageCache && PathIsFullyQualified(pCache->sczCurrentMachinePackageCache))
200 {
201 hr = PathBackslashTerminate(&pCache->sczCurrentMachinePackageCache);
202 ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name.");
203 }
204 else
205 {
206 if (pCache->sczCurrentMachinePackageCache)
207 {
208 LogErrorId(E_INVALIDARG, MSG_INVALID_POLICY_MACHINE_PACKAGE_CACHE, pCache->sczCurrentMachinePackageCache, NULL, NULL);
209 }
210
211 hr = StrAllocString(&pCache->sczCurrentMachinePackageCache, pCache->sczDefaultMachinePackageCache, 0);
212 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
213 }
214
215 hr = PathCompareCanonicalized(pCache->sczDefaultMachinePackageCache, pCache->sczCurrentMachinePackageCache, &fPathEqual);
216 ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
217
218 pCache->fCustomMachinePackageCache = !fPathEqual;
219
220
221 hr = ShelGetFolder(&sczAppData, CSIDL_LOCAL_APPDATA);
222 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
223
224 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultUserPackageCache);
225 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user");
226
227 hr = PathBackslashTerminate(&pCache->sczDefaultUserPackageCache);
228 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user");
229
230
231 hr = CalculateWorkingFolders(pCache, pInternalCommand);
232
233 pCache->fInitializedCache = TRUE;
234
235 LExit:
236 ReleaseStr(sczAppData);
237
238 return hr;
239 }
240
241
242 extern "C" HRESULT CacheInitializeSources(
243 __in BURN_CACHE* pCache,
244 __in BURN_REGISTRATION* pRegistration,
245 __in BURN_VARIABLES* pVariables
246 )
247 {
248 Assert(!pCache->fInitializedCacheSources);
249
250 HRESULT hr = S_OK;
251 LPWSTR sczCurrentPath = NULL;
252 LPWSTR sczCompletedFolder = NULL;
253 LPWSTR sczCompletedPath = NULL;
254 LPWSTR sczOriginalSource = NULL;
255 LPWSTR sczOriginalSourceFolder = NULL;
256 BOOL fPathEqual = FALSE;
257
258 hr = PathForCurrentProcess(&sczCurrentPath, NULL);
259 ExitOnFailure(hr, "Failed to get current process path.");
260
261 // Determine if we are running from the package cache or not.
262 hr = CacheGetCompletedPath(pCache, pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder);
263 ExitOnFailure(hr, "Failed to get completed path for bundle.");
264
265 hr = PathConcatRelativeToFullyQualifiedBase(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
266 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
267
268 hr = PathCompareCanonicalized(sczCurrentPath, sczCompletedPath, &fPathEqual);
269 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
270
271 pCache->fRunningFromCache = fPathEqual;
272
273 hr = PathGetDirectory(sczCurrentPath, &pCache->sczSourceProcessFolder);
274 ExitOnFailure(hr, "Failed to initialize cache source folder.");
275
276 // If we're not running from the cache, ensure the original source is set.
277 if (!pCache->fRunningFromCache)
278 {
279 // If the original source has not been set already then set it where the bundle is
280 // running from right now. This value will be persisted and we'll use it when launched
281 // from the package cache since none of our packages will be relative to those locations.
282 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
283 if (E_NOTFOUND == hr)
284 {
285 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, sczCurrentPath, FALSE, FALSE);
286 ExitOnFailure(hr, "Failed to set original source variable.");
287
288 hr = StrAllocString(&sczOriginalSource, sczCurrentPath, 0);
289 ExitOnFailure(hr, "Failed to copy current path to original source.");
290 }
291
292 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder);
293 if (E_NOTFOUND == hr)
294 {
295 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
296 ExitOnFailure(hr, "Failed to get directory from original source path.");
297
298 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE);
299 ExitOnFailure(hr, "Failed to set original source directory variable.");
300 }
301 }
302
303 pCache->fInitializedCacheSources = TRUE;
304
305 LExit:
306 ReleaseStr(sczCurrentPath);
307 ReleaseStr(sczCompletedFolder);
308 ReleaseStr(sczCompletedPath);
309 ReleaseStr(sczOriginalSource);
310 ReleaseStr(sczOriginalSourceFolder);
311
312 return hr;
313 }
314
315 extern "C" HRESULT CacheEnsureAcquisitionFolder(
316 __in BURN_CACHE* pCache
317 )
318 {
319 Assert(pCache->fInitializedCache);
320
321 HRESULT hr = S_OK;
322
323 hr = DirEnsureExists(pCache->sczAcquisitionFolder, NULL);
324 ExitOnFailure(hr, "Failed create acquisition folder.");
325
326 // Best effort to ensure our acquisition folder is not encrypted.
327 ::DecryptFileW(pCache->sczAcquisitionFolder, 0);
328
329 LExit:
330 return hr;
331 }
332
333 extern "C" HRESULT CacheEnsureBaseWorkingFolder(
334 __in BOOL fElevated,
335 __in BURN_CACHE* pCache,
336 __deref_out_z_opt LPWSTR* psczBaseWorkingFolder
337 )
338 {
339 Assert(pCache->fInitializedCache);
340
341 HRESULT hr = S_OK;
342 LPWSTR sczPotential = NULL;
343 PSECURITY_DESCRIPTOR psd = NULL;
344 LPSECURITY_ATTRIBUTES pWorkingFolderAcl = NULL;
345
346 if (!pCache->fInitializedBaseWorkingFolder)
347 {
348 // If elevated, allocate the pWorkingFolderAcl to protect the working folder to only SYSTEM and Admins.
349 if (fElevated)
350 {
351 LPCWSTR wzSddl = L"D:PAI(A;;FA;;;BA)(A;OICIIO;GA;;;BA)(A;;FA;;;SY)(A;OICIIO;GA;;;SY)";
352 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
353 {
354 ExitWithLastError(hr, "Failed to create the security descriptor for the working folder.");
355 }
356
357 pWorkingFolderAcl = reinterpret_cast<LPSECURITY_ATTRIBUTES>(MemAlloc(sizeof(SECURITY_ATTRIBUTES), TRUE));
358 pWorkingFolderAcl->nLength = sizeof(SECURITY_ATTRIBUTES);
359 pWorkingFolderAcl->lpSecurityDescriptor = psd;
360 pWorkingFolderAcl->bInheritHandle = FALSE;
361 }
362
363 for (DWORD i = 0; i < pCache->cPotentialBaseWorkingFolders; ++i)
364 {
365 hr = PathConcatRelativeToFullyQualifiedBase(pCache->rgsczPotentialBaseWorkingFolders[i], pCache->wzGuid, &sczPotential);
366 if (SUCCEEDED(hr))
367 {
368 hr = DirEnsureExists(sczPotential, pWorkingFolderAcl);
369 if (SUCCEEDED(hr))
370 {
371 pCache->sczBaseWorkingFolder = sczPotential;
372 sczPotential = NULL;
373 break;
374 }
375 }
376
377 LogErrorId(hr, MSG_INVALID_BASE_WORKING_FOLDER, sczPotential, NULL, NULL);
378 }
379
380 ExitOnNull(pCache->sczBaseWorkingFolder, hr, E_INVALIDSTATE, "No usable base working folder found.");
381
382 pCache->fInitializedBaseWorkingFolder = TRUE;
383 }
384
385 // Best effort to ensure our working folder is not encrypted.
386 ::DecryptFileW(pCache->sczBaseWorkingFolder, 0);
387
388 if (psczBaseWorkingFolder)
389 {
390 hr = StrAllocString(psczBaseWorkingFolder, pCache->sczBaseWorkingFolder, 0);
391 ExitOnFailure(hr, "Failed to copy working folder.");
392 }
393
394 LExit:
395 ReleaseMem(pWorkingFolderAcl);
396 if (psd)
397 {
398 ::LocalFree(psd);
399 }
400 ReleaseStr(sczPotential);
401
402 return hr;
403 }
404
405 extern "C" HRESULT CacheCalculateBundleWorkingPath(
406 __in BURN_CACHE* pCache,
407 __in LPCWSTR wzExecutableName,
408 __deref_out_z LPWSTR* psczWorkingPath
409 )
410 {
411 Assert(pCache->fInitializedCache);
412 Assert(pCache->fInitializedBaseWorkingFolder);
413
414 HRESULT hr = S_OK;
415
416 // If the bundle is running out of the package cache then we use that as the
417 // working folder since we feel safe in the package cache.
418 if (CacheBundleRunningFromCache(pCache))
419 {
420 hr = PathForCurrentProcess(psczWorkingPath, NULL);
421 ExitOnFailure(hr, "Failed to get current process path.");
422 }
423 else // Otherwise, use the real working folder.
424 {
425 hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", pCache->sczBaseWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName);
426 ExitOnFailure(hr, "Failed to calculate the bundle working path.");
427 }
428
429 LExit:
430 return hr;
431 }
432
433 extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath(
434 __in BURN_CACHE* pCache,
435 __in_z LPCWSTR wzBundleId,
436 __deref_out_z LPWSTR* psczWorkingPath
437 )
438 {
439 Assert(pCache->fInitializedCache);
440
441 HRESULT hr = S_OK;
442
443 hr = PathConcatRelativeToFullyQualifiedBase(pCache->sczAcquisitionFolder, wzBundleId, psczWorkingPath);
444 ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path.");
445
446 LExit:
447 return hr;
448 }
449
450 extern "C" HRESULT CacheCalculatePayloadWorkingPath(
451 __in BURN_CACHE* pCache,
452 __in BURN_PAYLOAD* pPayload,
453 __deref_out_z LPWSTR* psczWorkingPath
454 )
455 {
456 Assert(pCache->fInitializedCache);
457
458 HRESULT hr = S_OK;
459
460 hr = PathConcatRelativeToFullyQualifiedBase(pCache->sczAcquisitionFolder, pPayload->sczKey, psczWorkingPath);
461 ExitOnFailure(hr, "Failed to append Id as payload unverified path.");
462
463 LExit:
464 return hr;
465 }
466
467 extern "C" HRESULT CacheCalculateContainerWorkingPath(
468 __in BURN_CACHE* pCache,
469 __in BURN_CONTAINER* pContainer,
470 __deref_out_z LPWSTR* psczWorkingPath
471 )
472 {
473 Assert(pCache->fInitializedCache);
474
475 HRESULT hr = S_OK;
476
477 hr = PathConcatRelativeToFullyQualifiedBase(pCache->sczAcquisitionFolder, pContainer->sczHash, psczWorkingPath);
478 ExitOnFailure(hr, "Failed to append hash as container unverified path.");
479
480 LExit:
481 return hr;
482 }
483
484 extern "C" HRESULT CacheGetPerMachineRootCompletedPath(
485 __in BURN_CACHE* pCache,
486 __out_z LPWSTR* psczCurrentRootCompletedPath,
487 __out_z LPWSTR* psczDefaultRootCompletedPath
488 )
489 {
490 HRESULT hr = S_OK;
491
492 *psczCurrentRootCompletedPath = NULL;
493 *psczDefaultRootCompletedPath = NULL;
494
495 hr = SecurePerMachineCacheRoot(pCache);
496 ExitOnFailure(hr, "Failed to secure per-machine cache root.");
497
498 hr = GetRootPath(pCache, TRUE, TRUE, psczCurrentRootCompletedPath);
499 ExitOnFailure(hr, "Failed to get per-machine cache root.");
500
501 if (S_FALSE == hr)
502 {
503 hr = GetRootPath(pCache, TRUE, FALSE, psczDefaultRootCompletedPath);
504 ExitOnFailure(hr, "Failed to get default per-machine cache root.");
505
506 hr = S_FALSE;
507 }
508
509 LExit:
510 return hr;
511 }
512
513 extern "C" HRESULT CacheGetCompletedPath(
514 __in BURN_CACHE* pCache,
515 __in BOOL fPerMachine,
516 __in_z LPCWSTR wzCacheId,
517 __deref_out_z LPWSTR* psczCompletedPath
518 )
519 {
520 HRESULT hr = S_OK;
521 BOOL fRedirected = FALSE;
522 LPWSTR sczRootPath = NULL;
523 LPWSTR sczCurrentCompletedPath = NULL;
524 LPWSTR sczDefaultCompletedPath = NULL;
525
526 hr = GetRootPath(pCache, fPerMachine, TRUE, &sczRootPath);
527 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
528
529 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
530 fRedirected = S_FALSE == hr;
531
532 hr = PathConcatRelativeToFullyQualifiedBase(sczRootPath, wzCacheId, &sczCurrentCompletedPath);
533 ExitOnFailure(hr, "Failed to construct cache path.");
534
535 hr = PathBackslashTerminate(&sczCurrentCompletedPath);
536 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
537
538 // Return the old package cache directory if the new directory does not exist but the old directory does.
539 // If neither package cache directory exists return the (possibly) redirected package cache directory.
540 if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL))
541 {
542 hr = GetRootPath(pCache, fPerMachine, FALSE, &sczRootPath);
543 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
544
545 hr = PathConcatRelativeToFullyQualifiedBase(sczRootPath, wzCacheId, &sczDefaultCompletedPath);
546 ExitOnFailure(hr, "Failed to construct cache path.");
547
548 hr = PathBackslashTerminate(&sczDefaultCompletedPath);
549 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
550
551 if (DirExists(sczDefaultCompletedPath, NULL))
552 {
553 *psczCompletedPath = sczDefaultCompletedPath;
554 sczDefaultCompletedPath = NULL;
555
556 ExitFunction();
557 }
558 }
559
560 *psczCompletedPath = sczCurrentCompletedPath;
561 sczCurrentCompletedPath = NULL;
562
563 LExit:
564 ReleaseNullStr(sczDefaultCompletedPath);
565 ReleaseNullStr(sczCurrentCompletedPath);
566 ReleaseNullStr(sczRootPath);
567
568 return hr;
569 }
570
571 extern "C" HRESULT CacheGetResumePath(
572 __in_z LPCWSTR wzPayloadWorkingPath,
573 __deref_out_z LPWSTR* psczResumePath
574 )
575 {
576 HRESULT hr = S_OK;
577
578 hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
579 ExitOnFailure(hr, "Failed to create resume path.");
580
581 LExit:
582 return hr;
583 }
584
585 extern "C" HRESULT CacheGetLocalSourcePaths(
586 __in_z LPCWSTR wzRelativePath,
587 __in_z LPCWSTR wzSourcePath,
588 __in_z LPCWSTR wzDestinationPath,
589 __in_z_opt LPCWSTR wzLayoutDirectory,
590 __in BURN_CACHE* pCache,
591 __in BURN_VARIABLES* pVariables,
592 __inout LPWSTR** prgSearchPaths,
593 __out DWORD* pcSearchPaths,
594 __out DWORD* pdwLikelySearchPath,
595 __out DWORD* pdwDestinationSearchPath
596 )
597 {
598 AssertSz(pCache->fInitializedCacheSources, "Cache sources weren't initialized");
599
600 HRESULT hr = S_OK;
601 LPWSTR sczCurrentPath = NULL;
602 LPWSTR sczLastSourceFolder = NULL;
603 LPWSTR* psczPath = NULL;
604 BOOL fPreferSourcePathLocation = FALSE;
605 BOOL fTryLastFolder = FALSE;
606 BOOL fTryRelativePath = FALSE;
607 BOOL fSourceIsAbsolute = FALSE;
608 DWORD cSearchPaths = 0;
609 DWORD dwLikelySearchPath = 0;
610 DWORD dwDestinationSearchPath = 0;
611
612 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
613 fPreferSourcePathLocation = !pCache->fRunningFromCache || FAILED(hr);
614 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pCache->sczSourceProcessFolder, -1, sczLastSourceFolder, -1);
615 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1);
616 fSourceIsAbsolute = PathIsRooted(wzSourcePath);
617
618 // If the source path provided is a full path, try that first.
619 if (fSourceIsAbsolute)
620 {
621 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
622 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
623
624 psczPath = *prgSearchPaths + cSearchPaths;
625 ++cSearchPaths;
626
627 hr = StrAllocString(psczPath, wzSourcePath, 0);
628 ExitOnFailure(hr, "Failed to copy absolute source path.");
629 }
630 else
631 {
632 // If none of the paths exist, then most BAs will want to prompt the user with a possible path.
633 // The destination path is a temporary location and so not really a possible path.
634 dwLikelySearchPath = 1;
635 }
636
637 // Try the destination path next.
638 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
639 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
640
641 dwDestinationSearchPath = cSearchPaths;
642 psczPath = *prgSearchPaths + cSearchPaths;
643 ++cSearchPaths;
644
645 hr = StrAllocString(psczPath, wzDestinationPath, 0);
646 ExitOnFailure(hr, "Failed to copy absolute source path.");
647
648 if (!fSourceIsAbsolute)
649 {
650 // Calculate the source path location.
651 // In the case where we are in the bundle's package cache and
652 // couldn't find a last used source that will be the package cache path
653 // which isn't likely to have what we are looking for.
654 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
655 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
656
657 hr = PathConcat(pCache->sczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
658 ExitOnFailure(hr, "Failed to combine source process folder with source.");
659
660 // If we're not running from cache or we couldn't get the last source,
661 // try the source path location next.
662 if (fPreferSourcePathLocation)
663 {
664 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
665 ++cSearchPaths;
666 sczCurrentPath = NULL;
667 }
668
669 // If we have a last used source and it is not the source path location,
670 // add the last used source to the search path next.
671 if (fTryLastFolder)
672 {
673 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
674 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
675
676 psczPath = *prgSearchPaths + cSearchPaths;
677 ++cSearchPaths;
678
679 hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath);
680 ExitOnFailure(hr, "Failed to combine last source with source.");
681 }
682
683 if (!fPreferSourcePathLocation)
684 {
685 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
686 ++cSearchPaths;
687 sczCurrentPath = NULL;
688 }
689
690 // Also consider the layout directory if doing Layout.
691 if (wzLayoutDirectory)
692 {
693 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
694 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
695
696 psczPath = *prgSearchPaths + cSearchPaths;
697 ++cSearchPaths;
698
699 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
700 ExitOnFailure(hr, "Failed to combine layout source with source.");
701 }
702 }
703
704 if (fTryRelativePath)
705 {
706 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
707 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
708
709 hr = PathConcat(pCache->sczSourceProcessFolder, wzRelativePath, &sczCurrentPath);
710 ExitOnFailure(hr, "Failed to combine source process folder with relative.");
711
712 if (fPreferSourcePathLocation)
713 {
714 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
715 ++cSearchPaths;
716 sczCurrentPath = NULL;
717 }
718
719 if (fTryLastFolder)
720 {
721 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
722 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
723
724 psczPath = *prgSearchPaths + cSearchPaths;
725 ++cSearchPaths;
726
727 hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath);
728 ExitOnFailure(hr, "Failed to combine last source with relative.");
729 }
730
731 if (!fPreferSourcePathLocation)
732 {
733 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
734 ++cSearchPaths;
735 sczCurrentPath = NULL;
736 }
737
738 if (wzLayoutDirectory)
739 {
740 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
741 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
742
743 psczPath = *prgSearchPaths + cSearchPaths;
744 ++cSearchPaths;
745
746 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
747 ExitOnFailure(hr, "Failed to combine layout source with relative.");
748 }
749 }
750
751 LExit:
752 ReleaseStr(sczCurrentPath);
753 ReleaseStr(sczLastSourceFolder);
754
755 AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths");
756 *pcSearchPaths = cSearchPaths;
757 *pdwLikelySearchPath = dwLikelySearchPath;
758 *pdwDestinationSearchPath = dwDestinationSearchPath;
759
760 return hr;
761 }
762
763 extern "C" HRESULT CacheSetLastUsedSource(
764 __in BURN_VARIABLES* pVariables,
765 __in_z LPCWSTR wzSourcePath,
766 __in_z LPCWSTR wzRelativePath
767 )
768 {
769 HRESULT hr = S_OK;
770 size_t cchSourcePath = 0;
771 size_t cchRelativePath = 0;
772 size_t iSourceRelativePath = 0;
773 LPWSTR sczSourceFolder = NULL;
774 LPWSTR sczLastSourceFolder = NULL;
775 int nCompare = 0;
776
777 hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath);
778 ExitOnFailure(hr, "Failed to determine length of source path.");
779
780 hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath);
781 ExitOnFailure(hr, "Failed to determine length of relative path.");
782
783 // If the source path is smaller than the relative path (plus space for "X:\") then we know they
784 // are not relative to each other.
785 if (cchSourcePath < cchRelativePath + 3)
786 {
787 ExitFunction();
788 }
789
790 // If the source path ends with the relative path then this source could be a new path.
791 iSourceRelativePath = cchSourcePath - cchRelativePath;
792 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1))
793 {
794 hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath);
795 ExitOnFailure(hr, "Failed to trim source folder.");
796
797 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder);
798 if (SUCCEEDED(hr))
799 {
800 nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1);
801 }
802 else if (E_NOTFOUND == hr)
803 {
804 nCompare = CSTR_GREATER_THAN;
805 hr = S_OK;
806 }
807
808 if (CSTR_EQUAL != nCompare)
809 {
810 hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE);
811 ExitOnFailure(hr, "Failed to set last source.");
812 }
813 }
814
815 LExit:
816 ReleaseStr(sczLastSourceFolder);
817 ReleaseStr(sczSourceFolder);
818
819 return hr;
820 }
821
822 extern "C" HRESULT CacheSendProgressCallback(
823 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
824 __in DWORD64 dw64Progress,
825 __in DWORD64 dw64Total,
826 __in HANDLE hDestinationFile
827 )
828 {
829 static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
830
831 HRESULT hr = S_OK;
832 DWORD dwResult = PROGRESS_CONTINUE;
833 LARGE_INTEGER liTotalSize = { };
834 LARGE_INTEGER liTotalTransferred = { };
835
836 if (pCallback->pfnProgress)
837 {
838 liTotalSize.QuadPart = dw64Total;
839 liTotalTransferred.QuadPart = dw64Progress;
840
841 dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
842 switch (dwResult)
843 {
844 case PROGRESS_CONTINUE:
845 hr = S_OK;
846 break;
847
848 case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
849 case PROGRESS_STOP:
850 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
851 ExitOnRootFailure(hr, "UX aborted on download progress.");
852
853 case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
854 pCallback->pfnProgress = NULL;
855 hr = S_OK;
856 break;
857
858 default:
859 hr = E_UNEXPECTED;
860 ExitOnRootFailure(hr, "Invalid return code from progress routine.");
861 }
862 }
863
864 LExit:
865 return hr;
866 }
867
868 extern "C" void CacheSendErrorCallback(
869 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
870 __in HRESULT hrError,
871 __in_z_opt LPCWSTR wzError,
872 __out_opt BOOL* pfRetry
873 )
874 {
875 if (pfRetry)
876 {
877 *pfRetry = FALSE;
878 }
879
880 if (pCallback->pfnCancel)
881 {
882 int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv);
883 if (pfRetry && IDRETRY == nResult)
884 {
885 *pfRetry = TRUE;
886 }
887 }
888 }
889
890 extern "C" BOOL CacheBundleRunningFromCache(
891 __in BURN_CACHE* pCache
892 )
893 {
894 AssertSz(pCache->fInitializedCacheSources, "Cache sources weren't initialized");
895
896 return pCache->fRunningFromCache;
897 }
898
899 extern "C" HRESULT CachePreparePackage(
900 __in BURN_CACHE* pCache,
901 __in BURN_PACKAGE* pPackage
902 )
903 {
904 HRESULT hr = S_OK;
905
906 if (!pPackage->sczCacheFolder)
907 {
908 hr = CreateCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, NULL, &pPackage->sczCacheFolder);
909 }
910
911 return hr;
912 }
913
914 extern "C" HRESULT CacheBundleToWorkingDirectory(
915 __in BOOL fElevated,
916 __in BURN_CACHE* pCache,
917 __in_z LPCWSTR wzExecutableName,
918 __in BURN_SECTION* pSection,
919 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
920 )
921 {
922 Assert(pCache->fInitializedCache);
923
924 HRESULT hr = S_OK;
925 LPWSTR sczSourcePath = NULL;
926
927 // Initialize the source.
928 hr = PathForCurrentProcess(&sczSourcePath, NULL);
929 ExitOnFailure(hr, "Failed to get current process path.");
930
931 // If the bundle is running out of the package cache then we don't need to copy it to
932 // the working folder since we feel safe in the package cache and will run from there.
933 if (CacheBundleRunningFromCache(pCache))
934 {
935 hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0);
936 ExitOnFailure(hr, "Failed to use current process path as target path.");
937 }
938 else // otherwise, carry on putting the bundle in the working folder.
939 {
940 hr = CopyEngineToWorkingFolder(fElevated, pCache, sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath);
941 ExitOnFailure(hr, "Failed to copy engine to working folder.");
942 }
943
944 LExit:
945 ReleaseStr(sczSourcePath);
946
947 return hr;
948 }
949
950 extern "C" HRESULT CacheLayoutBundle(
951 __in_z LPCWSTR wzExecutableName,
952 __in_z LPCWSTR wzLayoutDirectory,
953 __in_z LPCWSTR wzSourceBundlePath,
954 __in DWORD64 qwBundleSize,
955 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
956 __in LPPROGRESS_ROUTINE pfnProgress,
957 __in LPVOID pContext
958 )
959 {
960 HRESULT hr = S_OK;
961 LPWSTR sczTargetPath = NULL;
962
963 hr = PathConcatRelativeToFullyQualifiedBase(wzLayoutDirectory, wzExecutableName, &sczTargetPath);
964 ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout.");
965
966 LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
967
968 hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext);
969 ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
970
971 LExit:
972 ReleaseStr(sczTargetPath);
973
974 return hr;
975 }
976
977 extern "C" HRESULT CacheCompleteBundle(
978 __in BURN_CACHE* pCache,
979 __in BOOL fPerMachine,
980 __in_z LPCWSTR wzExecutableName,
981 __in_z LPCWSTR wzBundleId,
982 __in_z LPCWSTR wzSourceBundlePath
983 #ifdef DEBUG
984 , __in_z LPCWSTR wzExecutablePath
985 #endif
986 )
987 {
988 HRESULT hr = S_OK;
989 BOOL fPathEqual = FALSE;
990 LPWSTR sczTargetDirectory = NULL;
991 LPWSTR sczTargetPath = NULL;
992
993 hr = CreateCompletedPath(pCache, fPerMachine, wzBundleId, NULL, &sczTargetDirectory);
994 ExitOnFailure(hr, "Failed to create completed cache path for bundle.");
995
996 hr = PathConcatRelativeToFullyQualifiedBase(sczTargetDirectory, wzExecutableName, &sczTargetPath);
997 ExitOnFailure(hr, "Failed to combine completed path with engine file name.");
998
999 // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured.
1000 Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1));
1001
1002 // If the bundle is running out of the package cache then we don't need to copy it there
1003 // (and don't want to since it'll be in use) so bail.
1004 hr = PathCompareCanonicalized(wzSourceBundlePath, sczTargetPath, &fPathEqual);
1005 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
1006
1007 if (fPathEqual)
1008 {
1009 ExitFunction();
1010 }
1011
1012 // Otherwise, carry on putting the bundle in the cache.
1013 LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
1014
1015 FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart.
1016
1017 hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1018 if (FAILED(hr) && FileExistsEx(sczTargetPath, NULL))
1019 {
1020 LogId(REPORT_WARNING, MSG_IGNORING_CACHE_BUNDLE_FAILURE, hr);
1021 ExitFunction1(hr = S_OK);
1022 }
1023 ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
1024
1025 // Reset the path permissions in the cache.
1026 hr = ResetPathPermissions(fPerMachine, sczTargetPath);
1027 ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath);
1028
1029 LExit:
1030 ReleaseStr(sczTargetPath);
1031 ReleaseStr(sczTargetDirectory);
1032
1033 return hr;
1034 }
1035
1036 extern "C" HRESULT CacheLayoutContainer(
1037 __in BURN_CONTAINER* pContainer,
1038 __in_z_opt LPCWSTR wzLayoutDirectory,
1039 __in_z LPCWSTR wzUnverifiedContainerPath,
1040 __in BOOL fMove,
1041 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1042 __in LPPROGRESS_ROUTINE pfnProgress,
1043 __in LPVOID pContext
1044 )
1045 {
1046 HRESULT hr = S_OK;
1047 LPWSTR sczCachedPath = NULL;
1048
1049 hr = PathConcatRelativeToFullyQualifiedBase(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath);
1050 ExitOnFailure(hr, "Failed to concat complete cached path.");
1051
1052 hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
1053 ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath);
1054
1055 LExit:
1056 ReleaseStr(sczCachedPath);
1057
1058 return hr;
1059 }
1060
1061 extern "C" HRESULT CacheLayoutPayload(
1062 __in BURN_PAYLOAD* pPayload,
1063 __in_z_opt LPCWSTR wzLayoutDirectory,
1064 __in_z LPCWSTR wzUnverifiedPayloadPath,
1065 __in BOOL fMove,
1066 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1067 __in LPPROGRESS_ROUTINE pfnProgress,
1068 __in LPVOID pContext
1069 )
1070 {
1071 HRESULT hr = S_OK;
1072 LPWSTR sczCachedPath = NULL;
1073
1074 hr = PathConcatRelativeToFullyQualifiedBase(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath);
1075 ExitOnFailure(hr, "Failed to concat complete cached path.");
1076
1077 hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
1078 ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath);
1079
1080 LExit:
1081 ReleaseStr(sczCachedPath);
1082
1083 return hr;
1084 }
1085
1086 extern "C" HRESULT CacheCompletePayload(
1087 __in BURN_CACHE* pCache,
1088 __in BOOL fPerMachine,
1089 __in BURN_PAYLOAD* pPayload,
1090 __in_z LPCWSTR wzCacheId,
1091 __in_z LPCWSTR wzWorkingPayloadPath,
1092 __in BOOL fMove,
1093 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1094 __in LPPROGRESS_ROUTINE pfnProgress,
1095 __in LPVOID pContext
1096 )
1097 {
1098 HRESULT hr = S_OK;
1099 LPWSTR sczCachedPath = NULL;
1100 LPWSTR sczUnverifiedPayloadPath = NULL;
1101
1102 hr = CreateCompletedPath(pCache, fPerMachine, wzCacheId, pPayload->sczFilePath, &sczCachedPath);
1103 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId);
1104
1105 // If the cached file matches what we expected, we're good.
1106 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext);
1107 if (SUCCEEDED(hr))
1108 {
1109 ExitFunction();
1110 }
1111
1112 hr = CreateUnverifiedPath(pCache, fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath);
1113 ExitOnFailure(hr, "Failed to create unverified path.");
1114
1115 // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file.
1116 if (FileExistsEx(wzWorkingPayloadPath, NULL))
1117 {
1118 hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1119 ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey);
1120 }
1121 else if (FileExistsEx(sczUnverifiedPayloadPath, NULL))
1122 {
1123 // Make sure the staging progress is sent even though there was nothing to do.
1124 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE);
1125 if (SUCCEEDED(hr))
1126 {
1127 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize);
1128 }
1129 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1130 ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey);
1131 }
1132 else // if the working path and unverified path do not exist, nothing we can do.
1133 {
1134 ExitWithRootFailure(hr, E_FILENOTFOUND, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath);
1135 }
1136
1137 hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath);
1138 ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey);
1139
1140 hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1141 LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL);
1142
1143 LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath);
1144
1145 hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1146 ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath);
1147
1148 ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
1149
1150 LExit:
1151 ReleaseStr(sczUnverifiedPayloadPath);
1152 ReleaseStr(sczCachedPath);
1153
1154 return hr;
1155 }
1156
1157 extern "C" HRESULT CacheVerifyContainer(
1158 __in BURN_CONTAINER* pContainer,
1159 __in_z LPCWSTR wzCachedDirectory,
1160 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1161 __in LPPROGRESS_ROUTINE pfnProgress,
1162 __in LPVOID pContext
1163 )
1164 {
1165 HRESULT hr = S_OK;
1166 LPWSTR sczCachedPath = NULL;
1167
1168 hr = PathConcatRelativeToFullyQualifiedBase(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath);
1169 ExitOnFailure(hr, "Failed to concat complete cached path.");
1170
1171 hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1172
1173 LExit:
1174 ReleaseStr(sczCachedPath);
1175
1176 return hr;
1177 }
1178
1179 extern "C" HRESULT CacheVerifyPayload(
1180 __in BURN_PAYLOAD* pPayload,
1181 __in_z LPCWSTR wzCachedDirectory,
1182 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1183 __in LPPROGRESS_ROUTINE pfnProgress,
1184 __in LPVOID pContext
1185 )
1186 {
1187 HRESULT hr = S_OK;
1188 LPWSTR sczCachedPath = NULL;
1189
1190 hr = PathConcatRelativeToFullyQualifiedBase(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
1191 ExitOnFailure(hr, "Failed to concat complete cached path.");
1192
1193 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1194
1195 LExit:
1196 ReleaseStr(sczCachedPath);
1197
1198 return hr;
1199 }
1200
1201 extern "C" HRESULT CacheRemoveBaseWorkingFolder(
1202 __in BURN_CACHE* pCache
1203 )
1204 {
1205 HRESULT hr = S_OK;
1206
1207 if (pCache->fInitializedBaseWorkingFolder)
1208 {
1209 // Try to clean out everything in the working folder.
1210 hr = DirEnsureDeleteEx(pCache->sczBaseWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1211 TraceError(hr, "Could not delete bundle engine working folder.");
1212 }
1213
1214 return hr;
1215 }
1216
1217 extern "C" HRESULT CacheRemoveBundle(
1218 __in BURN_CACHE* pCache,
1219 __in BOOL fPerMachine,
1220 __in_z LPCWSTR wzBundleId
1221 )
1222 {
1223 HRESULT hr = S_OK;
1224
1225 hr = RemoveBundleOrPackage(pCache, TRUE, fPerMachine, wzBundleId, wzBundleId);
1226 ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId);
1227
1228 LExit:
1229 return hr;
1230 }
1231
1232 extern "C" HRESULT CacheRemovePackage(
1233 __in BURN_CACHE* pCache,
1234 __in BOOL fPerMachine,
1235 __in_z LPCWSTR wzPackageId,
1236 __in_z LPCWSTR wzCacheId
1237 )
1238 {
1239 HRESULT hr = S_OK;
1240
1241 hr = RemoveBundleOrPackage(pCache, FALSE, fPerMachine, wzPackageId, wzCacheId);
1242 ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId);
1243
1244 LExit:
1245 return hr;
1246 }
1247
1248 static HRESULT CacheVerifyPayloadSignature(
1249 __in BURN_PAYLOAD* pPayload,
1250 __in_z LPCWSTR wzUnverifiedPayloadPath,
1251 __in HANDLE hFile,
1252 __in BURN_CACHE_STEP cacheStep,
1253 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1254 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1255 __in LPVOID pContext
1256 )
1257 {
1258 HRESULT hr = S_OK;
1259 LONG er = ERROR_SUCCESS;
1260 BOOL fFailedVerification = FALSE;
1261
1262 GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2;
1263 WINTRUST_FILE_INFO wfi = { };
1264 WINTRUST_DATA wtd = { };
1265 CRYPT_PROVIDER_DATA* pProviderData = NULL;
1266 CRYPT_PROVIDER_SGNR* pSigner = NULL;
1267
1268 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1269 ExitOnFailure(hr, "Aborted cache verify payload signature begin.");
1270
1271 fFailedVerification = TRUE;
1272
1273 // Verify the payload assuming online.
1274 wfi.cbStruct = sizeof(wfi);
1275 wfi.pcwszFilePath = wzUnverifiedPayloadPath;
1276 wfi.hFile = hFile;
1277
1278 wtd.cbStruct = sizeof(wtd);
1279 wtd.dwUnionChoice = WTD_CHOICE_FILE;
1280 wtd.pFile = &wfi;
1281 wtd.dwStateAction = WTD_STATEACTION_VERIFY;
1282 wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1283 wtd.dwUIChoice = WTD_UI_NONE;
1284
1285 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1286 if (er)
1287 {
1288 // Verify the payload assuming offline.
1289 wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
1290
1291 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1292 ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath);
1293 }
1294
1295 pProviderData = ::WTHelperProvDataFromStateData(wtd.hWVTStateData);
1296 ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate.");
1297
1298 pSigner = ::WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0);
1299 ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate.");
1300
1301 hr = VerifyPayloadAgainstCertChain(pPayload, pSigner->pChainContext);
1302 ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain.");
1303
1304 fFailedVerification = FALSE;
1305
1306 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize);
1307
1308 LExit:
1309 if (fFailedVerification)
1310 {
1311 // Make sure the BA process marks this payload as having failed verification.
1312 SendCacheFailureMessage(pfnCacheMessageHandler, pContext, cacheStep);
1313 }
1314
1315 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1316
1317 return hr;
1318 }
1319
1320 extern "C" void CacheCleanup(
1321 __in BOOL fPerMachine,
1322 __in BURN_CACHE* pCache
1323 )
1324 {
1325 Assert(pCache->fInitializedCache);
1326
1327 HRESULT hr = S_OK;
1328 LPWSTR sczFolder = NULL;
1329 LPWSTR sczFiles = NULL;
1330 LPWSTR sczDelete = NULL;
1331 HANDLE hFind = INVALID_HANDLE_VALUE;
1332 WIN32_FIND_DATAW wfd = { };
1333 size_t cchFileName = 0;
1334
1335 hr = CacheGetCompletedPath(pCache, fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder);
1336 if (SUCCEEDED(hr))
1337 {
1338 hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1339 }
1340
1341 if (!fPerMachine)
1342 {
1343 if (pCache->sczAcquisitionFolder)
1344 {
1345 hr = PathConcat(pCache->sczAcquisitionFolder, L"*.*", &sczFiles);
1346 if (SUCCEEDED(hr))
1347 {
1348 hFind = ::FindFirstFileW(sczFiles, &wfd);
1349 if (INVALID_HANDLE_VALUE != hFind)
1350 {
1351 do
1352 {
1353 // Skip directories.
1354 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1355 {
1356 continue;
1357 }
1358
1359 // Skip resume files (they end with ".R").
1360 hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName);
1361 if (FAILED(hr) ||
1362 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1]))
1363 {
1364 continue;
1365 }
1366
1367 hr = PathConcatCch(pCache->sczAcquisitionFolder, 0, wfd.cFileName, cchFileName, &sczDelete);
1368 if (SUCCEEDED(hr))
1369 {
1370 hr = FileEnsureDelete(sczDelete);
1371 }
1372 } while (::FindNextFileW(hFind, &wfd));
1373 }
1374 }
1375 }
1376 }
1377
1378 if (INVALID_HANDLE_VALUE != hFind)
1379 {
1380 ::FindClose(hFind);
1381 }
1382
1383 ReleaseStr(sczDelete);
1384 ReleaseStr(sczFiles);
1385 ReleaseStr(sczFolder);
1386 }
1387
1388 extern "C" void CacheUninitialize(
1389 __in BURN_CACHE* pCache
1390 )
1391 {
1392 ReleaseStrArray(pCache->rgsczPotentialBaseWorkingFolders, pCache->cPotentialBaseWorkingFolders);
1393 ReleaseStr(pCache->sczCurrentMachinePackageCache);
1394 ReleaseStr(pCache->sczDefaultMachinePackageCache);
1395 ReleaseStr(pCache->sczDefaultUserPackageCache);
1396 ReleaseStr(pCache->sczBaseWorkingFolder);
1397 ReleaseStr(pCache->sczAcquisitionFolder);
1398 ReleaseStr(pCache->sczSourceProcessFolder);
1399
1400 memset(pCache, 0, sizeof(BURN_CACHE));
1401 }
1402
1403 // Internal functions.
1404
1405 static HRESULT CalculatePotentialBaseWorkingFolders(
1406 __in BURN_CACHE* pCache,
1407 __in BURN_ENGINE_COMMAND* pInternalCommand,
1408 __in LPCWSTR wzAcquisitionFolder
1409 )
1410 {
1411 Assert(!pCache->rgsczPotentialBaseWorkingFolders && !pCache->cPotentialBaseWorkingFolders);
1412 HRESULT hr = S_OK;
1413 LPWSTR sczTemp = NULL;
1414 LPWSTR sczPolicy = NULL;
1415 BOOL fNeedsExpansion = FALSE;
1416
1417 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCache->rgsczPotentialBaseWorkingFolders), 6, sizeof(LPWSTR), 6);
1418 ExitOnFailure(hr, "Failed to initialize array.");
1419
1420 // The value from the command line takes precedence.
1421 if (pInternalCommand->sczEngineWorkingDirectory)
1422 {
1423 hr = PathExpand(&sczTemp, pInternalCommand->sczEngineWorkingDirectory, PATH_EXPAND_FULLPATH);
1424 ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczEngineWorkingDirectory);
1425
1426 pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1427 sczTemp = NULL;
1428 ++pCache->cPotentialBaseWorkingFolders;
1429 }
1430
1431 // The base working folder can be specified through policy,
1432 // but only use it if elevated because it should be secured against non-admin users.
1433 if (pInternalCommand->fInitiallyElevated)
1434 {
1435 hr = PolcReadUnexpandedString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, &fNeedsExpansion, &sczPolicy);
1436 ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory.");
1437
1438 if (S_FALSE != hr)
1439 {
1440 if (fNeedsExpansion)
1441 {
1442 hr = EnvExpandEnvironmentStringsForUser(NULL, sczPolicy, &sczTemp, NULL);
1443 ExitOnFailure(hr, "Failed to expand EngineWorkingDirectory policy directory.");
1444 }
1445 else
1446 {
1447 sczTemp = sczPolicy;
1448 sczPolicy = NULL;
1449 }
1450
1451 pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1452 sczTemp = NULL;
1453 ++pCache->cPotentialBaseWorkingFolders;
1454 }
1455 }
1456
1457 // Default to the acquisition folder, but need to use system temp path for security reasons if running elevated.
1458 if (pInternalCommand->fInitiallyElevated)
1459 {
1460 hr = PathGetSystemTempPaths(&pCache->rgsczPotentialBaseWorkingFolders, &pCache->cPotentialBaseWorkingFolders);
1461 ExitOnFailure(hr, "Failed to get system temp folder paths for base working folder.");
1462 }
1463 else
1464 {
1465 hr = StrAllocString(&sczTemp, wzAcquisitionFolder, 0);
1466 ExitOnFailure(hr, "Failed to copy acquisition folder path for base working folder.");
1467
1468 pCache->rgsczPotentialBaseWorkingFolders[pCache->cPotentialBaseWorkingFolders] = sczTemp;
1469 sczTemp = NULL;
1470 ++pCache->cPotentialBaseWorkingFolders;
1471 }
1472
1473 LExit:
1474 ReleaseStr(sczTemp);
1475 ReleaseStr(sczPolicy);
1476
1477 return hr;
1478 }
1479
1480 static HRESULT CalculateWorkingFolders(
1481 __in BURN_CACHE* pCache,
1482 __in BURN_ENGINE_COMMAND* pInternalCommand
1483 )
1484 {
1485 HRESULT hr = S_OK;
1486 LPWSTR sczBaseAcquisitionPath = NULL;
1487
1488 hr = PathGetTempPath(&sczBaseAcquisitionPath, NULL);
1489 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base.");
1490
1491 hr = PathBackslashTerminate(&sczBaseAcquisitionPath);
1492 ExitOnFailure(hr, "Failed to backslashify base engine working directory.");
1493
1494 hr = CalculatePotentialBaseWorkingFolders(pCache, pInternalCommand, sczBaseAcquisitionPath);
1495 ExitOnFailure(hr, "Failed to get potential base engine working directories.");
1496
1497 hr = GuidFixedCreate(pCache->wzGuid);
1498 ExitOnFailure(hr, "Failed to create working folder guid.");
1499
1500 pCache->wzGuid[GUID_STRING_LENGTH - 1] = L'\\';
1501 pCache->wzGuid[GUID_STRING_LENGTH] = L'\0';
1502
1503 hr = PathConcatRelativeToFullyQualifiedBase(sczBaseAcquisitionPath, pCache->wzGuid, &pCache->sczAcquisitionFolder);
1504 ExitOnFailure(hr, "Failed to append random guid on to temp path for acquisition folder.");
1505
1506 LExit:
1507 ReleaseStr(sczBaseAcquisitionPath);
1508
1509 return hr;
1510 }
1511
1512 static HRESULT GetRootPath(
1513 __in BURN_CACHE* pCache,
1514 __in BOOL fPerMachine,
1515 __in BOOL fAllowRedirect,
1516 __deref_out_z LPWSTR* psczRootPath
1517 )
1518 {
1519 Assert(pCache->fInitializedCache);
1520
1521 HRESULT hr = S_OK;
1522
1523 if (fPerMachine)
1524 {
1525 BOOL fRedirect = fAllowRedirect && pCache->fCustomMachinePackageCache;
1526
1527 hr = StrAllocString(psczRootPath, fRedirect ? pCache->sczCurrentMachinePackageCache : pCache->sczDefaultMachinePackageCache, 0);
1528 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine");
1529
1530 // Return S_FALSE if the current location is not the default location (redirected).
1531 hr = fRedirect ? S_FALSE : S_OK;
1532 }
1533 else
1534 {
1535 hr = StrAllocString(psczRootPath, pCache->sczDefaultUserPackageCache, 0);
1536 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user");
1537 }
1538
1539 LExit:
1540 return hr;
1541 }
1542
1543 static HRESULT GetLastUsedSourceFolder(
1544 __in BURN_VARIABLES* pVariables,
1545 __out_z LPWSTR* psczLastSource
1546 )
1547 {
1548 HRESULT hr = S_OK;
1549
1550 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1551 if (E_NOTFOUND == hr)
1552 {
1553 // Try the original source folder.
1554 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource);
1555 }
1556
1557 return hr;
1558 }
1559
1560 static HRESULT SecurePerMachineCacheRoot(
1561 __in BURN_CACHE* pCache
1562 )
1563 {
1564 HRESULT hr = S_OK;
1565 BOOL fRedirected = FALSE;
1566 LPWSTR sczCacheDirectory = NULL;
1567
1568 if (!pCache->fPerMachineCacheRootVerified)
1569 {
1570 // If we are doing a permachine install but have not yet verified that the root cache folder
1571 // was created with the correct ACLs yet, do that now.
1572 hr = GetRootPath(pCache, TRUE, TRUE, &sczCacheDirectory);
1573 ExitOnFailure(hr, "Failed to get cache directory.");
1574
1575 fRedirected = S_FALSE == hr;
1576
1577 hr = DirEnsureExists(sczCacheDirectory, NULL);
1578 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1579
1580 hr = SecurePath(sczCacheDirectory);
1581 ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory);
1582
1583 pCache->fPerMachineCacheRootVerified = TRUE;
1584
1585 if (!fRedirected)
1586 {
1587 pCache->fOriginalPerMachineCacheRootVerified = TRUE;
1588 }
1589 }
1590
1591 if (!pCache->fOriginalPerMachineCacheRootVerified)
1592 {
1593 // If we are doing a permachine install but have not yet verified that the original root cache folder
1594 // was created with the correct ACLs yet, do that now.
1595 hr = GetRootPath(pCache, TRUE, FALSE, &sczCacheDirectory);
1596 ExitOnFailure(hr, "Failed to get original cache directory.");
1597
1598 hr = DirEnsureExists(sczCacheDirectory, NULL);
1599 ExitOnFailure(hr, "Failed to create original cache directory: %ls", sczCacheDirectory);
1600
1601 hr = SecurePath(sczCacheDirectory);
1602 ExitOnFailure(hr, "Failed to secure original cache directory: %ls", sczCacheDirectory);
1603
1604 pCache->fOriginalPerMachineCacheRootVerified = TRUE;
1605 }
1606
1607 LExit:
1608 ReleaseStr(sczCacheDirectory);
1609
1610 return hr;
1611 }
1612
1613 static HRESULT CreateCompletedPath(
1614 __in BURN_CACHE* pCache,
1615 __in BOOL fPerMachine,
1616 __in LPCWSTR wzId,
1617 __in LPCWSTR wzFilePath,
1618 __out_z LPWSTR* psczCachePath
1619 )
1620 {
1621 HRESULT hr = S_OK;
1622 LPWSTR sczCacheDirectory = NULL;
1623 LPWSTR sczCacheFile = NULL;
1624
1625 if (fPerMachine)
1626 {
1627 hr = SecurePerMachineCacheRoot(pCache);
1628 ExitOnFailure(hr, "Failed to secure per-machine cache root.");
1629 }
1630
1631 // Get the cache completed path.
1632 hr = CacheGetCompletedPath(pCache, fPerMachine, wzId, &sczCacheDirectory);
1633 ExitOnFailure(hr, "Failed to get cache directory.");
1634
1635 // Ensure it exists.
1636 hr = DirEnsureExists(sczCacheDirectory, NULL);
1637 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1638
1639 if (!wzFilePath)
1640 {
1641 // Reset any permissions people might have tried to set on the directory
1642 // so we inherit the (correct!) security permissions from the parent directory.
1643 ResetPathPermissions(fPerMachine, sczCacheDirectory);
1644
1645 *psczCachePath = sczCacheDirectory;
1646 sczCacheDirectory = NULL;
1647 }
1648 else
1649 {
1650 // Get the cache completed file path.
1651 hr = PathConcatRelativeToFullyQualifiedBase(sczCacheDirectory, wzFilePath, &sczCacheFile);
1652 ExitOnFailure(hr, "Failed to construct cache file.");
1653
1654 // Don't reset permissions here. The payload's package must reset its cache folder when it starts caching.
1655
1656 *psczCachePath = sczCacheFile;
1657 sczCacheFile = NULL;
1658 }
1659
1660 LExit:
1661 ReleaseStr(sczCacheDirectory);
1662 ReleaseStr(sczCacheFile);
1663 return hr;
1664 }
1665
1666 static HRESULT CreateUnverifiedPath(
1667 __in BURN_CACHE* pCache,
1668 __in BOOL fPerMachine,
1669 __in_z LPCWSTR wzPayloadId,
1670 __out_z LPWSTR* psczUnverifiedPayloadPath
1671 )
1672 {
1673 HRESULT hr = S_OK;
1674 LPWSTR sczUnverifiedCacheFolder = NULL;
1675
1676 hr = CacheGetCompletedPath(pCache, fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder);
1677 ExitOnFailure(hr, "Failed to get cache directory.");
1678
1679 if (!pCache->fUnverifiedCacheFolderCreated)
1680 {
1681 hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL);
1682 ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder);
1683
1684 ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder);
1685
1686 pCache->fUnverifiedCacheFolderCreated = TRUE;
1687 }
1688
1689 hr = PathConcatRelativeToFullyQualifiedBase(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath);
1690 ExitOnFailure(hr, "Failed to concat payload id to unverified folder path.");
1691
1692 LExit:
1693 ReleaseStr(sczUnverifiedCacheFolder);
1694
1695 return hr;
1696 }
1697
1698 static HRESULT VerifyThenTransferContainer(
1699 __in BURN_CONTAINER* pContainer,
1700 __in_z LPCWSTR wzCachedPath,
1701 __in_z LPCWSTR wzUnverifiedContainerPath,
1702 __in BOOL fMove,
1703 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1704 __in LPPROGRESS_ROUTINE pfnProgress,
1705 __in LPVOID pContext
1706 )
1707 {
1708 HRESULT hr = S_OK;
1709 HANDLE hFile = INVALID_HANDLE_VALUE;
1710
1711 // Get the container on disk actual hash.
1712 hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1713 if (INVALID_HANDLE_VALUE == hFile)
1714 {
1715 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1716 }
1717
1718
1719 switch (pContainer->verification)
1720 {
1721 case BURN_CONTAINER_VERIFICATION_HASH:
1722 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, TRUE, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1723 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1724 break;
1725 default:
1726 ExitOnRootFailure(hr = E_INVALIDARG, "Container has no verification information: %ls", pContainer->sczId);
1727 break;
1728 }
1729
1730 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
1731
1732 hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1733
1734 LExit:
1735 ReleaseFileHandle(hFile);
1736
1737 return hr;
1738 }
1739
1740 static HRESULT VerifyThenTransferPayload(
1741 __in BURN_PAYLOAD* pPayload,
1742 __in_z LPCWSTR wzCachedPath,
1743 __in_z LPCWSTR wzUnverifiedPayloadPath,
1744 __in BOOL fMove,
1745 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1746 __in LPPROGRESS_ROUTINE pfnProgress,
1747 __in LPVOID pContext
1748 )
1749 {
1750 HRESULT hr = S_OK;
1751 HANDLE hFile = INVALID_HANDLE_VALUE;
1752
1753 // Get the payload on disk actual hash.
1754 hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1755 if (INVALID_HANDLE_VALUE == hFile)
1756 {
1757 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1758 }
1759
1760 switch (pPayload->verification)
1761 {
1762 case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE:
1763 hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1764 ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1765 break;
1766 case BURN_PAYLOAD_VERIFICATION_HASH:
1767 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, TRUE, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1768 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1769 break;
1770 case BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE: __fallthrough;
1771 default:
1772 ExitOnRootFailure(hr = E_INVALIDARG, "Payload has no verification information: %ls", pPayload->sczKey);
1773 break;
1774 }
1775
1776 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
1777
1778 hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1779
1780 LExit:
1781 ReleaseFileHandle(hFile);
1782
1783 return hr;
1784 }
1785
1786 static HRESULT CacheTransferFileWithRetry(
1787 __in_z LPCWSTR wzSourcePath,
1788 __in_z LPCWSTR wzDestinationPath,
1789 __in BOOL fMove,
1790 __in BURN_CACHE_STEP cacheStep,
1791 __in DWORD64 qwFileSize,
1792 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1793 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1794 __in LPVOID pContext
1795 )
1796 {
1797 HRESULT hr = S_OK;
1798
1799 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1800 ExitOnFailure(hr, "Aborted cache file transfer begin.");
1801
1802 // TODO: send progress during the file transfer.
1803 if (fMove)
1804 {
1805 hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1806 ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath);
1807 }
1808 else
1809 {
1810 hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1811 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath);
1812 }
1813
1814 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
1815
1816 LExit:
1817 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1818
1819 return hr;
1820 }
1821
1822 static HRESULT VerifyFileAgainstContainer(
1823 __in BURN_CONTAINER* pContainer,
1824 __in_z LPCWSTR wzVerifyPath,
1825 __in BOOL fAlreadyCached,
1826 __in BURN_CACHE_STEP cacheStep,
1827 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1828 __in LPPROGRESS_ROUTINE pfnProgress,
1829 __in LPVOID pContext
1830 )
1831 {
1832 HRESULT hr = S_OK;
1833 HANDLE hFile = INVALID_HANDLE_VALUE;
1834
1835 // Get the container on disk actual hash.
1836 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1837 if (INVALID_HANDLE_VALUE == hFile)
1838 {
1839 hr = HRESULT_FROM_WIN32(::GetLastError());
1840 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1841 {
1842 ExitFunction(); // do not log error when the file was not found.
1843 }
1844 ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath);
1845 }
1846
1847 switch (pContainer->verification)
1848 {
1849 case BURN_CONTAINER_VERIFICATION_HASH:
1850 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, TRUE, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1851 ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId);
1852 break;
1853 default:
1854 ExitOnRootFailure(hr = E_INVALIDARG, "Container has no verification information: %ls", pContainer->sczId);
1855 break;
1856 }
1857
1858 if (fAlreadyCached)
1859 {
1860 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath);
1861 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1862 }
1863
1864 LExit:
1865 ReleaseFileHandle(hFile);
1866
1867 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1868 {
1869 if (fAlreadyCached)
1870 {
1871 LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL);
1872 }
1873
1874 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1875 }
1876
1877 return hr;
1878 }
1879
1880 static HRESULT VerifyFileAgainstPayload(
1881 __in BURN_PAYLOAD* pPayload,
1882 __in_z LPCWSTR wzVerifyPath,
1883 __in BOOL fAlreadyCached,
1884 __in BURN_CACHE_STEP cacheStep,
1885 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1886 __in LPPROGRESS_ROUTINE pfnProgress,
1887 __in LPVOID pContext
1888 )
1889 {
1890 HRESULT hr = S_OK;
1891 HANDLE hFile = INVALID_HANDLE_VALUE;
1892 BOOL fVerifyFileSize = FALSE;
1893
1894 // Get the payload on disk actual hash.
1895 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1896 if (INVALID_HANDLE_VALUE == hFile)
1897 {
1898 hr = HRESULT_FROM_WIN32(::GetLastError());
1899 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1900 {
1901 ExitFunction(); // do not log error when the file was not found.
1902 }
1903 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1904 }
1905
1906 switch (pPayload->verification)
1907 {
1908 case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE:
1909 hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1910 ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey);
1911 break;
1912 case BURN_PAYLOAD_VERIFICATION_HASH:
1913 fVerifyFileSize = TRUE;
1914
1915 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, fVerifyFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1916 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1917
1918 break;
1919 case BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE:
1920 fVerifyFileSize = 0 != pPayload->qwFileSize;
1921
1922 if (pPayload->pbHash)
1923 {
1924 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, fVerifyFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1925 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1926 }
1927 else if (fVerifyFileSize)
1928 {
1929 hr = VerifyFileSize(hFile, pPayload->qwFileSize, wzVerifyPath);
1930 ExitOnFailure(hr, "Failed to verify file size for path: %ls", wzVerifyPath);
1931 }
1932
1933 break;
1934 default:
1935 ExitOnRootFailure(hr = E_INVALIDARG, "Payload has no verification information: %ls", pPayload->sczKey);
1936 break;
1937 }
1938
1939 if (fAlreadyCached)
1940 {
1941 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath);
1942 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1943 }
1944
1945 LExit:
1946 ReleaseFileHandle(hFile);
1947
1948 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1949 {
1950 if (fAlreadyCached)
1951 {
1952 LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL);
1953 }
1954
1955 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1956 }
1957
1958 return hr;
1959 }
1960
1961 static HRESULT AllocateSid(
1962 __in WELL_KNOWN_SID_TYPE type,
1963 __out PSID* ppSid
1964 )
1965 {
1966 HRESULT hr = S_OK;
1967 PSID pAllocSid = NULL;
1968 DWORD cbSid = SECURITY_MAX_SID_SIZE;
1969
1970 pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
1971 ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID.");
1972
1973 if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid))
1974 {
1975 ExitWithLastError(hr, "Failed to create well known SID.");
1976 }
1977
1978 *ppSid = pAllocSid;
1979 pAllocSid = NULL;
1980
1981 LExit:
1982 ReleaseMem(pAllocSid);
1983 return hr;
1984 }
1985
1986
1987 static HRESULT ResetPathPermissions(
1988 __in BOOL fPerMachine,
1989 __in_z LPCWSTR wzPath
1990 )
1991 {
1992 HRESULT hr = S_OK;
1993 DWORD er = ERROR_SUCCESS;
1994 DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
1995 ACL acl = { };
1996 PSID pSid = NULL;
1997
1998 if (fPerMachine)
1999 {
2000 hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid);
2001 ExitOnFailure(hr, "Failed to allocate administrator SID.");
2002
2003 // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent.
2004 if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION))
2005 {
2006 ExitWithLastError(hr, "Failed to initialize ACL.");
2007 }
2008
2009 dwSetSecurity |= OWNER_SECURITY_INFORMATION;
2010 }
2011
2012 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
2013 ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath);
2014
2015 ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits.
2016
2017 LExit:
2018 ReleaseMem(pSid);
2019 return hr;
2020 }
2021
2022
2023 static HRESULT GrantAccessAndAllocateSid(
2024 __in WELL_KNOWN_SID_TYPE type,
2025 __in DWORD dwGrantAccess,
2026 __in EXPLICIT_ACCESS* pAccess
2027 )
2028 {
2029 HRESULT hr = S_OK;
2030
2031 hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName));
2032 ExitOnFailure(hr, "Failed to allocate SID to grate access.");
2033
2034 pAccess->grfAccessMode = GRANT_ACCESS;
2035 pAccess->grfAccessPermissions = dwGrantAccess;
2036 pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
2037 pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID;
2038 pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
2039
2040 LExit:
2041 return hr;
2042 }
2043
2044
2045 static HRESULT SecurePath(
2046 __in LPCWSTR wzPath
2047 )
2048 {
2049 HRESULT hr = S_OK;
2050 DWORD er = ERROR_SUCCESS;
2051 EXPLICIT_ACCESSW access[4] = { };
2052 PACL pAcl = NULL;
2053
2054 // Administrators must be the first one in the array so we can reuse the allocated SID below.
2055 hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]);
2056 ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath);
2057
2058 hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]);
2059 ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath);
2060
2061 hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]);
2062 ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath);
2063
2064 hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]);
2065 ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath);
2066
2067 er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl);
2068 ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath);
2069
2070 // Set the ACL and ensure the Administrators group ends up the owner
2071 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
2072 reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
2073 ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath);
2074
2075 LExit:
2076 if (pAcl)
2077 {
2078 ::LocalFree(pAcl);
2079 }
2080
2081 for (DWORD i = 0; i < countof(access); ++i)
2082 {
2083 ReleaseMem(access[i].Trustee.ptstrName);
2084 }
2085
2086 return hr;
2087 }
2088
2089
2090 static HRESULT CopyEngineToWorkingFolder(
2091 __in BOOL fElevated,
2092 __in BURN_CACHE* pCache,
2093 __in_z LPCWSTR wzSourcePath,
2094 __in_z LPCWSTR wzWorkingFolderName,
2095 __in_z LPCWSTR wzExecutableName,
2096 __in BURN_SECTION* pSection,
2097 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
2098 )
2099 {
2100 HRESULT hr = S_OK;
2101 LPWSTR sczWorkingFolder = NULL;
2102 LPWSTR sczTargetDirectory = NULL;
2103 LPWSTR sczTargetPath = NULL;
2104 LPWSTR sczSourceDirectory = NULL;
2105 LPWSTR sczPayloadSourcePath = NULL;
2106 LPWSTR sczPayloadTargetPath = NULL;
2107
2108 hr = CacheEnsureBaseWorkingFolder(fElevated, pCache, &sczWorkingFolder);
2109 ExitOnFailure(hr, "Failed to create working path to copy engine.");
2110
2111 hr = PathConcatRelativeToFullyQualifiedBase(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory);
2112 ExitOnFailure(hr, "Failed to calculate the bundle working folder target name.");
2113
2114 hr = DirEnsureExists(sczTargetDirectory, NULL);
2115 ExitOnFailure(hr, "Failed create bundle working folder.");
2116
2117 hr = PathConcatRelativeToFullyQualifiedBase(sczTargetDirectory, wzExecutableName, &sczTargetPath);
2118 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
2119
2120 // Copy the engine without any attached containers to the working path.
2121 hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection);
2122 ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath);
2123
2124 if (psczEngineWorkingPath)
2125 {
2126 hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0);
2127 ExitOnFailure(hr, "Failed to copy target path for engine working path.");
2128 }
2129
2130 LExit:
2131 ReleaseStr(sczPayloadTargetPath);
2132 ReleaseStr(sczPayloadSourcePath);
2133 ReleaseStr(sczSourceDirectory);
2134 ReleaseStr(sczTargetPath);
2135 ReleaseStr(sczTargetDirectory);
2136 ReleaseStr(sczWorkingFolder);
2137
2138 return hr;
2139 }
2140
2141
2142 static HRESULT CopyEngineWithSignatureFixup(
2143 __in HANDLE hEngineFile,
2144 __in_z LPCWSTR wzEnginePath,
2145 __in_z LPCWSTR wzTargetPath,
2146 __in BURN_SECTION* pSection
2147 )
2148 {
2149 HRESULT hr = S_OK;
2150 HANDLE hTarget = INVALID_HANDLE_VALUE;
2151 LARGE_INTEGER li = { };
2152 DWORD dwZeroOriginals[3] = { };
2153
2154 hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
2155 if (INVALID_HANDLE_VALUE == hTarget)
2156 {
2157 ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath);
2158 }
2159
2160 hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN);
2161 ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath);
2162
2163 hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL);
2164 ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath);
2165
2166 // If the original executable was signed, let's put back the checksum and signature.
2167 if (pSection->dwOriginalSignatureOffset)
2168 {
2169 // Fix up the checksum.
2170 li.QuadPart = pSection->dwChecksumOffset;
2171 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
2172 {
2173 ExitWithLastError(hr, "Failed to seek to checksum in exe header.");
2174 }
2175
2176 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum));
2177 ExitOnFailure(hr, "Failed to update signature offset.");
2178
2179 // Fix up the signature information.
2180 li.QuadPart = pSection->dwCertificateTableOffset;
2181 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
2182 {
2183 ExitWithLastError(hr, "Failed to seek to signature table in exe header.");
2184 }
2185
2186 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset));
2187 ExitOnFailure(hr, "Failed to update signature offset.");
2188
2189 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize));
2190 ExitOnFailure(hr, "Failed to update signature offset.");
2191
2192 // Zero out the original information since that is how it was when the file was originally signed.
2193 li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset;
2194 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
2195 {
2196 ExitWithLastError(hr, "Failed to seek to original data in exe burn section header.");
2197 }
2198
2199 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals));
2200 ExitOnFailure(hr, "Failed to zero out original data offset.");
2201 }
2202
2203 LExit:
2204 ReleaseFileHandle(hTarget);
2205
2206 return hr;
2207 }
2208
2209
2210 static HRESULT RemoveBundleOrPackage(
2211 __in BURN_CACHE* pCache,
2212 __in BOOL fBundle,
2213 __in BOOL fPerMachine,
2214 __in_z LPCWSTR wzBundleOrPackageId,
2215 __in_z LPCWSTR wzCacheId
2216 )
2217 {
2218 HRESULT hr = S_OK;
2219 LPWSTR sczRootCacheDirectory = NULL;
2220 LPWSTR sczDirectory = NULL;
2221
2222 hr = CacheGetCompletedPath(pCache, fPerMachine, wzCacheId, &sczDirectory);
2223 ExitOnFailure(hr, "Failed to calculate cache path.");
2224
2225 LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory);
2226
2227 // Try really hard to remove the cache directory.
2228 hr = E_FAIL;
2229 for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry)
2230 {
2231 if (0 < iRetry)
2232 {
2233 ::Sleep(FILE_OPERATION_RETRY_WAIT);
2234 }
2235
2236 hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
2237 if (E_PATHNOTFOUND == hr)
2238 {
2239 break;
2240 }
2241 }
2242
2243 if (E_PATHNOTFOUND != hr && FAILED(hr))
2244 {
2245 LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr);
2246 hr = S_OK;
2247 }
2248 else
2249 {
2250 // Try to remove root package cache in the off chance it is now empty.
2251 hr = GetRootPath(pCache, fPerMachine, TRUE, &sczRootCacheDirectory);
2252 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
2253 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
2254
2255 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
2256 if (S_FALSE == hr)
2257 {
2258 hr = GetRootPath(pCache, fPerMachine, FALSE, &sczRootCacheDirectory);
2259 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
2260 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
2261 }
2262 }
2263
2264 LExit:
2265 ReleaseStr(sczDirectory);
2266 ReleaseStr(sczRootCacheDirectory);
2267
2268 return hr;
2269 }
2270
2271 static HRESULT VerifyFileSize(
2272 __in HANDLE hFile,
2273 __in DWORD64 qwFileSize,
2274 __in_z LPCWSTR wzUnverifiedPayloadPath
2275 )
2276 {
2277 HRESULT hr = S_OK;
2278 LONGLONG llSize = 0;
2279
2280 hr = FileSizeByHandle(hFile, &llSize);
2281 ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath);
2282
2283 if (static_cast<DWORD64>(llSize) != qwFileSize)
2284 {
2285 ExitOnRootFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize);
2286 }
2287
2288 LExit:
2289 return hr;
2290 }
2291
2292 static HRESULT VerifyHash(
2293 __in BYTE* pbHash,
2294 __in DWORD cbHash,
2295 __in DWORD64 qwFileSize,
2296 __in BOOL fVerifyFileSize,
2297 __in_z LPCWSTR wzUnverifiedPayloadPath,
2298 __in HANDLE hFile,
2299 __in BURN_CACHE_STEP cacheStep,
2300 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2301 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
2302 __in LPVOID pContext
2303 )
2304 {
2305 HRESULT hr = S_OK;
2306 BYTE rgbActualHash[SHA512_HASH_LEN] = { };
2307 DWORD64 qwHashedBytes = 0;
2308 LPWSTR pszExpected = NULL;
2309 LPWSTR pszActual = NULL;
2310 BOOL fFailedVerification = FALSE;
2311
2312 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
2313 ExitOnFailure(hr, "Aborted cache verify hash begin.");
2314
2315 fFailedVerification = TRUE;
2316
2317 if (fVerifyFileSize)
2318 {
2319 hr = VerifyFileSize(hFile, qwFileSize, wzUnverifiedPayloadPath);
2320 ExitOnFailure(hr, "Failed to verify file size for path: %ls", wzUnverifiedPayloadPath);
2321 }
2322
2323 // TODO: create a cryp hash file that sends progress.
2324 hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes);
2325 ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath);
2326
2327 // Compare hashes.
2328 if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash)))
2329 {
2330 hr = CRYPT_E_HASH_VALUE;
2331
2332 // Best effort to log the expected and actual hash value strings.
2333 if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) &&
2334 SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual)))
2335 {
2336 ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual);
2337 }
2338 else
2339 {
2340 ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath);
2341 }
2342 }
2343
2344 fFailedVerification = FALSE;
2345
2346 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
2347
2348 LExit:
2349 if (fFailedVerification)
2350 {
2351 // Make sure the BA process marks this container or payload as having failed verification.
2352 SendCacheFailureMessage(pfnCacheMessageHandler, pContext, cacheStep);
2353 }
2354
2355 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
2356
2357 ReleaseStr(pszActual);
2358 ReleaseStr(pszExpected);
2359
2360 return hr;
2361 }
2362
2363 static HRESULT VerifyPayloadAgainstCertChain(
2364 __in BURN_PAYLOAD* pPayload,
2365 __in PCCERT_CHAIN_CONTEXT pChainContext
2366 )
2367 {
2368 HRESULT hr = S_OK;
2369 PCCERT_CONTEXT pChainElementCertContext = NULL;
2370
2371 BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { };
2372 DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier);
2373 BYTE* pbThumbprint = NULL;
2374 DWORD cbThumbprint = 0;
2375
2376 // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier
2377 // and thumbprint (if a thumbprint was provided).
2378 HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match.
2379 for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i)
2380 {
2381 pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext;
2382
2383 // Get the certificate's public key identifier.
2384 if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier))
2385 {
2386 ExitWithLastError(hr, "Failed to get certificate public key identifier.");
2387 }
2388
2389 // Compare the certificate's public key identifier with the payload's public key identifier. If they
2390 // match, we're one step closer to the a positive result.
2391 if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier &&
2392 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier))
2393 {
2394 // If the payload specified a thumbprint for the certificate, verify it.
2395 if (pPayload->pbCertificateRootThumbprint)
2396 {
2397 hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint);
2398 ExitOnFailure(hr, "Failed to read certificate thumbprint.");
2399
2400 if (pPayload->cbCertificateRootThumbprint == cbThumbprint &&
2401 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint))
2402 {
2403 // If we got here, we found that our payload public key identifier and thumbprint
2404 // matched an element in the certficate chain.
2405 hrChainVerification = S_OK;
2406 break;
2407 }
2408
2409 ReleaseNullMem(pbThumbprint);
2410 }
2411 else // no thumbprint match necessary so we're good to go.
2412 {
2413 hrChainVerification = S_OK;
2414 break;
2415 }
2416 }
2417 }
2418 hr = hrChainVerification;
2419 ExitOnFailure(hr, "Failed to find expected public key in certificate chain.");
2420
2421 LExit:
2422 ReleaseMem(pbThumbprint);
2423
2424 return hr;
2425 }
2426
2427 static HRESULT SendCacheBeginMessage(
2428 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2429 __in LPVOID pContext,
2430 __in BURN_CACHE_STEP cacheStep
2431 )
2432 {
2433 HRESULT hr = S_OK;
2434 BURN_CACHE_MESSAGE message = { };
2435
2436 message.type = BURN_CACHE_MESSAGE_BEGIN;
2437 message.begin.cacheStep = cacheStep;
2438
2439 hr = pfnCacheMessageHandler(&message, pContext);
2440
2441 return hr;
2442 }
2443
2444 static HRESULT SendCacheSuccessMessage(
2445 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2446 __in LPVOID pContext,
2447 __in DWORD64 qwFileSize
2448 )
2449 {
2450 HRESULT hr = S_OK;
2451 BURN_CACHE_MESSAGE message = { };
2452
2453 message.type = BURN_CACHE_MESSAGE_SUCCESS;
2454 message.success.qwFileSize = qwFileSize;
2455
2456 hr = pfnCacheMessageHandler(&message, pContext);
2457
2458 return hr;
2459 }
2460
2461 static HRESULT SendCacheCompleteMessage(
2462 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2463 __in LPVOID pContext,
2464 __in HRESULT hrStatus
2465 )
2466 {
2467 HRESULT hr = S_OK;
2468 BURN_CACHE_MESSAGE message = { };
2469
2470 message.type = BURN_CACHE_MESSAGE_COMPLETE;
2471 message.complete.hrStatus = hrStatus;
2472
2473 hr = pfnCacheMessageHandler(&message, pContext);
2474
2475 return hr;
2476 }
2477
2478 static HRESULT SendCacheFailureMessage(
2479 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2480 __in LPVOID pContext,
2481 __in BURN_CACHE_STEP cacheStep
2482 )
2483 {
2484 HRESULT hr = S_OK;
2485 BURN_CACHE_MESSAGE message = { };
2486
2487 message.type = BURN_CACHE_MESSAGE_FAILURE;
2488 message.failure.cacheStep = cacheStep;
2489
2490 hr = pfnCacheMessageHandler(&message, pContext);
2491
2492 return hr;
2493 }