Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(301)

Side by Side Diff: chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc

Issue 1363613004: Implement anonymous, opt-in, collection of OS X binary integrity incidents. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove spurious VLOG() Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/safe_browsing/signature_evaluator_mac.h"
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <sys/xattr.h>
9
10 #include <string>
11 #include <vector>
12
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/mac/mac_util.h"
17 #include "base/mac/scoped_cftyperef.h"
18 #include "base/path_service.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "base/test/scoped_path_override.h"
21 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
22 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver .h"
23 #include "chrome/common/chrome_paths.h"
24 #include "chrome/common/safe_browsing/csd.pb.h"
25 #include "testing/gmock/include/gmock/gmock-matchers.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 using ::testing::_;
30 using ::testing::StrictMock;
31
32 namespace safe_browsing {
33
34 namespace {
35
36 const char* const xattrs[] = {
37 "com.apple.cs.CodeDirectory",
38 "com.apple.cs.CodeSignature",
39 "com.apple.cs.CodeRequirements",
40 "com.apple.cs.CodeResources",
41 "com.apple.cs.CodeApplication",
42 "com.apple.cs.CodeEntitlements",
43 };
44
45 } // namespace
46
47 class MacSignatureEvaluatorTest : public testing::Test {
48 protected:
49 void SetUp() override {
50 base::FilePath source_path;
51 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path));
52 testdata_path_ =
53 source_path.AppendASCII("safe_browsing").AppendASCII("mach_o");
54
55 base::FilePath dir_exe;
56 ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dir_exe));
57 base::FilePath file_exe;
58 ASSERT_TRUE(PathService::Get(base::FILE_EXE, &file_exe));
59
60 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
61 }
62
63 bool SetupXattrs(const base::FilePath& path) {
64 char sentinel = 'A';
65 for (const auto& xattr : xattrs) {
66 std::vector<uint8_t> buf(10);
67 memset(&buf[0], sentinel++, buf.size());
68 if (setxattr(path.value().c_str(), xattr, &buf[0], buf.size(), 0, 0) != 0)
69 return false;
70 }
71 return true;
72 }
73
74 base::FilePath testdata_path_;
75 base::ScopedTempDir temp_dir_;
76 };
77
78 TEST_F(MacSignatureEvaluatorTest, RelativePathComponentTest) {
79 EXPECT_FALSE(MacSignatureEvaluator::GetRelativePathComponent(
80 base::FilePath("/foo"),
81 base::FilePath("/bar"),
82 nullptr));
83 EXPECT_FALSE(MacSignatureEvaluator::GetRelativePathComponent(
84 base::FilePath("/foo/bar"),
85 base::FilePath("/bar/baz"),
86 nullptr));
87 EXPECT_FALSE(MacSignatureEvaluator::GetRelativePathComponent(
88 base::FilePath("/foo/x"),
89 base::FilePath("/foo/y"),
90 nullptr));
91
92 std::string output1;
93 EXPECT_TRUE(MacSignatureEvaluator::GetRelativePathComponent(
94 base::FilePath("/foo/bar"),
95 base::FilePath("/foo/bar/y"),
96 &output1));
97 EXPECT_EQ(output1, "y");
98
99 std::string output2;
100 EXPECT_TRUE(MacSignatureEvaluator::GetRelativePathComponent(
101 base::FilePath("/Applications/Google Chrome.app"),
102 base::FilePath("/Applications/Google Chrome.app/Contents/MacOS/foo"),
103 &output2));
104 EXPECT_EQ(output2, "Contents/MacOS/foo");
105 }
106
107 TEST_F(MacSignatureEvaluatorTest, SimpleTest) {
108 // This is a simple test that checks the validity of a signed executable.
109 // There is no designated requirement: we only check the embedded signature.
110 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
111 safe_browsing::MacSignatureEvaluator evaluator(path);
112 ASSERT_TRUE(evaluator.Initialize());
113
114 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
115 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
116 EXPECT_EQ(0, incident.contained_file_size());
117 }
118
119 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithDR) {
120 // This test checks the signer against a designated requirement description.
121 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
122 std::string requirement(
123 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
124 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
125 ASSERT_TRUE(evaluator.Initialize());
126
127 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
128 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
129 EXPECT_EQ(0, incident.contained_file_size());
130 }
131
132 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithBadDR) {
133 // Now test with a designated requirement that does not describe the signer.
134 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
135 safe_browsing::MacSignatureEvaluator evaluator(path, "anchor apple");
136 ASSERT_TRUE(evaluator.Initialize());
137
138 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
139 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
140 EXPECT_EQ(-67050, incident.sec_error());
141 EXPECT_TRUE(incident.has_signature());
142 ASSERT_TRUE(incident.has_file_basename());
143 EXPECT_EQ("signedexecutablefat", incident.file_basename());
144 }
145
146 TEST_F(MacSignatureEvaluatorTest, SimpleBundleTest) {
147 // Now test a simple, validly signed bundle.
148 base::FilePath path = testdata_path_.AppendASCII("test-bundle.app");
149
150 std::string requirement(
151 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
152 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
153 ASSERT_TRUE(evaluator.Initialize());
154
155 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
156 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
157 EXPECT_EQ(0, incident.contained_file_size());
158 }
159
160 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest32) {
161 // Now to a test modified, signed bundle.
162 base::FilePath path = testdata_path_.AppendASCII("modified-main-exec32.app");
163
164 std::string requirement(
165 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
166 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
167 ASSERT_TRUE(evaluator.Initialize());
168
169 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
170 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
171 EXPECT_EQ(-67061, incident.sec_error());
172 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
173 EXPECT_FALSE(incident.has_signature());
174 EXPECT_FALSE(incident.has_image_headers());
175 ASSERT_EQ(1, incident.contained_file_size());
176
177 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
178 contained_file = incident.contained_file(0);
Robert Sesek 2015/11/02 19:42:12 nit: +2
179 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
180 EXPECT_TRUE(contained_file.has_signature());
181 EXPECT_TRUE(contained_file.has_image_headers());
182 }
183
184 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest64) {
185 // Snow Leopard does not know about the 64-bit slice so this test is
186 // irrelevant.
187 if (!base::mac::IsOSLionOrLater())
188 return;
189
190 // Now to a test modified, signed bundle.
191 base::FilePath path =
192 testdata_path_.AppendASCII("modified-main-exec64.app");
193
194 std::string requirement(
195 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
196 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
197 ASSERT_TRUE(evaluator.Initialize());
198
199 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
200 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
201
202 EXPECT_EQ(-67061, incident.sec_error());
203 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
204 EXPECT_FALSE(incident.has_signature());
205 EXPECT_FALSE(incident.has_image_headers());
206 ASSERT_EQ(1, incident.contained_file_size());
207
208 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
209 contained_file = incident.contained_file(0);
Robert Sesek 2015/11/02 19:42:12 nit: +2
210 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
211 EXPECT_TRUE(contained_file.has_signature());
212 EXPECT_TRUE(contained_file.has_image_headers());
213 }
214
215 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleAndExecTest) {
216 // Now test a modified, signed bundle with resources added and the main
217 // executable modified.
218 base::FilePath path =
219 testdata_path_.AppendASCII("modified-bundle-and-exec.app");
220
221 std::string requirement(
222 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
223 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
224 ASSERT_TRUE(evaluator.Initialize());
225
226 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
227 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
228 EXPECT_EQ(-67061, incident.sec_error());
229 EXPECT_FALSE(incident.has_signature());
230 EXPECT_FALSE(incident.has_image_headers());
231 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
232 ASSERT_EQ(1, incident.contained_file_size());
233
234
235 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
236 contained_file = incident.contained_file(0);
Robert Sesek 2015/11/02 19:42:12 nit: +2
237 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
238 EXPECT_TRUE(contained_file.has_signature());
239 EXPECT_TRUE(contained_file.has_image_headers());
240 }
241
242 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleTest) {
243 // Now test a modified, signed bundle. This bundle has
244 // the following problems:
245 // 1) A file was added (This should not be reported)
246 // 2) libsigned64.dylib was modified
247 // 3) executable32 was modified
248
249 base::FilePath orig_path = testdata_path_.AppendASCII("modified-bundle.app");
250 base::FilePath copied_path =
251 temp_dir_.path().AppendASCII("modified-bundle.app");
252 CHECK(base::CopyDirectory(orig_path, copied_path, true));
253
254 // Setup the extended attributes, which don't persist in the git repo.
255 ASSERT_TRUE(SetupXattrs(
256 copied_path.AppendASCII("Contents/Resources/Base.lproj/MainMenu.nib")));
257
258 std::string requirement(
259 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
260 safe_browsing::MacSignatureEvaluator evaluator(copied_path, requirement);
261 ASSERT_TRUE(evaluator.Initialize());
262
263 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
264 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
265
266 EXPECT_TRUE(incident.has_file_basename());
267 EXPECT_EQ(copied_path.BaseName().value(), incident.file_basename());
268 EXPECT_FALSE(incident.has_signature());
269 EXPECT_FALSE(incident.has_image_headers());
270 EXPECT_EQ(-67054, incident.sec_error());
271 ASSERT_EQ(4, incident.contained_file_size());
272
273 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
274 main_exec = nullptr;
275 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
276 libsigned64 = nullptr;
277 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
278 executable32 = nullptr;
279 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
280 mainmenunib = nullptr;
281 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
282 codesign_cfg = nullptr;
283
284 for (const auto& contained_file : incident.contained_file()) {
285 if (contained_file.relative_path() == "Contents/MacOS/test-bundle") {
286 main_exec = &contained_file;
287 } else if (contained_file.relative_path() ==
288 "Contents/Frameworks/libsigned64.dylib") {
289 libsigned64 = &contained_file;
290 } else if (contained_file.relative_path() ==
291 "Contents/Resources/executable32") {
292 executable32 = &contained_file;
293 } else if (contained_file.relative_path() ==
294 "Contents/Resources/Base.lproj/MainMenu.nib") {
295 mainmenunib = &contained_file;
296 } else if (contained_file.relative_path() ==
297 "Contents/Resources/codesign.cfg") {
298 codesign_cfg = &contained_file;
299 }
300 }
301
302 ASSERT_NE(main_exec, nullptr);
303 ASSERT_NE(mainmenunib, nullptr);
304 ASSERT_NE(libsigned64, nullptr);
305 ASSERT_NE(executable32, nullptr);
306 // This is important. Do not collect information on extra files added.
307 EXPECT_EQ(codesign_cfg, nullptr);
308
309
310 EXPECT_TRUE(libsigned64->has_relative_path());
311 EXPECT_EQ("Contents/Frameworks/libsigned64.dylib",
312 libsigned64->relative_path());
Robert Sesek 2015/11/02 19:42:12 nit: align with the "
313 EXPECT_TRUE(libsigned64->has_signature());
314
315 EXPECT_TRUE(executable32->has_relative_path());
316 EXPECT_EQ("Contents/Resources/executable32", executable32->relative_path());
317 EXPECT_TRUE(executable32->has_signature());
318 EXPECT_TRUE(executable32->has_image_headers());
319
320 EXPECT_TRUE(mainmenunib->has_relative_path());
321 EXPECT_EQ("Contents/Resources/Base.lproj/MainMenu.nib",
322 mainmenunib->relative_path());
323 EXPECT_TRUE(mainmenunib->has_signature());
324 EXPECT_EQ(6, mainmenunib->signature().xattr_size());
325 // Manually convert the global xattrs array to a vector
326 std::vector<std::string> xattrs_known;
327 for (const auto& xattr : xattrs)
328 xattrs_known.push_back(xattr);
329
330 std::vector<std::string> xattrs_seen;
331 for (const auto& xattr : mainmenunib->signature().xattr()) {
332 ASSERT_TRUE(xattr.has_key());
333 EXPECT_TRUE(xattr.has_value());
334 xattrs_seen.push_back(xattr.key());
335 }
336 EXPECT_THAT(xattrs_known, ::testing::ContainerEq(xattrs_seen));
337 }
338
339 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698