Bug 1386279 - Renovate Linux sandbox file broker handling of access(). r=gcp
authorJed Davis <jld@mozilla.com>
Tue, 08 Aug 2017 18:02:31 -0600 (2017-08-09)
changeset 373707 c0838ed418422fa3ae21a6471bc33716dfc704f6
parent 373706 9499e1097909f47eb42c3e50c836eec8d37d6fb0
child 373708 7bafc0f1dbe498827fcad7089885f09444adc3ec
push id32308
push userarchaeopteryx@coole-files.de
push dateThu, 10 Aug 2017 15:20:33 +0000 (2017-08-10)
treeherdermozilla-central@5322c03f4c85 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp
bugs1386279, 1382246
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1386279 - Renovate Linux sandbox file broker handling of access(). r=gcp 1. X_OK is now allowed, and is limited only by the MAY_ACCESS permission. 2. The actual access() syscall is now used, if access is granted by the broker policy. This fixed bug 1382246, which explains the background. MozReview-Commit-ID: 926429PlBnL
security/sandbox/linux/broker/SandboxBroker.cpp
security/sandbox/linux/gtest/TestBroker.cpp
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -338,17 +338,17 @@ AllowOperation(int aReqFlags, int aPerms
     needed |= SandboxBroker::MAY_CREATE;
   }
   return (aPerms & needed) == needed;
 }
 
 static bool
 AllowAccess(int aReqFlags, int aPerms)
 {
-  if (aReqFlags & ~(R_OK|W_OK|F_OK)) {
+  if (aReqFlags & ~(R_OK|W_OK|X_OK|F_OK)) {
     return false;
   }
   int needed = 0;
   if (aReqFlags & R_OK) {
     needed |= SandboxBroker::MAY_READ;
   }
   if (aReqFlags & W_OK) {
     needed |= SandboxBroker::MAY_WRITE;
@@ -657,27 +657,17 @@ SandboxBroker::ThreadMain(void)
           }
         } else {
           AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
         }
         break;
 
       case SANDBOX_FILE_ACCESS:
         if (permissive || AllowAccess(req.mFlags, perms)) {
-          // This can't use access() itself because that uses the ruid
-          // and not the euid.  In theory faccessat() with AT_EACCESS
-          // would work, but Linux doesn't actually implement the
-          // flags != 0 case; glibc has a hack which doesn't even work
-          // in this case so it'll ignore the flag, and Bionic just
-          // passes through the syscall and always ignores the flags.
-          //
-          // Instead, because we've already checked the requested
-          // r/w/x bits against the policy, just return success if the
-          // file exists and hope that's close enough.
-          if (stat(pathBuf, (struct stat*)&respBuf) == 0) {
+          if (access(pathBuf, req.mFlags) == 0) {
             resp.mError = 0;
           } else {
             resp.mError = -errno;
           }
         } else {
           AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
         }
         break;
--- a/security/sandbox/linux/gtest/TestBroker.cpp
+++ b/security/sandbox/linux/gtest/TestBroker.cpp
@@ -128,16 +128,18 @@ SandboxBrokerTest::GetPolicy() const
 
   policy->AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways);
   policy->AddPath(MAY_READ, "/dev/zero", AddAlways);
   policy->AddPath(MAY_READ, "/var/empty/qwertyuiop", AddAlways);
   policy->AddPath(MAY_ACCESS, "/proc/self", AddAlways); // Warning: Linux-specific.
   policy->AddPath(MAY_READ | MAY_WRITE, "/tmp", AddAlways);
   policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublu", AddAlways);
   policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublublu", AddAlways);
+  // This should be non-writable by the user running the test:
+  policy->AddPath(MAY_READ | MAY_WRITE, "/etc", AddAlways);
 
   return Move(policy);
 }
 
 TEST_F(SandboxBrokerTest, OpenForRead)
 {
   int fd;
 
@@ -201,16 +203,24 @@ TEST_F(SandboxBrokerTest, Access)
 
   EXPECT_EQ(-ENOENT, Access("/var/empty/qwertyuiop", R_OK));
   EXPECT_EQ(-EACCES, Access("/var/empty/qwertyuiop", W_OK));
 
   EXPECT_EQ(0, Access("/proc/self", F_OK));
   EXPECT_EQ(-EACCES, Access("/proc/self", R_OK));
 
   EXPECT_EQ(-EACCES, Access("/proc/self/stat", F_OK));
+
+  EXPECT_EQ(0, Access("/tmp", X_OK));
+  EXPECT_EQ(0, Access("/tmp", R_OK|X_OK));
+  EXPECT_EQ(0, Access("/tmp", R_OK|W_OK|X_OK));
+  EXPECT_EQ(0, Access("/proc/self", X_OK));
+
+  EXPECT_EQ(0, Access("/etc", R_OK|X_OK));
+  EXPECT_EQ(-EACCES, Access("/etc", W_OK));
 }
 
 TEST_F(SandboxBrokerTest, Stat)
 {
   statstruct realStat, brokeredStat;
   ASSERT_EQ(0, statsyscall("/dev/null", &realStat)) << "Shouldn't ever fail!";
   EXPECT_EQ(0, Stat("/dev/null", &brokeredStat));
   EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
@@ -252,20 +262,17 @@ TEST_F(SandboxBrokerTest, Chmod)
 {
   PrePostTestCleanup();
 
   int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
   ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
   close(fd);
   // Set read only. SandboxBroker enforces 0600 mode flags.
   ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR));
-  // SandboxBroker doesn't use real access(), it just checks against
-  // the policy. So it can't see the change in permisions here.
-  // This won't work:
-  // EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK));
+  EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK));
   statstruct realStat;
   EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
   EXPECT_EQ((mode_t)S_IRUSR, realStat.st_mode & 0777);
 
   ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR | S_IWUSR));
   EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
   EXPECT_EQ((mode_t)(S_IRUSR | S_IWUSR), realStat.st_mode & 0777);
   EXPECT_EQ(0, unlink("/tmp/blublu"));