summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-06 21:12:32 +0000
committercevans@chromium.org <cevans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-06 21:12:32 +0000
commit1d46a7eea9496c81d5371c32f88d0ec4ef36985a (patch)
treeb8e23768e4310126f52a51b301ae98973285a776
parent2287762d50d4506eabe05dfe7c08126f7a621b57 (diff)
downloadchromium_src-1d46a7eea9496c81d5371c32f88d0ec4ef36985a.zip
chromium_src-1d46a7eea9496c81d5371c32f88d0ec4ef36985a.tar.gz
chromium_src-1d46a7eea9496c81d5371c32f88d0ec4ef36985a.tar.bz2
Merge 79618 - Landing for Julien Tinnes, jln@google.com:---chroot to /proc instead of /tmp. This gets rid of a lot of unnecessarycomplexity and fixes a race condition.(Original idea from Markus)The chroot helper will chroot to /proc/self/fdinfo (or /proc/self/fd). This ispretty safe because access to this directory is protected by the ptrace() checkin the kernel and the helper is privileged.Moreover, as soon as the helper _exit() and becomes a zombie, the directorywill be empty. Zygote should wait() for us to make everything deterministric.We also export SBX_HELPER_PID so that Zygote can specifically wait for thehelper. ---BUG=76542R=markus,aglReview URL: http://codereview.chromium.org/6683056
TBR=cevans@chromium.org Review URL: http://codereview.chromium.org/6802024 git-svn-id: svn://svn.chromium.org/chrome/branches/696/src@80694 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--sandbox/linux/suid/sandbox.c175
1 files changed, 51 insertions, 124 deletions
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c
index d42474a..1cf95284 100644
--- a/sandbox/linux/suid/sandbox.c
+++ b/sandbox/linux/suid/sandbox.c
@@ -37,29 +37,8 @@
#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.
@@ -79,84 +58,35 @@ static void FatalError(const char *msg, ...) {
exit(1);
}
-static int CloneChrootHelperProcess() {
+// 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() {
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
perror("socketpair");
- return -1;
+ 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)
+ 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;
}
- }
-
- // 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);
@@ -165,7 +95,7 @@ static int CloneChrootHelperProcess() {
perror("clone");
close(sv[0]);
close(sv[1]);
- return -1;
+ return false;
}
if (pid == 0) {
@@ -196,18 +126,12 @@ static int CloneChrootHelperProcess() {
if (msg != kMsgChrootMe)
FatalError("Unknown message from sandboxed process");
- 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");
+ // sanity check
+ if (chdir(safedir))
+ FatalError("Cannot chdir into /proc/ directory");
- if (chroot(proc_self_fd_str))
- FatalError("Cannot chroot into temp directory");
+ if (chroot(safedir))
+ FatalError("Cannot chroot into /proc/ directory");
if (chdir("/"))
FatalError("Cannot chdir to / after chroot");
@@ -221,13 +145,11 @@ static int CloneChrootHelperProcess() {
FatalError("Writing reply");
_exit(0);
- }
-
- if (close(chroot_dir_fd)) {
- close(sv[0]);
- close(sv[1]);
- perror("close(chroot_dir_fd)");
- return false;
+ // 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(sv[0])) {
@@ -236,19 +158,10 @@ static int CloneChrootHelperProcess() {
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), "%d", chroot_signal_fd);
+ int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
if (printed < 0 || printed >= (int)sizeof(desc_str)) {
fprintf(stderr, "Failed to snprintf\n");
return false;
@@ -256,7 +169,21 @@ static bool SpawnChrootHelper() {
if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
perror("setenv");
- close(chroot_signal_fd);
+ 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]);
return false;
}