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

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: Ran git cl format 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
« no previous file with comments | « chrome/browser/safe_browsing/signature_evaluator_mac.mm ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"), base::FilePath("/bar"), nullptr));
81 EXPECT_FALSE(MacSignatureEvaluator::GetRelativePathComponent(
82 base::FilePath("/foo/bar"), base::FilePath("/bar/baz"), nullptr));
83 EXPECT_FALSE(MacSignatureEvaluator::GetRelativePathComponent(
84 base::FilePath("/foo/x"), base::FilePath("/foo/y"), nullptr));
85
86 std::string output1;
87 EXPECT_TRUE(MacSignatureEvaluator::GetRelativePathComponent(
88 base::FilePath("/foo/bar"), base::FilePath("/foo/bar/y"), &output1));
89 EXPECT_EQ(output1, "y");
90
91 std::string output2;
92 EXPECT_TRUE(MacSignatureEvaluator::GetRelativePathComponent(
93 base::FilePath("/Applications/Google Chrome.app"),
94 base::FilePath("/Applications/Google Chrome.app/Contents/MacOS/foo"),
95 &output2));
96 EXPECT_EQ(output2, "Contents/MacOS/foo");
97 }
98
99 TEST_F(MacSignatureEvaluatorTest, SimpleTest) {
100 // This is a simple test that checks the validity of a signed executable.
101 // There is no designated requirement: we only check the embedded signature.
102 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
103 safe_browsing::MacSignatureEvaluator evaluator(path);
104 ASSERT_TRUE(evaluator.Initialize());
105
106 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
107 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
108 EXPECT_EQ(0, incident.contained_file_size());
109 }
110
111 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithDR) {
112 // This test checks the signer against a designated requirement description.
113 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
114 std::string requirement(
115 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
116 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
117 ASSERT_TRUE(evaluator.Initialize());
118
119 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
120 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
121 EXPECT_EQ(0, incident.contained_file_size());
122 }
123
124 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithBadDR) {
125 // Now test with a designated requirement that does not describe the signer.
126 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
127 safe_browsing::MacSignatureEvaluator evaluator(path, "anchor apple");
128 ASSERT_TRUE(evaluator.Initialize());
129
130 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
131 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
132 EXPECT_EQ(-67050, incident.sec_error());
133 EXPECT_TRUE(incident.has_signature());
134 ASSERT_TRUE(incident.has_file_basename());
135 EXPECT_EQ("signedexecutablefat", incident.file_basename());
136 }
137
138 TEST_F(MacSignatureEvaluatorTest, SimpleBundleTest) {
139 // Now test a simple, validly signed bundle.
140 base::FilePath path = testdata_path_.AppendASCII("test-bundle.app");
141
142 std::string requirement(
143 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
144 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
145 ASSERT_TRUE(evaluator.Initialize());
146
147 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
148 EXPECT_TRUE(evaluator.PerformEvaluation(&incident));
149 EXPECT_EQ(0, incident.contained_file_size());
150 }
151
152 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest32) {
153 // Now to a test modified, signed bundle.
154 base::FilePath path = testdata_path_.AppendASCII("modified-main-exec32.app");
155
156 std::string requirement(
157 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
158 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
159 ASSERT_TRUE(evaluator.Initialize());
160
161 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
162 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
163 EXPECT_EQ(-67061, incident.sec_error());
164 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
165 EXPECT_FALSE(incident.has_signature());
166 EXPECT_FALSE(incident.has_image_headers());
167 ASSERT_EQ(1, incident.contained_file_size());
168
169 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
170 contained_file = incident.contained_file(0);
171 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
172 EXPECT_TRUE(contained_file.has_signature());
173 EXPECT_TRUE(contained_file.has_image_headers());
174 }
175
176 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest64) {
177 // Snow Leopard does not know about the 64-bit slice so this test is
178 // irrelevant.
179 if (!base::mac::IsOSLionOrLater())
180 return;
181
182 // Now to a test modified, signed bundle.
183 base::FilePath path = testdata_path_.AppendASCII("modified-main-exec64.app");
184
185 std::string requirement(
186 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
187 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
188 ASSERT_TRUE(evaluator.Initialize());
189
190 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
191 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
192
193 EXPECT_EQ(-67061, incident.sec_error());
194 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
195 EXPECT_FALSE(incident.has_signature());
196 EXPECT_FALSE(incident.has_image_headers());
197 ASSERT_EQ(1, incident.contained_file_size());
198
199 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
200 contained_file = incident.contained_file(0);
201 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
202 EXPECT_TRUE(contained_file.has_signature());
203 EXPECT_TRUE(contained_file.has_image_headers());
204 }
205
206 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleAndExecTest) {
207 // Now test a modified, signed bundle with resources added and the main
208 // executable modified.
209 base::FilePath path =
210 testdata_path_.AppendASCII("modified-bundle-and-exec.app");
211
212 std::string requirement(
213 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
214 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
215 ASSERT_TRUE(evaluator.Initialize());
216
217 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
218 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
219 EXPECT_EQ(-67061, incident.sec_error());
220 EXPECT_FALSE(incident.has_signature());
221 EXPECT_FALSE(incident.has_image_headers());
222 EXPECT_EQ(path.BaseName().value(), incident.file_basename());
223 ASSERT_EQ(1, incident.contained_file_size());
224
225 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile&
226 contained_file = incident.contained_file(0);
227 EXPECT_EQ(contained_file.relative_path(), "Contents/MacOS/test-bundle");
228 EXPECT_TRUE(contained_file.has_signature());
229 EXPECT_TRUE(contained_file.has_image_headers());
230 }
231
232 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleTest) {
233 // Now test a modified, signed bundle. This bundle has
234 // the following problems:
235 // 1) A file was added (This should not be reported)
236 // 2) libsigned64.dylib was modified
237 // 3) executable32 was modified
238
239 base::FilePath orig_path = testdata_path_.AppendASCII("modified-bundle.app");
240 base::FilePath copied_path =
241 temp_dir_.path().AppendASCII("modified-bundle.app");
242 CHECK(base::CopyDirectory(orig_path, copied_path, true));
243
244 // Setup the extended attributes, which don't persist in the git repo.
245 ASSERT_TRUE(SetupXattrs(
246 copied_path.AppendASCII("Contents/Resources/Base.lproj/MainMenu.nib")));
247
248 std::string requirement(
249 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
250 safe_browsing::MacSignatureEvaluator evaluator(copied_path, requirement);
251 ASSERT_TRUE(evaluator.Initialize());
252
253 ClientIncidentReport_IncidentData_BinaryIntegrityIncident incident;
254 EXPECT_FALSE(evaluator.PerformEvaluation(&incident));
255
256 EXPECT_TRUE(incident.has_file_basename());
257 EXPECT_EQ(copied_path.BaseName().value(), incident.file_basename());
258 EXPECT_FALSE(incident.has_signature());
259 EXPECT_FALSE(incident.has_image_headers());
260 EXPECT_EQ(-67054, incident.sec_error());
261 ASSERT_EQ(4, incident.contained_file_size());
262
263 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
264 main_exec = nullptr;
265 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
266 libsigned64 = nullptr;
267 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
268 executable32 = nullptr;
269 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
270 mainmenunib = nullptr;
271 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident_ContainedFile*
272 codesign_cfg = nullptr;
273
274 for (const auto& contained_file : incident.contained_file()) {
275 if (contained_file.relative_path() == "Contents/MacOS/test-bundle") {
276 main_exec = &contained_file;
277 } else if (contained_file.relative_path() ==
278 "Contents/Frameworks/libsigned64.dylib") {
279 libsigned64 = &contained_file;
280 } else if (contained_file.relative_path() ==
281 "Contents/Resources/executable32") {
282 executable32 = &contained_file;
283 } else if (contained_file.relative_path() ==
284 "Contents/Resources/Base.lproj/MainMenu.nib") {
285 mainmenunib = &contained_file;
286 } else if (contained_file.relative_path() ==
287 "Contents/Resources/codesign.cfg") {
288 codesign_cfg = &contained_file;
289 }
290 }
291
292 ASSERT_NE(main_exec, nullptr);
293 ASSERT_NE(mainmenunib, nullptr);
294 ASSERT_NE(libsigned64, nullptr);
295 ASSERT_NE(executable32, nullptr);
296 // This is important. Do not collect information on extra files added.
297 EXPECT_EQ(codesign_cfg, nullptr);
298
299 EXPECT_TRUE(libsigned64->has_relative_path());
300 EXPECT_EQ("Contents/Frameworks/libsigned64.dylib",
301 libsigned64->relative_path());
302 EXPECT_TRUE(libsigned64->has_signature());
303
304 EXPECT_TRUE(executable32->has_relative_path());
305 EXPECT_EQ("Contents/Resources/executable32", executable32->relative_path());
306 EXPECT_TRUE(executable32->has_signature());
307 EXPECT_TRUE(executable32->has_image_headers());
308
309 EXPECT_TRUE(mainmenunib->has_relative_path());
310 EXPECT_EQ("Contents/Resources/Base.lproj/MainMenu.nib",
311 mainmenunib->relative_path());
312 EXPECT_TRUE(mainmenunib->has_signature());
313 EXPECT_EQ(6, mainmenunib->signature().xattr_size());
314 // Manually convert the global xattrs array to a vector
315 std::vector<std::string> xattrs_known;
316 for (const auto& xattr : xattrs)
317 xattrs_known.push_back(xattr);
318
319 std::vector<std::string> xattrs_seen;
320 for (const auto& xattr : mainmenunib->signature().xattr()) {
321 ASSERT_TRUE(xattr.has_key());
322 EXPECT_TRUE(xattr.has_value());
323 xattrs_seen.push_back(xattr.key());
324 }
325 EXPECT_THAT(xattrs_known, ::testing::ContainerEq(xattrs_seen));
326 }
327
328 } // namespace safe_browsing
OLDNEW
« no previous file with comments | « chrome/browser/safe_browsing/signature_evaluator_mac.mm ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698