1// Copyright 2014 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 "sandbox/mac/bootstrap_sandbox.h" 6 7#include <CoreFoundation/CoreFoundation.h> 8#import <Foundation/Foundation.h> 9#include <mach/mach.h> 10#include <servers/bootstrap.h> 11 12#include "base/logging.h" 13#include "base/mac/mac_util.h" 14#include "base/mac/mach_logging.h" 15#include "base/mac/scoped_nsobject.h" 16#include "base/mac/scoped_mach_port.h" 17#include "base/process/kill.h" 18#include "base/strings/stringprintf.h" 19#include "base/test/multiprocess_test.h" 20#include "base/test/test_timeouts.h" 21#import "testing/gtest_mac.h" 22#include "testing/multiprocess_func_list.h" 23 24NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test"; 25 26@interface DistributedNotificationObserver : NSObject { 27 @private 28 int receivedCount_; 29 base::scoped_nsobject<NSString> object_; 30} 31- (int)receivedCount; 32- (NSString*)object; 33- (void)waitForNotification; 34@end 35 36@implementation DistributedNotificationObserver 37- (id)init { 38 if ((self = [super init])) { 39 [[NSDistributedNotificationCenter defaultCenter] 40 addObserver:self 41 selector:@selector(observeNotification:) 42 name:kTestNotification 43 object:nil]; 44 } 45 return self; 46} 47 48- (void)dealloc { 49 [[NSDistributedNotificationCenter defaultCenter] 50 removeObserver:self 51 name:kTestNotification 52 object:nil]; 53 [super dealloc]; 54} 55 56- (int)receivedCount { 57 return receivedCount_; 58} 59 60- (NSString*)object { 61 return object_.get(); 62} 63 64- (void)waitForNotification { 65 object_.reset(); 66 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 67 TestTimeouts::action_timeout().InSeconds(), false); 68} 69 70- (void)observeNotification:(NSNotification*)notification { 71 ++receivedCount_; 72 object_.reset([[notification object] copy]); 73 CFRunLoopStop(CFRunLoopGetCurrent()); 74} 75@end 76 77//////////////////////////////////////////////////////////////////////////////// 78 79namespace sandbox { 80 81class BootstrapSandboxTest : public base::MultiProcessTest { 82 public: 83 virtual void SetUp() OVERRIDE { 84 base::MultiProcessTest::SetUp(); 85 86 sandbox_ = BootstrapSandbox::Create(); 87 ASSERT_TRUE(sandbox_.get()); 88 } 89 90 BootstrapSandboxPolicy BaselinePolicy() { 91 BootstrapSandboxPolicy policy; 92 if (base::mac::IsOSSnowLeopard()) 93 policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW); 94 return policy; 95 } 96 97 void RunChildWithPolicy(int policy_id, 98 const char* child_name, 99 base::ProcessHandle* out_pid) { 100 sandbox_->PrepareToForkWithPolicy(policy_id); 101 base::LaunchOptions options; 102 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name(); 103 base::ProcessHandle pid = SpawnChildWithOptions(child_name, options); 104 ASSERT_GT(pid, 0); 105 sandbox_->FinishedFork(pid); 106 int code = 0; 107 EXPECT_TRUE(base::WaitForExitCode(pid, &code)); 108 EXPECT_EQ(0, code); 109 if (out_pid) 110 *out_pid = pid; 111 } 112 113 protected: 114 scoped_ptr<BootstrapSandbox> sandbox_; 115}; 116 117const char kNotificationTestMain[] = "PostNotification"; 118 119// Run the test without the sandbox. 120TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) { 121 base::scoped_nsobject<DistributedNotificationObserver> observer( 122 [[DistributedNotificationObserver alloc] init]); 123 124 base::ProcessHandle pid = SpawnChild(kNotificationTestMain); 125 ASSERT_GT(pid, 0); 126 int code = 0; 127 EXPECT_TRUE(base::WaitForExitCode(pid, &code)); 128 EXPECT_EQ(0, code); 129 130 [observer waitForNotification]; 131 EXPECT_EQ(1, [observer receivedCount]); 132 EXPECT_EQ(pid, [[observer object] intValue]); 133} 134 135// Run the test with the sandbox enabled without notifications on the policy 136// whitelist. 137TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) { 138 base::scoped_nsobject<DistributedNotificationObserver> observer( 139 [[DistributedNotificationObserver alloc] init]); 140 141 sandbox_->RegisterSandboxPolicy(1, BaselinePolicy()); 142 RunChildWithPolicy(1, kNotificationTestMain, NULL); 143 144 [observer waitForNotification]; 145 EXPECT_EQ(0, [observer receivedCount]); 146 EXPECT_EQ(nil, [observer object]); 147} 148 149// Run the test with notifications permitted. 150TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) { 151 base::scoped_nsobject<DistributedNotificationObserver> observer( 152 [[DistributedNotificationObserver alloc] init]); 153 154 BootstrapSandboxPolicy policy(BaselinePolicy()); 155 // 10.9: 156 policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW); 157 policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW); 158 // 10.6: 159 policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW); 160 policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW); 161 sandbox_->RegisterSandboxPolicy(2, policy); 162 163 base::ProcessHandle pid; 164 RunChildWithPolicy(2, kNotificationTestMain, &pid); 165 166 [observer waitForNotification]; 167 EXPECT_EQ(1, [observer receivedCount]); 168 EXPECT_EQ(pid, [[observer object] intValue]); 169} 170 171MULTIPROCESS_TEST_MAIN(PostNotification) { 172 [[NSDistributedNotificationCenter defaultCenter] 173 postNotificationName:kTestNotification 174 object:[NSString stringWithFormat:@"%d", getpid()]]; 175 return 0; 176} 177 178const char kTestServer[] = "org.chromium.test_bootstrap_server"; 179 180TEST_F(BootstrapSandboxTest, PolicyDenyError) { 181 BootstrapSandboxPolicy policy(BaselinePolicy()); 182 policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR); 183 sandbox_->RegisterSandboxPolicy(1, policy); 184 185 RunChildWithPolicy(1, "PolicyDenyError", NULL); 186} 187 188MULTIPROCESS_TEST_MAIN(PolicyDenyError) { 189 mach_port_t port = MACH_PORT_NULL; 190 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, 191 &port); 192 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr); 193 CHECK(port == MACH_PORT_NULL); 194 195 kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server", 196 &port); 197 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr); 198 CHECK(port == MACH_PORT_NULL); 199 200 return 0; 201} 202 203TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) { 204 BootstrapSandboxPolicy policy(BaselinePolicy()); 205 policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT); 206 sandbox_->RegisterSandboxPolicy(1, policy); 207 208 RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL); 209} 210 211MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) { 212 mach_port_t port = MACH_PORT_NULL; 213 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, 214 &port); 215 CHECK_EQ(KERN_SUCCESS, kr); 216 CHECK(port != MACH_PORT_NULL); 217 return 0; 218} 219 220struct SubstitutePortAckSend { 221 mach_msg_header_t header; 222 char buf[32]; 223}; 224 225struct SubstitutePortAckRecv : public SubstitutePortAckSend { 226 mach_msg_trailer_t trailer; 227}; 228 229const char kSubstituteAck[] = "Hello, this is doge!"; 230 231TEST_F(BootstrapSandboxTest, PolicySubstitutePort) { 232 mach_port_t task = mach_task_self(); 233 234 mach_port_t port; 235 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, 236 &port)); 237 base::mac::ScopedMachReceiveRight scoped_port(port); 238 239 mach_port_urefs_t send_rights = 0; 240 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 241 &send_rights)); 242 EXPECT_EQ(0u, send_rights); 243 244 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, 245 MACH_MSG_TYPE_MAKE_SEND)); 246 base::mac::ScopedMachSendRight scoped_port_send(port); 247 248 send_rights = 0; 249 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 250 &send_rights)); 251 EXPECT_EQ(1u, send_rights); 252 253 BootstrapSandboxPolicy policy(BaselinePolicy()); 254 policy.rules[kTestServer] = Rule(port); 255 sandbox_->RegisterSandboxPolicy(1, policy); 256 257 RunChildWithPolicy(1, "PolicySubstitutePort", NULL); 258 259 struct SubstitutePortAckRecv msg; 260 bzero(&msg, sizeof(msg)); 261 msg.header.msgh_size = sizeof(msg); 262 msg.header.msgh_local_port = port; 263 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0, 264 msg.header.msgh_size, port, 265 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL); 266 EXPECT_EQ(KERN_SUCCESS, kr); 267 268 send_rights = 0; 269 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 270 &send_rights)); 271 EXPECT_EQ(1u, send_rights); 272 273 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf))); 274} 275 276MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) { 277 mach_port_t port = MACH_PORT_NULL; 278 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port); 279 CHECK_EQ(KERN_SUCCESS, kr); 280 CHECK(port != MACH_PORT_NULL); 281 282 struct SubstitutePortAckSend msg; 283 bzero(&msg, sizeof(msg)); 284 msg.header.msgh_size = sizeof(msg); 285 msg.header.msgh_remote_port = port; 286 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND); 287 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf)); 288 289 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); 290 291 return 0; 292} 293 294TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) { 295 mach_port_t task = mach_task_self(); 296 297 mach_port_t port; 298 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, 299 &port)); 300 base::mac::ScopedMachReceiveRight scoped_port_recv(port); 301 302 mach_port_urefs_t send_rights = 0; 303 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 304 &send_rights)); 305 EXPECT_EQ(0u, send_rights); 306 307 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, 308 MACH_MSG_TYPE_MAKE_SEND)); 309 base::mac::ScopedMachSendRight scoped_port_send(port); 310 311 send_rights = 0; 312 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 313 &send_rights)); 314 EXPECT_EQ(1u, send_rights); 315 316 mach_port_t bp; 317 ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp)); 318 base::mac::ScopedMachSendRight scoped_bp(bp); 319 320 char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess"; 321#pragma GCC diagnostic push 322#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 323 kern_return_t kr = bootstrap_register(bp, service_name, port); 324#pragma GCC diagnostic pop 325 EXPECT_EQ(KERN_SUCCESS, kr); 326 327 send_rights = 0; 328 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 329 &send_rights)); 330 EXPECT_EQ(1u, send_rights); 331 332 mach_port_t service_port; 333 EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port)); 334 base::mac::ScopedMachSendRight scoped_service_port(service_port); 335 336 send_rights = 0; 337 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, 338 &send_rights)); 339 // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per- 340 // process cache. 341 if (base::mac::IsOSSnowLeopard()) 342 EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights; 343 else 344 EXPECT_EQ(2u, send_rights); 345} 346 347const char kDefaultRuleTestAllow[] = 348 "org.chromium.sandbox.test.DefaultRuleAllow"; 349const char kDefaultRuleTestDeny[] = 350 "org.chromium.sandbox.test.DefaultRuleAllow.Deny"; 351 352TEST_F(BootstrapSandboxTest, DefaultRuleAllow) { 353 mach_port_t task = mach_task_self(); 354 355 mach_port_t port; 356 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, 357 &port)); 358 base::mac::ScopedMachReceiveRight scoped_port_recv(port); 359 360 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, 361 MACH_MSG_TYPE_MAKE_SEND)); 362 base::mac::ScopedMachSendRight scoped_port_send(port); 363 364 BootstrapSandboxPolicy policy; 365 policy.default_rule = Rule(POLICY_ALLOW); 366 policy.rules[kDefaultRuleTestAllow] = Rule(port); 367 policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR); 368 sandbox_->RegisterSandboxPolicy(3, policy); 369 370 base::scoped_nsobject<DistributedNotificationObserver> observer( 371 [[DistributedNotificationObserver alloc] init]); 372 373 int pid = 0; 374 RunChildWithPolicy(3, "DefaultRuleAllow", &pid); 375 EXPECT_GT(pid, 0); 376 377 [observer waitForNotification]; 378 EXPECT_EQ(1, [observer receivedCount]); 379 EXPECT_EQ(pid, [[observer object] intValue]); 380 381 struct SubstitutePortAckRecv msg; 382 bzero(&msg, sizeof(msg)); 383 msg.header.msgh_size = sizeof(msg); 384 msg.header.msgh_local_port = port; 385 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0, 386 msg.header.msgh_size, port, 387 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL); 388 EXPECT_EQ(KERN_SUCCESS, kr); 389 390 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf))); 391} 392 393MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) { 394 [[NSDistributedNotificationCenter defaultCenter] 395 postNotificationName:kTestNotification 396 object:[NSString stringWithFormat:@"%d", getpid()]]; 397 398 mach_port_t port = MACH_PORT_NULL; 399 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port, 400 const_cast<char*>(kDefaultRuleTestDeny), &port)); 401 CHECK(port == MACH_PORT_NULL); 402 403 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, 404 const_cast<char*>(kDefaultRuleTestAllow), &port)); 405 CHECK(port != MACH_PORT_NULL); 406 407 struct SubstitutePortAckSend msg; 408 bzero(&msg, sizeof(msg)); 409 msg.header.msgh_size = sizeof(msg); 410 msg.header.msgh_remote_port = port; 411 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND); 412 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf)); 413 414 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); 415 416 return 0; 417} 418 419TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) { 420 const int kTestPolicyId = 1; 421 mach_port_t task = mach_task_self(); 422 423 // Create a server port. 424 mach_port_t port; 425 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, 426 &port)); 427 base::mac::ScopedMachReceiveRight scoped_port_recv(port); 428 429 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, 430 MACH_MSG_TYPE_MAKE_SEND)); 431 base::mac::ScopedMachSendRight scoped_port_send(port); 432 433 // Set up the policy and register the port. 434 BootstrapSandboxPolicy policy(BaselinePolicy()); 435 policy.rules["sync"] = Rule(port); 436 sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy); 437 438 // Launch the child. 439 sandbox_->PrepareToForkWithPolicy(kTestPolicyId); 440 base::LaunchOptions options; 441 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name(); 442 base::ProcessHandle pid = 443 SpawnChildWithOptions("ChildOutliveSandbox", options); 444 ASSERT_GT(pid, 0); 445 sandbox_->FinishedFork(pid); 446 447 // Synchronize with the child. 448 mach_msg_empty_rcv_t rcv_msg; 449 bzero(&rcv_msg, sizeof(rcv_msg)); 450 kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0, 451 sizeof(rcv_msg), port, 452 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL); 453 ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr); 454 455 // Destroy the sandbox. 456 sandbox_.reset(); 457 458 // Synchronize again with the child. 459 mach_msg_empty_send_t send_msg; 460 bzero(&send_msg, sizeof(send_msg)); 461 send_msg.header.msgh_size = sizeof(send_msg); 462 send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port; 463 send_msg.header.msgh_bits = 464 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE); 465 kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0, 466 MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(), 467 MACH_PORT_NULL); 468 EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr); 469 470 int code = 0; 471 EXPECT_TRUE(base::WaitForExitCode(pid, &code)); 472 EXPECT_EQ(0, code); 473} 474 475MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) { 476 // Get the synchronization channel. 477 mach_port_t port = MACH_PORT_NULL; 478 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port)); 479 480 // Create a reply port. 481 mach_port_t reply_port; 482 CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(), 483 MACH_PORT_RIGHT_RECEIVE, &reply_port)); 484 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port); 485 486 // Send a message to shutdown the sandbox. 487 mach_msg_empty_send_t send_msg; 488 bzero(&send_msg, sizeof(send_msg)); 489 send_msg.header.msgh_size = sizeof(send_msg); 490 send_msg.header.msgh_local_port = reply_port; 491 send_msg.header.msgh_remote_port = port; 492 send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, 493 MACH_MSG_TYPE_MAKE_SEND_ONCE); 494 kern_return_t kr = mach_msg_send(&send_msg.header); 495 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send"; 496 497 // Flood the server's message queue with messages. There should be some 498 // pending when the sandbox is destroyed. 499 for (int i = 0; i < 20; ++i) { 500 mach_port_t tmp = MACH_PORT_NULL; 501 std::string name = base::StringPrintf("test.%d", i); 502 bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp); 503 } 504 505 // Ack that the sandbox has been shutdown. 506 mach_msg_empty_rcv_t rcv_msg; 507 bzero(&rcv_msg, sizeof(rcv_msg)); 508 rcv_msg.header.msgh_size = sizeof(rcv_msg); 509 rcv_msg.header.msgh_local_port = reply_port; 510 kr = mach_msg_receive(&rcv_msg.header); 511 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive"; 512 513 // Try to message the sandbox. 514 bootstrap_look_up(bootstrap_port, "test", &port); 515 516 return 0; 517} 518 519} // namespace sandbox 520