1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/file.h>
18 #include <androidfw/ResourceTypes.h>
19 #include <gtest/gtest.h>
20
21 #include <cstdio> // fclose
22 #include <fstream>
23 #include <memory>
24 #include <string>
25
26 #include "R.h"
27 #include "TestConstants.h"
28 #include "TestHelpers.h"
29 #include "idmap2/LogInfo.h"
30 #include "idmap2/ResourceMapping.h"
31
32 using android::Res_value;
33
34 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
35
36 namespace android::idmap2 {
37
38 #define ASSERT_RESULT(r) \
39 do { \
40 auto result = r; \
41 ASSERT_TRUE(result) << result.GetErrorMessage(); \
42 } while (0)
43
TestGetResourceMapping(const std::string & local_target_path,const std::string & local_overlay_path,const std::string & overlay_name,const PolicyBitmask & fulfilled_policies,bool enforce_overlayable)44 Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_path,
45 const std::string& local_overlay_path,
46 const std::string& overlay_name,
47 const PolicyBitmask& fulfilled_policies,
48 bool enforce_overlayable) {
49 const std::string target_path = (local_target_path[0] == '/')
50 ? local_target_path
51 : (GetTestDataPath() + "/" + local_target_path);
52 auto target = TargetResourceContainer::FromPath(target_path);
53 if (!target) {
54 return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str());
55 }
56
57 const std::string overlay_path = (local_overlay_path[0] == '/')
58 ? local_overlay_path
59 : (GetTestDataPath() + "/" + local_overlay_path);
60 auto overlay = OverlayResourceContainer::FromPath(overlay_path);
61 if (!overlay) {
62 return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str());
63 }
64
65 auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
66 if (!overlay_info) {
67 return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")",
68 overlay_name.c_str());
69 }
70
71 LogInfo log_info;
72 return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies,
73 enforce_overlayable, log_info);
74 }
75
MappingExists(const ResourceMapping & mapping,ResourceId target_resource,ResourceId overlay_resource,bool rewrite)76 Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
77 ResourceId overlay_resource, bool rewrite) {
78 auto target_map = mapping.GetTargetToOverlayMap();
79 auto entry_map = target_map.find(target_resource);
80 if (entry_map == target_map.end()) {
81 return Error("Failed to find mapping for target resource");
82 }
83
84 auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
85 if (actual_overlay_resource == nullptr) {
86 return Error("Target resource is not mapped to an overlay resource id");
87 }
88
89 if (*actual_overlay_resource != overlay_resource) {
90 return Error(R"(Expected id: "0x%02x" Actual id: "0x%02x")", overlay_resource,
91 *actual_overlay_resource);
92 }
93
94 auto overlay_map = mapping.GetOverlayToTargetMap();
95 auto overlay_iter = overlay_map.find(overlay_resource);
96 if ((overlay_iter != overlay_map.end()) != rewrite) {
97 return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
98 }
99
100 if (rewrite && overlay_iter->second != target_resource) {
101 return Error(R"(Expected rewrite id: "0x%02x" Actual id: "0x%02x")", target_resource,
102 overlay_iter->second);
103 }
104
105 return Result<Unit>({});
106 }
107
MappingExists(const ResourceMapping & mapping,const ResourceId & target_resource,const uint8_t type,const uint32_t value)108 Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
109 const uint8_t type, const uint32_t value) {
110 auto target_map = mapping.GetTargetToOverlayMap();
111 auto entry_map = target_map.find(target_resource);
112 if (entry_map == target_map.end()) {
113 return Error("Failed to find mapping for target resource");
114 }
115
116 auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
117 if (actual_overlay_value == nullptr) {
118 return Error("Target resource is not mapped to an inline value");
119 }
120
121 if (actual_overlay_value->data_type != type) {
122 return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
123 actual_overlay_value->data_type);
124 }
125
126 if (actual_overlay_value->data_value != value) {
127 return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
128 actual_overlay_value->data_value);
129 }
130
131 return Result<Unit>({});
132 }
133
TEST(ResourceMappingTests,ResourcesFromApkAssetsLegacy)134 TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
135 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
136 PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
137
138 ASSERT_TRUE(resources) << resources.GetErrorMessage();
139 auto& res = *resources;
140 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
141 ASSERT_RESULT(
142 MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
143 ASSERT_RESULT(
144 MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
145 ASSERT_RESULT(
146 MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
147 ASSERT_RESULT(
148 MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
149 }
150
TEST(ResourceMappingTests,ResourcesFromApkAssetsNonMatchingNames)151 TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
152 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames",
153 PolicyFlags::PUBLIC,
154 /* enforce_overlayable */ false);
155
156 ASSERT_TRUE(resources) << resources.GetErrorMessage();
157 auto& res = *resources;
158 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
159 ASSERT_RESULT(
160 MappingExists(res, R::target::string::str1, R::overlay::string::str4, true /* rewrite */));
161 ASSERT_RESULT(
162 MappingExists(res, R::target::string::str3, R::overlay::string::str1, true /* rewrite */));
163 ASSERT_RESULT(
164 MappingExists(res, R::target::string::str4, R::overlay::string::str3, true /* rewrite */));
165 }
166
TEST(ResourceMappingTests,DoNotRewriteNonOverlayResourceId)167 TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
168 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
169 "DifferentPackages", PolicyFlags::PUBLIC,
170 /* enforce_overlayable */ false);
171
172 ASSERT_TRUE(resources) << resources.GetErrorMessage();
173 auto& res = *resources;
174 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
175 ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
176 ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a,
177 false /* rewrite */)); // -> android:string/ok
178 ASSERT_RESULT(
179 MappingExists(res, R::target::string::str3, R::overlay::string::str3, true /* rewrite */));
180 }
181
TEST(ResourceMappingTests,InlineResources)182 TEST(ResourceMappingTests, InlineResources) {
183 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline",
184 PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
185
186 constexpr size_t overlay_string_pool_size = 10U;
187 ASSERT_TRUE(resources) << resources.GetErrorMessage();
188 auto& res = *resources;
189 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
190 ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
191 ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
192 overlay_string_pool_size + 0U)); // -> "Hello World"
193 ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
194 }
195
TEST(ResourceMappingTests,FabricatedOverlay)196 TEST(ResourceMappingTests, FabricatedOverlay) {
197 auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
198 .SetOverlayable("TestResources")
199 .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
200 .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
201 .Build();
202
203 ASSERT_TRUE(frro);
204 TemporaryFile tf;
205 std::ofstream out(tf.path);
206 ASSERT_TRUE((*frro).ToBinaryStream(out));
207 out.close();
208
209 auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme",
210 PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
211
212 ASSERT_TRUE(resources) << resources.GetErrorMessage();
213 auto& res = *resources;
214 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
215 ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
216 ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000));
217 ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
218 }
219
TEST(ResourceMappingTests,CreateIdmapFromApkAssetsPolicySystemPublic)220 TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
221 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
222 TestConstants::OVERLAY_NAME_ALL_POLICIES,
223 PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
224 /* enforce_overlayable */ true);
225
226 ASSERT_TRUE(resources) << resources.GetErrorMessage();
227 auto& res = *resources;
228 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
229 ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
230 R::overlay::string::policy_public, true /* rewrite */));
231 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
232 R::overlay::string::policy_system, true /* rewrite */));
233 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
234 R::overlay::string::policy_system_vendor, true /* rewrite */));
235 }
236
237 // Resources that are not declared as overlayable and resources that a protected by policies the
238 // overlay does not fulfill must not map to overlay resources.
TEST(ResourceMappingTests,CreateIdmapFromApkAssetsPolicySystemPublicInvalid)239 TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
240 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
241 TestConstants::OVERLAY_NAME_ALL_POLICIES,
242 PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
243 /* enforce_overlayable */ true);
244
245 ASSERT_TRUE(resources) << resources.GetErrorMessage();
246 auto& res = *resources;
247 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
248 ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
249 R::overlay::string::policy_public, true /* rewrite */));
250 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
251 R::overlay::string::policy_system, true /* rewrite */));
252 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
253 R::overlay::string::policy_system_vendor, true /* rewrite */));
254 }
255
256 // Resources that are not declared as overlayable and resources that a protected by policies the
257 // overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
258 // off.
TEST(ResourceMappingTests,ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable)259 TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
260 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
261 TestConstants::OVERLAY_NAME_ALL_POLICIES,
262 PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
263 /* enforce_overlayable */ false);
264
265 ASSERT_TRUE(resources) << resources.GetErrorMessage();
266 auto& res = *resources;
267 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U);
268 ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
269 R::overlay::string::not_overlayable, true /* rewrite */));
270 ASSERT_RESULT(
271 MappingExists(res, R::target::string::other, R::overlay::string::other, true /* rewrite */));
272 ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
273 R::overlay::string::policy_actor, true /* rewrite */));
274 ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::overlay::string::policy_odm,
275 true /* rewrite */));
276 ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::overlay::string::policy_oem,
277 true /* rewrite */));
278 ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
279 R::overlay::string::policy_product, true /* rewrite */));
280 ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
281 R::overlay::string::policy_public, true /* rewrite */));
282 ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
283 R::overlay::string::policy_config_signature, true /* rewrite */));
284 ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
285 R::overlay::string::policy_signature, true /* rewrite */));
286 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
287 R::overlay::string::policy_system, true /* rewrite */));
288 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
289 R::overlay::string::policy_system_vendor, true /* rewrite */));
290 }
291
292 // Overlays that do not target an <overlayable> tag can overlay any resource if overlayable
293 // enforcement is disabled.
TEST(ResourceMappingTests,ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName)294 TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
295 auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
296 PolicyFlags::PUBLIC,
297 /* enforce_overlayable */ false);
298
299 ASSERT_TRUE(resources) << resources.GetErrorMessage();
300 auto& res = *resources;
301 ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
302 ASSERT_RESULT(
303 MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
304 ASSERT_RESULT(
305 MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
306 ASSERT_RESULT(
307 MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
308 ASSERT_RESULT(
309 MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
310 }
311
312 // Overlays that are neither pre-installed nor signed with the same signature as the target cannot
313 // overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests,ResourcesFromApkAssetsDefaultPoliciesPublicFail)314 TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
315 auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
316 "NoTargetName", PolicyFlags::PUBLIC,
317 /* enforce_overlayable */ true);
318
319 ASSERT_TRUE(resources) << resources.GetErrorMessage();
320 ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
321 }
322
323 // Overlays that are pre-installed or are signed with the same signature as the target or are
324 // signed with the same signature as the reference package can overlay packages that have not
325 // defined overlayable resources.
TEST(ResourceMappingTests,ResourcesFromApkAssetsDefaultPolicies)326 TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
327 auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) {
328 auto resources =
329 TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
330 TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies,
331 /* enforce_overlayable */ true);
332
333 ASSERT_TRUE(resources) << resources.GetErrorMessage();
334 auto& res = *resources;
335 ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U);
336 ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
337 R::overlay::string::not_overlayable, true /* rewrite */));
338 ASSERT_RESULT(MappingExists(res, R::target::string::other, R::overlay::string::other,
339 true /* rewrite */));
340 ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
341 R::overlay::string::policy_actor, true /* rewrite */));
342 ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::overlay::string::policy_odm,
343 true /* rewrite */));
344 ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::overlay::string::policy_oem,
345 true /* rewrite */));
346 ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
347 R::overlay::string::policy_product, true /* rewrite */));
348 ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
349 R::overlay::string::policy_public, true /* rewrite */));
350 ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
351 R::overlay::string::policy_config_signature, true /* rewrite */));
352 ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
353 R::overlay::string::policy_signature, true /* rewrite */));
354 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
355 R::overlay::string::policy_system, true /* rewrite */));
356 ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
357 R::overlay::string::policy_system_vendor, true /* rewrite */));
358 };
359
360 CheckEntries(PolicyFlags::SIGNATURE);
361 CheckEntries(PolicyFlags::CONFIG_SIGNATURE);
362 CheckEntries(PolicyFlags::PRODUCT_PARTITION);
363 CheckEntries(PolicyFlags::SYSTEM_PARTITION);
364 CheckEntries(PolicyFlags::VENDOR_PARTITION);
365 CheckEntries(PolicyFlags::ODM_PARTITION);
366 CheckEntries(PolicyFlags::OEM_PARTITION);
367 }
368
369 } // namespace android::idmap2
370