File: setuid_sandbox_host.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (204 lines) | stat: -rw-r--r-- 7,242 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "sandbox/linux/suid/client/setuid_sandbox_host.h"

#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include <array>
#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/launch.h"
#include "base/process/process_metrics.h"
#include "base/strings/cstring_view.h"
#include "base/strings/string_number_conversions.h"
#include "sandbox/linux/suid/common/sandbox.h"
#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"

namespace sandbox {

namespace {

// Set an environment variable that reflects the API version we expect from the
// setuid sandbox. Old versions of the sandbox will ignore this.
void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
  env->SetVar(kSandboxEnvironmentApiRequest,
              base::NumberToString(kSUIDSandboxApiNumber));
}

// Unset environment variables that are expected to be set by the setuid
// sandbox. This is to allow nesting of one instance of the SUID sandbox
// inside another.
void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) {
  DCHECK(env_map);
  const auto environment_vars = std::to_array<base::NativeEnvironmentString>({
      kSandboxDescriptorEnvironmentVarName,
      kSandboxHelperPidEnvironmentVarName,
      kSandboxEnvironmentApiProvides,
      kSandboxPIDNSEnvironmentVarName,
      kSandboxNETNSEnvironmentVarName,
  });

  for (size_t i = 0; i < std::size(environment_vars); ++i) {
    // Setting values in EnvironmentMap to an empty-string will make
    // sure that they get unset from the environment via AlterEnvironment().
    (*env_map)[environment_vars[i]] = base::NativeEnvironmentString();
  }
}

// Wrapper around a shared C function.
// Returns the "saved" environment variable name corresponding to |envvar|
// in a new string or NULL.
std::string* CreateSavedVariableName(base::cstring_view env_var) {
  char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var.c_str());
  if (!saved_env_var)
    return nullptr;
  std::string* saved_env_var_copy = new std::string(saved_env_var);
  // SandboxSavedEnvironmentVariable is the C function that we wrap and uses
  // malloc() to allocate memory.
  free(saved_env_var);
  return saved_env_var_copy;
}

// The ELF loader will clear many environment variables so we save them to
// different names here so that the SUID sandbox can resolve them for the
// renderer.
void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) {
  for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
    const base::cstring_view env_var(kSUIDUnsafeEnvironmentVariables[i]);
    // Get the saved environment variable corresponding to envvar.
    std::unique_ptr<std::string> saved_env_var(
        CreateSavedVariableName(env_var));
    if (!saved_env_var)
      continue;

    std::optional<std::string> value = env->GetVar(env_var);
    if (value.has_value()) {
      env->SetVar(*saved_env_var, *value);
    } else {
      env->UnSetVar(*saved_env_var);
    }
  }
}

const char* GetDevelSandboxPath() {
  return getenv("CHROME_DEVEL_SANDBOX");
}

}  // namespace

std::unique_ptr<SetuidSandboxHost> SetuidSandboxHost::Create() {
  // Private constructor.
  return base::WrapUnique(new SetuidSandboxHost(base::Environment::Create()));
}

SetuidSandboxHost::SetuidSandboxHost(std::unique_ptr<base::Environment> env)
    : env_(std::move(env)) {
  DCHECK(env_);
}

SetuidSandboxHost::~SetuidSandboxHost() = default;

// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables
// the setuid sandbox. TODO(jln): fix this (crbug.com/245376).
bool SetuidSandboxHost::IsDisabledViaEnvironment() {
  const char* devel_sandbox_path = GetDevelSandboxPath();
  return devel_sandbox_path && (*devel_sandbox_path == '\0');
}

base::FilePath SetuidSandboxHost::GetSandboxBinaryPath() {
  base::FilePath sandbox_binary;
  base::FilePath exe_dir;
  if (base::PathService::Get(base::DIR_EXE, &exe_dir)) {
    base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
    if (base::PathExists(sandbox_candidate))
      sandbox_binary = sandbox_candidate;
  }

  // In user-managed builds, including development builds, an environment
  // variable is required to enable the sandbox. See
  // https://chromium.googlesource.com/chromium/src/+/main/docs/linux/suid_sandbox_development.md
  struct stat st;
  if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 &&
      st.st_uid == getuid()) {
    const char* devel_sandbox_path = GetDevelSandboxPath();
    if (devel_sandbox_path) {
      sandbox_binary = base::FilePath(devel_sandbox_path);
    }
  }

  return sandbox_binary;
}

void SetuidSandboxHost::PrependWrapper(base::CommandLine* cmd_line) {
  std::string sandbox_binary(GetSandboxBinaryPath().value());
  struct stat st;
  if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) {
    LOG(FATAL) << "The SUID sandbox helper binary is missing: "
               << sandbox_binary
               << " Aborting now. See "
                  "https://chromium.googlesource.com/"
                  "chromium/src/+/master/docs/"
                  "linux/suid_sandbox_development.md.";
  }

  if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) ||
      ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) {
    LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
                  "configured correctly. Rather than run without sandboxing "
                  "I'm aborting now. You need to make sure that "
               << sandbox_binary << " is owned by root and has mode 4755.";
  }

  cmd_line->PrependWrapper(sandbox_binary);
}

void SetuidSandboxHost::SetupLaunchOptions(
    base::LaunchOptions* options,
    base::ScopedFD* dummy_fd) {
  DCHECK(options);

  // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used.
  options->allow_new_privs = true;
  UnsetExpectedEnvironmentVariables(&options->environment);

  // Set dummy_fd to the reading end of a closed pipe.
  int pipe_fds[2];
  PCHECK(0 == pipe(pipe_fds));
  PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1])));
  dummy_fd->reset(pipe_fds[0]);

  // We no longer need a dummy socket for discovering the child's PID,
  // but the sandbox is still hard-coded to expect a file descriptor at
  // kZygoteIdFd. Fixing this requires a sandbox API change. :(
  options->fds_to_remap.push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd));
}

void SetuidSandboxHost::SetupLaunchEnvironment() {
  SaveSUIDUnsafeEnvironmentVariables(env_.get());
  SetSandboxAPIEnvironmentVariable(env_.get());
}

}  // namespace sandbox