Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sandbox/mac/bootstrap_sandbox.h" | 5 #include "sandbox/mac/bootstrap_sandbox.h" |
| 6 | 6 |
| 7 #include <servers/bootstrap.h> | 7 #include <servers/bootstrap.h> |
| 8 #include <unistd.h> | 8 #include <unistd.h> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/foundation_util.h" | 11 #include "base/mac/foundation_util.h" |
| 12 #include "base/mac/mach_logging.h" | 12 #include "base/mac/mach_logging.h" |
| 13 #include "base/rand_util.h" | |
| 13 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
| 14 #include "sandbox/mac/launchd_interception_server.h" | 15 #include "sandbox/mac/launchd_interception_server.h" |
| 16 #include "sandbox/mac/pre_exec_delegate.h" | |
| 15 | 17 |
| 16 namespace sandbox { | 18 namespace sandbox { |
| 17 | 19 |
| 18 const int kNotAPolicy = -1; | 20 namespace { |
| 21 | |
| 22 struct SandboxCheckInRequest { | |
| 23 mach_msg_header_t header; | |
| 24 mach_msg_body_t body; | |
|
Mark Mentovai
2015/09/17 19:45:15
Since requests aren’t complex, you don’t need the
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 25 uint64_t token; | |
| 26 }; | |
| 27 | |
| 28 struct SandboxCheckInReply { | |
| 29 mach_msg_header_t header; | |
| 30 mach_msg_body_t body; | |
| 31 mach_msg_port_descriptor_t bootstrap_port; | |
| 32 }; | |
| 33 | |
| 34 } // namespace | |
| 19 | 35 |
| 20 // static | 36 // static |
| 21 scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() { | 37 scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() { |
| 22 scoped_ptr<BootstrapSandbox> null; // Used for early returns. | 38 scoped_ptr<BootstrapSandbox> null; // Used for early returns. |
| 23 scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox()); | 39 scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox()); |
| 24 sandbox->server_.reset(new LaunchdInterceptionServer(sandbox.get())); | 40 sandbox->launchd_server_.reset(new LaunchdInterceptionServer(sandbox.get())); |
| 25 | 41 |
| 26 // Check in with launchd to get the receive right for the server that is | 42 // Check in with launchd to get the receive right for the server that is |
| 27 // published in the bootstrap namespace. | 43 // published in the bootstrap namespace. |
| 28 mach_port_t port = MACH_PORT_NULL; | 44 mach_port_t port = MACH_PORT_NULL; |
| 29 kern_return_t kr = bootstrap_check_in(bootstrap_port, | 45 kern_return_t kr = bootstrap_check_in(bootstrap_port, |
| 30 sandbox->server_bootstrap_name().c_str(), &port); | 46 sandbox->server_bootstrap_name().c_str(), &port); |
| 31 if (kr != KERN_SUCCESS) { | 47 if (kr != KERN_SUCCESS) { |
| 32 BOOTSTRAP_LOG(ERROR, kr) | 48 BOOTSTRAP_LOG(ERROR, kr) |
| 33 << "Failed to bootstrap_check_in the sandbox server."; | 49 << "Failed to bootstrap_check_in the sandbox server."; |
| 34 return null.Pass(); | 50 return null.Pass(); |
| 35 } | 51 } |
| 36 base::mac::ScopedMachReceiveRight scoped_port(port); | 52 sandbox->check_in_port_.reset(port); |
| 53 | |
| 54 BootstrapSandbox* __block sandbox_ptr = sandbox.get(); | |
| 55 sandbox->check_in_server_.reset(new base::DispatchSourceMach( | |
| 56 "org.chromium.sandbox.BootstrapClientManager", | |
| 57 sandbox->check_in_port_.get(), | |
| 58 ^{ sandbox_ptr->HandleChildCheckIn(); })); | |
| 59 sandbox->check_in_server_->Resume(); | |
| 37 | 60 |
| 38 // Start the sandbox server. | 61 // Start the sandbox server. |
| 39 if (sandbox->server_->Initialize(scoped_port.get())) | 62 if (!sandbox->launchd_server_->Initialize(MACH_PORT_NULL)) |
| 40 ignore_result(scoped_port.release()); // Transferred to server_. | |
| 41 else | |
| 42 return null.Pass(); | 63 return null.Pass(); |
| 43 | 64 |
| 44 return sandbox.Pass(); | 65 return sandbox.Pass(); |
| 45 } | 66 } |
| 46 | 67 |
| 68 // static | |
| 69 bool BootstrapSandbox::ClientCheckIn(mach_port_t sandbox_server_port, | |
| 70 uint64_t sandbox_token, | |
| 71 mach_port_t* new_bootstrap_port) { | |
| 72 // Create a reply port for the check in message. | |
| 73 mach_port_t reply_port; | |
| 74 kern_return_t kr = mach_port_allocate(mach_task_self(), | |
| 75 MACH_PORT_RIGHT_RECEIVE, | |
| 76 &reply_port); | |
| 77 if (kr != KERN_SUCCESS) { | |
| 78 MACH_LOG(ERROR, kr) << "mach_port_allocate"; | |
|
Mark Mentovai
2015/09/17 19:45:15
This function is called by the post-fork()-pre-exe
Robert Sesek
2015/09/17 20:27:23
Done. Left a comment as a warning to future editor
| |
| 79 return false; | |
| 80 } | |
| 81 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port); | |
| 82 | |
| 83 // Check in with the sandbox server, presenting the |sandbox_token| in | |
| 84 // exchange for a new task bootstrap port. | |
| 85 struct { | |
| 86 union { | |
| 87 SandboxCheckInRequest request; | |
| 88 SandboxCheckInReply reply; | |
|
Mark Mentovai
2015/09/17 19:45:15
You need to write this as union { request, struct
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 89 }; | |
| 90 mach_msg_audit_trailer_t trailer; | |
|
Mark Mentovai
2015/09/17 19:45:15
Since you’re not actually requesting the audit tra
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 91 } msg = {}; | |
| 92 msg.request.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, | |
| 93 MACH_MSG_TYPE_MAKE_SEND_ONCE) | | |
| 94 MACH_MSGH_BITS_COMPLEX; | |
| 95 msg.request.header.msgh_remote_port = sandbox_server_port; | |
| 96 msg.request.header.msgh_local_port = reply_port; | |
| 97 msg.request.header.msgh_size = sizeof(msg); | |
| 98 msg.request.token = sandbox_token; | |
| 99 | |
| 100 kr = mach_msg(&msg.request.header, MACH_SEND_MSG | MACH_RCV_MSG, | |
| 101 sizeof(msg.request), sizeof(msg), reply_port, | |
| 102 100 /*ms*/, MACH_PORT_NULL); | |
|
Mark Mentovai
2015/09/17 19:45:15
Why a timeout at all?
Anyway, you’re not even get
Robert Sesek
2015/09/17 20:27:23
Agreed no timeout necessary, just forgot to clean
| |
| 103 if (kr == KERN_SUCCESS) { | |
| 104 *new_bootstrap_port = msg.reply.bootstrap_port.name; | |
| 105 return true; | |
| 106 } else { | |
| 107 MACH_LOG(ERROR, kr) << "mach_msg"; | |
| 108 return false; | |
| 109 } | |
| 110 } | |
| 111 | |
| 47 BootstrapSandbox::~BootstrapSandbox() { | 112 BootstrapSandbox::~BootstrapSandbox() { |
| 48 } | 113 } |
| 49 | 114 |
| 50 void BootstrapSandbox::RegisterSandboxPolicy( | 115 void BootstrapSandbox::RegisterSandboxPolicy( |
| 51 int sandbox_policy_id, | 116 int sandbox_policy_id, |
| 52 const BootstrapSandboxPolicy& policy) { | 117 const BootstrapSandboxPolicy& policy) { |
| 53 CHECK(IsPolicyValid(policy)); | 118 CHECK(IsPolicyValid(policy)); |
| 54 CHECK_GT(sandbox_policy_id, kNotAPolicy); | |
| 55 base::AutoLock lock(lock_); | 119 base::AutoLock lock(lock_); |
| 56 DCHECK(policies_.find(sandbox_policy_id) == policies_.end()); | 120 DCHECK(policies_.find(sandbox_policy_id) == policies_.end()); |
| 57 policies_.insert(std::make_pair(sandbox_policy_id, policy)); | 121 policies_.insert(std::make_pair(sandbox_policy_id, policy)); |
| 58 } | 122 } |
| 59 | 123 |
| 60 void BootstrapSandbox::PrepareToForkWithPolicy(int sandbox_policy_id) { | 124 scoped_ptr<PreExecDelegate> BootstrapSandbox::NewClient(int sandbox_policy_id) { |
| 61 base::AutoLock lock(lock_); | 125 base::AutoLock lock(lock_); |
| 62 | 126 |
| 63 // Verify that this is a real policy. | 127 DCHECK(policies_.find(sandbox_policy_id) != policies_.end()); |
| 64 CHECK(policies_.find(sandbox_policy_id) != policies_.end()); | |
| 65 CHECK_EQ(kNotAPolicy, effective_policy_id_) | |
| 66 << "Cannot nest calls to PrepareToForkWithPolicy()"; | |
| 67 | 128 |
| 68 // Store the policy for the process we're about to create. | 129 uint64_t token; |
| 69 effective_policy_id_ = sandbox_policy_id; | 130 while (true) { |
|
Mark Mentovai
2015/09/17 19:45:15
Good. I was hoping to see this!
| |
| 131 token = base::RandUint64(); | |
| 132 if (awaiting_processes_.find(token) == awaiting_processes_.end()) | |
| 133 break; | |
| 134 } | |
| 135 | |
| 136 awaiting_processes_[token] = sandbox_policy_id; | |
| 137 | |
| 138 return make_scoped_ptr(new PreExecDelegate(server_bootstrap_name_, token)); | |
| 70 } | 139 } |
| 71 | 140 |
| 72 // TODO(rsesek): The |lock_| needs to be taken twice because | 141 void BootstrapSandbox::InvalidateClient(base::ProcessHandle handle) { |
| 73 // base::LaunchProcess handles both fork+exec, and holding the lock for the | |
| 74 // duration would block servicing of other bootstrap messages. If a better | |
| 75 // LaunchProcess existed (do arbitrary work without layering violations), this | |
| 76 // could be avoided. | |
| 77 | |
| 78 void BootstrapSandbox::FinishedFork(base::ProcessHandle handle) { | |
| 79 base::AutoLock lock(lock_); | |
| 80 | |
| 81 CHECK_NE(kNotAPolicy, effective_policy_id_) | |
| 82 << "Must PrepareToForkWithPolicy() before FinishedFork()"; | |
| 83 | |
| 84 // Apply the policy to the new process. | |
| 85 if (handle != base::kNullProcessHandle) { | |
| 86 const auto& existing_process = sandboxed_processes_.find(handle); | |
| 87 CHECK(existing_process == sandboxed_processes_.end()); | |
| 88 sandboxed_processes_.insert(std::make_pair(handle, effective_policy_id_)); | |
| 89 VLOG(3) << "Bootstrap sandbox enforced for pid " << handle; | |
| 90 } | |
| 91 | |
| 92 effective_policy_id_ = kNotAPolicy; | |
| 93 } | |
| 94 | |
| 95 void BootstrapSandbox::ChildDied(base::ProcessHandle handle) { | |
| 96 base::AutoLock lock(lock_); | 142 base::AutoLock lock(lock_); |
| 97 const auto& it = sandboxed_processes_.find(handle); | 143 const auto& it = sandboxed_processes_.find(handle); |
| 98 if (it != sandboxed_processes_.end()) | 144 if (it != sandboxed_processes_.end()) |
| 99 sandboxed_processes_.erase(it); | 145 sandboxed_processes_.erase(it); |
| 100 } | 146 } |
| 101 | 147 |
| 102 const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess( | 148 const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess( |
| 103 pid_t pid) const { | 149 pid_t pid) const { |
| 104 base::AutoLock lock(lock_); | 150 base::AutoLock lock(lock_); |
| 105 const auto& process = sandboxed_processes_.find(pid); | 151 const auto& process = sandboxed_processes_.find(pid); |
| 106 | |
| 107 // The new child could send bootstrap requests before the parent calls | |
| 108 // FinishedFork(). | |
| 109 int policy_id = effective_policy_id_; | |
| 110 if (process != sandboxed_processes_.end()) { | 152 if (process != sandboxed_processes_.end()) { |
| 111 policy_id = process->second; | 153 return &policies_.find(process->second)->second; |
| 112 } | 154 } |
| 113 | 155 |
| 114 if (policy_id == kNotAPolicy) | 156 return nullptr; |
| 115 return NULL; | |
| 116 | |
| 117 return &policies_.find(policy_id)->second; | |
| 118 } | 157 } |
| 119 | 158 |
| 120 BootstrapSandbox::BootstrapSandbox() | 159 BootstrapSandbox::BootstrapSandbox() |
| 121 : server_bootstrap_name_( | 160 : server_bootstrap_name_( |
| 122 base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(), | 161 base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(), |
| 123 getpid())), | 162 getpid())), |
| 124 real_bootstrap_port_(MACH_PORT_NULL), | 163 real_bootstrap_port_(MACH_PORT_NULL) { |
| 125 effective_policy_id_(kNotAPolicy) { | |
| 126 mach_port_t port = MACH_PORT_NULL; | 164 mach_port_t port = MACH_PORT_NULL; |
| 127 kern_return_t kr = task_get_special_port( | 165 kern_return_t kr = task_get_special_port( |
| 128 mach_task_self(), TASK_BOOTSTRAP_PORT, &port); | 166 mach_task_self(), TASK_BOOTSTRAP_PORT, &port); |
| 129 MACH_CHECK(kr == KERN_SUCCESS, kr); | 167 MACH_CHECK(kr == KERN_SUCCESS, kr); |
| 130 real_bootstrap_port_.reset(port); | 168 real_bootstrap_port_.reset(port); |
| 131 } | 169 } |
| 132 | 170 |
| 171 void BootstrapSandbox::HandleChildCheckIn() { | |
| 172 struct { | |
| 173 SandboxCheckInRequest request; | |
| 174 mach_msg_audit_trailer_t trailer; | |
| 175 } msg = {}; | |
| 176 msg.request.header.msgh_local_port = check_in_port_.get(); | |
| 177 msg.request.header.msgh_size = sizeof(msg.request); | |
| 178 const mach_msg_option_t kOptions = MACH_RCV_MSG | | |
| 179 MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | | |
|
Mark Mentovai
2015/09/17 19:45:15
MACH_MSG_TRAILER_FORMAT_0
Robert Sesek
2015/09/17 20:27:24
Done.
| |
| 180 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); | |
| 181 kern_return_t kr = mach_msg(&msg.request.header, kOptions, 0, | |
| 182 sizeof(msg), check_in_port_.get(), | |
| 183 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
| 184 if (kr != KERN_SUCCESS) { | |
| 185 MACH_LOG(ERROR, kr) << "HandleChildCheckIn mach_msg MACH_RCV_MSG"; | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 pid_t client_pid; | |
| 190 audit_token_to_au32(msg.trailer.msgh_audit, | |
| 191 NULL, NULL, NULL, NULL, NULL, &client_pid, NULL, NULL); | |
|
Mark Mentovai
2015/09/17 19:45:15
nullptr
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 192 | |
| 193 { | |
| 194 base::AutoLock lock(lock_); | |
| 195 | |
| 196 auto awaiting_it = awaiting_processes_.find(msg.request.token); | |
| 197 if (awaiting_it == awaiting_processes_.end()) { | |
| 198 LOG(ERROR) << "Received sandbox check-in message from unknown client."; | |
| 199 mach_msg_destroy(&msg.request); | |
| 200 return; | |
| 201 } | |
| 202 | |
| 203 sandboxed_processes_[client_pid] = awaiting_it->second; | |
| 204 awaiting_processes_.erase(awaiting_it); | |
| 205 } | |
| 206 | |
| 207 SandboxCheckInReply reply = {}; | |
| 208 reply.header.msgh_bits = MACH_MSGH_BITS_REMOTE(msg.request.header.msgh_bits) | | |
| 209 MACH_MSGH_BITS_COMPLEX; | |
| 210 reply.header.msgh_remote_port = msg.request.header.msgh_remote_port; | |
| 211 reply.header.msgh_size = sizeof(reply); | |
| 212 reply.body.msgh_descriptor_count = 1; | |
| 213 reply.bootstrap_port.name = launchd_server_->server_port(); | |
| 214 reply.bootstrap_port.disposition = MACH_MSG_TYPE_MAKE_SEND; | |
| 215 reply.bootstrap_port.type = MACH_MSG_PORT_DESCRIPTOR; | |
| 216 | |
| 217 kr = mach_msg(&reply.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, | |
| 218 sizeof(reply), 0, MACH_PORT_NULL, 100 /*ms*/, MACH_PORT_NULL); | |
|
Mark Mentovai
2015/09/17 19:45:15
I do see why you might want a timeout on this one.
| |
| 219 if (kr != KERN_SUCCESS) { | |
| 220 { | |
| 221 base::AutoLock lock(lock_); | |
| 222 sandboxed_processes_.erase(client_pid); | |
| 223 } | |
| 224 MACH_LOG(ERROR, kr) << "HandleChildCheckIn mach_msg MACH_SEND_MSG"; | |
| 225 mach_msg_destroy(&msg.request); | |
| 226 // msg.reply does not need to be destroyed as the right is created | |
| 227 // at mach_msg() send. | |
|
Mark Mentovai
2015/09/17 19:45:15
Say that the destroy is just to kill the send-once
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 228 return; | |
|
Mark Mentovai
2015/09/17 19:45:15
This is sort of an early return but not really.
Robert Sesek
2015/09/17 20:27:23
Done.
| |
| 229 } | |
| 230 } | |
| 231 | |
| 133 } // namespace sandbox | 232 } // namespace sandbox |
| OLD | NEW |