blob: 5407a2f03d1f6abc49edd752e88ca3812338e389 [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
5#include "component.h"
6
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
Greg Kerr019d59c2016-11-17 14:28:49 -080014#include <base/files/file_path.h>
15#include <base/files/file_util.h>
16#include <base/files/scoped_temp_dir.h>
17#include <base/logging.h>
18#include <base/memory/ptr_util.h>
19#include <crypto/secure_hash.h>
20#include <crypto/sha2.h>
21#include <gmock/gmock.h>
22#include <gtest/gtest.h>
23
Eric Caruso0b79bc82017-03-21 13:44:34 -070024#include "imageloader_impl.h"
25#include "mock_helper_process.h"
26#include "test_utilities.h"
27
Greg Kerr019d59c2016-11-17 14:28:49 -080028namespace imageloader {
29
30using testing::_;
31
32class ComponentTest : public testing::Test {
33 public:
34 ComponentTest() {
Eric Caruso0b79bc82017-03-21 13:44:34 -070035 keys_.push_back(std::vector<uint8_t>(std::begin(kDevPublicKey),
36 std::end(kDevPublicKey)));
Greg Kerr019d59c2016-11-17 14:28:49 -080037 CHECK(scoped_temp_dir_.CreateUniqueTempDir());
38 temp_dir_ = scoped_temp_dir_.path();
39 CHECK(base::SetPosixFilePermissions(temp_dir_, kComponentDirPerms));
40 }
41
42 bool TestCopyWithCorruptFile(const std::string& component_name,
43 const std::string& file_name) {
44 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
45 if (!base::CreateDirectory(bad_component_dir)) return false;
46 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
47 return false;
48
Eric Carusocbe1c5c2017-03-15 14:21:08 -070049 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070050 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070051 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080052 return false;
53
Eric Carusocbe1c5c2017-03-15 14:21:08 -070054 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070055 Component::Create(bad_component_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070056 if (!bad_component) return false;
Greg Kerr019d59c2016-11-17 14:28:49 -080057
58 base::FilePath file = bad_component_dir.Append(file_name);
59 const char data[] = "c";
60 if (!base::AppendToFile(file, data, sizeof(data))) return false;
61
62 base::FilePath bad_component_dest =
63 temp_dir_.Append(component_name + "invalid");
64 if (!base::CreateDirectory(bad_component_dest)) return false;
65
66 if (!base::SetPosixFilePermissions(bad_component_dest, kComponentDirPerms))
67 return false;
Eric Carusocbe1c5c2017-03-15 14:21:08 -070068 return bad_component->CopyTo(bad_component_dest) == false;
Greg Kerr019d59c2016-11-17 14:28:49 -080069 }
70
71 bool TestInitComponentWithCorruptFile(const std::string& component_name,
72 const std::string& file_name) {
73 base::FilePath bad_component_dir = temp_dir_.Append(component_name);
74 if (!base::CreateDirectory(bad_component_dir)) return false;
75 if (!base::SetPosixFilePermissions(bad_component_dir, kComponentDirPerms))
76 return false;
77
Eric Carusocbe1c5c2017-03-15 14:21:08 -070078 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070079 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070080 if (!component || !component->CopyTo(bad_component_dir))
Greg Kerr019d59c2016-11-17 14:28:49 -080081 return false;
82
83 base::FilePath file = bad_component_dir.Append(file_name);
84 // Read the file out and change the last byte.
85 std::string file_contents;
86 if (!base::ReadFileToString(file, &file_contents)) return false;
87 file_contents[file_contents.size() - 1] =
88 ~file_contents[file_contents.size() - 1];
89
90 if (!base::WriteFile(file, file_contents.data(), file_contents.size())) {
91 return false;
92 }
93
Eric Carusocbe1c5c2017-03-15 14:21:08 -070094 std::unique_ptr<Component> bad_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -070095 Component::Create(bad_component_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -070096 return bad_component == nullptr;
Greg Kerr019d59c2016-11-17 14:28:49 -080097 }
98
99 bool CompareFileContents(const base::FilePath& src,
100 const base::FilePath& dest,
101 const std::list<std::string>& filenames) {
102 for (const std::string& name : filenames) {
103 std::string source_file_contents;
104 std::string dest_file_contents;
105 if (!base::ReadFileToString(src.Append(name), &source_file_contents))
106 return false;
107 if (!base::ReadFileToString(dest.Append(name), &dest_file_contents))
108 return false;
109 if (source_file_contents != dest_file_contents) {
110 LOG(ERROR) << "File contents does not match for file: " << name;
111 return false;
112 }
113 }
114 return true;
115 }
116
Eric Caruso0b79bc82017-03-21 13:44:34 -0700117 Keys keys_;
Greg Kerr019d59c2016-11-17 14:28:49 -0800118 base::ScopedTempDir scoped_temp_dir_;
119 base::FilePath temp_dir_;
120};
121
122TEST_F(ComponentTest, InitComponentAndCheckManifest) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700123 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700124 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700125 ASSERT_NE(nullptr, component);
126
127 EXPECT_EQ(1, component->manifest().manifest_version);
128 EXPECT_EQ(kTestDataVersion, component->manifest().version);
Greg Kerr019d59c2016-11-17 14:28:49 -0800129 // Don't hardcode the sha256 hashes, but run some sanity checks.
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700130 EXPECT_EQ(crypto::kSHA256Length, component->manifest().image_sha256.size());
131 EXPECT_EQ(crypto::kSHA256Length, component->manifest().table_sha256.size());
132 EXPECT_NE(component->manifest().image_sha256,
133 component->manifest().table_sha256);
Greg Kerr019d59c2016-11-17 14:28:49 -0800134}
135
Xiaochu Liuc2264342017-08-14 16:37:42 -0700136TEST_F(ComponentTest, TestCopyAndMountComponentExt4) {
137 std::unique_ptr<Component> component =
138 Component::Create(GetTestDataPath("ext4_component"), keys_);
139 ASSERT_NE(nullptr, component);
140
141 const base::FilePath copied_dir = temp_dir_.Append("dest");
142 ASSERT_TRUE(base::CreateDirectory(copied_dir));
143 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
144
145 ASSERT_TRUE(component->CopyTo(copied_dir));
146
147 std::unique_ptr<Component> copied_component =
148 Component::Create(copied_dir, keys_);
149 ASSERT_NE(nullptr, copied_component);
150
151 const base::FilePath mount_dir = temp_dir_.Append("mount");
152 ASSERT_TRUE(base::CreateDirectory(copied_dir));
153 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
154
155 // Note: this fails to test the actual mounting process since it is just a
156 // mock here. The platform_ImageLoader autotest tests the real helper
157 // process running as a dbus service.
158 auto helper_mock = base::MakeUnique<MockHelperProcess>();
159 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kExt4, _))
160 .Times(1);
161 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
162 .WillByDefault(testing::Return(true));
163 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
164}
165
166TEST_F(ComponentTest, TestCopyAndMountComponentSquashfs) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700167 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700168 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700169 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800170
171 const base::FilePath copied_dir = temp_dir_.Append("dest");
172 ASSERT_TRUE(base::CreateDirectory(copied_dir));
173 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
174
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700175 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800176
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700177 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700178 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700179 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800180
181 const base::FilePath mount_dir = temp_dir_.Append("mount");
182 ASSERT_TRUE(base::CreateDirectory(copied_dir));
183 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
184
Greg Kerr9944e242017-01-26 15:09:31 -0800185 // Note: this fails to test the actual mounting process since it is just a
186 // mock here. The platform_ImageLoader autotest tests the real helper
187 // process running as a dbus service.
188 auto helper_mock = base::MakeUnique<MockHelperProcess>();
Xiaochu Liuc2264342017-08-14 16:37:42 -0700189 EXPECT_CALL(*helper_mock, SendMountCommand(_, _, FileSystem::kSquashFS, _))
190 .Times(1);
191 ON_CALL(*helper_mock, SendMountCommand(_, _, _, _))
Greg Kerr9944e242017-01-26 15:09:31 -0800192 .WillByDefault(testing::Return(true));
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700193 ASSERT_TRUE(copied_component->Mount(helper_mock.get(), mount_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800194}
195
196TEST_F(ComponentTest, CheckFilesAfterCopy) {
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700197 std::unique_ptr<Component> component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700198 Component::Create(GetTestComponentPath(), keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700199 ASSERT_NE(nullptr, component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800200
201 const base::FilePath copied_dir = temp_dir_.Append("dest");
202 ASSERT_TRUE(base::CreateDirectory(copied_dir));
203 ASSERT_TRUE(base::SetPosixFilePermissions(copied_dir, kComponentDirPerms));
204
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700205 ASSERT_TRUE(component->CopyTo(copied_dir));
Greg Kerr019d59c2016-11-17 14:28:49 -0800206
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700207 std::unique_ptr<Component> copied_component =
Eric Caruso0b79bc82017-03-21 13:44:34 -0700208 Component::Create(copied_dir, keys_);
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700209 ASSERT_NE(nullptr, copied_component);
Greg Kerr019d59c2016-11-17 14:28:49 -0800210
211 // Check that all the files are present, except for the manifest.json which
212 // should be discarded.
213 std::list<std::string> original_files;
214 std::list<std::string> copied_files;
215 GetFilesInDir(GetTestComponentPath(), &original_files);
216 GetFilesInDir(copied_dir, &copied_files);
217
218 EXPECT_THAT(original_files,
219 testing::UnorderedElementsAre(
220 "imageloader.json", "imageloader.sig.1", "manifest.json",
221 "table", "image.squash", "manifest.fingerprint"));
222 ASSERT_THAT(copied_files,
223 testing::UnorderedElementsAre(
224 "imageloader.json", "imageloader.sig.1", "table",
225 "image.squash", "manifest.fingerprint"));
226 EXPECT_TRUE(
227 CompareFileContents(GetTestComponentPath(), copied_dir, copied_files));
228}
229
230TEST_F(ComponentTest, IsValidFingerprintFile) {
Greg Kerr019d59c2016-11-17 14:28:49 -0800231 const std::string valid_manifest =
232 "1.3464353b1ed78574e05f3ffe84b52582572b2fe7202f3824a3761e54ace8bb1";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700233 EXPECT_TRUE(Component::IsValidFingerprintFile(valid_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800234
235 const std::string invalid_unicode_manifest = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ ";
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700236 EXPECT_FALSE(Component::IsValidFingerprintFile(invalid_unicode_manifest));
Greg Kerr019d59c2016-11-17 14:28:49 -0800237
Eric Carusocbe1c5c2017-03-15 14:21:08 -0700238 EXPECT_FALSE(Component::IsValidFingerprintFile("\x49\x34\x19-43.*+abc"));
Greg Kerr019d59c2016-11-17 14:28:49 -0800239}
240
241TEST_F(ComponentTest, InitComponentWithBadFiles) {
242 EXPECT_TRUE(
243 TestInitComponentWithCorruptFile("bad-component1", "imageloader.json"));
244 EXPECT_TRUE(
245 TestInitComponentWithCorruptFile("bad-component2", "imageloader.sig.1"));
246}
247
248// Now corrupt the manifest of an already initialized component to verify that
249// the copy operation fails.
250TEST_F(ComponentTest, CopyWithBadFiles) {
251 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component1", "image.squash"));
252 EXPECT_TRUE(TestCopyWithCorruptFile("bad-component2", "table"));
253 EXPECT_TRUE(
254 TestCopyWithCorruptFile("bad-component3", "manifest.fingerprint"));
255}
256
257TEST_F(ComponentTest, CopyValidImage) {
258 const int image_size = 4096 * 4;
259
260 base::FilePath image_path = temp_dir_.Append("image");
261 std::vector<char> image(image_size,
262 0xBB); // large enough to test streaming read.
263 ASSERT_EQ(image_size,
264 base::WriteFile(image_path, image.data(), image.size()));
265
266 std::vector<uint8_t> hash(crypto::kSHA256Length);
267
268 std::unique_ptr<crypto::SecureHash> sha256(
269 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
270 sha256->Update(image.data(), image.size());
271 sha256->Finish(hash.data(), hash.size());
272
Eric Caruso089bbff2017-03-21 11:34:15 -0700273 Component component(GetTestComponentPath(), 1);
Greg Kerr019d59c2016-11-17 14:28:49 -0800274 base::FilePath image_dest = temp_dir_.Append("image.copied");
275 ASSERT_TRUE(component.CopyComponentFile(image_path, image_dest, hash));
276
277 // Check if the image file actually exists and has the correct contents.
278 std::string resulting_image;
279 ASSERT_TRUE(base::ReadFileToStringWithMaxSize(image_dest, &resulting_image,
280 image_size));
281
282 EXPECT_TRUE(memcmp(image.data(), resulting_image.data(), image_size) == 0);
283}
284
285} // namespace imageloader