Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / common / safe_browsing / mach_o_image_reader_mac_unittest.cc
blob2fa1bf34a197749424bb526214b00f21806cb5a3
1 // Copyright 2015 The Chromium 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.
5 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
7 #include <arpa/inet.h>
8 #include <libkern/OSByteOrder.h>
9 #include <mach-o/loader.h>
10 #include <string.h>
11 #include <uuid/uuid.h>
13 #include "base/files/file_path.h"
14 #include "base/files/memory_mapped_file.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace safe_browsing {
21 namespace {
23 // Definitions from
24 // <http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/codesign.h>.
26 enum {
27 CSMAGIC_CODEDIRECTORY = 0xfade0c02,
28 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,
30 CSSLOT_CODEDIRECTORY = 0,
33 struct CodeSigningBlob {
34 uint32_t type;
35 uint32_t offset;
38 struct CodeSigningSuperBlob {
39 uint32_t magic;
40 uint32_t length;
41 uint32_t count;
42 CodeSigningBlob index[];
45 struct CodeSigningDirectory {
46 uint32_t magic;
47 uint32_t length;
48 uint32_t version;
49 uint32_t flags;
50 uint32_t hashOffset;
51 uint32_t identOffset;
52 uint32_t nSpecialSlots;
53 uint32_t nCodeSlots;
54 uint32_t codeLimit;
55 uint8_t hashSize;
56 uint8_t hashType;
57 uint8_t spare1;
58 uint8_t pageSize;
59 uint32_t spare2;
60 // Version 0x20100.
61 uint32_t scatterOffset;
62 // Version 0x20200.
63 uint32_t teamOffset;
66 class MachOImageReaderTest : public testing::Test {
67 protected:
68 void OpenTestFile(const char* file_name, base::MemoryMappedFile* file) {
69 base::FilePath test_data;
70 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data));
72 base::FilePath path = test_data.AppendASCII("safe_browsing")
73 .AppendASCII("mach_o")
74 .AppendASCII(file_name);
76 ASSERT_TRUE(file->Initialize(path));
79 // Returns the identity of the signed code data.
80 void GetSigningIdentity(const std::vector<uint8_t>& signature,
81 std::string* identity) {
82 auto super_blob =
83 reinterpret_cast<const CodeSigningSuperBlob*>(&signature[0]);
84 EXPECT_EQ(CSMAGIC_EMBEDDED_SIGNATURE, ntohl(super_blob->magic));
85 ASSERT_EQ(CSSLOT_CODEDIRECTORY, ntohl(super_blob->index[0].type));
86 size_t dir_offset = ntohl(super_blob->index[0].offset);
87 auto directory =
88 reinterpret_cast<const CodeSigningDirectory*>(&signature[dir_offset]);
89 ASSERT_EQ(CSMAGIC_CODEDIRECTORY, ntohl(directory->magic));
90 size_t ident_offset = ntohl(directory->identOffset) + dir_offset;
91 *identity =
92 std::string(reinterpret_cast<const char*>(&signature[ident_offset]));
95 // Returns the hash of the code data itself. Note that this is not the
96 // CDHash, but is instead the hash in the CodeDirectory blob, which is
97 // over the contents of the signed data. This is visible as hash #0
98 // when using `codesign -d -vvvvvv`.
99 void GetCodeSignatureHash(const std::vector<uint8_t>& signature,
100 std::vector<uint8_t>* hash) {
101 auto super_blob =
102 reinterpret_cast<const CodeSigningSuperBlob*>(&signature[0]);
103 EXPECT_EQ(CSMAGIC_EMBEDDED_SIGNATURE, ntohl(super_blob->magic));
104 ASSERT_EQ(CSSLOT_CODEDIRECTORY, ntohl(super_blob->index[0].type));
105 size_t dir_offset = ntohl(super_blob->index[0].offset);
106 auto directory =
107 reinterpret_cast<const CodeSigningDirectory*>(&signature[dir_offset]);
108 ASSERT_EQ(CSMAGIC_CODEDIRECTORY, ntohl(directory->magic));
109 size_t hash_offset = ntohl(directory->hashOffset) + dir_offset;
110 std::vector<uint8_t> actual_hash(&signature[hash_offset],
111 &signature[hash_offset + directory->hashSize]);
112 EXPECT_EQ(20u, actual_hash.size());
113 *hash = actual_hash;
116 void ExpectCodeSignatureHash(const std::vector<uint8_t>& signature,
117 const char* expected) {
118 std::vector<uint8_t> actual_hash;
119 GetCodeSignatureHash(signature, &actual_hash);
121 std::vector<uint8_t> expected_hash;
122 ASSERT_TRUE(base::HexStringToBytes(expected, &expected_hash));
123 EXPECT_EQ(expected_hash, actual_hash);
127 TEST_F(MachOImageReaderTest, Executable32) {
128 base::MemoryMappedFile file;
129 ASSERT_NO_FATAL_FAILURE(OpenTestFile("executable32", &file));
130 MachOImageReader reader;
131 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
133 EXPECT_FALSE(reader.IsFat());
134 EXPECT_FALSE(reader.Is64Bit());
135 EXPECT_TRUE(reader.GetMachHeader());
136 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.GetFileType());
137 EXPECT_EQ(15u, reader.GetLoadCommands().size());
139 std::vector<uint8_t> signature;
140 EXPECT_FALSE(reader.GetCodeSignatureInfo(&signature));
141 EXPECT_TRUE(signature.empty());
143 // Test an arbitrary load command.
144 auto commands = reader.GetLoadCommands();
145 ASSERT_EQ(15u, commands.size());
146 auto command = commands[11];
147 ASSERT_EQ(static_cast<uint32_t>(LC_LOAD_DYLIB), command.cmd());
148 auto actual = command.as_command<dylib_command>();
149 EXPECT_EQ(2u, actual->dylib.timestamp);
150 EXPECT_EQ(0x4ad0101u, actual->dylib.current_version);
151 EXPECT_EQ(0x10000u, actual->dylib.compatibility_version);
154 TEST_F(MachOImageReaderTest, Executable64) {
155 base::MemoryMappedFile file;
156 ASSERT_NO_FATAL_FAILURE(OpenTestFile("executable64", &file));
157 MachOImageReader reader;
158 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
160 EXPECT_FALSE(reader.IsFat());
161 EXPECT_TRUE(reader.Is64Bit());
162 EXPECT_TRUE(reader.GetMachHeader());
163 EXPECT_TRUE(reader.GetMachHeader64());
164 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.GetFileType());
165 EXPECT_EQ(15u, reader.GetLoadCommands().size());
167 std::vector<uint8_t> signature;
168 EXPECT_FALSE(reader.GetCodeSignatureInfo(&signature));
169 EXPECT_TRUE(signature.empty());
172 TEST_F(MachOImageReaderTest, ExecutableFat) {
173 base::MemoryMappedFile file;
174 ASSERT_NO_FATAL_FAILURE(OpenTestFile("executablefat", &file));
175 MachOImageReader reader;
176 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
178 EXPECT_TRUE(reader.IsFat());
179 auto images = reader.GetFatImages();
180 ASSERT_EQ(2u, images.size());
182 // Note: this image is crafted to have 32-bit first.
184 EXPECT_FALSE(images[0]->IsFat());
185 EXPECT_FALSE(images[0]->Is64Bit());
186 EXPECT_TRUE(images[0]->GetMachHeader());
187 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), images[0]->GetFileType());
189 std::vector<uint8_t> signature;
190 EXPECT_FALSE(images[0]->GetCodeSignatureInfo(&signature));
191 EXPECT_TRUE(signature.empty());
195 EXPECT_FALSE(images[1]->IsFat());
196 EXPECT_TRUE(images[1]->Is64Bit());
197 EXPECT_TRUE(images[1]->GetMachHeader());
198 EXPECT_TRUE(images[1]->GetMachHeader64());
199 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), images[1]->GetFileType());
201 std::vector<uint8_t> signature;
202 EXPECT_FALSE(images[1]->GetCodeSignatureInfo(&signature));
203 EXPECT_TRUE(signature.empty());
205 // Test an arbitrary load command.
206 auto commands = images[1]->GetLoadCommands();
207 ASSERT_EQ(15u, commands.size());
208 auto command = commands[1];
209 ASSERT_EQ(static_cast<uint32_t>(LC_SEGMENT_64), command.cmd());
210 auto actual = command.as_command<segment_command_64>();
211 EXPECT_EQ("__TEXT", std::string(actual->segname));
212 EXPECT_EQ(0u, actual->fileoff);
213 EXPECT_EQ(4096u, actual->filesize);
214 EXPECT_EQ(0x7, actual->maxprot);
215 EXPECT_EQ(0x5, actual->initprot);
216 EXPECT_EQ(3u, actual->nsects);
217 EXPECT_EQ(0u, actual->flags);
221 TEST_F(MachOImageReaderTest, ExecutablePPC) {
222 base::MemoryMappedFile file;
223 ASSERT_NO_FATAL_FAILURE(OpenTestFile("executableppc", &file));
224 MachOImageReader reader;
225 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
227 EXPECT_FALSE(reader.IsFat());
228 EXPECT_FALSE(reader.Is64Bit());
229 EXPECT_TRUE(reader.GetMachHeader());
230 EXPECT_EQ(OSSwapInt32(MH_EXECUTE), reader.GetFileType());
231 EXPECT_EQ(10u, reader.GetLoadCommands().size());
233 std::vector<uint8_t> signature;
234 EXPECT_FALSE(reader.GetCodeSignatureInfo(&signature));
235 EXPECT_TRUE(signature.empty());
238 TEST_F(MachOImageReaderTest, Dylib32) {
239 base::MemoryMappedFile file;
240 ASSERT_NO_FATAL_FAILURE(OpenTestFile("lib32.dylib", &file));
241 MachOImageReader reader;
242 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
244 EXPECT_FALSE(reader.IsFat());
245 EXPECT_FALSE(reader.Is64Bit());
246 EXPECT_TRUE(reader.GetMachHeader());
247 EXPECT_EQ(static_cast<uint32_t>(MH_DYLIB), reader.GetFileType());
248 EXPECT_EQ(13u, reader.GetLoadCommands().size());
250 std::vector<uint8_t> signature;
251 EXPECT_FALSE(reader.GetCodeSignatureInfo(&signature));
252 EXPECT_TRUE(signature.empty());
255 TEST_F(MachOImageReaderTest, Dylib64) {
256 base::MemoryMappedFile file;
257 ASSERT_NO_FATAL_FAILURE(OpenTestFile("lib64.dylib", &file));
258 MachOImageReader reader;
259 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
261 EXPECT_FALSE(reader.IsFat());
262 EXPECT_TRUE(reader.Is64Bit());
263 EXPECT_TRUE(reader.GetMachHeader());
264 EXPECT_TRUE(reader.GetMachHeader64());
265 EXPECT_EQ(static_cast<uint32_t>(MH_DYLIB), reader.GetFileType());
266 EXPECT_EQ(13u, reader.GetLoadCommands().size());
268 std::vector<uint8_t> signature;
269 EXPECT_FALSE(reader.GetCodeSignatureInfo(&signature));
270 EXPECT_TRUE(signature.empty());
272 // Test an arbitrary load command.
273 auto commands = reader.GetLoadCommands();
274 ASSERT_EQ(13u, commands.size());
275 auto command = commands[6];
276 ASSERT_EQ(static_cast<uint32_t>(LC_UUID), command.cmd());
277 uuid_t expected = {0xB6, 0xB5, 0x12, 0xD7,
278 0x64, 0xE9,
279 0x3F, 0x7A,
280 0xAB, 0x4A,
281 0x87, 0x46, 0x36, 0x76, 0x87, 0x47};
282 EXPECT_EQ(0, uuid_compare(expected,
283 command.as_command<uuid_command>()->uuid));
286 TEST_F(MachOImageReaderTest, DylibFat) {
287 base::MemoryMappedFile file;
288 ASSERT_NO_FATAL_FAILURE(OpenTestFile("libfat.dylib", &file));
289 MachOImageReader reader;
290 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
292 EXPECT_TRUE(reader.IsFat());
293 auto images = reader.GetFatImages();
294 ASSERT_EQ(2u, images.size());
296 // Note: this image is crafted to have 64-bit first.
298 EXPECT_FALSE(images[0]->IsFat());
299 EXPECT_TRUE(images[0]->Is64Bit());
300 EXPECT_TRUE(images[0]->GetMachHeader());
301 EXPECT_TRUE(images[0]->GetMachHeader64());
302 EXPECT_EQ(static_cast<uint32_t>(MH_DYLIB), images[0]->GetFileType());
304 std::vector<uint8_t> signature;
305 EXPECT_FALSE(images[0]->GetCodeSignatureInfo(&signature));
306 EXPECT_TRUE(signature.empty());
310 EXPECT_FALSE(images[1]->IsFat());
311 EXPECT_FALSE(images[1]->Is64Bit());
312 EXPECT_TRUE(images[1]->GetMachHeader());
313 EXPECT_EQ(static_cast<uint32_t>(MH_DYLIB), images[1]->GetFileType());
315 std::vector<uint8_t> signature;
316 EXPECT_FALSE(images[1]->GetCodeSignatureInfo(&signature));
317 EXPECT_TRUE(signature.empty());
321 TEST_F(MachOImageReaderTest, SignedExecutable32) {
322 base::MemoryMappedFile file;
323 ASSERT_NO_FATAL_FAILURE(OpenTestFile("signedexecutable32", &file));
324 MachOImageReader reader;
325 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
327 EXPECT_FALSE(reader.IsFat());
328 EXPECT_FALSE(reader.Is64Bit());
329 EXPECT_TRUE(reader.GetMachHeader());
330 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.GetFileType());
331 EXPECT_EQ(16u, reader.GetLoadCommands().size());
333 std::vector<uint8_t> signature;
334 EXPECT_TRUE(reader.GetCodeSignatureInfo(&signature));
335 EXPECT_EQ(9344u, signature.size());
337 std::string identity;
338 GetSigningIdentity(signature, &identity);
339 EXPECT_EQ("signedexecutable32", identity);
341 ExpectCodeSignatureHash(signature,
342 "11fb88eb63c10dfc3d24a2545ea2a9c50c2921b5");
345 TEST_F(MachOImageReaderTest, SignedExecutableFat) {
346 base::MemoryMappedFile file;
347 ASSERT_NO_FATAL_FAILURE(OpenTestFile("signedexecutablefat", &file));
348 MachOImageReader reader;
349 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
351 EXPECT_TRUE(reader.IsFat());
352 auto images = reader.GetFatImages();
353 ASSERT_EQ(2u, images.size());
355 // Note: this image is crafted to have 32-bit first.
357 EXPECT_FALSE(images[0]->IsFat());
358 EXPECT_FALSE(images[0]->Is64Bit());
359 EXPECT_TRUE(images[0]->GetMachHeader());
360 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), images[0]->GetFileType());
362 std::vector<uint8_t> signature;
363 EXPECT_TRUE(images[0]->GetCodeSignatureInfo(&signature));
364 EXPECT_EQ(9344u, signature.size());
366 std::string identity;
367 GetSigningIdentity(signature, &identity);
368 EXPECT_EQ("signedexecutablefat", identity);
370 ExpectCodeSignatureHash(signature,
371 "11fb88eb63c10dfc3d24a2545ea2a9c50c2921b5");
375 EXPECT_FALSE(images[1]->IsFat());
376 EXPECT_TRUE(images[1]->Is64Bit());
377 EXPECT_TRUE(images[1]->GetMachHeader());
378 EXPECT_TRUE(images[1]->GetMachHeader64());
379 EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), images[1]->GetFileType());
381 std::vector<uint8_t> signature;
382 EXPECT_TRUE(images[1]->GetCodeSignatureInfo(&signature));
383 EXPECT_EQ(9344u, signature.size());
385 std::string identity;
386 GetSigningIdentity(signature, &identity);
387 EXPECT_EQ("signedexecutablefat", identity);
389 ExpectCodeSignatureHash(signature,
390 "750a57326ba85857371094900475defd837f5e14");
394 TEST_F(MachOImageReaderTest, SignedDylib64) {
395 base::MemoryMappedFile file;
396 ASSERT_NO_FATAL_FAILURE(OpenTestFile("libsigned64.dylib", &file));
397 MachOImageReader reader;
398 ASSERT_TRUE(reader.Initialize(file.data(), file.length()));
400 EXPECT_FALSE(reader.IsFat());
401 EXPECT_TRUE(reader.Is64Bit());
402 EXPECT_TRUE(reader.GetMachHeader());
403 EXPECT_TRUE(reader.GetMachHeader64());
404 EXPECT_EQ(static_cast<uint32_t>(MH_DYLIB), reader.GetFileType());
405 EXPECT_EQ(14u, reader.GetLoadCommands().size());
407 std::vector<uint8_t> signature;
408 EXPECT_TRUE(reader.GetCodeSignatureInfo(&signature));
409 EXPECT_EQ(9328u, signature.size());
411 std::string identity;
412 GetSigningIdentity(signature, &identity);
413 EXPECT_EQ("libsigned64", identity);
415 ExpectCodeSignatureHash(signature,
416 "8b1c79b60bb53a7f17b5618d5feb10dc8b88d806");
419 TEST_F(MachOImageReaderTest, NotMachO) {
420 base::MemoryMappedFile file;
421 ASSERT_NO_FATAL_FAILURE(OpenTestFile("src.c", &file));
422 MachOImageReader reader;
423 EXPECT_FALSE(reader.Initialize(file.data(), file.length()));
426 } // namespace
427 } // namespace safe_browsing