1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "sandbox/mac/launchd_interception_server.h" 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <servers/bootstrap.h> 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/logging.h" 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/mac/mach_logging.h" 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "sandbox/mac/bootstrap_sandbox.h" 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)namespace sandbox { 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// The buffer size for all launchd messages. This comes from 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// is larger than the __ReplyUnion. 18f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)const mach_msg_size_t kBufferSize = 2096; 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)LaunchdInterceptionServer::LaunchdInterceptionServer( 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const BootstrapSandbox* sandbox) 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : sandbox_(sandbox), 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sandbox_port_(MACH_PORT_NULL), 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) compat_shim_(GetLaunchdCompatibilityShim()) { 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)LaunchdInterceptionServer::~LaunchdInterceptionServer() { 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) { 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) mach_port_t task = mach_task_self(); 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) kern_return_t kr; 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Allocate the dummy sandbox port. 35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) mach_port_t port; 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) != 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) KERN_SUCCESS) { 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port."; 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sandbox_port_.reset(port); 4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_, 4346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) { 4446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right."; 4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return false; 4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) } 4746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) sandbox_send_port_.reset(sandbox_port_); 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) message_server_.reset( 506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) new MachMessageServer(this, server_receive_right, kBufferSize)); 51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return message_server_->Initialize(); 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void LaunchdInterceptionServer::DemuxMessage(mach_msg_header_t* request, 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) mach_msg_header_t* reply) { 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(3) << "Incoming message #" << request->msgh_id; 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) pid_t sender_pid = message_server_->GetMessageSenderPID(request); 59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) const BootstrapSandboxPolicy* policy = 60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) sandbox_->PolicyForProcess(sender_pid); 61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (policy == NULL) { 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // No sandbox policy is in place for the sender of this message, which 636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) // means it came from the unknown. Reject it. 646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) VLOG(3) << "Message from unknown pid " << sender_pid << " rejected."; 656d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) message_server_->RejectMessage(request, MIG_REMOTE_ERROR); 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (request->msgh_id == compat_shim_.msg_id_look_up2) { 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Filter messages sent via bootstrap_look_up to enforce the sandbox policy 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // over the bootstrap namespace. 72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) HandleLookUp(request, reply, policy); 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (request->msgh_id == compat_shim_.msg_id_swap_integer) { 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Ensure that any vproc_swap_integer requests are safe. 75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) HandleSwapInteger(request, reply); 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // All other messages are not permitted. 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "Rejecting unhandled message #" << request->msgh_id; 79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) message_server_->RejectMessage(reply, MIG_REMOTE_ERROR); 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void LaunchdInterceptionServer::HandleLookUp( 84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) mach_msg_header_t* request, 85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) mach_msg_header_t* reply, 86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) const BootstrapSandboxPolicy* policy) { 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const std::string request_service_name( 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) compat_shim_.look_up2_get_request_name(request)); 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(2) << "Incoming look_up2 request for " << request_service_name; 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // Find the Rule for this service. If a named rule is not found, use the 92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // default specified by the policy. 9346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) const BootstrapSandboxPolicy::NamedRules::const_iterator it = 9446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) policy->rules.find(request_service_name); 9546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) Rule rule(policy->default_rule); 9646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (it != policy->rules.end()) 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rule = it->second; 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (rule.result == POLICY_ALLOW) { 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This service is explicitly allowed, so this message will not be 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // intercepted by the sandbox. 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name; 103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) ForwardMessage(request); 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (rule.result == POLICY_DENY_ERROR) { 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // The child is not permitted to look up this service. Send a MIG error 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // reply to the client. Returning a NULL or unserviced port for a look up 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // can cause clients to crash or hang. 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name; 109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) message_server_->RejectMessage(reply, BOOTSTRAP_UNKNOWN_SERVICE); 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (rule.result == POLICY_DENY_DUMMY_PORT || 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rule.result == POLICY_SUBSTITUTE_PORT) { 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // The policy result is to deny access to the real service port, replying 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // with a sandboxed port in its stead. Use either the dummy sandbox_port_ 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // or the one specified in the policy. 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(1) << "Intercepting look_up2 with a sandboxed service port: " 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) << request_service_name; 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) mach_port_t result_port; 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (rule.result == POLICY_DENY_DUMMY_PORT) 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result_port = sandbox_port_.get(); 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) else 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result_port = rule.substitute_port; 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) compat_shim_.look_up2_fill_reply(reply, result_port); 12546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // If the message was sent successfully, clear the result_port out of the 12646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // message so that it is not destroyed at the end of ReceiveMessage. The 12746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // above-inserted right has been moved out of the process, and destroying 12846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // the message will unref yet another right. 129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (message_server_->SendReply(reply)) 13046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL); 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NOTREACHED(); 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void LaunchdInterceptionServer::HandleSwapInteger(mach_msg_header_t* request, 137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) mach_msg_header_t* reply) { 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Only allow getting information out of launchd. Do not allow setting 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // values. Two commonly observed values that are retrieved are 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED. 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (compat_shim_.swap_integer_is_get_only(request)) { 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(2) << "Forwarding vproc swap_integer message."; 143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) ForwardMessage(request); 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) VLOG(2) << "Rejecting non-read-only swap_integer message."; 146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) message_server_->RejectMessage(reply, BOOTSTRAP_NOT_PRIVILEGED); 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void LaunchdInterceptionServer::ForwardMessage(mach_msg_header_t* request) { 150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port()); 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} // namespace sandbox 154