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 /*
18  * The tests in this file operate on a higher level than the tests in the other
19  * files. Here, all tests execute the idmap2 binary and only depend on
20  * libidmap2 to verify the output of idmap2.
21  */
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 
28 #include <cerrno>
29 #include <cstdlib>
30 #include <cstring>  // strerror
31 #include <fstream>
32 #include <memory>
33 #include <sstream>
34 #include <string>
35 #include <vector>
36 
37 #include "R.h"
38 #include "TestHelpers.h"
39 #include "androidfw/PosixUtils.h"
40 #include "gmock/gmock.h"
41 #include "gtest/gtest.h"
42 #include "idmap2/FileUtils.h"
43 #include "idmap2/Idmap.h"
44 #include "private/android_filesystem_config.h"
45 
46 using ::android::util::ExecuteBinary;
47 using ::testing::NotNull;
48 
49 namespace android::idmap2 {
50 
51 class Idmap2BinaryTests : public Idmap2Tests {};
52 
53 namespace {
54 
AssertIdmap(const Idmap & idmap,const std::string & target_apk_path,const std::string & overlay_apk_path)55 void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
56                  const std::string& overlay_apk_path) {
57   // check that the idmap file looks reasonable (IdmapTests is responsible for
58   // more in-depth verification)
59   ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
60   ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
61   ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
62   ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
63   ASSERT_EQ(idmap.GetData().size(), 1U);
64 }
65 
66 #define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path)                      \
67   do {                                                                                  \
68     ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
69   } while (0)
70 
71 #ifdef __ANDROID__
72 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2           \
73   do {                                          \
74     const uid_t uid = getuid();                 \
75     if (uid != AID_ROOT && uid != AID_SYSTEM) { \
76       GTEST_SKIP();                             \
77     }                                           \
78   } while (0)
79 #else
80 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2
81 #endif
82 
83 }  // namespace
84 
TEST_F(Idmap2BinaryTests,Create)85 TEST_F(Idmap2BinaryTests, Create) {
86   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
87 
88   // clang-format off
89   auto result = ExecuteBinary({"idmap2",
90                                "create",
91                                "--target-apk-path", GetTargetApkPath(),
92                                "--overlay-apk-path", GetOverlayApkPath(),
93                                "--idmap-path", GetIdmapPath()});
94   // clang-format on
95   ASSERT_THAT(result, NotNull());
96   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
97 
98   struct stat st;
99   ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
100 
101   std::ifstream fin(GetIdmapPath());
102   const auto idmap = Idmap::FromBinaryStream(fin);
103   fin.close();
104 
105   ASSERT_TRUE(idmap);
106   ASSERT_IDMAP(**idmap, GetTargetApkPath(), GetOverlayApkPath());
107 
108   unlink(GetIdmapPath().c_str());
109 }
110 
TEST_F(Idmap2BinaryTests,Dump)111 TEST_F(Idmap2BinaryTests, Dump) {
112   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
113 
114   // clang-format off
115   auto result = ExecuteBinary({"idmap2",
116                                "create",
117                                "--target-apk-path", GetTargetApkPath(),
118                                "--overlay-apk-path", GetOverlayApkPath(),
119                                "--idmap-path", GetIdmapPath()});
120   // clang-format on
121   ASSERT_THAT(result, NotNull());
122   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
123 
124   // clang-format off
125   result = ExecuteBinary({"idmap2",
126                           "dump",
127                           "--idmap-path", GetIdmapPath()});
128   // clang-format on
129   ASSERT_THAT(result, NotNull());
130   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
131   ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
132             std::string::npos);
133   ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
134             std::string::npos);
135   ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
136             std::string::npos);
137   ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
138             std::string::npos);
139 
140   // clang-format off
141   result = ExecuteBinary({"idmap2",
142                           "dump",
143                           "--verbose",
144                           "--idmap-path", GetIdmapPath()});
145   // clang-format on
146   ASSERT_THAT(result, NotNull());
147   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
148   ASSERT_NE(result->stdout.find("00000000: 504d4449  magic"), std::string::npos);
149 
150   // clang-format off
151   result = ExecuteBinary({"idmap2",
152                           "dump",
153                           "--verbose",
154                           "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
155   // clang-format on
156   ASSERT_THAT(result, NotNull());
157   ASSERT_NE(result->status, EXIT_SUCCESS);
158 
159   unlink(GetIdmapPath().c_str());
160 }
161 
TEST_F(Idmap2BinaryTests,Scan)162 TEST_F(Idmap2BinaryTests, Scan) {
163   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
164 
165   const std::string overlay_static_no_name_apk_path =
166       GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
167   const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
168   const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
169   const std::string idmap_static_no_name_path =
170       Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
171   const std::string idmap_static_1_path =
172       Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
173   const std::string idmap_static_2_path =
174       Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
175 
176   // single input directory, recursive
177   // clang-format off
178   auto result = ExecuteBinary({"idmap2",
179                                "scan",
180                                "--input-directory", GetTestDataPath(),
181                                "--recursive",
182                                "--target-package-name", "test.target",
183                                "--target-apk-path", GetTargetApkPath(),
184                                "--output-directory", GetTempDirPath(),
185                                "--override-policy", "public"});
186   // clang-format on
187   ASSERT_THAT(result, NotNull());
188   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
189   std::stringstream expected;
190   expected << idmap_static_no_name_path << std::endl;
191   expected << idmap_static_1_path << std::endl;
192   expected << idmap_static_2_path << std::endl;
193   ASSERT_EQ(result->stdout, expected.str());
194 
195   auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
196   auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
197   auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream);
198   ASSERT_TRUE(idmap_static_no_name);
199   ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
200 
201   auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
202   auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
203   auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream);
204   ASSERT_TRUE(idmap_static_1);
205   ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
206 
207   auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
208   auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
209   auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream);
210   ASSERT_TRUE(idmap_static_2);
211   ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
212 
213   unlink(idmap_static_no_name_path.c_str());
214   unlink(idmap_static_2_path.c_str());
215   unlink(idmap_static_1_path.c_str());
216 
217   // multiple input directories, non-recursive
218   // clang-format off
219   result = ExecuteBinary({"idmap2",
220                           "scan",
221                           "--input-directory", GetTestDataPath() + "/target",
222                           "--input-directory", GetTestDataPath() + "/overlay",
223                           "--target-package-name", "test.target",
224                           "--target-apk-path", GetTargetApkPath(),
225                           "--output-directory", GetTempDirPath(),
226                           "--override-policy", "public"});
227   // clang-format on
228   ASSERT_THAT(result, NotNull());
229   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
230   ASSERT_EQ(result->stdout, expected.str());
231   unlink(idmap_static_no_name_path.c_str());
232   unlink(idmap_static_2_path.c_str());
233   unlink(idmap_static_1_path.c_str());
234 
235   // the same input directory given twice, but no duplicate entries
236   // clang-format off
237   result = ExecuteBinary({"idmap2",
238                           "scan",
239                           "--input-directory", GetTestDataPath(),
240                           "--input-directory", GetTestDataPath(),
241                           "--recursive",
242                           "--target-package-name", "test.target",
243                           "--target-apk-path", GetTargetApkPath(),
244                           "--output-directory", GetTempDirPath(),
245                           "--override-policy", "public"});
246   // clang-format on
247   ASSERT_THAT(result, NotNull());
248   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
249   ASSERT_EQ(result->stdout, expected.str());
250   unlink(idmap_static_no_name_path.c_str());
251   unlink(idmap_static_2_path.c_str());
252   unlink(idmap_static_1_path.c_str());
253 
254   // no APKs in input-directory: ok, but no output
255   // clang-format off
256   result = ExecuteBinary({"idmap2",
257                           "scan",
258                           "--input-directory", GetTempDirPath(),
259                           "--target-package-name", "test.target",
260                           "--target-apk-path", GetTargetApkPath(),
261                           "--output-directory", GetTempDirPath(),
262                           "--override-policy", "public"});
263   // clang-format on
264   ASSERT_THAT(result, NotNull());
265   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
266   ASSERT_EQ(result->stdout, "");
267 
268   // the signature idmap failing to generate should not cause scanning to fail
269   // clang-format off
270   result = ExecuteBinary({"idmap2",
271                           "scan",
272                           "--input-directory", GetTestDataPath(),
273                           "--recursive",
274                           "--target-package-name", "test.target",
275                           "--target-apk-path", GetTargetApkPath(),
276                           "--output-directory", GetTempDirPath(),
277                           "--override-policy", "public"});
278   // clang-format on
279   ASSERT_THAT(result, NotNull());
280   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
281   ASSERT_EQ(result->stdout, expected.str());
282   unlink(idmap_static_no_name_path.c_str());
283   unlink(idmap_static_2_path.c_str());
284   unlink(idmap_static_1_path.c_str());
285 }
286 
TEST_F(Idmap2BinaryTests,Lookup)287 TEST_F(Idmap2BinaryTests, Lookup) {
288   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
289 
290   // clang-format off
291   auto result = ExecuteBinary({"idmap2",
292                                "create",
293                                "--target-apk-path", GetTargetApkPath(),
294                                "--overlay-apk-path", GetOverlayApkPath(),
295                                "--idmap-path", GetIdmapPath()});
296   // clang-format on
297   ASSERT_THAT(result, NotNull());
298   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
299 
300   // clang-format off
301   result = ExecuteBinary({"idmap2",
302                           "lookup",
303                           "--idmap-path", GetIdmapPath(),
304                           "--config", "",
305                           "--resid", R::target::string::literal::str1});
306   // clang-format on
307   ASSERT_THAT(result, NotNull());
308   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
309   ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
310   ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
311 
312   // clang-format off
313   result = ExecuteBinary({"idmap2",
314                           "lookup",
315                           "--idmap-path", GetIdmapPath(),
316                           "--config", "",
317                           "--resid", "test.target:string/str1"});
318   // clang-format on
319   ASSERT_THAT(result, NotNull());
320   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
321   ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
322   ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
323 
324   // clang-format off
325   result = ExecuteBinary({"idmap2",
326                           "lookup",
327                           "--idmap-path", GetIdmapPath(),
328                           "--config", "sv",
329                           "--resid", "test.target:string/str1"});
330   // clang-format on
331   ASSERT_THAT(result, NotNull());
332   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
333   ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
334 
335   unlink(GetIdmapPath().c_str());
336 }
337 
TEST_F(Idmap2BinaryTests,InvalidCommandLineOptions)338 TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
339   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
340 
341   const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
342 
343   // missing mandatory options
344   // clang-format off
345   auto result = ExecuteBinary({"idmap2",
346                                "create"});
347   // clang-format on
348   ASSERT_THAT(result, NotNull());
349   ASSERT_NE(result->status, EXIT_SUCCESS);
350 
351   // missing argument to option
352   // clang-format off
353   result = ExecuteBinary({"idmap2",
354                           "create",
355                           "--target-apk-path", GetTargetApkPath(),
356                           "--overlay-apk-path", GetOverlayApkPath(),
357                           "--idmap-path"});
358   // clang-format on
359   ASSERT_THAT(result, NotNull());
360   ASSERT_NE(result->status, EXIT_SUCCESS);
361 
362   // invalid target apk path
363   // clang-format off
364   result = ExecuteBinary({"idmap2",
365                           "create",
366                           "--target-apk-path", invalid_target_apk_path,
367                           "--overlay-apk-path", GetOverlayApkPath(),
368                           "--idmap-path", GetIdmapPath()});
369   // clang-format on
370   ASSERT_THAT(result, NotNull());
371   ASSERT_NE(result->status, EXIT_SUCCESS);
372 
373   // unknown policy
374   // clang-format off
375   result = ExecuteBinary({"idmap2",
376                           "create",
377                           "--target-apk-path", GetTargetApkPath(),
378                           "--overlay-apk-path", GetOverlayApkPath(),
379                           "--idmap-path", GetIdmapPath(),
380                           "--policy", "this-does-not-exist"});
381   // clang-format on
382   ASSERT_THAT(result, NotNull());
383   ASSERT_NE(result->status, EXIT_SUCCESS);
384 }
385 
386 }  // namespace android::idmap2
387