Skip to content

Commit

Permalink
Merge pull request #6343 from nextcloud/feature/applyPermissionsFromS…
Browse files Browse the repository at this point in the history
…erverToFolders

newly created folders will be read-only when needed
  • Loading branch information
mgallien authored Mar 12, 2024
2 parents ea9c19e + ff9953b commit 61251cd
Show file tree
Hide file tree
Showing 17 changed files with 668 additions and 82 deletions.
16 changes: 8 additions & 8 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: qt-5.15

steps:
- name: cmake
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand All @@ -13,7 +13,7 @@ steps:
- cmake -G Ninja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DADD_E2E_TESTS=ON -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64 ../src

- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand All @@ -22,7 +22,7 @@ steps:
- ninja

- name: test
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand Down Expand Up @@ -74,23 +74,23 @@ name: qt-5.15-clang

steps:
- name: cmake
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
commands:
- cd /drone/build
- cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=clang-14 -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_BUILD_TYPE=Debug -DQUICK_COMPILER=ON -DBUILD_UPDATER=ON -DBUILD_TESTING=1 -DADD_E2E_TESTS=ON -DECM_ENABLE_SANITIZERS=address -DCMAKE_CXX_FLAGS=-Werror -DOPENSSL_ROOT_DIR=/usr/local/lib64 ../src
- name: compile
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
commands:
- cd /drone/build
- ninja
- name: test
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
image: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
volumes:
- name: build
path: /drone/build
Expand Down Expand Up @@ -142,7 +142,7 @@ name: AppImage

steps:
- name: build
image: ghcr.io/nextcloud/continuous-integration-client-appimage:client-appimage-9
image: ghcr.io/nextcloud/continuous-integration-client-appimage:client-appimage-10
environment:
CI_UPLOAD_GIT_TOKEN:
from_secret: CI_UPLOAD_GIT_TOKEN
Expand Down Expand Up @@ -196,6 +196,6 @@ trigger:
- push
---
kind: signature
hmac: d72110d7f9cba086ca21f9f4f4032ae87f3d9555ab4c5f55d3aeb3df99cab540
hmac: a8fd97516ee53ca8c938ad413e030dc7df483f116c4b19b5811e359960b7b44d

...
2 changes: 1 addition & 1 deletion .github/workflows/linux-clang-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: Linux Clang compilation and tests
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
steps:
- uses: actions/checkout@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux-gcc-compile-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: Linux GCC compilation and tests
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
steps:
- uses: actions/checkout@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
name: SonarCloud analysis
runs-on: ubuntu-22.04
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-14
container: ghcr.io/nextcloud/continuous-integration-client:client-5.15-15
env:
SONAR_SERVER_URL: "https://sonarcloud.io"
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
Expand Down
14 changes: 8 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 17)
cmake_policy(SET CMP0071 NEW) # Enable use of QtQuick compiler/generated code

find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
if (CLANG_TIDY_EXE)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} -checks=-*,modernize-use-auto,modernize-use-using,modernize-use-nodiscard,modernize-use-nullptr,modernize-use-override,cppcoreguidelines-pro-type-static-cast-downcast,modernize-use-default-member-init,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-init-variables)
endif()

project(client)

if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OSX deployment version")
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 17)

include(FeatureSummary)

find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
if (CLANG_TIDY_EXE)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} -checks=-*,modernize-use-auto,modernize-use-using,modernize-use-nodiscard,modernize-use-nullptr,modernize-use-override,cppcoreguidelines-pro-type-static-cast-downcast,modernize-use-default-member-init,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-init-variables)
endif()

set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES)

set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
Expand Down
2 changes: 1 addition & 1 deletion admin/linux/build-appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ chmod a+x linuxdeployqt.AppImage
rm ./linuxdeployqt.AppImage
cp -r ./squashfs-root ./linuxdeployqt-squashfs-root
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs -qmldir=${DESKTOP_CLIENT_ROOT}/src/gui

# Set origin
Expand Down
9 changes: 7 additions & 2 deletions src/common/filesystembase.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@

#include "config.h"

#include "csync/ocsynclib.h"

#include <QString>
#include <ctime>
#include <QFileInfo>
#include <QLoggingCategory>

#include <csync/ocsynclib.h>
#include <ctime>

class QFile;

Expand All @@ -42,6 +43,10 @@ OCSYNC_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcFileSystem)
* @brief This file contains file system helper
*/
namespace FileSystem {
enum class FolderPermissions {
ReadOnly,
ReadWrite,
};

/**
* @brief Mark the file as hidden (only has effects on windows)
Expand Down
5 changes: 5 additions & 0 deletions src/libsync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ target_link_libraries(nextcloudsync
KF5::Archive
)

target_compile_features(nextcloudsync
PRIVATE
cxx_std_17
)

find_package(Qt5 REQUIRED COMPONENTS Gui Widgets Svg)
target_link_libraries(nextcloudsync PUBLIC Qt5::Gui Qt5::Widgets Qt5::Svg)

Expand Down
179 changes: 176 additions & 3 deletions src/libsync/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@
#include "filesystem.h"

#include "common/utility.h"
#include "csync.h"
#include "vio/csync_vio_local.h"
#include "std/c_time.h"

#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDirIterator>
#include <QCoreApplication>

#include "csync.h"
#include "vio/csync_vio_local.h"
#include "std/c_time.h"
#ifdef Q_OS_WIN
#include <securitybaseapi.h>
#include <sddl.h>
#endif

namespace OCC {

Expand Down Expand Up @@ -189,5 +194,173 @@ bool FileSystem::getInode(const QString &filename, quint64 *inode)
return false;
}

bool FileSystem::setFolderPermissions(const QString &path,
FileSystem::FolderPermissions permissions) noexcept
{
try {
switch (permissions) {
case OCC::FileSystem::FolderPermissions::ReadOnly:
std::filesystem::permissions(path.toStdWString(), std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
break;
case OCC::FileSystem::FolderPermissions::ReadWrite:
break;
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}

#ifdef Q_OS_WIN
SECURITY_INFORMATION info = DACL_SECURITY_INFORMATION;
std::unique_ptr<char[]> securityDescriptor;
auto neededLength = 0ul;

if (!GetFileSecurityW(path.toStdWString().c_str(), info, nullptr, 0, &neededLength)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << lastError;
return false;
}

securityDescriptor.reset(new char[neededLength]);

if (!GetFileSecurityW(path.toStdWString().c_str(), info, securityDescriptor.get(), neededLength, &neededLength)) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << GetLastError();
return false;
}
}

int daclPresent = false, daclDefault = false;
PACL resultDacl = nullptr;
if (!GetSecurityDescriptorDacl(securityDescriptor.get(), &daclPresent, &resultDacl, &daclDefault)) {
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << GetLastError();
return false;
}
if (!daclPresent) {
qCWarning(lcFileSystem) << "error when calling DACL needed to set a folder read-only or read-write is missing" << path;
return false;
}

PSID sid = nullptr;
if (!ConvertStringSidToSidW(L"S-1-5-32-545", &sid))
{
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << GetLastError();
return false;
}

ACL_SIZE_INFORMATION aclSize;
if (!GetAclInformation(resultDacl, &aclSize, sizeof(aclSize), AclSizeInformation)) {
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << GetLastError();
return false;
}

const auto newAclSize = aclSize.AclBytesInUse + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(sid);
qCDebug(lcFileSystem) << "allocated a new DACL object of size" << newAclSize;

std::unique_ptr<ACL> newDacl{reinterpret_cast<PACL>(new char[newAclSize])};
if (!InitializeAcl(newDacl.get(), newAclSize, ACL_REVISION)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling InitializeAcl" << path;
return false;
}

qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << lastError;
return false;
}

if (permissions == FileSystem::FolderPermissions::ReadOnly) {
qCInfo(lcFileSystem) << path << "will be read only";
if (!AddAccessDeniedAce(newDacl.get(), ACL_REVISION, FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD, sid)) {
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce << path" << GetLastError();
return false;
}
}

for (int i = 0; i < aclSize.AceCount; ++i) {
void *currentAce = nullptr;
if (!GetAce(resultDacl, i, &currentAce)) {
qCWarning(lcFileSystem) << "error when calling GetAce" << path << GetLastError();
return false;
}

const auto currentAceHeader = reinterpret_cast<PACE_HEADER>(currentAce);

if (permissions == FileSystem::FolderPermissions::ReadWrite) {
qCInfo(lcFileSystem) << path << "will be read write";
}
if (permissions == FileSystem::FolderPermissions::ReadWrite && (ACCESS_DENIED_ACE_TYPE == (currentAceHeader->AceType & ACCESS_DENIED_ACE_TYPE))) {
qCWarning(lcFileSystem) << "AceHeader" << path << currentAceHeader->AceFlags << currentAceHeader->AceSize << currentAceHeader->AceType;
continue;
}

if (!AddAce(newDacl.get(), ACL_REVISION, i + 1, currentAce, currentAceHeader->AceSize)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling AddAce" << path;
return false;
}

if (lastError != ERROR_INVALID_PARAMETER) {
qCWarning(lcFileSystem) << "invalid parameter error when calling AddAce" << path << "ACL size" << newAclSize;
return false;
}

qCWarning(lcFileSystem) << "error when calling AddAce" << path << lastError << "acl index" << (i + 1);
return false;
}
}

SECURITY_DESCRIPTOR newSecurityDescriptor;
if (!InitializeSecurityDescriptor(&newSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << GetLastError();
return false;
}

if (!SetSecurityDescriptorDacl(&newSecurityDescriptor, true, newDacl.get(), false)) {
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << GetLastError();
return false;
}

if (!SetFileSecurityW(path.toStdWString().c_str(), info, &newSecurityDescriptor)) {
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << path << GetLastError();
return false;
}
#endif

try {
switch (permissions) {
case OCC::FileSystem::FolderPermissions::ReadOnly:
break;
case OCC::FileSystem::FolderPermissions::ReadWrite:
std::filesystem::permissions(path.toStdWString(), std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
break;
}
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}

return true;
}

bool FileSystem::isFolderReadOnly(const std::filesystem::path &path) noexcept
{
try {
const auto folderStatus = std::filesystem::status(path);
const auto folderPermissions = folderStatus.permissions();
return (folderPermissions & std::filesystem::perms::owner_write) != std::filesystem::perms::owner_write;
}
catch (const std::filesystem::filesystem_error &e)
{
qCWarning(lcFileSystem()) << "exception when checking folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
return false;
}
}


} // namespace OCC
Loading

0 comments on commit 61251cd

Please sign in to comment.