permissions: add perms for plugin loading (#10184)
Adds permission management for loading plugins --------- Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
This commit is contained in:
parent
2118440488
commit
5bd7ff884d
12 changed files with 416 additions and 91 deletions
|
|
@ -7,6 +7,9 @@
|
|||
#include "../../Compositor.hpp"
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
|
@ -64,10 +67,7 @@ static const char* permissionToHumanString(eDynamicPermissionType type) {
|
|||
return "error";
|
||||
}
|
||||
|
||||
static std::expected<std::string, std::string> binaryNameForWlClient(wl_client* client) {
|
||||
pid_t pid = 0;
|
||||
wl_client_get_credentials(client, &pid, nullptr, nullptr);
|
||||
|
||||
static std::expected<std::string, std::string> binaryNameForPid(pid_t pid) {
|
||||
if (pid <= 0)
|
||||
return std::unexpected("No pid for client");
|
||||
|
||||
|
|
@ -102,6 +102,13 @@ static std::expected<std::string, std::string> binaryNameForWlClient(wl_client*
|
|||
return fullPath;
|
||||
}
|
||||
|
||||
static std::expected<std::string, std::string> binaryNameForWlClient(wl_client* client) {
|
||||
pid_t pid = 0;
|
||||
wl_client_get_credentials(client, &pid, nullptr, nullptr);
|
||||
|
||||
return binaryNameForPid(pid);
|
||||
}
|
||||
|
||||
void CDynamicPermissionManager::clearConfigPermissions() {
|
||||
std::erase_if(m_rules, [](const auto& e) { return e->m_source == PERMISSION_RULE_SOURCE_CONFIG; });
|
||||
}
|
||||
|
|
@ -183,22 +190,99 @@ eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionMode(wl_c
|
|||
return PERMISSION_RULE_ALLOW_MODE_PENDING;
|
||||
}
|
||||
|
||||
void CDynamicPermissionManager::askForPermission(wl_client* client, const std::string& binaryPath, eDynamicPermissionType type) {
|
||||
auto rule = m_rules.emplace_back(SP<CDynamicPermissionRule>(new CDynamicPermissionRule(client, type, PERMISSION_RULE_ALLOW_MODE_PENDING)));
|
||||
eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionModeWithString(pid_t pid, const std::string& str, eDynamicPermissionType permission) {
|
||||
static auto PPERM = CConfigValue<Hyprlang::INT>("ecosystem:enforce_permissions");
|
||||
|
||||
if (*PPERM == 0)
|
||||
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
|
||||
|
||||
const auto LOOKUP = binaryNameForPid(pid);
|
||||
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: checking permission {} for key {} (binary {})", permissionToString(permission), str,
|
||||
LOOKUP.has_value() ? LOOKUP.value() : "lookup failed: " + LOOKUP.error());
|
||||
|
||||
// first, check if we have the client + perm combo in our cache.
|
||||
auto it = std::ranges::find_if(m_rules, [str, permission, pid](const auto& e) { return e->m_keyString == str && pid && pid == e->m_pid && e->m_type == permission; });
|
||||
if (it == m_rules.end()) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission not cached, checking key");
|
||||
|
||||
it = std::ranges::find_if(m_rules, [key = str, permission, &LOOKUP](const auto& e) {
|
||||
if (e->m_type != permission)
|
||||
return false; // wrong perm
|
||||
|
||||
if (!e->m_binaryRegex)
|
||||
return false; // no regex
|
||||
|
||||
// regex match
|
||||
if (RE2::FullMatch(key, *e->m_binaryRegex) || (LOOKUP.has_value() && RE2::FullMatch(LOOKUP.value(), *e->m_binaryRegex)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (it == m_rules.end())
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: no rule for key");
|
||||
else {
|
||||
if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed by config rule");
|
||||
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
|
||||
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied by config rule");
|
||||
return PERMISSION_RULE_ALLOW_MODE_DENY;
|
||||
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending by config rule");
|
||||
return PERMISSION_RULE_ALLOW_MODE_PENDING;
|
||||
} else
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission ask by config rule");
|
||||
}
|
||||
|
||||
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed before by user");
|
||||
return PERMISSION_RULE_ALLOW_MODE_ALLOW;
|
||||
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied before by user");
|
||||
return PERMISSION_RULE_ALLOW_MODE_DENY;
|
||||
} else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) {
|
||||
Debug::log(TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending before by user");
|
||||
return PERMISSION_RULE_ALLOW_MODE_PENDING;
|
||||
}
|
||||
|
||||
// if we are here, we need to ask.
|
||||
askForPermission(nullptr, str, permission, pid);
|
||||
|
||||
return PERMISSION_RULE_ALLOW_MODE_PENDING;
|
||||
}
|
||||
|
||||
void CDynamicPermissionManager::askForPermission(wl_client* client, const std::string& binaryPath, eDynamicPermissionType type, pid_t pid) {
|
||||
auto rule = m_rules.emplace_back(SP<CDynamicPermissionRule>(new CDynamicPermissionRule(client, type, PERMISSION_RULE_ALLOW_MODE_PENDING)));
|
||||
|
||||
if (!client)
|
||||
rule->m_keyString = binaryPath;
|
||||
|
||||
rule->m_pid = pid;
|
||||
|
||||
std::string description = "";
|
||||
if (binaryPath.empty())
|
||||
description = std::format("An unknown application (wayland client ID 0x{:x}) is {}.", (uintptr_t)client, permissionToHumanString(type));
|
||||
else {
|
||||
else if (client) {
|
||||
std::string binaryName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath;
|
||||
description = std::format("An application <b>{}</b> ({}) is {}.", binaryName, binaryPath, permissionToHumanString(type));
|
||||
}
|
||||
} else if (pid >= 0) {
|
||||
if (type == PERMISSION_TYPE_PLUGIN) {
|
||||
const auto LOOKUP = binaryNameForPid(pid);
|
||||
description = std::format("An application <b>{}</b> is {}:<br/><b>{}</b>", LOOKUP.value_or("Unknown"), permissionToHumanString(type), binaryPath);
|
||||
} else {
|
||||
const auto LOOKUP = binaryNameForPid(pid);
|
||||
description = std::format("An application <b>{}</b> ({}) is {}.", LOOKUP.value_or("Unknown"), binaryPath, permissionToHumanString(type));
|
||||
}
|
||||
} else
|
||||
description = std::format("An application is {}:<br/><b>{}</b>", permissionToHumanString(type), binaryPath);
|
||||
|
||||
description += "<br/><br/>Do you want to allow this?";
|
||||
|
||||
std::vector<std::string> options;
|
||||
|
||||
if (!binaryPath.empty()) {
|
||||
if (!binaryPath.empty() && client) {
|
||||
description += "<br/><br/><i>Hint: you can set persistent rules for these in the Hyprland config file.</i>";
|
||||
options = {"Deny", "Allow and remember app", "Allow once"};
|
||||
} else
|
||||
|
|
@ -250,7 +334,35 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s
|
|||
}
|
||||
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> CDynamicPermissionManager::promiseFor(wl_client* client, eDynamicPermissionType permission) {
|
||||
auto rule = std::ranges::find_if(m_rules, [client, permission](const auto& e) { return e->m_client == client && e->m_type == permission; });
|
||||
auto rule = std::ranges::find_if(m_rules, [&client, &permission](const auto& e) { return e->m_client == client && e->m_type == permission; });
|
||||
if (rule == m_rules.end())
|
||||
return nullptr;
|
||||
|
||||
if (!(*rule)->m_promise)
|
||||
return nullptr;
|
||||
|
||||
if ((*rule)->m_promiseResolverForExternal)
|
||||
return nullptr;
|
||||
|
||||
return CPromise<eDynamicPermissionAllowMode>::make([rule](SP<CPromiseResolver<eDynamicPermissionAllowMode>> r) { (*rule)->m_promiseResolverForExternal = r; });
|
||||
}
|
||||
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> CDynamicPermissionManager::promiseFor(const std::string& key, eDynamicPermissionType permission) {
|
||||
auto rule = std::ranges::find_if(m_rules, [&key, &permission](const auto& e) { return e->m_keyString == key && e->m_type == permission; });
|
||||
if (rule == m_rules.end())
|
||||
return nullptr;
|
||||
|
||||
if (!(*rule)->m_promise)
|
||||
return nullptr;
|
||||
|
||||
if ((*rule)->m_promiseResolverForExternal)
|
||||
return nullptr;
|
||||
|
||||
return CPromise<eDynamicPermissionAllowMode>::make([rule](SP<CPromiseResolver<eDynamicPermissionAllowMode>> r) { (*rule)->m_promiseResolverForExternal = r; });
|
||||
}
|
||||
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> CDynamicPermissionManager::promiseFor(pid_t pid, const std::string& key, eDynamicPermissionType permission) {
|
||||
auto rule = std::ranges::find_if(m_rules, [&pid, &permission, &key](const auto& e) { return e->m_pid == pid && e->m_keyString == key && e->m_type == permission; });
|
||||
if (rule == m_rules.end())
|
||||
return nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../helpers/AsyncDialogBox.hpp"
|
||||
#include <vector>
|
||||
#include <wayland-server-core.h>
|
||||
#include <sys/types.h>
|
||||
#include "../../helpers/defer/Promise.hpp"
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
|
|
@ -56,6 +57,8 @@ class CDynamicPermissionRule {
|
|||
wl_client* const m_client = nullptr;
|
||||
std::string m_binaryPath = "";
|
||||
UP<re2::RE2> m_binaryRegex;
|
||||
std::string m_keyString = "";
|
||||
pid_t m_pid = 0;
|
||||
|
||||
eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK;
|
||||
SP<CAsyncDialogBox> m_dialogBox; // for pending
|
||||
|
|
@ -76,14 +79,19 @@ class CDynamicPermissionManager {
|
|||
// (will continue returning false if the user does not agree, of course.)
|
||||
eDynamicPermissionAllowMode clientPermissionMode(wl_client* client, eDynamicPermissionType permission);
|
||||
|
||||
// for plugins for now. Pid 0 means unknown
|
||||
eDynamicPermissionAllowMode clientPermissionModeWithString(pid_t pid, const std::string& str, eDynamicPermissionType permission);
|
||||
|
||||
// get a promise for the result. Returns null if there already was one requested for the client.
|
||||
// Returns null if state is not pending
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> promiseFor(wl_client* client, eDynamicPermissionType permission);
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> promiseFor(const std::string& str, eDynamicPermissionType permission);
|
||||
SP<CPromise<eDynamicPermissionAllowMode>> promiseFor(pid_t pid, const std::string& key, eDynamicPermissionType permission);
|
||||
|
||||
void removeRulesForClient(wl_client* client);
|
||||
|
||||
private:
|
||||
void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type);
|
||||
void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type, pid_t pid = 0);
|
||||
|
||||
//
|
||||
std::vector<SP<CDynamicPermissionRule>> m_rules;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue