1 // Copyright (c) 2012 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 <sys/types.h> 6 #include <sys/wait.h> 7 #include <unistd.h> 8 9 #include "base/environment.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/posix/eintr_wrapper.h" 13 #include "base/strings/string_number_conversions.h" 14 15 #include "sandbox/linux/services/init_process_reaper.h" 16 #include "sandbox/linux/suid/common/sandbox.h" 17 #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" 18 #include "setuid_sandbox_client.h" 19 20 namespace { 21 22 // Set an environment variable that reflects the API version we expect from the 23 // setuid sandbox. Old versions of the sandbox will ignore this. 24 void SetSandboxAPIEnvironmentVariable(base::Environment* env) { 25 env->SetVar(sandbox::kSandboxEnvironmentApiRequest, 26 base::IntToString(sandbox::kSUIDSandboxApiNumber)); 27 } 28 29 // Wrapper around a shared C function. 30 // Returns the "saved" environment variable name corresponding to |envvar| 31 // in a new string or NULL. 32 std::string* CreateSavedVariableName(const char* env_var) { 33 char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var); 34 if (!saved_env_var) 35 return NULL; 36 std::string* saved_env_var_copy = new std::string(saved_env_var); 37 // SandboxSavedEnvironmentVariable is the C function that we wrap and uses 38 // malloc() to allocate memory. 39 free(saved_env_var); 40 return saved_env_var_copy; 41 } 42 43 // The ELF loader will clear many environment variables so we save them to 44 // different names here so that the SUID sandbox can resolve them for the 45 // renderer. 46 void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) { 47 for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) { 48 const char* env_var = kSUIDUnsafeEnvironmentVariables[i]; 49 // Get the saved environment variable corresponding to envvar. 50 scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var)); 51 if (saved_env_var == NULL) 52 continue; 53 54 std::string value; 55 if (env->GetVar(env_var, &value)) 56 env->SetVar(saved_env_var->c_str(), value); 57 else 58 env->UnSetVar(saved_env_var->c_str()); 59 } 60 } 61 62 int GetHelperApi(base::Environment* env) { 63 std::string api_string; 64 int api_number = 0; // Assume API version 0 if no environment was found. 65 if (env->GetVar(sandbox::kSandboxEnvironmentApiProvides, &api_string) && 66 !base::StringToInt(api_string, &api_number)) { 67 // It's an error if we could not convert the API number. 68 api_number = -1; 69 } 70 return api_number; 71 } 72 73 // Convert |var_name| from the environment |env| to an int. 74 // Return -1 if the variable does not exist or the value cannot be converted. 75 int EnvToInt(base::Environment* env, const char* var_name) { 76 std::string var_string; 77 int var_value = -1; 78 if (env->GetVar(var_name, &var_string) && 79 !base::StringToInt(var_string, &var_value)) { 80 var_value = -1; 81 } 82 return var_value; 83 } 84 85 pid_t GetHelperPID(base::Environment* env) { 86 return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName); 87 } 88 89 // Get the IPC file descriptor used to communicate with the setuid helper. 90 int GetIPCDescriptor(base::Environment* env) { 91 return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName); 92 } 93 94 } // namespace 95 96 namespace sandbox { 97 98 SetuidSandboxClient* SetuidSandboxClient::Create() { 99 base::Environment* environment(base::Environment::Create()); 100 SetuidSandboxClient* sandbox_client(new(SetuidSandboxClient)); 101 102 CHECK(environment); 103 sandbox_client->env_ = environment; 104 return sandbox_client; 105 } 106 107 SetuidSandboxClient::SetuidSandboxClient() 108 : env_(NULL), 109 sandboxed_(false) { 110 } 111 112 SetuidSandboxClient::~SetuidSandboxClient() { 113 delete env_; 114 } 115 116 bool SetuidSandboxClient::ChrootMe() { 117 int ipc_fd = GetIPCDescriptor(env_); 118 119 if (ipc_fd < 0) { 120 LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor"; 121 return false; 122 } 123 124 if (HANDLE_EINTR(write(ipc_fd, &kMsgChrootMe, 1)) != 1) { 125 PLOG(ERROR) << "Failed to write to chroot pipe"; 126 return false; 127 } 128 129 // We need to reap the chroot helper process in any event. 130 pid_t helper_pid = GetHelperPID(env_); 131 // If helper_pid is -1 we wait for any child. 132 if (waitpid(helper_pid, NULL, 0) < 0) { 133 PLOG(ERROR) << "Failed to wait for setuid helper to die"; 134 return false; 135 } 136 137 char reply; 138 if (HANDLE_EINTR(read(ipc_fd, &reply, 1)) != 1) { 139 PLOG(ERROR) << "Failed to read from chroot pipe"; 140 return false; 141 } 142 143 if (reply != kMsgChrootSuccessful) { 144 LOG(ERROR) << "Error code reply from chroot helper"; 145 return false; 146 } 147 148 // We now consider ourselves "fully sandboxed" as far as the 149 // setuid sandbox is concerned. 150 sandboxed_ = true; 151 return true; 152 } 153 154 bool SetuidSandboxClient::CreateInitProcessReaper( 155 base::Closure* post_fork_parent_callback) { 156 return sandbox::CreateInitProcessReaper(post_fork_parent_callback); 157 } 158 159 bool SetuidSandboxClient::IsSuidSandboxUpToDate() const { 160 return GetHelperApi(env_) == kSUIDSandboxApiNumber; 161 } 162 163 bool SetuidSandboxClient::IsSuidSandboxChild() const { 164 return GetIPCDescriptor(env_) >= 0; 165 } 166 167 bool SetuidSandboxClient::IsInNewPIDNamespace() const { 168 return env_->HasVar(kSandboxPIDNSEnvironmentVarName); 169 } 170 171 bool SetuidSandboxClient::IsInNewNETNamespace() const { 172 return env_->HasVar(kSandboxNETNSEnvironmentVarName); 173 } 174 175 bool SetuidSandboxClient::IsSandboxed() const { 176 return sandboxed_; 177 } 178 179 void SetuidSandboxClient::SetupLaunchEnvironment() { 180 SaveSUIDUnsafeEnvironmentVariables(env_); 181 SetSandboxAPIEnvironmentVariable(env_); 182 } 183 184 } // namespace sandbox 185 186