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()