sal/osl/unx/file.cxx         |   46 ++++++++++++++++++++++++++++++++++---------
 sal/qa/osl/file/osl_File.cxx |   25 +++++++++++++++++++++++
 2 files changed, 62 insertions(+), 9 deletions(-)

New commits:
commit c3bdba781e8c9d61ca9e2a3d8d2eaca435b9aaad
Author:     Szymon Kłos <szymon.k...@collabora.com>
AuthorDate: Wed Nov 29 14:00:32 2023 +0100
Commit:     Szymon Kłos <szymon.k...@collabora.com>
CommitDate: Thu Nov 30 20:08:06 2023 +0100

    sal: osl::File allow to create files in sandbox
    
    "realpath" returns NULL for path which doesn't exist.
    Allow usage of non-existing paths if parent is allowed.
    This allows to successfully start the sandboxed kit.
    
    osl_setAllowedPaths will allow now to use:
    /foo/bar/nonexisting - previously it was ignored, is needed for LOK
    but /foo/bar/nonexisting/newlevel - still cannot be used
    
    isForbidden now checks parents of non-existing dir and assumes
    the same permissions, if parent doesn't exist - it tries with
    parent of parent, etc ...
    
    Change-Id: I1052747ca284d2f81dfd5c5fbf893936e7426220
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160111
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
index 0556be7fc97a..d5b76a09a47a 100644
--- a/sal/osl/unx/file.cxx
+++ b/sal/osl/unx/file.cxx
@@ -788,6 +788,18 @@ static std::vector<OString> allowedPathsRead;
 static std::vector<OString> allowedPathsReadWrite;
 static std::vector<OString> allowedPathsExecute;
 
+static OString getParentFolder(const OString &rFilePath)
+{
+    sal_Int32 n = rFilePath.lastIndexOf('/');
+    OString folderPath;
+    if (n < 1)
+        folderPath = ".";
+    else
+        folderPath = rFilePath.copy(0, n);
+
+    return folderPath;
+}
+
 SAL_DLLPUBLIC void osl_setAllowedPaths(
         rtl_uString *pustrFilePaths
     )
@@ -818,9 +830,25 @@ SAL_DLLPUBLIC void osl_setAllowedPaths(
         }
 
         char resolvedPath[PATH_MAX];
-        if (realpath(aPath.getStr(), resolvedPath))
+        bool isResolved = !!realpath(aPath.getStr(), resolvedPath);
+        bool notExists = !isResolved && errno == ENOENT;
+
+        if (notExists)
         {
-            OString aPushPath = OString(resolvedPath, strlen(resolvedPath));
+            sal_Int32 n = aPath.lastIndexOf('/');
+            OString folderPath = getParentFolder(aPath);
+            isResolved = !!realpath(folderPath.getStr(), resolvedPath);
+            notExists = !isResolved && errno == ENOENT;
+
+            if (notExists || !isResolved || strlen(resolvedPath) + 
aPath.getLength() - n + 1 >= PATH_MAX)
+                return; // too bad
+            else
+                strcat(resolvedPath, aPath.getStr() + n);
+        }
+
+        if (isResolved)
+        {
+            OString aPushPath(resolvedPath, strlen(resolvedPath));
             if (eType == 'r')
                 allowedPathsRead.push_back(aPushPath);
             else if (eType == 'w')
@@ -850,13 +878,13 @@ bool isForbidden(const OString &filePath, int nFlags)
         // fail to resolve. Thankfully our I/O APIs don't allow
         // symlink creation to race here.
         sal_Int32 n = filePath.lastIndexOf('/');
-        OString folderPath;
-        if (n < 1)
-            folderPath = ".";
-        else
-            folderPath = filePath.copy(0, n);
-        if (!realpath(folderPath.getStr(), resolvedPath) ||
-            strlen(resolvedPath) + filePath.getLength() - n + 1 >= PATH_MAX)
+        OString folderPath = getParentFolder(filePath);
+
+        bool isResolved = !!realpath(folderPath.getStr(), resolvedPath);
+        bool notExists = !isResolved && errno == ENOENT;
+        if (notExists) // folder doesn't exist, check parent, in the end of 
chain checks "."
+            return isForbidden(folderPath, nFlags);
+        else if (!isResolved || strlen(resolvedPath) + filePath.getLength() - 
n + 1 >= PATH_MAX)
             return true; // too bad
         else
             strcat(resolvedPath, filePath.getStr() + n);
diff --git a/sal/qa/osl/file/osl_File.cxx b/sal/qa/osl/file/osl_File.cxx
index 0703e89493b9..cdd72d45fa72 100644
--- a/sal/qa/osl/file/osl_File.cxx
+++ b/sal/qa/osl/file/osl_File.cxx
@@ -1352,6 +1352,19 @@ namespace osl_Forbidden
         void forbidden()
         {
             File::setAllowedPaths(maScratchGood);
+
+            // check some corner cases first
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("read bad should be forbidden",
+                                         true, File::isForbidden(".", 
osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("read bad should be forbidden",
+                                         true, File::isForbidden("", 
osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("read bad should be forbidden",
+                                         true, File::isForbidden("a", 
osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("read bad should be forbidden",
+                                         true, File::isForbidden("/", 
osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("read from non-existent should be 
allowed",
+                                         false, 
File::isForbidden(maScratchGood + "/notthere/file", osl_File_OpenFlag_Read));
+
             CPPUNIT_ASSERT_EQUAL_MESSAGE("read bad should be forbidden",
                                          true, File::isForbidden(maScratchBad, 
osl_File_OpenFlag_Read));
             CPPUNIT_ASSERT_EQUAL_MESSAGE("read from good should be allowed",
@@ -1376,6 +1389,18 @@ namespace osl_Forbidden
                                          true, 
File::isForbidden(maScratchGood, 0x80));
             CPPUNIT_ASSERT_EQUAL_MESSAGE("exec from bad should be allowed",
                                          false, 
File::isForbidden(maScratchBad, 0x80));
+
+            File::setAllowedPaths(":r:" + maScratchBad);
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to non-existent should be 
forbidden",
+                                         true, File::isForbidden(maScratchGood 
+ "/notthere", osl_File_OpenFlag_Write));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to non-existent should be 
forbidden 2",
+                                         true, File::isForbidden(maScratchGood 
+ "/notthere/file", osl_File_OpenFlag_Write));
+
+            File::setAllowedPaths(":r:" + maScratchBad + ":w:" + maScratchGood 
+ "/notthere");
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to non-existent should be 
allowed",
+                                         false, 
File::isForbidden(maScratchGood + "/notthere", osl_File_OpenFlag_Write));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to non-existent should be 
allowed 2",
+                                         false, 
File::isForbidden(maScratchGood + "/notthere/file", osl_File_OpenFlag_Write));
         }
 
         void open()

Reply via email to