OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/linux/services/namespace_sandbox.h" |
| 6 |
| 7 #include <sched.h> |
| 8 #include <stdlib.h> |
| 9 #include <sys/socket.h> |
| 10 #include <sys/types.h> |
| 11 #include <unistd.h> |
| 12 |
| 13 #include <string> |
| 14 #include <utility> |
| 15 |
| 16 #include "base/bind.h" |
| 17 #include "base/command_line.h" |
| 18 #include "base/environment.h" |
| 19 #include "base/files/scoped_file.h" |
| 20 #include "base/logging.h" |
| 21 #include "base/posix/eintr_wrapper.h" |
| 22 #include "base/process/launch.h" |
| 23 #include "base/process/process.h" |
| 24 #include "base/strings/stringprintf.h" |
| 25 #include "sandbox/linux/services/namespace_utils.h" |
| 26 |
| 27 namespace sandbox { |
| 28 |
| 29 namespace { |
| 30 const char kPipeValue = '\xcc'; |
| 31 |
| 32 class BlockOnPipeDelegate : public base::LaunchOptions::PreExecDelegate { |
| 33 public: |
| 34 explicit BlockOnPipeDelegate(int fd) : fd_(fd) {} |
| 35 |
| 36 ~BlockOnPipeDelegate() override {} |
| 37 |
| 38 void RunAsyncSafe() override { |
| 39 char c; |
| 40 RAW_CHECK(HANDLE_EINTR(read(fd_, &c, 1)) == 1); |
| 41 RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0); |
| 42 RAW_CHECK(c == kPipeValue); |
| 43 } |
| 44 |
| 45 private: |
| 46 int fd_; |
| 47 DISALLOW_COPY_AND_ASSIGN(BlockOnPipeDelegate); |
| 48 }; |
| 49 |
| 50 void SetEnvironForNamespaceType(base::EnvironmentMap* environ, |
| 51 base::NativeEnvironmentString env_var, |
| 52 bool value) { |
| 53 // An empty string causes the env var to be unset in the child process. |
| 54 (*environ)[env_var] = value ? "1" : ""; |
| 55 } |
| 56 |
| 57 const char kSandboxUSERNSEnvironmentVarName[] = "SBX_USER_NS"; |
| 58 const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS"; |
| 59 const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS"; |
| 60 |
| 61 } // namespace |
| 62 |
| 63 NamespaceSandbox::NamespaceSandbox() : launch_called_(false) { |
| 64 } |
| 65 |
| 66 NamespaceSandbox::~NamespaceSandbox() { |
| 67 } |
| 68 |
| 69 void NamespaceSandbox::AddFdMapping(int from_fd, int to_fd) { |
| 70 fds_to_remap_.push_back(std::make_pair(from_fd, to_fd)); |
| 71 } |
| 72 |
| 73 base::Process NamespaceSandbox::Launch(const base::CommandLine& cmdline) { |
| 74 CHECK(!launch_called_) << "NamespaceSandbox may only be used once."; |
| 75 launch_called_ = true; |
| 76 |
| 77 int clone_flags = 0; |
| 78 int ns_types[] = {CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET}; |
| 79 for (const int ns_type : ns_types) { |
| 80 if (NamespaceUtils::KernelSupportsUnprivilegedNamespace(ns_type)) { |
| 81 clone_flags |= ns_type; |
| 82 } |
| 83 } |
| 84 CHECK(clone_flags & CLONE_NEWUSER); |
| 85 |
| 86 int fds[2]; |
| 87 PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); |
| 88 PCHECK(shutdown(fds[0], SHUT_WR) == 0); |
| 89 PCHECK(shutdown(fds[1], SHUT_RD) == 0); |
| 90 base::ScopedFD read_fd(fds[0]); |
| 91 base::ScopedFD write_fd(fds[1]); |
| 92 |
| 93 fds_to_remap_.push_back(std::make_pair(read_fd.get(), read_fd.get())); |
| 94 |
| 95 BlockOnPipeDelegate block_on_pipe_delegate(read_fd.get()); |
| 96 launch_options_.pre_exec_delegate = &block_on_pipe_delegate; |
| 97 launch_options_.fds_to_remap = &fds_to_remap_; |
| 98 launch_options_.clone_flags = clone_flags; |
| 99 |
| 100 const std::pair<int, const char*> clone_flag_environ[] = { |
| 101 std::make_pair(CLONE_NEWUSER, kSandboxUSERNSEnvironmentVarName), |
| 102 std::make_pair(CLONE_NEWPID, kSandboxPIDNSEnvironmentVarName), |
| 103 std::make_pair(CLONE_NEWNET, kSandboxNETNSEnvironmentVarName), |
| 104 }; |
| 105 |
| 106 base::EnvironmentMap* environ = &launch_options_.environ; |
| 107 for (const auto& entry : clone_flag_environ) { |
| 108 const int flag = entry.first; |
| 109 const char* environ_name = entry.second; |
| 110 SetEnvironForNamespaceType(environ, environ_name, clone_flags & flag); |
| 111 } |
| 112 |
| 113 base::Process process = base::LaunchProcess(cmdline, launch_options_); |
| 114 read_fd.reset(); |
| 115 |
| 116 if (process.IsValid()) { |
| 117 WriteUidGidMapAndUnblockProcess(process.Pid(), write_fd.Pass()); |
| 118 } |
| 119 |
| 120 return process.Pass(); |
| 121 } |
| 122 |
| 123 void NamespaceSandbox::WriteUidGidMapAndUnblockProcess(pid_t pid, |
| 124 base::ScopedFD fd) { |
| 125 const std::string uid_map_path = base::StringPrintf("/proc/%d/uid_map", pid); |
| 126 const std::string gid_map_path = base::StringPrintf("/proc/%d/gid_map", pid); |
| 127 CHECK(NamespaceUtils::WriteToIdMapFile(uid_map_path.c_str(), getuid())); |
| 128 CHECK(NamespaceUtils::WriteToIdMapFile(gid_map_path.c_str(), getgid())); |
| 129 PCHECK(HANDLE_EINTR(send(fd.get(), &kPipeValue, 1, MSG_NOSIGNAL)) == 1); |
| 130 } |
| 131 |
| 132 bool NamespaceSandbox::InNewUserNamespace() { |
| 133 return getenv(kSandboxUSERNSEnvironmentVarName) != nullptr; |
| 134 } |
| 135 |
| 136 bool NamespaceSandbox::InNewPidNamespace() { |
| 137 return getenv(kSandboxPIDNSEnvironmentVarName) != nullptr; |
| 138 } |
| 139 |
| 140 bool NamespaceSandbox::InNewNetNamespace() { |
| 141 return getenv(kSandboxNETNSEnvironmentVarName) != nullptr; |
| 142 } |
| 143 |
| 144 } // namespace sandbox |
OLD | NEW |