aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-05-03 12:23:31 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-05-11 19:11:19 -0500
commitcd921db764df9578733c85c29e8c6c368f4c7e78 (patch)
tree8c04087da4430101afa9c065cc5c6555335d4b32
parent67dd3ced3b383eb8423156cdabd3f9ce037caba8 (diff)
downloadwix-cd921db764df9578733c85c29e8c6c368f4c7e78.tar.gz
wix-cd921db764df9578733c85c29e8c6c368f4c7e78.tar.bz2
wix-cd921db764df9578733c85c29e8c6c368f4c7e78.zip
Enforce payload and container verification.
-rw-r--r--src/burn/engine/apply.cpp4
-rw-r--r--src/burn/engine/cache.cpp95
-rw-r--r--src/burn/engine/container.cpp50
-rw-r--r--src/burn/engine/container.h8
-rw-r--r--src/burn/engine/payload.cpp127
-rw-r--r--src/burn/engine/payload.h8
-rw-r--r--src/burn/engine/pseudobundle.cpp5
-rw-r--r--src/burn/test/BurnUnitTest/CacheTest.cpp2
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp2
9 files changed, 209 insertions, 92 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index b4fa9dac..4d527409 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -1410,12 +1410,12 @@ static HRESULT AcquireContainerOrPayload(
1410 fMinimumFileSize = TRUE; 1410 fMinimumFileSize = TRUE;
1411 qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize; 1411 qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize;
1412 } 1412 }
1413 else if (pContainer->pbHash && pContext->wzLayoutDirectory) 1413 else
1414 { 1414 {
1415 qwFileSize = pContainer->qwFileSize; 1415 qwFileSize = pContainer->qwFileSize;
1416 } 1416 }
1417 } 1417 }
1418 else if (pPayload->pbHash) 1418 else if (BURN_PAYLOAD_VERIFICATION_HASH == pPayload->verification || BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE == pPayload->verification)
1419 { 1419 {
1420 qwFileSize = pPayload->qwFileSize; 1420 qwFileSize = pPayload->qwFileSize;
1421 } 1421 }
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index 02cad4a5..44267cbc 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -114,10 +114,16 @@ static HRESULT RemoveBundleOrPackage(
114 __in_z LPCWSTR wzBundleOrPackageId, 114 __in_z LPCWSTR wzBundleOrPackageId,
115 __in_z LPCWSTR wzCacheId 115 __in_z LPCWSTR wzCacheId
116 ); 116 );
117static HRESULT VerifyFileSize(
118 __in HANDLE hFile,
119 __in DWORD64 qwFileSize,
120 __in_z LPCWSTR wzUnverifiedPayloadPath
121 );
117static HRESULT VerifyHash( 122static HRESULT VerifyHash(
118 __in BYTE* pbHash, 123 __in BYTE* pbHash,
119 __in DWORD cbHash, 124 __in DWORD cbHash,
120 __in DWORD64 qwFileSize, 125 __in DWORD64 qwFileSize,
126 __in BOOL fVerifyFileSize,
121 __in_z LPCWSTR wzUnverifiedPayloadPath, 127 __in_z LPCWSTR wzUnverifiedPayloadPath,
122 __in HANDLE hFile, 128 __in HANDLE hFile,
123 __in BURN_CACHE_STEP cacheStep, 129 __in BURN_CACHE_STEP cacheStep,
@@ -1513,11 +1519,16 @@ static HRESULT VerifyThenTransferContainer(
1513 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath); 1519 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1514 } 1520 }
1515 1521
1516 // Container should have a hash we can use to verify with. 1522
1517 if (pContainer->pbHash) 1523 switch (pContainer->verification)
1518 { 1524 {
1519 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); 1525 case BURN_CONTAINER_VERIFICATION_HASH:
1526 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, TRUE, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1520 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); 1527 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1528 break;
1529 default:
1530 ExitOnRootFailure(hr = E_INVALIDARG, "Container has no verification information: %ls", pContainer->sczId);
1531 break;
1521 } 1532 }
1522 1533
1523 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); 1534 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
@@ -1550,10 +1561,16 @@ static HRESULT VerifyThenTransferPayload(
1550 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); 1561 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1551 } 1562 }
1552 1563
1553 if (pPayload->pbHash) // the payload should have a hash we can use to verify it. 1564 switch (pPayload->verification)
1554 { 1565 {
1555 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); 1566 case BURN_PAYLOAD_VERIFICATION_HASH:
1567 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, TRUE, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1556 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); 1568 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1569 break;
1570 case BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE: __fallthrough;
1571 default:
1572 ExitOnRootFailure(hr = E_INVALIDARG, "Payload has no verification information: %ls", pPayload->sczKey);
1573 break;
1557 } 1574 }
1558 1575
1559 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); 1576 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
@@ -1627,10 +1644,15 @@ static HRESULT VerifyFileAgainstContainer(
1627 ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath); 1644 ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath);
1628 } 1645 }
1629 1646
1630 if (pContainer->pbHash) // the container should have a hash we can use to verify it. 1647 switch (pContainer->verification)
1631 { 1648 {
1632 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); 1649 case BURN_CONTAINER_VERIFICATION_HASH:
1650 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, TRUE, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1633 ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); 1651 ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId);
1652 break;
1653 default:
1654 ExitOnRootFailure(hr = E_INVALIDARG, "Container has no verification information: %ls", pContainer->sczId);
1655 break;
1634 } 1656 }
1635 1657
1636 if (fAlreadyCached) 1658 if (fAlreadyCached)
@@ -1667,6 +1689,7 @@ static HRESULT VerifyFileAgainstPayload(
1667{ 1689{
1668 HRESULT hr = S_OK; 1690 HRESULT hr = S_OK;
1669 HANDLE hFile = INVALID_HANDLE_VALUE; 1691 HANDLE hFile = INVALID_HANDLE_VALUE;
1692 BOOL fVerifyFileSize = FALSE;
1670 1693
1671 // Get the payload on disk actual hash. 1694 // Get the payload on disk actual hash.
1672 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 1695 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
@@ -1680,10 +1703,33 @@ static HRESULT VerifyFileAgainstPayload(
1680 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); 1703 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1681 } 1704 }
1682 1705
1683 if (pPayload->pbHash) // the payload should have a hash we can use to verify it. 1706 switch (pPayload->verification)
1684 { 1707 {
1685 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); 1708 case BURN_PAYLOAD_VERIFICATION_HASH:
1709 fVerifyFileSize = TRUE;
1710
1711 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, fVerifyFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1686 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); 1712 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1713
1714 break;
1715 case BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE:
1716 fVerifyFileSize = 0 != pPayload->qwFileSize;
1717
1718 if (pPayload->pbHash)
1719 {
1720 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, fVerifyFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1721 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1722 }
1723 else if (fVerifyFileSize)
1724 {
1725 hr = VerifyFileSize(hFile, pPayload->qwFileSize, wzVerifyPath);
1726 ExitOnFailure(hr, "Failed to verify file size for path: %ls", wzVerifyPath);
1727 }
1728
1729 break;
1730 default:
1731 ExitOnRootFailure(hr = E_INVALIDARG, "Payload has no verification information: %ls", pPayload->sczKey);
1732 break;
1687 } 1733 }
1688 1734
1689 if (fAlreadyCached) 1735 if (fAlreadyCached)
@@ -2015,10 +2061,32 @@ LExit:
2015 return hr; 2061 return hr;
2016} 2062}
2017 2063
2064static HRESULT VerifyFileSize(
2065 __in HANDLE hFile,
2066 __in DWORD64 qwFileSize,
2067 __in_z LPCWSTR wzUnverifiedPayloadPath
2068 )
2069{
2070 HRESULT hr = S_OK;
2071 LONGLONG llSize = 0;
2072
2073 hr = FileSizeByHandle(hFile, &llSize);
2074 ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath);
2075
2076 if (static_cast<DWORD64>(llSize) != qwFileSize)
2077 {
2078 ExitOnRootFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize);
2079 }
2080
2081LExit:
2082 return hr;
2083}
2084
2018static HRESULT VerifyHash( 2085static HRESULT VerifyHash(
2019 __in BYTE* pbHash, 2086 __in BYTE* pbHash,
2020 __in DWORD cbHash, 2087 __in DWORD cbHash,
2021 __in DWORD64 qwFileSize, 2088 __in DWORD64 qwFileSize,
2089 __in BOOL fVerifyFileSize,
2022 __in_z LPCWSTR wzUnverifiedPayloadPath, 2090 __in_z LPCWSTR wzUnverifiedPayloadPath,
2023 __in HANDLE hFile, 2091 __in HANDLE hFile,
2024 __in BURN_CACHE_STEP cacheStep, 2092 __in BURN_CACHE_STEP cacheStep,
@@ -2032,19 +2100,16 @@ static HRESULT VerifyHash(
2032 HRESULT hr = S_OK; 2100 HRESULT hr = S_OK;
2033 BYTE rgbActualHash[SHA512_HASH_LEN] = { }; 2101 BYTE rgbActualHash[SHA512_HASH_LEN] = { };
2034 DWORD64 qwHashedBytes = 0; 2102 DWORD64 qwHashedBytes = 0;
2035 LONGLONG llSize = 0;
2036 LPWSTR pszExpected = NULL; 2103 LPWSTR pszExpected = NULL;
2037 LPWSTR pszActual = NULL; 2104 LPWSTR pszActual = NULL;
2038 2105
2039 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); 2106 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
2040 ExitOnFailure(hr, "Aborted cache verify hash begin."); 2107 ExitOnFailure(hr, "Aborted cache verify hash begin.");
2041 2108
2042 hr = FileSizeByHandle(hFile, &llSize); 2109 if (fVerifyFileSize)
2043 ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath);
2044
2045 if (static_cast<DWORD64>(llSize) != qwFileSize)
2046 { 2110 {
2047 ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize); 2111 hr = VerifyFileSize(hFile, qwFileSize, wzUnverifiedPayloadPath);
2112 ExitOnFailure(hr, "Failed to verify file size for path: %ls", wzUnverifiedPayloadPath);
2048 } 2113 }
2049 2114
2050 // TODO: create a cryp hash file that sends progress. 2115 // TODO: create a cryp hash file that sends progress.
diff --git a/src/burn/engine/container.cpp b/src/burn/engine/container.cpp
index 0cce3131..c6f2ada8 100644
--- a/src/burn/engine/container.cpp
+++ b/src/burn/engine/container.cpp
@@ -50,16 +50,9 @@ extern "C" HRESULT ContainersParseFromXml(
50 hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); 50 hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId);
51 ExitOnFailure(hr, "Failed to get @Id."); 51 ExitOnFailure(hr, "Failed to get @Id.");
52 52
53 // @Primary
54 hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary);
55 if (E_NOTFOUND != hr)
56 {
57 ExitOnFailure(hr, "Failed to get @Primary.");
58 }
59
60 // @Attached 53 // @Attached
61 hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); 54 hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached);
62 if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached 55 if (E_NOTFOUND != hr)
63 { 56 {
64 ExitOnFailure(hr, "Failed to get @Attached."); 57 ExitOnFailure(hr, "Failed to get @Attached.");
65 } 58 }
@@ -87,10 +80,7 @@ extern "C" HRESULT ContainersParseFromXml(
87 { 80 {
88 // @FilePath 81 // @FilePath
89 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); 82 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath);
90 if (E_NOTFOUND != hr) 83 ExitOnFailure(hr, "Failed to get @FilePath.");
91 {
92 ExitOnFailure(hr, "Failed to get @FilePath.");
93 }
94 } 84 }
95 85
96 // The source path starts as the file path. 86 // The source path starts as the file path.
@@ -99,23 +89,32 @@ extern "C" HRESULT ContainersParseFromXml(
99 89
100 // @DownloadUrl 90 // @DownloadUrl
101 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); 91 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl);
102 if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url 92 if (E_NOTFOUND != hr)
103 { 93 {
104 ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided."); 94 ExitOnFailure(hr, "Failed to get @DownloadUrl.");
105 } 95 }
106 96
107 // @Hash 97 // @Hash
108 hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); 98 hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash);
109 if (SUCCEEDED(hr)) 99 ExitOnFailure(hr, "Failed to get @Hash.");
110 { 100
111 hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); 101 hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash);
112 ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); 102 ExitOnFailure(hr, "Failed to hex decode the Container/@Hash.");
113 } 103
114 else if (E_NOTFOUND != hr) 104 // @FileSize
105 hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz);
106 ExitOnFailure(hr, "Failed to get @FileSize.");
107
108 hr = StrStringToUInt64(scz, 0, &pContainer->qwFileSize);
109 ExitOnFailure(hr, "Failed to parse @FileSize.");
110
111 if (!pContainer->qwFileSize)
115 { 112 {
116 ExitOnFailure(hr, "Failed to get @Hash."); 113 ExitOnRootFailure(hr = E_INVALIDDATA, "File size is required when verifying by hash for container: %ls", pContainer->sczId);
117 } 114 }
118 115
116 pContainer->verification = BURN_CONTAINER_VERIFICATION_HASH;
117
119 // prepare next iteration 118 // prepare next iteration
120 ReleaseNullObject(pixnNode); 119 ReleaseNullObject(pixnNode);
121 } 120 }
@@ -136,6 +135,7 @@ extern "C" HRESULT ContainersInitialize(
136 ) 135 )
137{ 136{
138 HRESULT hr = S_OK; 137 HRESULT hr = S_OK;
138 DWORD64 qwSize = 0;
139 139
140 if (pContainers->rgContainers) 140 if (pContainers->rgContainers)
141 { 141 {
@@ -147,8 +147,13 @@ extern "C" HRESULT ContainersInitialize(
147 // manifest contained and get the offset to the container. 147 // manifest contained and get the offset to the container.
148 if (pContainer->fAttached) 148 if (pContainer->fAttached)
149 { 149 {
150 hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); 150 hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &qwSize, &pContainer->fActuallyAttached);
151 ExitOnFailure(hr, "Failed to get attached container information."); 151 ExitOnFailure(hr, "Failed to get attached container information.");
152
153 if (qwSize != pContainer->qwFileSize)
154 {
155 ExitOnFailure(hr, "Attached container '%ls' size '%llu' didn't match size from manifest: '%llu'", pContainer->sczId, qwSize, pContainer->qwFileSize);
156 }
152 } 157 }
153 } 158 }
154 } 159 }
@@ -195,7 +200,6 @@ extern "C" HRESULT ContainerOpenUX(
195 200
196 // open attached container 201 // open attached container
197 container.type = BURN_CONTAINER_TYPE_CABINET; 202 container.type = BURN_CONTAINER_TYPE_CABINET;
198 container.fPrimary = TRUE;
199 container.fAttached = TRUE; 203 container.fAttached = TRUE;
200 container.dwAttachedIndex = 0; 204 container.dwAttachedIndex = 0;
201 205
diff --git a/src/burn/engine/container.h b/src/burn/engine/container.h
index c2c1c9a8..d454a248 100644
--- a/src/burn/engine/container.h
+++ b/src/burn/engine/container.h
@@ -52,6 +52,12 @@ enum BURN_CAB_OPERATION
52 BURN_CAB_OPERATION_CLOSE, 52 BURN_CAB_OPERATION_CLOSE,
53}; 53};
54 54
55enum BURN_CONTAINER_VERIFICATION
56{
57 BURN_CONTAINER_VERIFICATION_NONE,
58 BURN_CONTAINER_VERIFICATION_HASH,
59};
60
55 61
56// structs 62// structs
57 63
@@ -59,7 +65,6 @@ typedef struct _BURN_CONTAINER
59{ 65{
60 LPWSTR sczId; 66 LPWSTR sczId;
61 BURN_CONTAINER_TYPE type; 67 BURN_CONTAINER_TYPE type;
62 BOOL fPrimary;
63 BOOL fAttached; 68 BOOL fAttached;
64 DWORD dwAttachedIndex; 69 DWORD dwAttachedIndex;
65 DWORD64 qwFileSize; 70 DWORD64 qwFileSize;
@@ -69,6 +74,7 @@ typedef struct _BURN_CONTAINER
69 74
70 BYTE* pbHash; 75 BYTE* pbHash;
71 DWORD cbHash; 76 DWORD cbHash;
77 BURN_CONTAINER_VERIFICATION verification;
72 DWORD64 qwAttachedOffset; 78 DWORD64 qwAttachedOffset;
73 BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. 79 BOOL fActuallyAttached; // indicates whether an attached container is attached or missing.
74 80
diff --git a/src/burn/engine/payload.cpp b/src/burn/engine/payload.cpp
index 72eb3476..392a3dd4 100644
--- a/src/burn/engine/payload.cpp
+++ b/src/burn/engine/payload.cpp
@@ -27,6 +27,8 @@ extern "C" HRESULT PayloadsParseFromXml(
27 IXMLDOMNode* pixnNode = NULL; 27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0; 28 DWORD cNodes = 0;
29 LPWSTR scz = NULL; 29 LPWSTR scz = NULL;
30 BOOL fChainPayload = pContainers && pLayoutPayloads; // These are required when parsing chain payloads.
31 BOOL fValidFileSize = FALSE;
30 32
31 // select payload nodes 33 // select payload nodes
32 hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes); 34 hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes);
@@ -51,6 +53,7 @@ extern "C" HRESULT PayloadsParseFromXml(
51 for (DWORD i = 0; i < cNodes; ++i) 53 for (DWORD i = 0; i < cNodes; ++i)
52 { 54 {
53 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; 55 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
56 fValidFileSize = FALSE;
54 57
55 hr = XmlNextElement(pixnNodes, &pixnNode, NULL); 58 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 ExitOnFailure(hr, "Failed to get next node."); 59 ExitOnFailure(hr, "Failed to get next node.");
@@ -63,27 +66,36 @@ extern "C" HRESULT PayloadsParseFromXml(
63 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath); 66 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath);
64 ExitOnFailure(hr, "Failed to get @FilePath."); 67 ExitOnFailure(hr, "Failed to get @FilePath.");
65 68
66 // @Packaging 69 // @SourcePath
67 hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); 70 hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath);
68 ExitOnFailure(hr, "Failed to get @Packaging."); 71 ExitOnFailure(hr, "Failed to get @SourcePath.");
69 72
70 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) 73 if (!fChainPayload)
71 { 74 {
75 // All non-chain payloads are embedded in the UX container.
72 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; 76 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED;
73 } 77 }
74 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1))
75 {
76 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
77 }
78 else 78 else
79 { 79 {
80 hr = E_INVALIDARG; 80 // @Packaging
81 ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz); 81 hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz);
82 } 82 ExitOnFailure(hr, "Failed to get @Packaging.");
83 83
84 // @Container 84 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1))
85 if (pContainers) 85 {
86 { 86 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED;
87 }
88 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1))
89 {
90 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
91 }
92 else
93 {
94 hr = E_INVALIDARG;
95 ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz);
96 }
97
98 // @Container
87 hr = XmlGetAttributeEx(pixnNode, L"Container", &scz); 99 hr = XmlGetAttributeEx(pixnNode, L"Container", &scz);
88 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) 100 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
89 { 101 {
@@ -93,52 +105,67 @@ extern "C" HRESULT PayloadsParseFromXml(
93 hr = ContainerFindById(pContainers, scz, &pPayload->pContainer); 105 hr = ContainerFindById(pContainers, scz, &pPayload->pContainer);
94 ExitOnFailure(hr, "Failed to to find container: %ls", scz); 106 ExitOnFailure(hr, "Failed to to find container: %ls", scz);
95 } 107 }
96 }
97 108
98 // @LayoutOnly 109 // @LayoutOnly
99 hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly); 110 hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly);
100 if (E_NOTFOUND != hr) 111 if (E_NOTFOUND != hr)
101 { 112 {
102 ExitOnFailure(hr, "Failed to get @LayoutOnly."); 113 ExitOnFailure(hr, "Failed to get @LayoutOnly.");
103 } 114 }
104 115
105 // @SourcePath 116 // @DownloadUrl
106 hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); 117 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl);
107 ExitOnFailure(hr, "Failed to get @SourcePath."); 118 if (E_NOTFOUND != hr)
119 {
120 ExitOnFailure(hr, "Failed to get @DownloadUrl.");
121 }
108 122
109 // @DownloadUrl 123 // @FileSize
110 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); 124 hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz);
111 if (E_NOTFOUND != hr) 125 if (E_NOTFOUND != hr)
112 { 126 {
113 ExitOnFailure(hr, "Failed to get @DownloadUrl."); 127 ExitOnFailure(hr, "Failed to get @FileSize.");
114 }
115 128
116 // @FileSize 129 hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize);
117 hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); 130 ExitOnFailure(hr, "Failed to parse @FileSize.");
118 if (E_NOTFOUND != hr)
119 {
120 ExitOnFailure(hr, "Failed to get @FileSize.");
121 131
122 hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize); 132 fValidFileSize = TRUE;
123 ExitOnFailure(hr, "Failed to parse @FileSize."); 133 }
124 } 134
135 // @Hash
136 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz);
137 if (E_NOTFOUND != hr)
138 {
139 ExitOnFailure(hr, "Failed to get @Hash.");
125 140
126 // @Hash 141 hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash);
127 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); 142 ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash.");
128 ExitOnFailure(hr, "Failed to get @Hash.");
129 143
130 hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); 144 if (BURN_PAYLOAD_VERIFICATION_NONE == pPayload->verification)
131 ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); 145 {
146 pPayload->verification = BURN_PAYLOAD_VERIFICATION_HASH;
147 }
148 }
132 149
133 if (pPayload->fLayoutOnly && pLayoutPayloads) 150 if (BURN_PAYLOAD_VERIFICATION_NONE == pPayload->verification)
134 { 151 {
135 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5); 152 ExitOnRootFailure(hr = E_INVALIDDATA, "There was no verification information for payload: %ls", pPayload->sczKey);
136 ExitOnFailure(hr, "Failed to allocate memory for layout payloads."); 153 }
154 else if (BURN_PAYLOAD_VERIFICATION_HASH == pPayload->verification && !fValidFileSize)
155 {
156 ExitOnRootFailure(hr = E_INVALIDDATA, "File size is required when verifying by hash for payload: %ls", pPayload->sczKey);
157 }
137 158
138 pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload; 159 if (pPayload->fLayoutOnly)
139 ++pLayoutPayloads->cItems; 160 {
161 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5);
162 ExitOnFailure(hr, "Failed to allocate memory for layout payloads.");
140 163
141 pLayoutPayloads->qwTotalSize += pPayload->qwFileSize; 164 pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload;
165 ++pLayoutPayloads->cItems;
166
167 pLayoutPayloads->qwTotalSize += pPayload->qwFileSize;
168 }
142 } 169 }
143 170
144 // prepare next iteration 171 // prepare next iteration
diff --git a/src/burn/engine/payload.h b/src/burn/engine/payload.h
index f28b437f..6fc6de5e 100644
--- a/src/burn/engine/payload.h
+++ b/src/burn/engine/payload.h
@@ -23,6 +23,13 @@ enum BURN_PAYLOAD_STATE
23 BURN_PAYLOAD_STATE_CACHED, 23 BURN_PAYLOAD_STATE_CACHED,
24}; 24};
25 25
26enum BURN_PAYLOAD_VERIFICATION
27{
28 BURN_PAYLOAD_VERIFICATION_NONE,
29 BURN_PAYLOAD_VERIFICATION_HASH,
30 BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE,
31};
32
26 33
27// structs 34// structs
28 35
@@ -36,6 +43,7 @@ typedef struct _BURN_PAYLOAD
36 43
37 BYTE* pbHash; 44 BYTE* pbHash;
38 DWORD cbHash; 45 DWORD cbHash;
46 BURN_PAYLOAD_VERIFICATION verification;
39 47
40 LPWSTR sczSourcePath; 48 LPWSTR sczSourcePath;
41 BURN_CONTAINER* pContainer; 49 BURN_CONTAINER* pContainer;
diff --git a/src/burn/engine/pseudobundle.cpp b/src/burn/engine/pseudobundle.cpp
index 180cc621..1b2eae75 100644
--- a/src/burn/engine/pseudobundle.cpp
+++ b/src/burn/engine/pseudobundle.cpp
@@ -69,6 +69,11 @@ extern "C" HRESULT PseudoBundleInitialize(
69 memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash); 69 memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash);
70 } 70 }
71 71
72 if (BOOTSTRAPPER_RELATION_UPDATE == relationType)
73 {
74 pPayload->verification = BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE;
75 }
76
72 pPackage->Exe.fPseudoBundle = TRUE; 77 pPackage->Exe.fPseudoBundle = TRUE;
73 78
74 pPackage->type = BURN_PACKAGE_TYPE_EXE; 79 pPackage->type = BURN_PACKAGE_TYPE_EXE;
diff --git a/src/burn/test/BurnUnitTest/CacheTest.cpp b/src/burn/test/BurnUnitTest/CacheTest.cpp
index d0cc237f..e9ad555b 100644
--- a/src/burn/test/BurnUnitTest/CacheTest.cpp
+++ b/src/burn/test/BurnUnitTest/CacheTest.cpp
@@ -71,6 +71,8 @@ namespace Bootstrapper
71 payload.sczFilePath = L"CacheSignatureTest.File"; 71 payload.sczFilePath = L"CacheSignatureTest.File";
72 payload.pbHash = pb; 72 payload.pbHash = pb;
73 payload.cbHash = cb; 73 payload.cbHash = cb;
74 payload.qwFileSize = 27;
75 payload.verification = BURN_PAYLOAD_VERIFICATION_HASH;
74 76
75 hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context); 77 hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context);
76 Assert::Equal(S_OK, hr); 78 Assert::Equal(S_OK, hr);
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
index 001acaee..045c510e 100644
--- a/src/burn/test/BurnUnitTest/PlanTest.cpp
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -71,7 +71,7 @@ namespace Bootstrapper
71 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 71 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
72 72
73 Assert::Equal(107082ull, pPlan->qwEstimatedSize); 73 Assert::Equal(107082ull, pPlan->qwEstimatedSize);
74 Assert::Equal(506145ull, pPlan->qwCacheSizeTotal); 74 Assert::Equal(522548ull, pPlan->qwCacheSizeTotal);
75 75
76 fRollback = FALSE; 76 fRollback = FALSE;
77 dwIndex = 0; 77 dwIndex = 0;