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