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