| Index: sandbox/linux/suid/sandbox.cc
|
| diff --git a/sandbox/linux/suid/sandbox.cc b/sandbox/linux/suid/sandbox.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..16ad531932d9dd7204078a70bb459b57dcea2d7b
|
| --- /dev/null
|
| +++ b/sandbox/linux/suid/sandbox.cc
|
| @@ -0,0 +1,213 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <asm/unistd.h>
|
| +#include <errno.h>
|
| +#include <sched.h>
|
| +#include <signal.h>
|
| +#include <stdarg.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +#include <sys/prctl.h>
|
| +#include <sys/resource.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/stat.h>
|
| +#include <sys/time.h>
|
| +#include <sys/types.h>
|
| +#include <unistd.h>
|
| +
|
| +#if !defined(CLONE_NEWPID)
|
| +#define CLONE_NEWPID 20000000
|
| +#endif
|
| +
|
| +static const char kSandboxPath[] = "/var/run/chrome-sandbox";
|
| +static const char kChromeBinary[] = "/opt/google/chrome/chrome";
|
| +
|
| +// These are the magic byte values which the sandboxed process uses to request
|
| +// that it be chrooted.
|
| +static const char kMsgChrootMe = 'C';
|
| +static const char kMsgChrootSuccessful = 'O';
|
| +
|
| +static void FatalError(const char *msg, ...) {
|
| + va_list ap;
|
| + va_start(ap, msg);
|
| +
|
| + vfprintf(stderr, msg, ap);
|
| + fprintf(stderr, ": %s\n", strerror(errno));
|
| + fflush(stderr);
|
| + _exit(1);
|
| +}
|
| +
|
| +static int CloneChrootHelperProcess() {
|
| + int sv[2];
|
| + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
|
| + perror("socketpair");
|
| + return -1;
|
| + }
|
| +
|
| + const pid_t pid = syscall(
|
| + __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
|
| +
|
| + if (pid == -1) {
|
| + perror("clone");
|
| + close(sv[0]);
|
| + close(sv[1]);
|
| + return -1;
|
| + }
|
| +
|
| + if (pid == 0) {
|
| + // We share our files structure with an untrusted process. As a security in
|
| + // depth measure, we make sure that we can't open anything by mistake.
|
| + // TODO: drop CAP_SYS_RESOURCE
|
| +
|
| + const struct rlimit nofile = {0, 0};
|
| + if (setrlimit(RLIMIT_NOFILE, &nofile))
|
| + FatalError("Setting RLIMIT_NOFILE");
|
| +
|
| + if (close(sv[1]))
|
| + FatalError("close");
|
| +
|
| + char msg;
|
| + ssize_t bytes;
|
| + do {
|
| + bytes = read(sv[0], &msg, 1);
|
| + } while (bytes == -1 && errno == EINTR);
|
| +
|
| + if (bytes == 0)
|
| + exit(0);
|
| + if (bytes != 1)
|
| + FatalError("read");
|
| +
|
| + if (msg != kMsgChrootMe)
|
| + FatalError("Unknown message from sandboxed process");
|
| +
|
| + if (chdir(kSandboxPath))
|
| + FatalError("Cannot chdir into %s", kSandboxPath);
|
| +
|
| + struct stat st;
|
| + if (stat("/", &st))
|
| + FatalError("stat");
|
| +
|
| + if (st.st_uid || st.st_gid || st.st_mode & S_IWOTH)
|
| + FatalError("Bad permissions on chroot directory (%s)", kSandboxPath);
|
| +
|
| + if (chroot(kSandboxPath))
|
| + FatalError("Cannot chroot into %s", kSandboxPath);
|
| +
|
| + if (chdir("/"))
|
| + FatalError("Cannot chdir to / after chroot");
|
| +
|
| + const char reply = kMsgChrootSuccessful;
|
| + do {
|
| + bytes = write(sv[0], &reply, 1);
|
| + } while (bytes == -1 && errno == EINTR);
|
| +
|
| + if (bytes != 1)
|
| + FatalError("Writing reply");
|
| +
|
| + exit(0);
|
| + }
|
| +
|
| + if (close(sv[0])) {
|
| + close(sv[1]);
|
| + perror("close");
|
| + return false;
|
| + }
|
| +
|
| + return sv[1];
|
| +}
|
| +
|
| +static bool SpawnChrootHelper() {
|
| + const int chroot_signal_fd = CloneChrootHelperProcess();
|
| +
|
| + if (chroot_signal_fd == -1)
|
| + return false;
|
| +
|
| + // In the parent process, we install an environment variable containing the
|
| + // number of the file descriptor.
|
| + char desc_str[64];
|
| + snprintf(desc_str, sizeof(desc_str), "%d", chroot_signal_fd);
|
| +
|
| + if (setenv("SBX_D", desc_str, 1)) {
|
| + perror("setenv");
|
| + close(chroot_signal_fd);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +static bool MoveToNewPIDNamespace() {
|
| + const pid_t pid = syscall(
|
| + __NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
|
| +
|
| + if (pid == -1)
|
| + return false;
|
| +
|
| + if (pid)
|
| + syscall(__NR_exit, 0);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +static bool DropRoot() {
|
| + if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
|
| + perror("prctl(PR_SET_DUMPABLE)");
|
| + return false;
|
| + }
|
| +
|
| + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
|
| + perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
|
| + return false;
|
| + }
|
| +
|
| + gid_t rgid, egid, sgid;
|
| + if (getresgid(&rgid, &egid, &sgid)) {
|
| + perror("getresgid");
|
| + return false;
|
| + }
|
| +
|
| + if (setresgid(rgid, rgid, rgid)) {
|
| + perror("setresgid");
|
| + return false;
|
| + }
|
| +
|
| + uid_t ruid, euid, suid;
|
| + if (getresuid(&ruid, &euid, &suid)) {
|
| + perror("getresuid");
|
| + return false;
|
| + }
|
| +
|
| + if (setresuid(ruid, ruid, ruid)) {
|
| + perror("setresuid");
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +int main(int argc, char **argv) {
|
| + if (argc == 1) {
|
| + fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
|
| + return 1;
|
| + }
|
| +
|
| + if (strcmp(argv[1], kChromeBinary)) {
|
| + fprintf(stderr, "This wrapper can only run %s!\n", kChromeBinary);
|
| + return 1;
|
| + }
|
| +
|
| + if (!MoveToNewPIDNamespace())
|
| + return 1;
|
| + if (!SpawnChrootHelper())
|
| + return 1;
|
| + if (!DropRoot())
|
| + return 1;
|
| +
|
| + execv(argv[1], &argv[1]);
|
| + FatalError("execv failed");
|
| +
|
| + return 1;
|
| +}
|
|
|