Implement uid sandboxing for private-sharing 49/320449/4
authorZofia Abramowska <z.abramowska@samsung.com>
Mon, 10 Feb 2025 15:44:44 +0000 (16:44 +0100)
committerKrzysztof Malysa <k.malysa@samsung.com>
Wed, 12 Mar 2025 12:57:51 +0000 (13:57 +0100)
* Expand Acl and Acl::Entry to support entries modification
* Enable private sharing for nosmack environment
* Add Acl entries for shared path
* Remove Acl entries when sharing is dropped
* Remove all private sharing when target application is
  uninstalled

Change-Id: Ie2645d972083df7e912a076f989f3a3f13ad7a33

src/client/client-security-manager.cpp
src/common/acl.cpp
src/common/include/acl.h
src/common/include/service_impl.h
src/common/include/smack-labels.h
src/common/service_impl.cpp
src/common/smack-labels.cpp
test/test_acl.cpp

index d5c3265b7457d31feb813838843c93c8ca9f4c5c..83627f3dbb88b08693eefe5acbaa3d9ba97c110e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2024 Samsung Electronics Co., Ltd. All rights reserved
+ * Copyright (c) 2014-2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -2022,11 +2022,6 @@ int security_manager_app_has_privilege(const char *app_name, const char *privile
 SECURITY_MANAGER_API
 int security_manager_private_sharing_req_new(private_sharing_req **pp_req)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     if (!pp_req)
             return SECURITY_MANAGER_ERROR_INPUT_PARAM;
 
@@ -2042,10 +2037,6 @@ int security_manager_private_sharing_req_new(private_sharing_req **pp_req)
 SECURITY_MANAGER_API
 void security_manager_private_sharing_req_free(private_sharing_req *p_req)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return;
-    }
     delete p_req;
 }
 
@@ -2053,11 +2044,6 @@ SECURITY_MANAGER_API
 int security_manager_private_sharing_req_set_owner_appid(
     private_sharing_req *p_req, const char *app_name)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     return try_catch([&] {
         if (!p_req || !app_name)
                 return SECURITY_MANAGER_ERROR_INPUT_PARAM;
@@ -2070,11 +2056,6 @@ SECURITY_MANAGER_API
 int security_manager_private_sharing_req_set_target_appid(
     private_sharing_req *p_req, const char *app_name)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     return try_catch([&] {
         if (!p_req || !app_name)
                 return SECURITY_MANAGER_ERROR_INPUT_PARAM;
@@ -2088,11 +2069,6 @@ int security_manager_private_sharing_req_add_paths(private_sharing_req *p_req,
                                                    const char **pp_paths,
                                                    size_t path_count)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     return try_catch([&] {
         if (!p_req || !pp_paths)
             return SECURITY_MANAGER_ERROR_INPUT_PARAM;
@@ -2106,11 +2082,6 @@ int security_manager_private_sharing_req_add_paths(private_sharing_req *p_req,
 SECURITY_MANAGER_API
 int security_manager_private_sharing_apply(const private_sharing_req *p_req)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     using namespace SecurityManager;
     return try_catch([&]() -> int {
         if (!p_req)
@@ -2126,11 +2097,6 @@ int security_manager_private_sharing_apply(const private_sharing_req *p_req)
 SECURITY_MANAGER_API
 int security_manager_private_sharing_drop(const private_sharing_req *p_req)
 {
-    if (!smack_simple_check()) {
-        LogWarning("Running in no-smack mode, private sharing is a no-op, due to no Smack");
-        return SECURITY_MANAGER_SUCCESS;
-    }
-
     using namespace SecurityManager;
     return try_catch([&]() -> int {
         if (!p_req)
index 5b9fa62cb9b460e0b825d6710383bf5640a1a26d..ca6f08c03b14d98cde0405f9fec2f13ef233a65f 100644 (file)
  */
 
 #include "acl.h"
+#include "dpl/errno_string.h"
+#include "dpl/exception.h"
+#include <acl/libacl.h>
+#include <memory>
+#include <sys/acl.h>
+#include <vector>
 
 namespace SecurityManager {
 
+Acl::Acl(acl_t acl) : m_acl(acl) {}
+
+void Acl::Entry::syncToAssignedRawEntry() {
+    acl_permset_t permset = nullptr;
+
+    if (0 != acl_get_permset(m_rawEntry, &permset)) {
+        LogAndThrowErrno(Exception::Base, "acl_get_permset");
+    }
+
+    if (0 != acl_clear_perms(permset)) {
+        LogAndThrowErrno(Exception::Base, "acl_clear_permset");
+    }
+
+    if (0 != acl_add_perm(permset, m_permissions)) {
+        LogAndThrowErrno(Exception::Base, "acl_add_perm");
+    }
+
+    if (0 != acl_set_tag_type(m_rawEntry, m_tag)) {
+        LogAndThrowErrno(Exception::Base, "acl_set_tag_type");
+    }
+
+    void *qualifier = nullptr;
+    if (auto *uidQualifier = std::get_if<UidQualifier>(&m_qualifier)) {
+        qualifier = &uidQualifier->uid;
+    } else if (auto *gidQualifier = std::get_if<GidQualifier>(&m_qualifier)) {
+        qualifier = &gidQualifier->gid;
+    }
+
+    if (qualifier != nullptr) {
+        if (0 != acl_set_qualifier(m_rawEntry, qualifier)) {
+            LogAndThrowErrno(Exception::Base, "acl_set_qualifier");
+        }
+    }
+}
+
+Acl::Entry Acl::UidEntry(acl_perm_t permissions, uid_t uid) noexcept {
+    return Acl::Entry(permissions, ACL_USER, Acl::UidQualifier{uid});
+}
+
+Acl::Entry Acl::GidEntry(acl_perm_t permissions, gid_t gid) noexcept {
+    return Acl::Entry(permissions, ACL_GROUP, Acl::GidQualifier{gid});
+}
+
+Acl::Entry Acl::Entry::FromRawEntry(acl_entry_t entry) {
+    acl_tag_t tag_type;
+    if (0 != acl_get_tag_type(entry, &tag_type)) {
+        LogAndThrowErrno(Exception::Base, "acl_get_tag_type");
+    }
+
+    acl_permset_t permset;
+
+    if (0 != acl_get_permset(entry, &permset)) {
+        LogAndThrowErrno(Exception::Base, "acl_get_permset");
+    }
+
+    acl_perm_t perms = 0;
+    for (auto perm : {ACL_READ, ACL_WRITE, ACL_EXECUTE}) {
+        int ret = acl_get_perm(permset, perm);
+        if (ret == 0) {
+            continue;
+        } else if (ret == 1) {
+            perms |= perm;
+        } else {
+            LogAndThrowErrno(Exception::Base, "acl_get_perm");
+        }
+    }
+
+    auto acl_qualifier =
+        std::unique_ptr<void, decltype(&acl_free)>{acl_get_qualifier(entry), &acl_free};
+
+    switch (tag_type) {
+    case ACL_USER: {
+        if (!acl_qualifier) {
+            LogAndThrowErrno(Exception::Base, "acl_get_qualifier");
+        }
+        uid_t uid = *(static_cast<uid_t*>(acl_qualifier.get()));
+        auto uidEntry = UidEntry(perms, uid);
+        uidEntry.m_rawEntry = entry;
+        return uidEntry;
+    }
+    case ACL_GROUP: {
+        if (!acl_qualifier) {
+            LogAndThrowErrno(Exception::Base, "acl_get_qualifier");
+        }
+        gid_t gid = *(static_cast<gid_t*>(acl_qualifier.get()));
+        auto gid_entry = GidEntry(perms, gid);
+        gid_entry.m_rawEntry = entry;
+        return gid_entry;
+    }
+    default: {
+        if (acl_qualifier) {
+            ThrowMsg(Exception::Base, "Unexpected acl_qualifier for tag type " << tag_type);
+        }
+    }
+    }
+
+    return Entry(perms, tag_type);
+}
+
 Acl Acl::Make(acl_perm_t ownerPerms,
               acl_perm_t groupPerms,
               acl_perm_t otherPerms,
-              std::vector<EntryPtr>&& _entries)
+              std::vector<Entryentries)
 {
-    auto entries = std::move(_entries);
-
     // Acl requires exactly one ACL_USER_OBJ, ACL_GROUP_OBJ and ACL_OTHER entry
     entries.reserve(entries.size() + 3);
-    entries.emplace_back(new Acl::Entry(ownerPerms, ACL_USER_OBJ));
-    entries.emplace_back(new Acl::Entry(groupPerms, ACL_GROUP_OBJ));
-    entries.emplace_back(new Acl::Entry(otherPerms, ACL_OTHER));
+    entries.emplace_back(Acl::Entry(ownerPerms, ACL_USER_OBJ));
+    entries.emplace_back(Acl::Entry(groupPerms, ACL_GROUP_OBJ));
+    entries.emplace_back(Acl::Entry(otherPerms, ACL_OTHER));
+
+    return Acl(std::move(entries));
+}
 
-    return Acl(entries);
+Acl Acl::FromFile(const std::string &path, acl_type_t acl_type) {
+    acl_t acl = acl_get_file(path.c_str(), acl_type);
+    if (acl == nullptr) {
+        LogAndThrowErrno(Exception::Base, "acl_get_file");
+    }
+
+    return Acl(acl);
+}
+
+std::vector<Acl::Entry> Acl::toEntries() const {
+    std::vector<Entry> entries;
+
+    int entries_count = acl_entries(m_acl);
+    if (entries_count < 0) {
+        LogAndThrowErrno(Exception::Base, "acl_entries");
+    }
+
+    entries.reserve(entries_count);
+
+    int ret;
+    acl_entry_t entry;
+    while (1 == (ret = acl_get_entry(m_acl, ACL_NEXT_ENTRY, &entry))) {
+        entries.emplace_back(Acl::Entry::FromRawEntry(entry));
+    }
+
+    if (ret == -1) {
+        LogAndThrowErrno(Exception::Base, "acl_get_entry");
+    }
+
+    return entries;
+}
+
+void Acl::addEntry(Entry&& entry) {
+    if (!entry.isRawEntryAssigned()) {
+        acl_entry_t new_entry = nullptr;
+        if (0 != acl_create_entry(&m_acl, &new_entry)) {
+            LogAndThrowErrno(Exception::Base, "acl_create_entry");
+        }
+        entry.assignRawEntry(new_entry);
+    }
+
+    entry.syncToAssignedRawEntry();
+}
+
+void Acl::deleteEntry(Entry&& entry) {
+    if (0 != acl_delete_entry(m_acl, entry.getAssignedRawEntry())) {
+        LogAndThrowErrno(Exception::Base, "acl_delete_entry");
+    }
 }
 
-Acl::Acl(const std::vector<EntryPtr> &entries)
+
+Acl::Acl(std::vector<Entry> entries)
 {
     if (entries.empty()) {
         return;
@@ -48,63 +202,29 @@ Acl::Acl(const std::vector<EntryPtr> &entries)
     auto freeAcl = [](acl_t *toFree) {acl_free(*toFree);};
     m_acl = acl_init(entries.size() + 1); // + 1 for ACL_MASK entry
     if (m_acl == nullptr) {
-        LogErrno("acl_init");
-        Throw(Exception::Base);
+        LogAndThrowErrno(Exception::Base, "acl_init");
     }
     std::unique_ptr<acl_t, decltype(freeAcl)> deleter(&m_acl, freeAcl);
 
-    acl_entry_t entry;
-    for (const auto &e : entries) {
-        if (0 != acl_create_entry(&m_acl, &entry)) {
-            LogErrno("acl_create_entry");
-            Throw(Exception::Base);
-        }
-
-        acl_permset_t permset = nullptr;
-        if (0 != acl_get_permset(entry, &permset)) {
-            LogErrno("acl_get_permset");
-            Throw(Exception::Base);
-        }
-
-        if (0 != acl_clear_perms(permset)) {
-            LogErrno("acl_clear_perms");
-            Throw(Exception::Base);
-        }
-
-        for (auto &p : { ACL_READ, ACL_WRITE, ACL_EXECUTE }) {
-            if (e->permissions & p) {
-                if (0 != acl_add_perm(permset, p)) {
-                    LogErrno("acl_add_perm");
-                    Throw(Exception::Base);
-                }
-            }
-        }
-        if (0 != acl_set_permset(entry, permset)) {
-            LogErrno("acl_set_permset");
-            Throw(Exception::Base);
-        }
 
-        if (0 != acl_set_tag_type(entry, e->tag)) {
-            LogErrno("acl_set_tag_type");
-            Throw(Exception::Base);
-        }
-
-        e->setQualifier(entry);
+    for (auto &e : entries) {
+        addEntry(std::move(e));
     }
 
     // Acl requires one ACL_MASK entry if there are ACL_USER or ACL_GROUP entries present.
     if (0 != acl_calc_mask(&m_acl)) {
-        LogErrno("acl_calc_mask");
-        Throw(Exception::Base);
+        LogAndThrowErrno(Exception::Base, "acl_calc_mask");
     }
 
+    LogWarning("created acl: " << toString());
     int last;
     int ret = acl_check(m_acl, &last);
     if (ret != 0) {
-        if (ret > 0)
+        if (ret > 0) {
             LogError("Check failed for entry " << last << ": " << acl_error(ret));
-        else
+        } else {
             LogErrno("acl_check");
+        }
         Throw(Exception::Base);
     }
 
@@ -119,11 +239,23 @@ void Acl::apply(const std::string &path, acl_type_t type) const
     }
 
     if (0 != acl_set_file(path.c_str(), type, m_acl)) {
-        LogErrno("acl_set_file");
+        LogErrno("acl_set_file for " << path);
         Throw(Exception::Base);
     }
 }
 
+std::string Acl::toString() const {
+    ssize_t len;
+    auto textPtr = std::unique_ptr<char, decltype(&acl_free)>{acl_to_text(m_acl, &len), &acl_free};
+    if (!textPtr) {
+        LogErrno("acl_to_text");
+        return "";
+    }
+
+    return std::string{textPtr.get(), static_cast<size_t>(len)};
+
+}
+
 Acl::Acl(Acl&& other) noexcept
 {
     m_acl = other.m_acl;
@@ -134,8 +266,9 @@ Acl& Acl::operator=(Acl&& other) noexcept
 {
     if (&other != this)
     {
-        if (m_acl)
+        if (m_acl) {
             acl_free(m_acl);
+        }
         m_acl = other.m_acl;
         other.m_acl = nullptr;
     }
index df54f0da0f2f610d0cedec65b7b7fdf33ef66bf6..7036224ee5c733b62c0158e51d7c909f8cf1c664 100644 (file)
 #pragma once
 
 #include <acl/libacl.h>
+#include <sys/acl.h>
 #include <vector>
 #include <memory>
+#include <variant>
 
 #include "dpl/exception.h"
 #include "dpl/log/log.h"
@@ -33,63 +35,85 @@ namespace SecurityManager {
 
 class Acl
 {
-private:
+public:
+    struct UidQualifier { uid_t uid; };
+    struct GidQualifier { gid_t gid; };
+    struct NoQualifier {};
+
+    using Qualifier = std::variant<UidQualifier, GidQualifier, NoQualifier>;
+
     class Entry
     {
+        friend class Acl;
+
     public:
-        Entry(acl_perm_t permissions_, acl_tag_t tag_) :
-                permissions(permissions_), tag(tag_)
+        Entry(acl_perm_t permissions, acl_tag_t tag = ACL_UNDEFINED_TAG, Qualifier qualifier = NoQualifier{}) :
+            m_permissions(permissions), m_tag(tag), m_qualifier(qualifier)
         {
         }
 
-        Entry(const Entry&) = default;
-        Entry(Entry&&) noexcept = default;
-        Entry& operator=(const Entry&) = default;
-        Entry& operator=(Entry&&) noexcept = default;
+        Entry() = delete;
+        Entry(Entry&& other) {
+            m_rawEntry = other.m_rawEntry;
+            m_tag = other.m_tag;
+            m_permissions = other.m_permissions;
+            m_qualifier = other.m_qualifier;
 
-        virtual ~Entry() = default;
+            other.m_rawEntry = nullptr;
+            other.m_tag = ACL_UNDEFINED_TAG;
+            other.m_permissions = 0;
+            other.m_qualifier = NoQualifier{};
 
-        virtual void setQualifier(acl_entry_t&) const
+        }
+
+        Entry& operator=(const Entry&) = delete;
+        Entry& operator=(Entry&&) = delete;
+
+        Qualifier getQualifier() const
         {
+            return m_qualifier;
         }
 
-        acl_perm_t permissions;
-        acl_tag_t tag;
-    };
+        acl_perm_t getPermissions() const {
+            return m_permissions;
+        }
 
-public:
-    static constexpr inline acl_perm_t R = ACL_READ;
-    static constexpr inline acl_perm_t W = ACL_WRITE;
-    static constexpr inline acl_perm_t X = ACL_EXECUTE;
-    static constexpr inline acl_perm_t RW = ACL_READ | ACL_WRITE;
-    static constexpr inline acl_perm_t RX = ACL_READ | ACL_EXECUTE;
-    static constexpr inline acl_perm_t RWX = ACL_READ | ACL_WRITE | ACL_EXECUTE;
+        acl_tag_t getTag() const {
+            return m_tag;
+        }
 
-    using EntryPtr = std::unique_ptr<Entry>;
+    private:
+        acl_entry_t getAssignedRawEntry() const
+        {
+            return m_rawEntry;
+        }
 
-    template<typename Id, acl_tag_t Tag>
-    class IdEntry : public Entry
-    {
-    public:
-        IdEntry(acl_perm_t permissions, Id id) :
-                Entry(permissions, Tag), m_qualifier(id)
+        bool isRawEntryAssigned() const
         {
+            return m_rawEntry != nullptr;
         }
 
-    private:
-        void setQualifier(acl_entry_t &entry) const override
+        void assignRawEntry(acl_entry_t entry)
         {
-            if (0 != acl_set_qualifier(entry, &m_qualifier)) {
-                LogErrno("acl_set_qualifier");
-                Throw(Exception::Base);
-            }
+            m_rawEntry = entry;
         }
 
-        Id m_qualifier;
+        static Entry FromRawEntry(acl_entry_t entry);
+
+        void syncToAssignedRawEntry();
+
+        acl_perm_t m_permissions;
+        acl_tag_t m_tag;
+        acl_entry_t m_rawEntry = nullptr;
+        Qualifier m_qualifier;
     };
 
-    using UidEntry = IdEntry<uid_t, ACL_USER>;
-    using GidEntry = IdEntry<gid_t, ACL_GROUP>;
+    static constexpr inline acl_perm_t R = ACL_READ;
+    static constexpr inline acl_perm_t W = ACL_WRITE;
+    static constexpr inline acl_perm_t X = ACL_EXECUTE;
+    static constexpr inline acl_perm_t RW = ACL_READ | ACL_WRITE;
+    static constexpr inline acl_perm_t RX = ACL_READ | ACL_EXECUTE;
+    static constexpr inline acl_perm_t RWX = ACL_READ | ACL_WRITE | ACL_EXECUTE;
 
     class Exception
     {
@@ -97,7 +121,7 @@ public:
         DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base)
     };
 
-    Acl() : Acl(std::vector<EntryPtr>())
+    Acl() : Acl(std::vector<Entry>())
     {
     }
 
@@ -111,14 +135,26 @@ public:
     static Acl Make(acl_perm_t ownerPerms,
                     acl_perm_t groupPerms,
                     acl_perm_t otherPerms,
-                    std::vector<EntryPtr>&& entries = {});
+                    std::vector<Entry> entries = {});
+    static Acl FromFile(const std::string &path, acl_type_t acl_type);
+
+    std::vector<Entry> toEntries() const;
+
+    void addEntry(Entry&& entry);
+    void deleteEntry(Entry&& entry);
 
     void apply(const std::string &path, acl_type_t type) const;
 
+    std::string toString() const;
+
+    static Entry UidEntry(acl_perm_t permissions, uid_t uid) noexcept;
+    static Entry GidEntry(acl_perm_t permissions, uid_t uid) noexcept;
+
 private:
-    explicit Acl(const std::vector<EntryPtr> &entries);
+    explicit Acl(std::vector<Entry> entries);
+    explicit Acl(acl_t);
 
-    acl_t m_acl = nullptr;
+    mutable acl_t m_acl = nullptr;
 };
 
 } // namespace SecurityManager
index df5ac100061347bb0aec62f250b8c7e584080e44..200d30757612b8fd3deb63d0262191f268eb2bb5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2023 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2014-2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -37,6 +37,7 @@
 #include "cynara.h"
 #include "mount-namespace.h"
 #include "nsmount-logic.h"
+#include "security-manager-types.h"
 #include "security-manager.h"
 #include "smack-common.h"
 #include "smack-rules.h"
@@ -474,6 +475,8 @@ private:
                                     UninstallHelper &uh,
                                     uid_t processUId);
 
+    void appUninstallPrivateSharing(app_inst_req::app &app);
+
     int appUninstallSmackRules(app_inst_req &req, UninstallHelper &uh);
 
     void tryUpdatePermissibleFile(uid_t uid, app_install_type installationType,
@@ -504,6 +507,10 @@ private:
                               const Smack::Label &targetAppLabel,
                               const std::string &path);
 
+    int dropOnePrivateSharing(const std::string &ownerAppName,
+                              const std::string &targetAppName,
+                              const std::string &path);
+
     void updateRunningAppSmackPolicy(
         const MountNS::AppContext &appContext,
         const std::map<std::string, std::vector<std::string>> &appsAllowedPrivileges);
index 236d9808a1366fee571d54ec78ad3f1060a502c5..3ba55c016896f0fce09fb0c79c03d1361d70ba5c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2014-2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -30,6 +30,7 @@
 #pragma once
 
 #include <string>
+#include <sys/acl.h>
 #include <utility>
 #include <optional>
 #include <smack-common.h>
@@ -98,7 +99,32 @@ void setupPkgBasePath(
  * @param pkgName[in] package identifier
  * @param path[in] path
  */
-void setupSharedPrivatePath(const std::string &pkgName, const std::string &path);
+void setupSharedPrivatePath(const std::string &pkgName,
+                            const std::string &path);
+
+/**
+ * Checks if application has RWX access to path and others do not have any access
+ *
+ * @param appUid[in] application uid
+ * @param path[in] path
+ */
+bool isAppPrivatePath(uid_t appUid, const std::string &path);
+
+/**
+ * Adds ACL rules required for target application to access path.
+ *
+ * @param targetAppUid[in] target application uid
+ * @param path[in] path
+ */
+void addPrivateSharing(uid_t targetAppUid, const std::string &path);
+
+/**
+ * Removes ACL rules required for target application to access path.
+ *
+ * @param targetAppUid[in] target application uid
+ * @param path[in] path
+ */
+void removePrivateSharing(uid_t targetAppUid, const std::string &path);
 
 /**
  * Generates application name for a label fetched from Cynara
index cb53a1bd5e637e60de582e0ad87b8575cfbf8821..2a6b1590fe04f27db66626d8d09dc21ba92b698c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2024 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2014-2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
  * @brief       Implementation of the service methods
  */
 
+#include <cassert>
 #include <fcntl.h>
 #include <linux/xattr.h>
 #include <limits.h>
 #include <pwd.h>
+#include <string>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <dirent.h>
 #include <cstring>
 #include <algorithm>
-#include <memory>
 #include <sys/types.h>
 #include <grp.h>
 #include <optional>
@@ -53,6 +54,7 @@
 #include <dpl/errno_string.h>
 
 #include <config.h>
+#include <vector>
 #include "acl.h"
 #include "check-proper-drop.h"
 #include "protocols.h"
@@ -1016,6 +1018,21 @@ void ServiceImpl::appUninstallCynaraPolicies(app_inst_req::app& app, app_inst_re
         ui.oldAppDefinedPrivileges, AppDefinedPrivilegesVector());
 }
 
+void ServiceImpl::appUninstallPrivateSharing(app_inst_req::app &app) {
+    if (smack_simple_check()) {
+        // Nothing to do, as Smack can revoke subject
+        return;
+    }
+    std::map<std::string, std::vector<std::string>> targetSharingMap;
+    m_privilegeDb.GetPrivateSharingForTarget(app.appName, targetSharingMap);
+
+    for (const auto& [ownerApp, paths] : targetSharingMap) {
+        for (const auto& path: paths) {
+            dropOnePrivateSharing(ownerApp, app.appName, path);
+        }
+    }
+}
+
 int ServiceImpl::appUninstallSmackRules(app_inst_req &req, UninstallHelper &uh)
 {
     try {
@@ -1070,6 +1087,7 @@ int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &req)
 {
     try {
         // initial checks
+        LogWarning("appUninstall started for pkg " << req.pkgName);
         int toRemove = 0;
         int ret = appUninstallInitialChecks(creds, req, toRemove);
         if (ret != SECURITY_MANAGER_SUCCESS) {
@@ -1104,7 +1122,7 @@ int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &req)
             if (!app.appName.empty()) {
                 // [db] remove app privileges
                 appUninstallPrivileges(app, req, uh);
-
+                appUninstallPrivateSharing(app);
                 auto uid = getProcessUId(app, req);
 
                 // [db] remove app
@@ -2003,6 +2021,29 @@ int ServiceImpl::appHasPrivilege(
     return SECURITY_MANAGER_SUCCESS;
 }
 
+std::unordered_set<std::string> getUniqueParentDirs(const std::vector<std::string> &paths) {
+    std::unordered_set<std::string> parentDirs;
+    for (const auto &path : paths) {
+        for (auto parentPath = FS::getParentDirectoryName(path);
+             !parentPath.empty();
+             parentPath = FS::getParentDirectoryName(parentPath)) {
+            parentDirs.insert(parentPath);
+        }
+    }
+
+    return parentDirs;
+}
+
+
+int ServiceImpl::dropOnePrivateSharing(
+        const std::string &ownerAppName,
+        const std::string &targetAppName,
+        const std::string &path)
+{
+    assert(!smack_simple_check());
+    return dropOnePrivateSharing(ownerAppName, "", {}, targetAppName, "", path);
+}
+
 int ServiceImpl::dropOnePrivateSharing(
         const std::string &ownerAppName,
         const std::string &ownerPkgName,
@@ -2013,6 +2054,9 @@ int ServiceImpl::dropOnePrivateSharing(
 {
     int errorRet;
     try {
+        LogDebug("Drop private sharing called for owner=" << ownerAppName
+                   << ", target=" << targetAppName
+                   << ", path=" << path);
         int targetPathCount, pathCount, ownerTargetCount;
         m_privilegeDb.DropPrivateSharing(ownerAppName, targetAppName, path);
         m_privilegeDb.GetTargetPathSharingCount(targetAppName, path, targetPathCount);
@@ -2022,12 +2066,52 @@ int ServiceImpl::dropOnePrivateSharing(
             return SECURITY_MANAGER_SUCCESS;
         }
         //This function can be also called when application is uninstalled, so path won't exist
-        if (pathCount < 1 && FS::fileExists(path)) {
-            SmackLabels::setupPath(ownerPkgName, path, SECURITY_MANAGER_PATH_RW);
+        if (FS::fileExists(path)) {
+            if (smack_simple_check() && pathCount < 1) {
+                SmackLabels::setupPath(ownerPkgName, path, SECURITY_MANAGER_PATH_RW);
+            } else if (!smack_simple_check()) {
+                std::string target_puid, owner_puid, unused_agid;
+                m_privilegeDb.GetAppPuidAndAgid(targetAppName, target_puid, unused_agid);
+                m_privilegeDb.GetAppPuidAndAgid(ownerAppName, owner_puid, unused_agid);
+
+                if (target_puid.empty() || owner_puid.empty()) {
+                    LogWarning("Private sharing retained after application uninstall!");
+                } else {
+                    uid_t owner_uid = std::stoul(owner_puid);
+                    uid_t target_uid = std::stoul(target_puid);
+                    // Other shared paths might have common parent directories.
+                    // Target app still needs to have access to them.
+                    auto otherSharedPaths = [&] {
+                        std::map<std::string, std::vector<std::string>> targetSharingMap;
+                        m_privilegeDb.GetPrivateSharingForTarget(targetAppName, targetSharingMap);
+                        return std::move(targetSharingMap[ownerAppName]);
+                    }();
+                    otherSharedPaths.erase(
+                        std::remove_if(otherSharedPaths.begin(), otherSharedPaths.end(),
+                                       [&](const auto& sharedPath) { return sharedPath == path; }),
+                        otherSharedPaths.end());
+                    auto parentDirs = getUniqueParentDirs(otherSharedPaths);
+                    auto currentPath = path;
+                    do {
+                        SmackLabels::removePrivateSharing(target_uid, currentPath);
+
+                        auto parentDir = FS::getParentDirectoryName(currentPath);
+                        if (parentDirs.count(parentDir) == 1) {
+                            // Some other private sharing has this directory on its path.
+                            // Do not remove access from it and its parent directories.
+                            break;
+                        }
+                        currentPath = parentDir;
+                    } while (!currentPath.empty() && SmackLabels::isAppPrivatePath(owner_uid, currentPath));
+                }
+            }
+        }
+
+        if (smack_simple_check()) {
+            Smack::Label pathLabel = SmackLabels::generateSharedPrivateLabel(ownerPkgName, path);
+            m_smackRules.dropPrivateSharingRules(ownerPkgName, ownerPkgLabels, targetAppLabel,
+                                                 pathLabel, pathCount < 1, ownerTargetCount < 1);
         }
-        Smack::Label pathLabel = SmackLabels::generateSharedPrivateLabel(ownerPkgName, path);
-        m_smackRules.dropPrivateSharingRules(ownerPkgName, ownerPkgLabels, targetAppLabel,
-                                           pathLabel, pathCount < 1, ownerTargetCount < 1);
         return SECURITY_MANAGER_SUCCESS;
     } catch (const PrivilegeDb::Exception::Base &e) {
         LogError("Error while dropping private sharing in database: " << e.DumpToString());
@@ -2062,6 +2146,8 @@ int ServiceImpl::applyPrivatePathSharing(
     Smack::Labels pkgsLabels;
 
     try {
+        LogDebug("applyPrivatePathSharing owner=" << ownerAppName
+                 << " target=" << targetAppName);
         if (!authenticate(creds, PRIVILEGE_APPSHARING_ADMIN)) {
             LogError("Caller is not permitted to manage file sharing");
             return SECURITY_MANAGER_ERROR_ACCESS_DENIED;
@@ -2079,13 +2165,28 @@ int ServiceImpl::applyPrivatePathSharing(
             return SECURITY_MANAGER_ERROR_APP_UNKNOWN;
         }
 
+        uid_t owner_uid = 0;
+        if (!smack_simple_check()) {
+            std::string owner_puid, unused_owner_agid;
+            m_privilegeDb.GetAppPuidAndAgid(ownerAppName, owner_puid, unused_owner_agid);
+            owner_uid = std::stoul(owner_puid);
+        }
+
         for (const auto &path : paths) {
-            std::string pathLabel = SmackLabels::getSmackLabelFromPath(path);
-            if (pathLabel != SmackLabels::generatePathRWLabel(ownerPkgName)) {
-                Smack::Label generatedPathLabel = SmackLabels::generateSharedPrivateLabel(ownerPkgName, path);
-                if (generatedPathLabel != pathLabel) {
-                    LogError("Path " << path << " has label " << pathLabel << " and dosen't belong"
-                             " to application " << ownerAppName);
+            if (smack_simple_check()){
+                std::string pathLabel = SmackLabels::getSmackLabelFromPath(path);
+                if (pathLabel != SmackLabels::generatePathRWLabel(ownerPkgName)) {
+                    Smack::Label generatedPathLabel =
+                        SmackLabels::generateSharedPrivateLabel(ownerPkgName, path);
+                    if (generatedPathLabel != pathLabel) {
+                        LogError("Path " << path << " has label " << pathLabel
+                                 << " and dosen't belong to application " << ownerAppName);
+                        return SECURITY_MANAGER_ERROR_APP_NOT_PATH_OWNER;
+                    }
+                }
+            } else {
+                if (!SmackLabels::isAppPrivatePath(owner_uid, path)) {
+                    LogError("Path " << path << " does not belong to application " << ownerAppName);
                     return SECURITY_MANAGER_ERROR_APP_NOT_PATH_OWNER;
                 }
             }
@@ -2115,12 +2216,25 @@ int ServiceImpl::applyPrivatePathSharing(
                 //Nothing to do, only counter needed incrementing
                 continue;
             }
-            m_smackRules.applyPrivateSharingRules(ownerPkgName, pkgsLabels,
-                                                targetAppLabel, pathLabel,
-                                                (pathCount > 0),
-                                                (ownerTargetCount > 0));
-            if (pathCount <= 0) {
-                SmackLabels::setupSharedPrivatePath(ownerPkgName, path);
+            if (smack_simple_check()) {
+                m_smackRules.applyPrivateSharingRules(ownerPkgName, pkgsLabels,
+                                                      targetAppLabel, pathLabel,
+                                                      (pathCount > 0),
+                                                      (ownerTargetCount > 0));
+                if (pathCount <= 0) {
+                    SmackLabels::setupSharedPrivatePath(ownerPkgName, path);
+                }
+            } else {
+                // ACLs for parent directories
+                std::string target_puid, unused_agid;
+                m_privilegeDb.GetAppPuidAndAgid(targetAppName, target_puid, unused_agid);
+                uid_t target_uid = std::stoul(target_puid);
+
+                auto currentPath = path;
+                do {
+                    SmackLabels::addPrivateSharing(target_uid, currentPath);
+                    currentPath = FS::getParentDirectoryName(currentPath);
+                } while (!currentPath.empty() && SmackLabels::isAppPrivatePath(owner_uid, currentPath));
             }
         }
         trans.commit();
@@ -2159,6 +2273,7 @@ int ServiceImpl::dropPrivatePathSharing(
 {
     int errorRet;
     try {
+        LogDebug("Drop private sharing called for " << ownerAppName << " and " << targetAppName);
         if (!authenticate(creds, PRIVILEGE_APPSHARING_ADMIN)) {
             LogError("Caller is not permitted to manage file sharing");
             return SECURITY_MANAGER_ERROR_ACCESS_DENIED;
@@ -2179,6 +2294,7 @@ int ServiceImpl::dropPrivatePathSharing(
         }
 
         for (const auto &path : paths) {
+            // FIXME: when ownerAppName == targetAppName the sharing will not exist in db
             if (!sharingExists(targetAppName, path)) {
                 LogError("Sharing doesn't exist: owner=" << ownerAppName
                          << ", target=" << targetAppName << ", path=" << path);
index 328b6569b9cf7ee5861f05bc34f792332aabf730..7629a7ab56667441bcb47beef2ce3915d18ee5fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2023 Samsung Electronics Co., Ltd. All rights reserved
+ * Copyright (c) 2014-2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -29,6 +29,7 @@
  */
 
 #include <crypt.h>
+#include <sys/acl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <fts.h>
 #include <cstring>
 #include <cstdlib>
-#include <fstream>
-#include <functional>
+#include <filesystem>
 #include <string>
-#include <sstream>
 #include <algorithm>
 
 #include <dpl/log/log.h>
 #include <dpl/errno_string.h>
+#include <variant>
+#include <vector>
 
 #include "acl.h"
+#include "dpl/exception.h"
 #include "security-manager.h"
 #include "service_impl_utils.h"
 #include "smack-check.h"
@@ -234,11 +236,10 @@ void setupPath(const uid_t owner,
     dac.owner = owner;
     // Make system_access the group. No app should run with it.
     dac.group = getSystemAccessGid();
-
     acl_perm_t appPerms;
     acl_perm_t otherPerms;
     bool follow_symlink;
-    std::vector<Acl::EntryPtr> entries;
+    std::vector<Acl::Entry> entries;
 
     switch (pathType) {
     case SECURITY_MANAGER_PATH_OWNER_RW_OTHER_RO:
@@ -272,7 +273,7 @@ void setupPath(const uid_t owner,
         appPerms = 0;
         otherPerms = 0;
         follow_symlink = false;
-        entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::RWX, *authorGId));
+        entries.emplace_back(Acl::GidEntry(Acl::RWX, *authorGId));
         break;
 
     default:
@@ -281,8 +282,9 @@ void setupPath(const uid_t owner,
     }
 
     if (appPerms != 0) {
-        for (auto &processUId : processUIds)
-            entries.emplace_back(std::make_unique<Acl::UidEntry>(appPerms, processUId));
+        for (auto &processUId : processUIds) {
+            entries.emplace_back(Acl::UidEntry(appPerms, processUId));
+        }
     }
 
     // RWX for the owner(5001) and system_access
@@ -307,14 +309,86 @@ void setupPkgBasePath(const Credentials &creds,
     if (0 != chown(basePath.c_str(), creds.uid, getSystemAccessGid()))
         ThrowErrno(SmackException::FileError, "chown() for path: " << basePath);
 
-    std::vector<Acl::EntryPtr> entries;
+    std::vector<Acl::Entry> entries;
     for (auto &processUId : processUIds)
-        entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RX, processUId));
+        entries.emplace_back(Acl::UidEntry(Acl::RX, processUId));
 
     auto acl = Acl::Make(Acl::RWX, Acl::RWX, Acl::RX, std::move(entries));
     acl.apply(basePath, ACL_TYPE_ACCESS);
 }
 
+bool isAppPrivatePath(uid_t appUid, const std::string &path) {
+    static constexpr auto dir_acl_types = {ACL_TYPE_ACCESS, ACL_TYPE_DEFAULT};
+    static constexpr auto file_acl_types = {ACL_TYPE_ACCESS};
+    const auto &acl_types = std::filesystem::is_directory(path) ? dir_acl_types : file_acl_types;
+
+    bool has_owner_rights = false;
+    bool has_other_rights = false;
+    for (auto acl_type : acl_types) {
+        auto acl = Acl::FromFile(path, acl_type);
+        LogDebug("acl from " << path << ":\n" << acl.toString());
+        for (const auto &entry : acl.toEntries()) {
+            auto qualifier = entry.getQualifier();
+            if (auto *uidQualifier = std::get_if<Acl::UidQualifier>(&qualifier)) {
+                if (appUid == uidQualifier->uid
+                    && (entry.getPermissions() & Acl::RW) == Acl::RW)
+                {
+                    has_owner_rights = true;
+                }
+            } else if (entry.getTag() == ACL_OTHER && (entry.getPermissions() != 0)) {
+                has_other_rights = true;
+            }
+        }
+    }
+
+    return has_owner_rights && !has_other_rights;
+}
+
+void addPrivateSharing(uid_t targetAppUid, const std::string &path) {
+    LogDebug("add private sharing called for target=" << targetAppUid
+             <<" path=" << path);
+    acl_perm_t perms = std::filesystem::is_directory(path) ? Acl::X : Acl::RX;
+
+    auto acl = Acl::FromFile(path, ACL_TYPE_ACCESS);
+    LogDebug("acl for " << path << " before add:\n " << acl.toString());
+
+    bool entry_exists = false;
+    for (auto &entry: acl.toEntries()) {
+        auto qualifier = entry.getQualifier();
+        if (auto *uidQualifier = std::get_if<Acl::UidQualifier>(&qualifier)) {
+            if (targetAppUid == uidQualifier->uid) {
+                entry_exists = true;
+                break;
+            }
+        }
+    }
+    if (!entry_exists) {
+        acl.addEntry(Acl::UidEntry(perms, targetAppUid));
+    }
+    LogDebug("acl for " << path << " after add:\n " << acl.toString());
+    acl.apply(path, ACL_TYPE_ACCESS);
+}
+
+void removePrivateSharing(uid_t targetAppUid, const std::string &path) {
+    LogDebug("remove private sharing called for target=" << targetAppUid
+             <<" path=" << path);
+    auto acl = Acl::FromFile(path, ACL_TYPE_ACCESS);
+    LogDebug("acl for " << path << " before remove:\n " << acl.toString());
+
+    for (auto &entry: acl.toEntries()) {
+        auto qualifier = entry.getQualifier();
+        if (auto *uidQualifier = std::get_if<Acl::UidQualifier>(&qualifier)) {
+            if (targetAppUid == uidQualifier->uid) {
+                acl.deleteEntry(std::move(entry));
+                // No other entry for this app should be on the list
+                break;
+            }
+        }
+    }
+    LogDebug("acl for " << path << " after remove:\n " << acl.toString());
+    acl.apply(path, ACL_TYPE_ACCESS);
+}
+
 void setupSharedPrivatePath(const std::string &pkgName, const std::string &path) {
     pathSetSmack(path.c_str(), generateSharedPrivateLabel(pkgName, path), XATTR_NAME_SMACK);
 }
index 8b74f1513fd29613b99f36efa609bffc97e878b4..73c18e857e0fae632a1ef30f5748e06a3c5e4fdd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2025 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd. All rights reserved
  *
  * This file is licensed under the terms of MIT License or the Apache License
  * Version 2.0 of your choice. See the LICENSE.MIT file for MIT license details.
@@ -398,9 +398,9 @@ POSITIVE_TEST_CASE(T1820_access_entries)
     Dir dir(TEST_DIR, mode);
     File file(TEST_FILE, mode);
 
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RWX, 5001));
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::RX, 100));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::UidEntry(Acl::RWX, 5001));
+    entries.emplace_back(Acl::GidEntry(Acl::RX, 100));
 
     auto acl = Acl::Make(0, 0, 0, std::move(entries));
 
@@ -420,9 +420,9 @@ POSITIVE_TEST_CASE(T1830_default_entries)
     mode_t mode = 0000;
     Dir dir(TEST_DIR, mode);
 
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RWX, 5001));
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::RX, 100));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::UidEntry(Acl::RWX, 5001));
+    entries.emplace_back(Acl::GidEntry(Acl::RX, 100));
 
     auto acl = Acl::Make(0, 0, 0, std::move(entries));
 
@@ -473,15 +473,15 @@ NEGATIVE_TEST_CASE(T1880_duplicated_entries)
 {
     Dir dir(TEST_DIR, 0000);
 
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RWX, 5001));
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::R, 5001));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::UidEntry(Acl::RWX, 5001));
+    entries.emplace_back(Acl::UidEntry(Acl::R, 5001));
 
     BOOST_REQUIRE_THROW(Acl::Make(0, 0, 0, std::move(entries)), Acl::Exception::Base);
 
-    entries = std::vector<Acl::EntryPtr>{};
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::RWX, 100));
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::R, 100));
+    entries = std::vector<Acl::Entry>{};
+    entries.emplace_back(Acl::GidEntry(Acl::RWX, 100));
+    entries.emplace_back(Acl::GidEntry(Acl::R, 100));
 
     BOOST_REQUIRE_THROW(Acl::Make(0, 0, 0, std::move(entries)), Acl::Exception::Base);
 }
@@ -491,9 +491,9 @@ POSITIVE_TEST_CASE(T1890_default_file_creation)
     mode_t mode = 0060; // '6' corresponds with ACL_MASK so make it identical (see man 5 acl)
     Dir dir(TEST_DIR, mode);
 
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RW, 5001));
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::R, 100));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::UidEntry(Acl::RW, 5001));
+    entries.emplace_back(Acl::GidEntry(Acl::R, 100));
 
     auto acl = Acl::Make(0, 0, 0, std::move(entries));
 
@@ -561,8 +561,8 @@ POSITIVE_TEST_CASE(T1910_mask)
     CheckAccess(dir, "u::-,g::x,o::-"); // no ACL yet -> no mask
 
     // change the ACL_MASK using ACL_GROUP entry
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::R, 100));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::GidEntry(Acl::R, 100));
 
     auto acl = Acl::Make(0, Acl::X, 0, std::move(entries));
     BOOST_REQUIRE_NO_THROW(acl.apply(dir.Path(), ACL_TYPE_ACCESS));
@@ -571,9 +571,9 @@ POSITIVE_TEST_CASE(T1910_mask)
     CheckMode(dir, 0050); // ACL_MASK affects group permission
 
     // change the ACL_MASK using ACL_USER entry
-    std::vector<Acl::EntryPtr> entries2;
-    entries2.emplace_back(std::make_unique<Acl::GidEntry>(Acl::R, 100));
-    entries2.emplace_back(std::make_unique<Acl::UidEntry>(Acl::W, 5001));
+    std::vector<Acl::Entry> entries2;
+    entries2.emplace_back(Acl::GidEntry(Acl::R, 100));
+    entries2.emplace_back(Acl::UidEntry(Acl::W, 5001));
     acl = Acl::Make(0, Acl::X, 0, std::move(entries2));
     BOOST_REQUIRE_NO_THROW(acl.apply(dir.Path(), ACL_TYPE_ACCESS));
 
@@ -597,9 +597,9 @@ POSITIVE_TEST_CASE(T1920_executable_permission)
     mode_t mode = 0750;
     Dir dir(TEST_DIR, mode);
 
-    std::vector<Acl::EntryPtr> entries;
-    entries.emplace_back(std::make_unique<Acl::UidEntry>(Acl::RWX, 5001));
-    entries.emplace_back(std::make_unique<Acl::GidEntry>(Acl::RX, 100));
+    std::vector<Acl::Entry> entries;
+    entries.emplace_back(Acl::UidEntry(Acl::RWX, 5001));
+    entries.emplace_back(Acl::GidEntry(Acl::RX, 100));
 
     auto acl = Acl::Make(0, 0, 0, std::move(entries));