/*
- * 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.
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;
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;
}
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;
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;
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;
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)
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)
*/
#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<Entry> entries)
{
- 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;
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);
}
}
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;
{
if (&other != this)
{
- if (m_acl)
+ if (m_acl) {
acl_free(m_acl);
+ }
m_acl = other.m_acl;
other.m_acl = nullptr;
}
#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"
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
{
DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base)
};
- Acl() : Acl(std::vector<EntryPtr>())
+ Acl() : Acl(std::vector<Entry>())
{
}
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
/*
- * 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.
#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"
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,
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);
/*
- * 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.
#pragma once
#include <string>
+#include <sys/acl.h>
#include <utility>
#include <optional>
#include <smack-common.h>
* @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
/*
- * 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>
#include <dpl/errno_string.h>
#include <config.h>
+#include <vector>
#include "acl.h"
#include "check-proper-drop.h"
#include "protocols.h"
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 {
{
try {
// initial checks
+ LogWarning("appUninstall started for pkg " << req.pkgName);
int toRemove = 0;
int ret = appUninstallInitialChecks(creds, req, toRemove);
if (ret != SECURITY_MANAGER_SUCCESS) {
if (!app.appName.empty()) {
// [db] remove app privileges
appUninstallPrivileges(app, req, uh);
-
+ appUninstallPrivateSharing(app);
auto uid = getProcessUId(app, req);
// [db] remove app
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,
{
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);
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());
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;
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;
}
}
//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();
{
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;
}
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);
/*
- * 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.
*/
#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"
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:
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:
}
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
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);
}
/*
- * 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.
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));
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));
{
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);
}
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));
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));
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));
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));