diff options
author | laforge@chromium.org <laforge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-30 18:30:24 +0000 |
---|---|---|
committer | laforge@chromium.org <laforge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-30 18:30:24 +0000 |
commit | 0e00451fcfc8cb9974abd21f45bec77082ecce44 (patch) | |
tree | a38ad351f6bf5d71870799cf0c924ced7dfa5337 /sandbox/linux | |
parent | a0b23d89112857c91aaa991465166a59993a15cf (diff) | |
download | chromium_src-0e00451fcfc8cb9974abd21f45bec77082ecce44.zip chromium_src-0e00451fcfc8cb9974abd21f45bec77082ecce44.tar.gz chromium_src-0e00451fcfc8cb9974abd21f45bec77082ecce44.tar.bz2 |
Revert 79618 - Landing for Julien Tinnes, jln@google.com:
---
chroot to /proc instead of /tmp. This gets rid of a lot of unnecessary
complexity and fixes a race condition.
(Original idea from Markus)
The chroot helper will chroot to /proc/self/fdinfo (or /proc/self/fd). This is
pretty safe because access to this directory is protected by the ptrace() check
in the kernel and the helper is privileged.
Moreover, as soon as the helper _exit() and becomes a zombie, the directory
will be empty. Zygote should wait() for us to make everything deterministric.
We also export SBX_HELPER_PID so that Zygote can specifically wait for the
helper.
---
BUG=76542
R=markus,agl
Review URL: http://codereview.chromium.org/6683056
TBR=cevans@chromium.org
Review URL: http://codereview.chromium.org/6675053
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79867 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/linux')
-rw-r--r-- | sandbox/linux/suid/sandbox.c | 175 |
1 files changed, 124 insertions, 51 deletions
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c index 1cf95284..d42474a 100644 --- a/sandbox/linux/suid/sandbox.c +++ b/sandbox/linux/suid/sandbox.c @@ -37,8 +37,29 @@ #define CLONE_NEWNET 0x40000000 #endif +#if !defined(BTRFS_SUPER_MAGIC) +#define BTRFS_SUPER_MAGIC 0x9123683E +#endif +#if !defined(EXT2_SUPER_MAGIC) +#define EXT2_SUPER_MAGIC 0xEF53 +#endif +#if !defined(EXT3_SUPER_MAGIC) +#define EXT3_SUPER_MAGIC 0xEF53 +#endif +#if !defined(EXT4_SUPER_MAGIC) +#define EXT4_SUPER_MAGIC 0xEF53 +#endif +#if !defined(REISERFS_SUPER_MAGIC) +#define REISERFS_SUPER_MAGIC 0x52654973 +#endif +#if !defined(TMPFS_MAGIC) +#define TMPFS_MAGIC 0x01021994 +#endif +#if !defined(XFS_SUPER_MAGIC) +#define XFS_SUPER_MAGIC 0x58465342 +#endif + static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; -static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; // These are the magic byte values which the sandboxed process uses to request // that it be chrooted. @@ -58,35 +79,84 @@ static void FatalError(const char *msg, ...) { exit(1); } -// We will chroot() to the helper's /proc/self directory. Anything there will -// not exist anymore if we make sure to wait() for the helper. -// -// /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty -// even if the helper survives as a zombie. -// -// There is very little reason to use fdinfo/ instead of fd/ but we are -// paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/ -#define SAFE_DIR "/proc/self/fdinfo" -#define SAFE_DIR2 "/proc/self/fd" - -static bool SpawnChrootHelper() { +static int CloneChrootHelperProcess() { int sv[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { perror("socketpair"); - return false; + return -1; } - char *safedir = NULL; - struct stat sdir_stat; - if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) - safedir = SAFE_DIR; - else - if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) - safedir = SAFE_DIR2; - else { - fprintf(stderr, "Could not find %s\n", SAFE_DIR2); - return false; + // Some people mount /tmp on a non-POSIX filesystem (e.g. NFS). This + // breaks all sorts of assumption in our code. So, if we don't recognize the + // filesystem, we will try to use an alternative location for our temp + // directory. + char tempDirectoryTemplate[80] = "/tmp/chrome-sandbox-chroot-XXXXXX"; + struct statfs sfs; + if (!statfs("/tmp", &sfs) && + (unsigned long)sfs.f_type != BTRFS_SUPER_MAGIC && + (unsigned long)sfs.f_type != EXT2_SUPER_MAGIC && + (unsigned long)sfs.f_type != EXT3_SUPER_MAGIC && + (unsigned long)sfs.f_type != EXT4_SUPER_MAGIC && + (unsigned long)sfs.f_type != REISERFS_SUPER_MAGIC && + (unsigned long)sfs.f_type != TMPFS_MAGIC && + (unsigned long)sfs.f_type != XFS_SUPER_MAGIC) { + // If /dev/shm exists, it is supposed to be a tmpfs filesystem. While we + // are not actually using it for shared memory, moving our temp directory + // into a known tmpfs filesystem is preferable over using a potentially + // unreliable non-POSIX filesystem. + if (!statfs("/dev/shm", &sfs) && sfs.f_type == TMPFS_MAGIC) { + *tempDirectoryTemplate = '\000'; + strncat(tempDirectoryTemplate, "/dev/shm/chrome-sandbox-chroot-XXXXXX", + sizeof(tempDirectoryTemplate) - 1); + } else { + // Neither /tmp is a well-known POSIX filesystem, nor /dev/shm is a + // tmpfs. After all, we now use /tmp as the location of our temp + // directory, but we quite likely fail the moment we try to access it + // through chroot_dir_fd. If so, we will print a verbose error message + // (see below) } + } + + // We create a temp directory for our chroot. Nobody should ever write into + // it, so it's root:root mode 000. + const char* temp_dir = mkdtemp(tempDirectoryTemplate); + if (!temp_dir) { + perror("Failed to create temp directory for chroot"); + return -1; + } + + const int chroot_dir_fd = open(temp_dir, O_DIRECTORY | O_RDONLY); + if (chroot_dir_fd < 0) { + rmdir(temp_dir); + perror("Failed to open chroot temp directory"); + return -1; + } + + if (rmdir(temp_dir)) { + perror("rmdir"); + return -1; + } + + char proc_self_fd_str[128]; + int printed = snprintf(proc_self_fd_str, sizeof(proc_self_fd_str), + "/proc/self/fd/%d", chroot_dir_fd); + if (printed < 0 || printed >= (int)sizeof(proc_self_fd_str)) { + fprintf(stderr, "Error in snprintf"); + return -1; + } + + if (fchown(chroot_dir_fd, 0 /* root */, 0 /* root */)) { + fprintf(stderr, "Could not set up sandbox work directory. Maybe, /tmp is " + "a non-POSIX filesystem and /dev/shm doesn't exist " + "either. Consider mounting a \"tmpfs\" on /tmp.\n"); + return -1; + } + + if (fchmod(chroot_dir_fd, 0000 /* no-access */)) { + perror("fchmod"); + return -1; + } + const pid_t pid = syscall( __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); @@ -95,7 +165,7 @@ static bool SpawnChrootHelper() { perror("clone"); close(sv[0]); close(sv[1]); - return false; + return -1; } if (pid == 0) { @@ -126,12 +196,18 @@ static bool SpawnChrootHelper() { if (msg != kMsgChrootMe) FatalError("Unknown message from sandboxed process"); - // sanity check - if (chdir(safedir)) - FatalError("Cannot chdir into /proc/ directory"); + if (fchdir(chroot_dir_fd)) + FatalError("Cannot chdir into chroot temp directory"); + + struct stat st; + if (fstat(chroot_dir_fd, &st)) + FatalError("stat"); + + if (st.st_uid || st.st_gid || st.st_mode & 0777) + FatalError("Bad permissions on chroot temp directory"); - if (chroot(safedir)) - FatalError("Cannot chroot into /proc/ directory"); + if (chroot(proc_self_fd_str)) + FatalError("Cannot chroot into temp directory"); if (chdir("/")) FatalError("Cannot chdir to / after chroot"); @@ -145,11 +221,13 @@ static bool SpawnChrootHelper() { FatalError("Writing reply"); _exit(0); - // We now become a zombie. /proc/self/fd(info) is now an empty dir and we - // are chrooted there. - // Our (unprivileged) parent should not even be able to open "." or "/" - // since they would need to pass the ptrace() check. If our parent wait() - // for us, our root directory will completely disappear. + } + + if (close(chroot_dir_fd)) { + close(sv[0]); + close(sv[1]); + perror("close(chroot_dir_fd)"); + return false; } if (close(sv[0])) { @@ -158,10 +236,19 @@ static bool SpawnChrootHelper() { 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]; - int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]); + int printed = snprintf(desc_str, sizeof(desc_str), "%d", chroot_signal_fd); if (printed < 0 || printed >= (int)sizeof(desc_str)) { fprintf(stderr, "Failed to snprintf\n"); return false; @@ -169,21 +256,7 @@ static bool SpawnChrootHelper() { if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) { perror("setenv"); - close(sv[1]); - return false; - } - - // We also install an environment variable containing the pid of the child - char helper_pid_str[64]; - printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid); - if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) { - fprintf(stderr, "Failed to snprintf\n"); - return false; - } - - if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { - perror("setenv"); - close(sv[1]); + close(chroot_signal_fd); return false; } |