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 |