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

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: Created 5 years, 2 months 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 GetExecPath(const base::FilePath& bundle_url, base::FilePath* result) {
64 base::ScopedCFTypeRef<CFStringRef> path_str(
65 base::SysUTF8ToCFStringRef(bundle_url.value()));
66 if (!path_str.get())
67 return false;
68 base::ScopedCFTypeRef<CFURLRef> path_url(CFURLCreateWithFileSystemPath(
69 kCFAllocatorDefault, path_str, kCFURLPOSIXPathStyle, false));
70 if (!path_url.get())
71 return false;
72 base::ScopedCFTypeRef<CFBundleRef> bundle(
73 CFBundleCreate(kCFAllocatorDefault, path_url));
74 if (!bundle.get())
75 return false;
76
77 base::ScopedCFTypeRef<CFURLRef> exec_url(CFBundleCopyExecutableURL(bundle));
78 UInt8 path_buf[PATH_MAX];
79 if (!CFURLGetFileSystemRepresentation(exec_url, true, path_buf,
80 sizeof(path_buf))) {
81 return false;
82 }
83
84 *result = base::FilePath(reinterpret_cast<const char*>(path_buf));
85 return true;
86 }
87
88 bool SetupXattrs(const base::FilePath& path) {
89 char sentinel = 'A';
90 for (const auto& xattr : xattrs) {
91 std::vector<uint8_t> buf(10);
92 memset(&buf[0], sentinel++, buf.size());
93 if (setxattr(path.value().c_str(), xattr, &buf[0], buf.size(), 0, 0) != 0)
94 return false;
95 }
96 return true;
97 }
98
99 base::FilePath testdata_path_;
100 base::ScopedTempDir temp_dir_;
101 };
102
103 TEST_F(MacSignatureEvaluatorTest, SimpleTest) {
104 // This is a simple test that checks the validity of a signed executable.
105 // There is no designated requirement: we only check the embedded signature.
106 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
107 safe_browsing::MacSignatureEvaluator evaluator(path);
108 ASSERT_TRUE(evaluator.Initialize());
109
110 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
111 results;
112 EXPECT_TRUE(evaluator.PerformEvaluation(&results));
113 EXPECT_EQ(0u, results.size());
114 }
115
116 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithDR) {
117 // This test checks the signer against a designated requirement description.
118 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
119 std::string requirement(
120 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
121 safe_browsing::MacSignatureEvaluator evaluator(path, requirement);
122 ASSERT_TRUE(evaluator.Initialize());
123
124 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
125 results;
126 EXPECT_TRUE(evaluator.PerformEvaluation(&results));
127 EXPECT_EQ(0u, results.size());
128 }
129
130 TEST_F(MacSignatureEvaluatorTest, SimpleTestWithBadDR) {
131 // Now test with a designated requirement that does not describe the signer.
132 base::FilePath path = testdata_path_.AppendASCII("signedexecutablefat");
133 safe_browsing::MacSignatureEvaluator evaluator(path, "anchor apple");
134 ASSERT_TRUE(evaluator.Initialize());
135
136 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
137 results;
138 EXPECT_FALSE(evaluator.PerformEvaluation(&results));
139 ASSERT_EQ(1u, results.size());
140
141 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident& result =
142 results[0];
143 EXPECT_EQ(-67050, result.sec_error());
144 EXPECT_TRUE(result.has_signature());
145 ASSERT_TRUE(result.has_file_basename());
146 EXPECT_EQ("signedexecutablefat", result.file_basename());
147 }
148
149 TEST_F(MacSignatureEvaluatorTest, SimpleBundleTest) {
150 // Now test a simple, validly signed bundle.
151 base::FilePath path = testdata_path_.AppendASCII("test-bundle.app");
152 base::FilePath exec_path;
153 ASSERT_TRUE(GetExecPath(path, &exec_path));
154
155 std::string requirement(
156 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
157 safe_browsing::MacSignatureEvaluator evaluator(exec_path, requirement);
158 ASSERT_TRUE(evaluator.Initialize());
159
160 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
161 results;
162 EXPECT_TRUE(evaluator.PerformEvaluation(&results));
163 EXPECT_EQ(0u, results.size());
164 }
165
166 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest32) {
167 // Now to a test modified, signed bundle.
168 base::FilePath path = testdata_path_.AppendASCII("modified-main-exec32.app");
169 base::FilePath exec_path;
170 ASSERT_TRUE(GetExecPath(path, &exec_path));
171
172 std::string requirement(
173 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
174 safe_browsing::MacSignatureEvaluator evaluator(exec_path, requirement);
175 ASSERT_TRUE(evaluator.Initialize());
176
177 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
178 results;
179 EXPECT_FALSE(evaluator.PerformEvaluation(&results));
180 ASSERT_EQ(1u, results.size());
181
182 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident& incident =
183 results[0];
184 EXPECT_EQ(-67061, incident.sec_error());
185 EXPECT_EQ(exec_path.BaseName().value(), incident.file_basename());
186 }
187
188 TEST_F(MacSignatureEvaluatorTest, ModifiedMainExecTest64) {
189 // Snow Leopard does not know about the 64-bit slice so this test is
190 // irrelevant.
191 if (!base::mac::IsOSLionOrLater())
192 return;
193
194 // Now to a test modified, signed bundle.
195 base::FilePath path =
196 testdata_path_.AppendASCII("modified-main-exec64.app");
197 base::FilePath exec_path;
198 ASSERT_TRUE(GetExecPath(path, &exec_path));
199
200 std::string requirement(
201 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
202 safe_browsing::MacSignatureEvaluator evaluator(exec_path, requirement);
203 ASSERT_TRUE(evaluator.Initialize());
204
205 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
206 results;
207 EXPECT_FALSE(evaluator.PerformEvaluation(&results));
208 ASSERT_EQ(1u, results.size());
209
210 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident& incident =
211 results[0];
212 EXPECT_EQ(-67061, incident.sec_error());
213 EXPECT_EQ(exec_path.BaseName().value(), incident.file_basename());
214 }
215
216 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleAndExecTest) {
217 // Now test a modified, signed bundle with resources added and the main
218 // executable modified.
219 base::FilePath path =
220 testdata_path_.AppendASCII("modified-bundle-and-exec.app");
221 base::FilePath exec_path;
222 ASSERT_TRUE(GetExecPath(path, &exec_path));
223
224 std::string requirement(
225 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
226 safe_browsing::MacSignatureEvaluator evaluator(exec_path, requirement);
227 ASSERT_TRUE(evaluator.Initialize());
228
229 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
230 results;
231 EXPECT_FALSE(evaluator.PerformEvaluation(&results));
232 ASSERT_EQ(1u, results.size());
233
234 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident& incident1 =
235 results[0];
236 EXPECT_EQ(-67061, incident1.sec_error());
237 EXPECT_TRUE(incident1.has_signature());
238 EXPECT_EQ(exec_path.BaseName().value(), incident1.file_basename());
239 }
240
241 TEST_F(MacSignatureEvaluatorTest, ModifiedBundleTest) {
242 // Now test a modified, signed bundle. This bundle has
243 // the following problems:
244 // 1) A file was added (This should not be reported)
245 // 2) libsigned64.dylib was modified
246 // 3) executable32 was modified
247
248 base::FilePath orig_path = testdata_path_.AppendASCII("modified-bundle.app");
249 base::FilePath copied_path =
250 temp_dir_.path().AppendASCII("modified-bundle.app");
251 CHECK(base::CopyDirectory(orig_path, copied_path, true));
252
253 base::FilePath exec_path;
254 ASSERT_TRUE(GetExecPath(copied_path, &exec_path));
255
256 // Setup the extended attributes, which don't persist in the git repo.
257 ASSERT_TRUE(SetupXattrs(
258 copied_path.AppendASCII("Contents/Resources/Base.lproj/MainMenu.nib")));
259
260 std::string requirement(
261 "certificate leaf[subject.CN]=\"untrusted@goat.local\"");
262 safe_browsing::MacSignatureEvaluator evaluator(exec_path, requirement);
263 ASSERT_TRUE(evaluator.Initialize());
264
265 std::vector<ClientIncidentReport_IncidentData_BinaryIntegrityIncident>
266 results;
267 EXPECT_FALSE(evaluator.PerformEvaluation(&results));
268 ASSERT_EQ(4u, results.size());
269
270 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident* main_exec =
271 nullptr;
272 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident* libsigned64 =
273 nullptr;
274 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident*
275 executable32 = nullptr;
276 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident* mainmenunib =
277 nullptr;
278 const ClientIncidentReport_IncidentData_BinaryIntegrityIncident*
279 codesign_cfg = nullptr;
280
281 for (const auto& incident : results) {
282 if (incident.file_basename() == exec_path.BaseName().value())
283 main_exec = &incident;
284 else if (incident.file_basename() == "libsigned64.dylib")
285 libsigned64 = &incident;
286 else if (incident.file_basename() == "executable32")
287 executable32 = &incident;
288 else if (incident.file_basename() == "MainMenu.nib")
289 mainmenunib = &incident;
290 else if (incident.file_basename() == "codesign.cfg")
291 codesign_cfg = &incident;
292 }
293 ASSERT_NE(main_exec, 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(main_exec->has_file_basename());
300 EXPECT_EQ(exec_path.BaseName().value(), main_exec->file_basename());
301 EXPECT_TRUE(main_exec->has_signature());
302 EXPECT_EQ(-67054, main_exec->sec_error());
303
304 EXPECT_TRUE(libsigned64->has_file_basename());
305 EXPECT_EQ("libsigned64.dylib", libsigned64->file_basename());
306 EXPECT_TRUE(libsigned64->has_signature());
307
308 EXPECT_TRUE(executable32->has_file_basename());
309 EXPECT_EQ("executable32", executable32->file_basename());
310 EXPECT_TRUE(executable32->has_signature());
311
312 EXPECT_TRUE(mainmenunib->has_file_basename());
313 EXPECT_EQ("MainMenu.nib", mainmenunib->file_basename());
314 EXPECT_TRUE(mainmenunib->has_signature());
315 EXPECT_EQ(6, mainmenunib->signature().xattr_size());
316 // Manually convert the global xattrs array to a vector
317 std::vector<std::string> xattrs_known;
318 for (const auto& xattr : xattrs)
319 xattrs_known.push_back(xattr);
320
321 std::vector<std::string> xattrs_seen;
322 for (const auto& xattr : mainmenunib->signature().xattr()) {
323 ASSERT_TRUE(xattr.has_key());
324 EXPECT_TRUE(xattr.has_value());
325 xattrs_seen.push_back(xattr.key());
326 }
327 EXPECT_THAT(xattrs_known, ::testing::ContainerEq(xattrs_seen));
328 }
329
330 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698