include/osl/file.h           |   48 ++++++++++++++++
 include/osl/file.hxx         |   52 ++++++++++++++++++
 sal/osl/unx/file.cxx         |  124 +++++++++++++++++++++++++++++++++++++++++++
 sal/osl/unx/file_impl.hxx    |    2 
 sal/osl/unx/file_misc.cxx    |   34 +++++++++++
 sal/osl/unx/file_stat.cxx    |    6 ++
 sal/osl/unx/file_volume.cxx  |    4 +
 sal/osl/unx/pipe.cxx         |    4 +
 sal/osl/unx/process.cxx      |    5 +
 sal/osl/unx/profile.cxx      |    5 +
 sal/qa/osl/file/osl_File.cxx |  118 ++++++++++++++++++++++++++++++++++++++++
 sal/util/sal.map             |    6 ++
 12 files changed, 407 insertions(+), 1 deletion(-)

New commits:
commit de81e7d933fbab6fe1546b513900e1c50a9aab38
Author:     Michael Meeks <[email protected]>
AuthorDate: Wed Nov 22 19:37:38 2023 +0000
Commit:     Michael Meeks <[email protected]>
CommitDate: Wed Nov 29 20:21:33 2023 +0100

    sal: initial osl::File sand-boxing commit for Unix.
    
    Change-Id: If2c106fef9640499b82b5cf350cb5169beb219fb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159838
    Reviewed-by: Szymon Kłos <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Michael Meeks <[email protected]>

diff --git a/include/osl/file.h b/include/osl/file.h
index 07d2beb2ae1f..98ad568550b9 100644
--- a/include/osl/file.h
+++ b/include/osl/file.h
@@ -960,6 +960,54 @@ SAL_DLLPUBLIC oslFileError SAL_CALL osl_readFile(
 SAL_DLLPUBLIC oslFileError SAL_CALL osl_isEndOfFile(
         oslFileHandle Handle, sal_Bool *pIsEOF );
 
+/** Resets the list of paths and associated permissions for osl
+
+    @param[in] pustrFileURLs
+    Contains a ':' delimited list of control strings and paths.
+    Control segments are a short path that refers to the following
+    segments and contain either:
+
+    r: read-only paths follow (the default)
+    w: read & write paths follow
+    x: executable paths follow
+
+    Any real paths (ie. having resolved all symlinks)
+    accessed outside of these paths will cause an
+    osl_File_E_ACCESS error in the relevant method calls.
+
+    This method is Unix specific.
+
+    @see osl_isForbiddenPath
+
+    @since LibreOffice 7.7
+*/
+SAL_DLLPUBLIC void SAL_CALL osl_setAllowedPaths(
+    rtl_uString *pustrFileURLs
+    );
+
+/**
+   Determine if the passed in path is not contained inside
+   the allowed paths, or if no allowed paths are set it
+   will not forbid any access.
+
+   @param[in] pustrFileURL
+   A URL (or path) that we want to check if it is forbidden
+   to access.
+
+   @param[in] nFlags
+   Flags to determine what type of access is requested,
+   osl_File_OpenFlag_Read | Write, or 0x80 for 'execute'.
+
+   This method is Unix specific.
+
+   @see osl_setAllowedPaths
+
+   @since LibreOffice 7.7
+ */
+SAL_DLLPUBLIC sal_Bool SAL_CALL osl_isForbiddenPath(
+    rtl_uString *pustrFileURL, int nFlags
+    );
+
 /** Write a number of bytes to a file.
 
     Writes a number of bytes to a file.
diff --git a/include/osl/file.hxx b/include/osl/file.hxx
index fcbabe96d674..6330247ba818 100644
--- a/include/osl/file.hxx
+++ b/include/osl/file.hxx
@@ -313,6 +313,58 @@ public:
 
         return static_cast< RC >( osl_createTempFile(pustr_dir_url, pHandle, 
ppustr_tmp_file_url) );
     }
+
+
+    /** Resets the list of paths and associated permissions for osl
+
+        @param[in] rPaths
+        Contains a ':' delimited list of control strings and paths.
+        Control segments are a short path that refers to the following
+        segments and contain either:
+
+        r: read-only paths follow (the default)
+        w: read & write paths follow
+        x: executable paths follow
+
+        Any real paths (ie. having resolved all symlinks)
+        accessed outside of these paths will cause an
+        osl_File_E_ACCESS error in the relevant method calls.
+
+        This method is Unix specific.
+
+        @see osl_isForbiddenPath
+
+        @since LibreOffice 7.7
+    */
+
+    static void setAllowedPaths(const OUString &rPaths)
+    {
+        osl_setAllowedPaths(rPaths.pData);
+    }
+
+    /**
+       Determine if the passed in path is not contained inside
+       the allowed paths, or if no allowed paths are set it
+       will not forbid any access.
+
+       @param[in] pustrFileURL
+       A URL (or path) that we want to check if it is forbidden
+       to access.
+
+       @param[in] nFlags
+       Flags to determine what type of access is requested,
+       osl_File_OpenFlag_Read | Write, or 0x80 for 'execute'.
+
+       This method is Unix specific.
+
+       @see osl_setAllowedPaths
+
+       @since LibreOffice 7.7
+    */
+    static bool isForbidden(const OUString &rPath, int nFlags)
+    {
+        return osl_isForbiddenPath(rPath.pData, nFlags);
+    }
 };
 
 
diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
index a39365b9b115..0556be7fc97a 100644
--- a/sal/osl/unx/file.cxx
+++ b/sal/osl/unx/file.cxx
@@ -783,6 +783,125 @@ static bool osl_file_queryLocking(sal_uInt32 uFlags)
     return false;
 }
 
+static bool abortOnForbidden = false;
+static std::vector<OString> allowedPathsRead;
+static std::vector<OString> allowedPathsReadWrite;
+static std::vector<OString> allowedPathsExecute;
+
+SAL_DLLPUBLIC void osl_setAllowedPaths(
+        rtl_uString *pustrFilePaths
+    )
+{
+    allowedPathsRead.clear();
+    allowedPathsReadWrite.clear();
+    allowedPathsExecute.clear();
+
+    if (!pustrFilePaths)
+        return;
+
+    char eType = 'r';
+    sal_Int32 nIndex = 0;
+    OUString aPaths(pustrFilePaths);
+    do
+    {
+        OString aPath = rtl::OUStringToOString(
+            aPaths.getToken(0, ':', nIndex),
+            RTL_TEXTENCODING_UTF8);
+
+        if (aPath.getLength() == 0)
+            continue;
+
+        if (aPath.getLength() == 1)
+        {
+            eType = aPath[0];
+            continue;
+        }
+
+        char resolvedPath[PATH_MAX];
+        if (realpath(aPath.getStr(), resolvedPath))
+        {
+            OString aPushPath = OString(resolvedPath, strlen(resolvedPath));
+            if (eType == 'r')
+                allowedPathsRead.push_back(aPushPath);
+            else if (eType == 'w')
+            {
+                allowedPathsRead.push_back(aPushPath);
+                allowedPathsReadWrite.push_back(aPushPath);
+            }
+            else if (eType == 'x')
+                allowedPathsExecute.push_back(aPushPath);
+        }
+    }
+    while (nIndex != -1);
+
+    abortOnForbidden = !!getenv("SAL_ABORT_ON_FORBIDDEN");
+}
+
+bool isForbidden(const OString &filePath, int nFlags)
+{
+    // avoid realpath cost unless configured
+    if (allowedPathsRead.size() == 0)
+        return false;
+
+    char resolvedPath[PATH_MAX];
+    if (!realpath(filePath.getStr(), resolvedPath))
+    {
+        // write calls path a non-existent path that realpath will
+        // 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)
+            return true; // too bad
+        else
+            strcat(resolvedPath, filePath.getStr() + n);
+    }
+
+    const std::vector<OString> *pCheckPaths = &allowedPathsRead;
+    if (nFlags & osl_File_OpenFlag_Write ||
+        nFlags & osl_File_OpenFlag_Create)
+        pCheckPaths = &allowedPathsReadWrite;
+    else if (nFlags & 0x80)
+        pCheckPaths = &allowedPathsExecute;
+
+    bool allowed = false;
+    for (const auto &it : *pCheckPaths) {
+        if (!strncmp(resolvedPath, it.getStr(), it.getLength()))
+        {
+            allowed = true;
+            break;
+        }
+    }
+
+    if (!allowed)
+        SAL_WARN("sal.osl", "access outside sandbox to " <<
+                 ((nFlags & osl_File_OpenFlag_Write ||
+                   nFlags & osl_File_OpenFlag_Create) ? "w" :
+                  (nFlags & 0x80) ? "x" : "r") << ":" <<
+                 filePath << " which is really " << resolvedPath <<
+                 (allowed ? " allowed " : " forbidden") <<
+                 " check list: " << pCheckPaths->size());
+
+    if (abortOnForbidden && !allowed)
+        abort(); // a bit abrupt - but don't try to escape.
+
+    return !allowed;
+}
+
+SAL_DLLPUBLIC sal_Bool SAL_CALL osl_isForbiddenPath(
+    rtl_uString *pustrFileURL, int nFlags
+    )
+{
+    return isForbidden(
+        rtl::OUStringToOString(OUString(pustrFileURL),
+                               RTL_TEXTENCODING_UTF8), nFlags);
+}
+
 #ifdef HAVE_O_EXLOCK
 #define OPEN_WRITE_FLAGS ( O_RDWR | O_EXLOCK | O_NONBLOCK )
 #define OPEN_CREATE_FLAGS ( O_CREAT | O_RDWR | O_EXLOCK | O_NONBLOCK )
@@ -987,6 +1106,10 @@ oslFileError openFilePath(const OString& filePath, 
oslFileHandle* pHandle,
     if (flags & O_EXCL && !(flags & O_CREAT))
         flags &= ~O_EXCL;
 
+    // Sandboxing hook
+    if (isForbidden( filePath, uFlags ))
+        return osl_File_E_ACCES;
+
     /* open the file */
     int fd = open_c( filePath, flags, mode );
     if (fd == -1)
@@ -1601,4 +1724,5 @@ oslFileError SAL_CALL osl_setFileSize(oslFileHandle 
Handle, sal_uInt64 uSize)
     return pImpl->setSize(uSize);
 }
 
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_impl.hxx b/sal/osl/unx/file_impl.hxx
index 2b374cb68ac0..3b4b5d58025b 100644
--- a/sal/osl/unx/file_impl.hxx
+++ b/sal/osl/unx/file_impl.hxx
@@ -41,6 +41,8 @@ struct DirectoryItem_Impl
     oslFileType getFileType() const;
 };
 
+bool isForbidden(const OString &filePath, int nFlags);
+
 oslFileError openFile(
     rtl_uString * pustrFileURL, oslFileHandle * pHandle, sal_uInt32 uFlags,
     mode_t mode);
diff --git a/sal/osl/unx/file_misc.cxx b/sal/osl/unx/file_misc.cxx
index 01842dc73154..dc9a41de9fc9 100644
--- a/sal/osl/unx/file_misc.cxx
+++ b/sal/osl/unx/file_misc.cxx
@@ -150,6 +150,9 @@ oslFileError SAL_CALL osl_openDirectory(rtl_uString* 
ustrDirectoryURL, oslDirect
 
     osl_systemPathRemoveSeparator(path.pData);
 
+    if (isForbidden(path.getStr(), osl_File_OpenFlag_Read))
+        return osl_File_E_ACCES;
+
 #ifdef MACOSX
     {
         auto const n = std::max(int(path.getLength() + 1), int(PATH_MAX));
@@ -353,6 +356,9 @@ oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* 
ustrFileURL, oslDirector
 
     osl_systemPathRemoveSeparator(strSystemPath.pData);
 
+    if (isForbidden(strSystemPath, osl_File_OpenFlag_Read))
+        return osl_File_E_ACCES;
+
     if (osl::access(strSystemPath, F_OK) == -1)
     {
         osl_error = oslTranslateFileError(errno);
@@ -436,6 +442,9 @@ oslFileError SAL_CALL osl_removeDirectory( rtl_uString* 
ustrDirectoryURL )
 
 oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
 {
+    if (isForbidden(pszPath, osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
     int nRet=0;
     int mode
         = (((flags & osl_File_OpenFlag_Read) == 0
@@ -465,6 +474,9 @@ oslFileError osl_psz_createDirectory(char const * pszPath, 
sal_uInt32 flags)
 
 static oslFileError osl_psz_removeDirectory( const char* pszPath )
 {
+    if (isForbidden(pszPath, osl_File_OpenFlag_Write))
+        return osl_File_E_ACCES;
+
     int nRet = rmdir(pszPath);
 
     if ( nRet < 0 )
@@ -560,6 +572,9 @@ oslFileError SAL_CALL osl_createDirectoryPath(
 
     osl::systemPathRemoveSeparator(sys_path);
 
+    if (isForbidden(sys_path.getStr(), osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
     // const_cast because sys_path is a local copy which we want to modify 
inplace instead of
     // copy it into another buffer on the heap again
     return create_dir_recursively_(sys_path.pData->buffer, 
aDirectoryCreationCallbackFunc, pData);
@@ -594,6 +609,10 @@ oslFileError SAL_CALL osl_moveFile( rtl_uString* 
ustrFileURL, rtl_uString* ustrD
     if( eRet != osl_File_E_None )
         return eRet;
 
+    if (isForbidden(srcPath, osl_File_OpenFlag_Read) ||
+        isForbidden(destPath, osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
 #ifdef MACOSX
     if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( 
destPath, PATH_MAX ) != 0 )
       return oslTranslateFileError( errno );
@@ -607,6 +626,10 @@ oslFileError SAL_CALL osl_replaceFile(rtl_uString* 
ustrFileURL, rtl_uString* ust
     int nGid = -1;
     char destPath[PATH_MAX];
     oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
+
+    if (isForbidden(destPath, osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
     if (eRet == osl_File_E_None)
     {
         struct stat aFileStat;
@@ -683,6 +706,9 @@ oslFileError SAL_CALL osl_removeFile(rtl_uString* 
ustrFileURL)
       return oslTranslateFileError(errno);
 #endif/* MACOSX */
 
+    if (isForbidden(path, osl_File_OpenFlag_Write))
+        return osl_File_E_ACCES;
+
     return osl_unlinkFile(path);
 }
 
@@ -738,6 +764,10 @@ static oslFileError osl_unlinkFile(const char* pszPath)
 
 static oslFileError osl_psz_moveFile(const char* pszPath, const char* 
pszDestPath)
 {
+    if (isForbidden(pszPath, osl_File_OpenFlag_Read) ||
+        isForbidden(pszDestPath, osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
     int nRet = rename(pszPath,pszDestPath);
 
     if (nRet < 0)
@@ -765,6 +795,10 @@ static oslFileError osl_psz_copyFile( const char* pszPath, 
const char* pszDestPa
     size_t nSourceSize=0;
     bool DestFileExists=true;
 
+    if (isForbidden(pszPath, osl_File_OpenFlag_Read) ||
+        isForbidden(pszDestPath, osl_File_OpenFlag_Create))
+        return osl_File_E_ACCES;
+
     /* mfe: does the source file really exists? */
     nRet = lstat_c(pszPath,&aFileStat);
 
diff --git a/sal/osl/unx/file_stat.cxx b/sal/osl/unx/file_stat.cxx
index 3b702905cb99..b695f6093a2d 100644
--- a/sal/osl/unx/file_stat.cxx
+++ b/sal/osl/unx/file_stat.cxx
@@ -273,6 +273,9 @@ static oslFileError osl_psz_setFileAttributes( const char* 
pszFilePath, sal_uInt
 
     OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), 
"osl_File_Attribute_Hidden doesn't work under Unix");
 
+    if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
+        return osl_File_E_ACCES;
+
     if (uAttributes & osl_File_Attribute_OwnRead)
         nNewMode |= S_IRUSR;
 
@@ -338,6 +341,9 @@ static oslFileError osl_psz_setFileTime (
     struct tm* pTM=0;
 #endif
 
+    if (isForbidden(pszFilePath, osl_File_OpenFlag_Write))
+        return osl_File_E_ACCES;
+
     nRet = lstat_c(pszFilePath,&aFileStat);
 
     if ( nRet < 0 )
diff --git a/sal/osl/unx/file_volume.cxx b/sal/osl/unx/file_volume.cxx
index 5e661bd656c3..07e5e08b4f50 100644
--- a/sal/osl/unx/file_volume.cxx
+++ b/sal/osl/unx/file_volume.cxx
@@ -27,6 +27,7 @@
 
 #include "file_error_transl.hxx"
 #include "file_url.hxx"
+#include "file_impl.hxx"
 #include "system.hxx"
 
 #include <errno.h>
@@ -199,6 +200,9 @@ static oslFileError osl_psz_getVolumeInformation (
     if (!pInfo)
         return osl_File_E_INVAL;
 
+    if (isForbidden(pszDirectory, osl_File_OpenFlag_Read))
+        return osl_File_E_ACCES;
+
     pInfo->uValidFields = 0;
     pInfo->uAttributes  = 0;
     pInfo->uTotalSpace = 0;
diff --git a/sal/osl/unx/pipe.cxx b/sal/osl/unx/pipe.cxx
index 45d31e00826b..9c91c49cf5cc 100644
--- a/sal/osl/unx/pipe.cxx
+++ b/sal/osl/unx/pipe.cxx
@@ -31,6 +31,7 @@
 
 #include "sockimpl.hxx"
 #include "secimpl.hxx"
+#include "file_impl.hxx"
 #include "unixerrnostring.hxx"
 
 #include <cassert>
@@ -208,6 +209,9 @@ static oslPipe osl_psz_createPipe(const char *pszPipeName, 
oslPipeOptions Option
 
     SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << 
name << "'");
 
+    if (isForbidden(name.getStr(), osl_File_OpenFlag_Create))
+        return nullptr;
+
     addr.sun_family = AF_UNIX;
     // coverity[fixed_size_dest : FALSE] - safe, see check above
     strcpy(addr.sun_path, name.getStr());
diff --git a/sal/osl/unx/process.cxx b/sal/osl/unx/process.cxx
index f75534fdc311..20a6844b8495 100644
--- a/sal/osl/unx/process.cxx
+++ b/sal/osl/unx/process.cxx
@@ -19,7 +19,7 @@
 
 #include <sal/config.h>
 #include <rtl/ustring.hxx>
-
+#include "file_impl.hxx"
 #include <cassert>
 
 /*
@@ -593,6 +593,9 @@ oslProcessError osl_psz_executeProcess(char *pszImageName,
         return osl_Process_E_NotFound;
     }
 
+    if (isForbidden(pszImageName, 0x80 /* execute */))
+        return osl_Process_E_NoPermission;
+
     Data.m_pszArgs[0] = strdup(pszImageName);
     Data.m_pszArgs[1] = nullptr;
 
diff --git a/sal/osl/unx/profile.cxx b/sal/osl/unx/profile.cxx
index 354fe626f390..8f0422fb71b0 100644
--- a/sal/osl/unx/profile.cxx
+++ b/sal/osl/unx/profile.cxx
@@ -20,12 +20,14 @@
 #include "system.hxx"
 #include "readwrite_helper.hxx"
 #include "file_url.hxx"
+#include "file_impl.hxx"
 #include "unixerrnostring.hxx"
 
 #include <osl/diagnose.h>
 #include <osl/profile.h>
 #include <osl/process.h>
 #include <osl/thread.h>
+#include <osl/file.h>
 #include <rtl/alloc.h>
 #include <sal/log.hxx>
 
@@ -934,6 +936,9 @@ static osl_TFile* openFileImpl(const char* pszFilename, 
oslProfileOption Profile
     osl_TFile* pFile = static_cast<osl_TFile*>(calloc(1, sizeof(osl_TFile)));
     bool       bWriteable = false;
 
+    if ( isForbidden( pszFilename, osl_File_OpenFlag_Write ) )
+        return nullptr;
+
     if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
     {
         bWriteable = true;
diff --git a/sal/qa/osl/file/osl_File.cxx b/sal/qa/osl/file/osl_File.cxx
index 718fb65423d1..0703e89493b9 100644
--- a/sal/qa/osl/file/osl_File.cxx
+++ b/sal/qa/osl/file/osl_File.cxx
@@ -1317,6 +1317,124 @@ namespace osl_FileBase
     CPPUNIT_REGISTRY_ADD_TO_DEFAULT("osl_osl::FileBase");
 }
 
+#if (defined UNX)
+
+namespace osl_Forbidden
+{
+
+    class Forbidden : public CppUnit::TestFixture
+    {
+        OUString maScratchBad;
+        OUString maScratchGood;
+    public:
+        void setUp() override
+        {
+            // create a directory to play in
+            createTestDirectory(aTmpName3);
+            OUString aBadURL = aTmpName3 + "/bad";
+            OUString aGoodURL = aTmpName3 + "/good";
+            createTestDirectory(aBadURL);
+            createTestDirectory(aGoodURL);
+            File::getSystemPathFromFileURL(aBadURL, maScratchBad);
+            File::getSystemPathFromFileURL(aGoodURL, maScratchGood);
+        }
+
+        void tearDown() override
+        {
+            osl_setAllowedPaths(nullptr);
+            OUString aBadURL = aTmpName3 + "/bad";
+            OUString aGoodURL = aTmpName3 + "/good";
+            deleteTestDirectory(aBadURL);
+            deleteTestDirectory(aGoodURL);
+            deleteTestDirectory(aTmpName3);
+        }
+
+        void forbidden()
+        {
+            File::setAllowedPaths(maScratchGood);
+            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",
+                                         false, 
File::isForbidden(maScratchGood, osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to good should be forbidden",
+                                         true, 
File::isForbidden(maScratchGood, osl_File_OpenFlag_Write));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("create in good should be forbidden",
+                                         true, 
File::isForbidden(maScratchGood, osl_File_OpenFlag_Create));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("exec from good should be forbidden",
+                                         true, 
File::isForbidden(maScratchGood, 0x80));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to non-existent should be 
forbidden",
+                                         true, File::isForbidden(maScratchBad 
+ "/notthere", osl_File_OpenFlag_Write));
+
+            File::setAllowedPaths("w:" + maScratchGood + ":x:" + maScratchBad);
+            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", 
// w implies 'r'
+                                         false, 
File::isForbidden(maScratchGood, osl_File_OpenFlag_Read));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("write to good should be allowed",
+                                         false, 
File::isForbidden(maScratchGood, osl_File_OpenFlag_Write));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("exec from good should be forbidden",
+                                         true, 
File::isForbidden(maScratchGood, 0x80));
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("exec from bad should be allowed",
+                                         false, 
File::isForbidden(maScratchBad, 0x80));
+        }
+
+        void open()
+        {
+            File::setAllowedPaths(maScratchGood);
+            File testFile(maScratchBad + "/open");
+            auto nError1 = testFile.open(osl_File_OpenFlag_Read | 
osl_File_OpenFlag_Write);
+            CPPUNIT_ASSERT_EQUAL_MESSAGE("disabled path allowed", 
osl::FileBase::E_ACCES, nError1);
+            deleteTestFile(testFile.getURL());
+        }
+
+        void copy()
+        {
+            File::setAllowedPaths("w:" + maScratchGood);
+            File testGood(maScratchGood + "/good");
+            File testGoodTo(maScratchGood + "/good_to");
+            File testBad(maScratchBad + "/bad");
+
+            auto nError1 = testGood.open(osl_File_OpenFlag_Create);
+            CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, nError1);
+
+            auto nErrorCopy = File::copy(maScratchGood + "/good", 
maScratchGood + "/good_to");
+            CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, nErrorCopy);
+
+            auto nErrorCopyBad = File::copy(maScratchGood + "/good_to", 
maScratchBad + "/bad");
+            CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_ACCES, nErrorCopyBad);
+
+            deleteTestFile(maScratchGood + "/good_to");
+            deleteTestFile(maScratchGood + "/good");
+        }
+
+        void nextTests()
+        {
+            // more entry points to test
+#if 0
+            auto nError1 = File::move(aTmpName4, aCanURL1);
+            auto nError2 = File::remove(aTmpName4);
+            auto nError3 = File::setAttributes(aTmpName6, 
osl_File_Attribute_ReadOnly);
+            bool bOk = osl_getSystemTime(pTV_current);
+            CPPUNIT_ASSERT(bOk);
+            auto nError4 = File::setTime(aTmpName6, *pTV_current, 
*pTV_current, *pTV_current);
+            CPPUNIT_ASSERT_EQUAL_MESSAGE(errorToStr(nError2).getStr(), 
osl::FileBase::E_None, nError2);
+#endif
+        }
+
+        CPPUNIT_TEST_SUITE(Forbidden);
+        CPPUNIT_TEST(forbidden);
+//        CPPUNIT_TEST(open);
+//        CPPUNIT_TEST(copy);
+//        CPPUNIT_TEST(nextTests);
+        CPPUNIT_TEST_SUITE_END();
+    };
+
+    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Forbidden::Forbidden, 
"osl_Forbidden");
+
+    CPPUNIT_REGISTRY_ADD_TO_DEFAULT("osl_Forbidden");
+}
+#endif
+
 namespace osl_FileStatus
 {
     //  testing the method
diff --git a/sal/util/sal.map b/sal/util/sal.map
index c5c3e4d55641..586a41ee997d 100644
--- a/sal/util/sal.map
+++ b/sal/util/sal.map
@@ -761,6 +761,12 @@ PRIVATE_1.8 { # LibreOffice 7.3
         rtl_uString_newReplaceStrAtUtf16L;
 } PRIVATE_1.7;
 
+PRIVATE_1.9 { # LibreOffice 7.7
+    global:
+        osl_setAllowedPaths;
+        osl_isForbiddenPath;
+} PRIVATE_1.8;
+
 PRIVATE_textenc.1 { # LibreOffice 3.6
     global:
         _ZN3sal6detail7textenc20convertCharToUnicode*;

Reply via email to