Bug 1416016 - Resolve symlinks in the file broker as a last resort. r=jld a=jcristau
authorGian-Carlo Pascutto <gcp@mozilla.com>
Fri, 09 Mar 2018 21:14:39 +0100
changeset 462635 4ef3cb563636756a8ea649e2b2ed1bd7e8a460b2
parent 462634 d9668297a5189515fbcd886ba7930c1fba931fab
child 462636 99bb39ead9042e058ac12c17c01aeb6e572200ac
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjld, jcristau
bugs1416016
milestone60.0
Bug 1416016 - Resolve symlinks in the file broker as a last resort. r=jld a=jcristau MozReview-Commit-ID: B7OMyARk9u8
security/sandbox/linux/broker/SandboxBroker.cpp
security/sandbox/linux/broker/SandboxBroker.h
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -541,27 +541,33 @@ DoConnect(const char* aPath, size_t aLen
   if (connect(fd, reinterpret_cast<struct sockaddr*>(&sun), sizeof(sun)) < 0) {
     close(fd);
     return -1;
   }
   return fd;
 }
 
 size_t
-SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen)
+SandboxBroker::RealPath(char* aPath, size_t aBufSize, size_t aPathLen)
+{
+  char* result = realpath(aPath, nullptr);
+  if (result != nullptr) {
+    base::strlcpy(aPath, result, aBufSize);
+    free(result);
+    // Size changed, but guaranteed to be 0 terminated
+    aPathLen = strlen(aPath);
+  }
+  return aPathLen;
+}
+
+size_t
+SandboxBroker::ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen)
 {
   if (strstr(aPath, "..") != nullptr) {
-    char* result = realpath(aPath, nullptr);
-    if (result != nullptr) {
-      base::strlcpy(aPath, result, aBufSize);
-      free(result);
-      // Size changed, but guaranteed to be 0 terminated
-      aPathLen = strlen(aPath);
-    }
-    // ValidatePath will handle failure to translate
+    return RealPath(aPath, aBufSize, aPathLen);
   }
   return aPathLen;
 }
 
 size_t
 SandboxBroker::RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen)
 {
   nsAutoCString path(aPath);
@@ -779,42 +785,52 @@ SandboxBroker::ThreadMain(void)
       // We do not assume the second path is 0-terminated, this is
       // enforced below.
       strncpy(pathBuf2, recvBuf + first_len + 1, kMaxPathLen + 1);
 
       // First string is guaranteed to be 0-terminated.
       pathLen = first_len;
 
       // Look up the first pathname but first translate relative paths.
-      pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen);
+      pathLen = ConvertRelativePath(pathBuf, sizeof(pathBuf), pathLen);
       perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
 
       // We don't have permissions on the requested dir.
       if (!perms) {
         // Was it a tempdir that we can remap?
         pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
         perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
         if (!perms) {
           // Did we arrive from a symlink in a path that is not writable?
           // Then try to figure out the original path and see if that is
           // readable. Work on the original path, this reverses
-          // ConvertToRealPath above.
+            // ConvertRelative above.
           int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
           if (symlinkPerms > 0) {
             perms = symlinkPerms;
           }
+          if (!perms) {
+            // Now try the opposite case: translate symlinks to their
+            // actual destination file. Firefox always resolves symlinks,
+            // and in most cases we have whitelisted fixed paths that
+            // libraries will rely on and try to open. So this codepath
+            // is mostly useful for Mesa which had its kernel interface
+            // moved around.
+            pathLen = RealPath(pathBuf, sizeof(pathBuf), pathLen);
+            perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
+          }
         }
       }
 
       // Same for the second path.
       pathLen2 = strnlen(pathBuf2, kMaxPathLen);
       if (pathLen2 > 0) {
         // Force 0 termination.
         pathBuf2[pathLen2] = '\0';
-        pathLen2 = ConvertToRealPath(pathBuf2, sizeof(pathBuf2), pathLen2);
+        pathLen2 = ConvertRelativePath(pathBuf2, sizeof(pathBuf2), pathLen2);
         int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2));
 
         // Take the intersection of the permissions for both paths.
         perms &= perms2;
       }
     } else {
       // Failed to receive intelligible paths.
       perms = 0;
--- a/security/sandbox/linux/broker/SandboxBroker.h
+++ b/security/sandbox/linux/broker/SandboxBroker.h
@@ -140,17 +140,18 @@ class SandboxBroker final
   PathMap mSymlinkMap;
 
   SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid,
                 int& aClientFd);
   void ThreadMain(void) override;
   void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath);
   void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath);
   // Remap relative paths to absolute paths.
-  size_t ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen);
+  size_t ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen);
+  size_t RealPath(char* aPath, size_t aBufSize, size_t aPathLen);
   // Remap references to /tmp and friends to the content process tempdir
   size_t RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen);
   nsCString ReverseSymlinks(const nsACString& aPath);
   // Retrieves permissions for the path the original symlink sits in.
   int SymlinkPermissions(const char* aPath, const size_t aPathLen);
   // In SandboxBrokerRealPath.cpp
   char* SymlinkPath(const Policy* aPolicy, const char* __restrict aPath,
                     char* __restrict aResolved, int* aPermission);