blob: e4092867af4c793d07b28a33b0f09ceebc45facd [file] [log] [blame]
Greg Kerr019d59c2016-11-17 14:28:49 -08001// Copyright 2016 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Chan045849f2017-12-18 17:27:07 -08005#include "imageloader/component.h"
Greg Kerr019d59c2016-11-17 14:28:49 -08006
7#include <stdint.h>
8
9#include <list>
Eric Carusocbe1c5c2017-03-15 14:21:08 -070010#include <memory>
Greg Kerr019d59c2016-11-17 14:28:49 -080011#include <string>
12#include <vector>
13
Qijiang Fan713061e2021-03-08 15:45:12 +090014#include <base/check.h>
Greg Kerr019d59c2016-11-17 14:28:49 -080015#include <base/files/file_path.h>
16#include <base/files/file_util.h>
17#include <base/files/scoped_temp_dir.h>
18#include <base/logging.h>
Greg Kerr019d59c2016-11-17 14:28:49 -080019#include <crypto/secure_hash.h>
20#include <crypto/sha2.h>
21#include <gmock/gmock.h>
22#include <gtest/gtest.h>
23
Ben Chan045849f2017-12-18 17:27:07 -080024#include "imageloader/imageloader_impl.h"
Amin Hassani17a185b2021-02-10 12:07:57 -080025#include "imageloader/mock_global_context.h"
Greg Kerr09f06de2018-02-16 15:32:07 -080026#include "imageloader/mock_helper_process_proxy.h"
Ben Chan045849f2017-12-18 17:27:07 -080027#include "imageloader/test_utilities.h"
Eric Caruso0b79bc82017-03-21 13:44:34 -070028
Greg Kerr019d59c2016-11-17 14:28:49 -080029namespace imageloader {
30
31using testing::_;
32
33class ComponentTest : public testing::Test {
34 public:
35 ComponentTest() {
Eric Caruso0b79bc82017-03-21 13:44:34 -070036 keys_.push_back(std::vector<uint8_t>(std::begin(kDevPublicKey),
37 std::end(kDevPublicKey)));
Greg Kerr019d59c2016-11-17 14:28:49 -080038 CHECK(scoped_temp_dir_.CreateUniqueTempDir());
Eric Carusoa5dfc942018-01-22 15:44:45 -080039 temp_dir_ = scoped_temp_dir_.GetPath();
Greg Kerr019d59c2016-11-17 14:28:49 -080040 CHECK(base::SetPosixFilePermissions(temp_dir_, kComponentDirPerms));
41 }
42
Amin Hassani17a185b2021-02-10 12:07:57 -080043 void SetUp() override {
44 g_ctx_.SetAsCurrent();
45 ON_CALL(g_ctx_, IsOfficialBuild()).WillByDefault(testing::Return(true));
46 }
47
Greg Kerr019d59c2016-11-17 14:28:49 -080048 bool TestCopyWithCorruptFile(const std::string& component_name,
49 const std::string& file_name) {
50 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
Colin Howesad6271a2018-11-21 15:36:05 -080051 if (!base::CreateDirectory(bad_component_dir))
52 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080053 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
54 return false;
55
Eric Carusocbe1c5c2017-03-15 14:21:08 -070056 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070057 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070058 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080059 return false;
60
Eric Carusocbe1c5c2017-03-15 14:21:08 -070061 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070062 Component::Create(bad_component_dir, keys_);
Colin Howesad6271a2018-11-21 15:36:05 -080063 if (!bad_component)
64 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080065
66 base::FilePath file = bad_component_dir.Append(file_name);
67 const char data[] = "c";
Colin Howesad6271a2018-11-21 15:36:05 -080068 if (!base::AppendToFile(file, data, sizeof(data)))
69 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080070
71 base::FilePath bad_component_dest =
72 temp_dir_.Append(component_name + "invalid");
Colin Howesad6271a2018-11-21 15:36:05 -080073 if (!base::CreateDirectory(bad_component_dest))
74 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080075
76 if (!base::SetPosixFilePermissions(bad_component_dest, kComponentDirPerms))
77 return false;
Eric Carusocbe1c5c2017-03-15 14:21:08 -070078 return bad_component->CopyTo(bad_component_dest) == false;
Greg Kerr019d59c2016-11-17 14:28:49 -080079 }
80
81 bool TestInitComponentWithCorruptFile(const std::string& component_name,
82 const std::string& file_name) {
83 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
Colin Howesad6271a2018-11-21 15:36:05 -080084 if (!base::CreateDirectory(bad_component_dir))
85 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080086 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
87 return false;
88
Eric Carusocbe1c5c2017-03-15 14:21:08 -070089 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070090 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070091 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080092 return false;
93
94 base::FilePath file = bad_component_dir.Append(file_name);
95 // Read the file out and change the last byte.
96 std::string file_contents;
Colin Howesad6271a2018-11-21 15:36:05 -080097 if (!base::ReadFileToString(file, &file_contents))
98 return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080099 file_contents[file_contents.size() - 1] =
100 ~file_contents[file_contents.size() - 1];
101
102 if (!base::WriteFile(file, file_contents.data(), file_contents.size())) {
103 return false;
104 }
105
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700106 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700107 Component::Create(bad_component_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700108 return bad_component == nullptr;
Greg Kerr019d59c2016-11-17 14:28:49 -0800109 }
110
111 bool CompareFileContents(const base::FilePath& src,
112 const base::FilePath& dest,
113 const std::list<std::string>& filenames) {
114 for (const std::string& name : filenames) {
115 std::string source_file_contents;
116 std::string dest_file_contents;
117 if (!base::ReadFileToString(src.Append(name), &source_file_contents))
118 return false;
119 if (!base::ReadFileToString(dest.Append(name), &dest_file_contents))
120 return false;
121 if (source_file_contents != dest_file_contents) {
122 LOG(ERROR) << "File contents does not match for file: " << name;
123 return false;
124 }
125 }
126 return true;
127 }
128
Eric Caruso0b79bc82017-03-21 13:44:34 -0700129 Keys keys_;
Greg Kerr019d59c2016-11-17 14:28:49 -0800130 base::ScopedTempDir scoped_temp_dir_;
131 base::FilePath temp_dir_;
Amin Hassani17a185b2021-02-10 12:07:57 -0800132
133 MockGlobalContext g_ctx_;
Greg Kerr019d59c2016-11-17 14:28:49 -0800134};
135
136TEST_F(ComponentTest, InitComponentAndCheckManifest) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700137 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700138 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700139 ASSERT_NE(nullptr, component);
140
Xiaochu Liuc209aab2018-06-19 13:42:15 -0700141 EXPECT_EQ(1, component->manifest().manifest_version());
142 EXPECT_EQ(kTestDataVersion, component->manifest().version());
Greg Kerr04c1cee2020-10-15 14:08:44 +0000143 // Don't hardcode the sha256 hashes, but run some validity checks.
Xiaochu Liuc209aab2018-06-19 13:42:15 -0700144 EXPECT_EQ(crypto::kSHA256Length, component->manifest().image_sha256().size());
145 EXPECT_EQ(crypto::kSHA256Length, component->manifest().table_sha256().size());
146 EXPECT_NE(component->manifest().image_sha256(),
147 component->manifest().table_sha256());
Greg Kerr019d59c2016-11-17 14:28:49 -0800148}
149
Xiaochu Liuc2264342017-08-14 16:37:42 -0700150TEST_F(ComponentTest, TestCopyAndMountComponentExt4) {
151 std::unique_ptr<Component> component =
152 Component::Create(GetTestDataPath("ext4_component"), keys_);
153 ASSERT_NE(nullptr, component);
154
155 const base::FilePath copied_dir = temp_dir_.Append("dest");
156 ASSERT_TRUE(base::CreateDirectory(copied_dir));
157 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
158
159 ASSERT_TRUE(component->CopyTo(copied_dir));
160
161 std::unique_ptr<Component> copied_component =
162 Component::Create(copied_dir, keys_);
163 ASSERT_NE(nullptr, copied_component);
164
165 const base::FilePath mount_dir = temp_dir_.Append("mount");
166 ASSERT_TRUE(base::CreateDirectory(copied_dir));
167 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
168
169 // Note: this fails to test the actual mounting process since it is just a
170 // mock here. The platform_ImageLoader autotest tests the real helper
171 // process running as a dbus service.
Greg Kerr09f06de2018-02-16 15:32:07 -0800172 auto helper_mock = std::make_unique<MockHelperProcessProxy>();
Xiaochu Liue61e1d62018-11-12 13:20:09 -0800173 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kExt4, _))
Xiaochu Liuc2264342017-08-14 16:37:42 -0700174 .Times(1);
175 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
176 .WillByDefault(testing::Return(true));
177 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
178}
179
180TEST_F(ComponentTest, TestCopyAndMountComponentSquashfs) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700181 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700182 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700183 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800184
185 const base::FilePath copied_dir = temp_dir_.Append("dest");
186 ASSERT_TRUE(base::CreateDirectory(copied_dir));
187 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
188
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700189 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800190
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700191 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700192 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700193 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800194
195 const base::FilePath mount_dir = temp_dir_.Append("mount");
196 ASSERT_TRUE(base::CreateDirectory(copied_dir));
197 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
198
Greg Kerr9944e242017-01-26 15:09:31 -0800199 // Note: this fails to test the actual mounting process since it is just a
200 // mock here. The platform_ImageLoader autotest tests the real helper
201 // process running as a dbus service.
Greg Kerr09f06de2018-02-16 15:32:07 -0800202 auto helper_mock = std::make_unique<MockHelperProcessProxy>();
Xiaochu Liue61e1d62018-11-12 13:20:09 -0800203 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kSquashFS, _))
Xiaochu Liuc2264342017-08-14 16:37:42 -0700204 .Times(1);
205 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
Greg Kerr9944e242017-01-26 15:09:31 -0800206 .WillByDefault(testing::Return(true));
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700207 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800208}
209
210TEST_F(ComponentTest, CheckFilesAfterCopy) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700211 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700212 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700213 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800214
215 const base::FilePath copied_dir = temp_dir_.Append("dest");
216 ASSERT_TRUE(base::CreateDirectory(copied_dir));
217 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
218
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700219 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800220
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700221 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700222 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700223 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800224
225 // Check that all the files are present, except for the manifest.json which
226 // should be discarded.
227 std::list<std::string> original_files;
228 std::list<std::string> copied_files;
229 GetFilesInDir(GetTestComponentPath(), &original_files);
230 GetFilesInDir(copied_dir, &copied_files);
231
232 EXPECT_THAT(original_files,
233 testing::UnorderedElementsAre(
234 "imageloader.json", "imageloader.sig.1", "manifest.json",
235 "table", "image.squash", "manifest.fingerprint"));
236 ASSERT_THAT(copied_files,
237 testing::UnorderedElementsAre(
238 "imageloader.json", "imageloader.sig.1", "table",
239 "image.squash", "manifest.fingerprint"));
240 EXPECT_TRUE(
241 CompareFileContents(GetTestComponentPath(), copied_dir, copied_files));
242}
243
Amin Hassani17a185b2021-02-10 12:07:57 -0800244TEST_F(ComponentTest, CheckNoSignatureComponentFail) {
245 EXPECT_FALSE(Component::Create(GetNoSignatureComponentPath(), keys_));
246}
247
248TEST_F(ComponentTest, CheckNoSignatureFilesAfterCopy) {
249 // Make non-official build.
250 EXPECT_CALL(g_ctx_, IsOfficialBuild()).WillRepeatedly(testing::Return(false));
251
252 base::FilePath component_path = GetNoSignatureComponentPath();
253 std::unique_ptr<Component> component =
254 Component::Create(component_path, keys_);
255 ASSERT_TRUE(component);
256
257 const base::FilePath copied_dir = temp_dir_.Append("dest");
258 ASSERT_TRUE(base::CreateDirectory(copied_dir));
259 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
260
261 ASSERT_TRUE(component->CopyTo(copied_dir));
262
263 std::unique_ptr<Component> copied_component =
264 Component::Create(copied_dir, keys_);
265 ASSERT_NE(nullptr, copied_component);
266
267 // Check that all the files are present. The signature file should just be
268 // ignored.
269 std::list<std::string> original_files;
270 std::list<std::string> copied_files;
271 GetFilesInDir(component_path, &original_files);
272 GetFilesInDir(copied_dir, &copied_files);
273
274 EXPECT_THAT(original_files,
275 testing::UnorderedElementsAre("imageloader.json", "manifest.json",
276 "table", "image.squash"));
277 ASSERT_THAT(copied_files, testing::UnorderedElementsAre(
278 "imageloader.json", "table", "image.squash"));
279 EXPECT_TRUE(CompareFileContents(component_path, copied_dir, copied_files));
280}
281
Greg Kerr019d59c2016-11-17 14:28:49 -0800282TEST_F(ComponentTest, IsValidFingerprintFile) {
Greg Kerr019d59c2016-11-17 14:28:49 -0800283 const std::string valid_manifest =
284 "1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700285 EXPECT_TRUE(Component::IsValidFingerprintFile(valid_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800286
287 const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700288 EXPECT_FALSE(Component::IsValidFingerprintFile(invalid_unicode_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800289
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700290 EXPECT_FALSE(Component::IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
Greg Kerr019d59c2016-11-17 14:28:49 -0800291}
292
293TEST_F(ComponentTest, InitComponentWithBadFiles) {
294 EXPECT_TRUE(
295 TestInitComponentWithCorruptFile("bad-component1", "imageloader.json"));
296 EXPECT_TRUE(
297 TestInitComponentWithCorruptFile("bad-component2", "imageloader.sig.1"));
298}
299
300// Now corrupt the manifest of an already initialized component to verify that
301// the copy operation fails.
302TEST_F(ComponentTest, CopyWithBadFiles) {
303 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component1", "image.squash"));
304 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component2", "table"));
305 EXPECT_TRUE(
306 TestCopyWithCorruptFile("bad-component3", "manifest.fingerprint"));
307}
308
309TEST_F(ComponentTest, CopyValidImage) {
310 const int image_size = 4096 * 4;
311
312 base::FilePath image_path = temp_dir_.Append("image");
313 std::vector<char> image(image_size,
314 0xBB); // large enough to test streaming read.
315 ASSERT_EQ(image_size,
316 base::WriteFile(image_path, image.data(), image.size()));
317
318 std::vector<uint8_t> hash(crypto::kSHA256Length);
319
320 std::unique_ptr<crypto::SecureHash> sha256(
321 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
322 sha256->Update(image.data(), image.size());
323 sha256->Finish(hash.data(), hash.size());
324
Eric Caruso089bbff2017-03-21 11:34:15 -0700325 Component component(GetTestComponentPath(), 1);
Greg Kerr019d59c2016-11-17 14:28:49 -0800326 base::FilePath image_dest = temp_dir_.Append("image.copied");
327 ASSERT_TRUE(component.CopyComponentFile(image_path, image_dest, hash));
328
329 // Check if the image file actually exists and has the correct contents.
330 std::string resulting_image;
331 ASSERT_TRUE(base::ReadFileToStringWithMaxSize(image_dest, &resulting_image,
332 image_size));
333
Ben Chana92a9f02017-12-18 17:47:23 -0800334 EXPECT_EQ(0, memcmp(image.data(), resulting_image.data(), image_size));
Greg Kerr019d59c2016-11-17 14:28:49 -0800335}
336
337} // namespace imageloader