mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-12 14:50:36 +01:00
hyprpm: clean up root access and properly check input (#10304)
Some checks are pending
Build Hyprland / Build Hyprland (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland with Meson (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland without precompiled headers (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland in pure Wayland (Arch) (push) Waiting to run
Build Hyprland / Code Style (Arch) (push) Waiting to run
Nix (CI) / update-inputs (push) Waiting to run
Nix (CI) / build (push) Waiting to run
Security Checks / Flawfinder Checks (push) Waiting to run
Some checks are pending
Build Hyprland / Build Hyprland (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland with Meson (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland without precompiled headers (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland in pure Wayland (Arch) (push) Waiting to run
Build Hyprland / Code Style (Arch) (push) Waiting to run
Nix (CI) / update-inputs (push) Waiting to run
Nix (CI) / build (push) Waiting to run
Security Checks / Flawfinder Checks (push) Waiting to run
* manifest: reject bad names from parsing * sys: restructure root functions
This commit is contained in:
parent
948277895e
commit
f8bbe5124c
7 changed files with 224 additions and 111 deletions
|
@ -3,11 +3,37 @@
|
|||
#include <toml++/toml.hpp>
|
||||
#include <print>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "PluginManager.hpp"
|
||||
#include "../helpers/Die.hpp"
|
||||
#include "../helpers/Sys.hpp"
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
|
||||
static std::string getTempRoot() {
|
||||
static auto ENV = getenv("XDG_RUNTIME_DIR");
|
||||
if (!ENV) {
|
||||
std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const auto STR = ENV + std::string{"/hyprpm/"};
|
||||
|
||||
return STR;
|
||||
}
|
||||
|
||||
// write the state to a file
|
||||
static bool writeState(const std::string& str, const std::string& to) {
|
||||
// create temp file in a safe temp root
|
||||
std::ofstream of(getTempRoot() + ".temp-state", std::ios::trunc);
|
||||
if (!of.good())
|
||||
return false;
|
||||
|
||||
of << str;
|
||||
of.close();
|
||||
|
||||
return NSys::root::install(getTempRoot() + ".temp-state", to, "644");
|
||||
}
|
||||
|
||||
std::filesystem::path DataState::getDataStatePath() {
|
||||
return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername);
|
||||
}
|
||||
|
@ -37,11 +63,16 @@ void DataState::ensureStateStoreExists() {
|
|||
std::error_code ec;
|
||||
if (!std::filesystem::exists(getHeadersPath(), ec) || ec) {
|
||||
std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now..."));
|
||||
if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec)
|
||||
NSys::runAsSuperuser("mkdir -p -m 755 '/var/cache/hyprpm/'");
|
||||
if (!std::filesystem::exists(getDataStatePath(), ec) || ec)
|
||||
NSys::runAsSuperuser("mkdir -p -m 755 '" + getDataStatePath().string() + "'");
|
||||
NSys::runAsSuperuser("mkdir -p -m 755 '" + getHeadersPath() + "'");
|
||||
if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) {
|
||||
if (!NSys::root::createDirectory("/var/cache/hyprpm", "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!std::filesystem::exists(getDataStatePath(), ec) || ec) {
|
||||
if (!NSys::root::createDirectory(getDataStatePath().string(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!NSys::root::createDirectory(getHeadersPath(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,8 +82,10 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
|||
const auto PATH = getDataStatePath() / repo.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec)
|
||||
NSys::runAsSuperuser("mkdir -p -m 755 '" + PATH.string() + "'");
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("addNewPluginRepo: failed to create cache dir");
|
||||
}
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"repository", toml::table{
|
||||
|
@ -66,8 +99,10 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
|||
const auto filename = p.name + ".so";
|
||||
|
||||
// copy .so to the good place and chmod 755
|
||||
if (std::filesystem::exists(p.filename))
|
||||
NSys::runAsSuperuser("cp '" + p.filename + "' '" + (PATH / filename).string() + "' && chmod 755 '" + (PATH / filename).string() + "'");
|
||||
if (std::filesystem::exists(p.filename)) {
|
||||
if (!NSys::root::install(p.filename, (PATH / filename).string(), "0755"))
|
||||
Debug::die("addNewPluginRepo: failed to install so file");
|
||||
}
|
||||
|
||||
DATA.emplace(p.name, toml::table{
|
||||
{"filename", filename},
|
||||
|
@ -80,8 +115,8 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
|||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
NSys::runAsSuperuser("cat << EOF > " + (PATH / "state.toml").string() + "\n" + ss.str() + "\nEOF");
|
||||
NSys::runAsSuperuser("chmod 644 '" + (PATH / "state.toml").string() + "'");
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
}
|
||||
|
||||
bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
|
@ -123,7 +158,8 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
|
|||
return; // WTF?
|
||||
|
||||
// scary!
|
||||
NSys::runAsSuperuser("rm -r '" + PATH + "'");
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("removePluginRepo: failed to remove dir");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +171,10 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
|||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec)
|
||||
NSys::runAsSuperuser("mkdir -p -m 755 '" + PATH.string() + "'");
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("updateGlobalState: failed to create dir");
|
||||
}
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"state", toml::table{
|
||||
|
@ -149,8 +187,8 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
|||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
NSys::runAsSuperuser("cat << EOF > " + (PATH / "state.toml").string() + "\n" + ss.str() + "\nEOF");
|
||||
NSys::runAsSuperuser("chmod 644 '" + (PATH / "state.toml").string() + "'");
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
}
|
||||
|
||||
SGlobalState DataState::getGlobalState() {
|
||||
|
@ -229,8 +267,8 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
|||
std::stringstream ss;
|
||||
ss << modifiedState;
|
||||
|
||||
NSys::runAsSuperuser("cat << EOF > " + stateFile.string() + "\n" + ss.str() + "\nEOF");
|
||||
NSys::runAsSuperuser("chmod 644 '" + stateFile.string() + "'");
|
||||
if (!writeState(ss.str(), stateFile.string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -250,5 +288,6 @@ void DataState::purgeAllCache() {
|
|||
if (PATH.contains('\''))
|
||||
return;
|
||||
// scary!
|
||||
NSys::runAsSuperuser("rm -r '" + PATH + "'");
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("Failed to run a superuser cmd");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#include "Manifest.hpp"
|
||||
#include <toml++/toml.hpp>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
// Alphanumerics and -_ allowed for plugin names. No magic names.
|
||||
// [A-Za-z0-9\-_]*
|
||||
static bool validManifestName(const std::string_view& n) {
|
||||
return std::ranges::all_of(n, [](const char& c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == '=' || (c >= '0' && c <= '9'); });
|
||||
}
|
||||
|
||||
CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
auto manifest = toml::parse_file(path);
|
||||
|
@ -11,11 +17,17 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
|||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_vPlugins.push_back(plugin);
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
for (auto& plugin : m_plugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.version = manifest[plugin.name]["version"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
|
||||
|
@ -37,21 +49,21 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
|||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == MANIFEST_HYPRPM) {
|
||||
m_sRepository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
m_repository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
if (authors) {
|
||||
for (auto&& a : *authors) {
|
||||
m_sRepository.authors.push_back(a.as_string()->value_or("?"));
|
||||
m_repository.authors.push_back(a.as_string()->value_or("?"));
|
||||
}
|
||||
} else {
|
||||
auto author = manifest["repository"]["author"].value_or("");
|
||||
if (!std::string{author}.empty())
|
||||
m_sRepository.authors.push_back(author);
|
||||
m_repository.authors.push_back(author);
|
||||
}
|
||||
|
||||
auto pins = manifest["repository"]["commit_pins"].as_array();
|
||||
|
@ -59,7 +71,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
|||
for (auto&& pin : *pins) {
|
||||
auto pinArr = pin.as_array();
|
||||
if (pinArr && pinArr->get(1))
|
||||
m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
m_repository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,11 +80,17 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
|||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_vPlugins.push_back(plugin);
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
for (auto& plugin : m_plugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["output"].value_or("?");
|
||||
plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0);
|
||||
|
@ -94,12 +112,12 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
|||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ???
|
||||
m_bGood = false;
|
||||
m_good = false;
|
||||
}
|
||||
}
|
|
@ -27,8 +27,8 @@ class CManifest {
|
|||
std::string name;
|
||||
std::vector<std::string> authors;
|
||||
std::vector<std::pair<std::string, std::string>> commitPins;
|
||||
} m_sRepository;
|
||||
} m_repository;
|
||||
|
||||
std::vector<SManifestPlugin> m_vPlugins;
|
||||
bool m_bGood = true;
|
||||
std::vector<SManifestPlugin> m_plugins;
|
||||
bool m_good = true;
|
||||
};
|
|
@ -236,14 +236,14 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!pManifest->m_bGood) {
|
||||
if (!pManifest->m_good) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.m_iSteps = 2;
|
||||
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
|
||||
for (auto const& pl : pManifest->m_vPlugins) {
|
||||
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_plugins.size()) + " plugins:"));
|
||||
for (auto const& pl : pManifest->m_plugins) {
|
||||
std::string message = "→ " + pl.name + " by ";
|
||||
for (auto const& a : pl.authors) {
|
||||
message += a + ", ";
|
||||
|
@ -256,12 +256,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
progress.printMessageAbove(message);
|
||||
}
|
||||
|
||||
if (!pManifest->m_sRepository.commitPins.empty()) {
|
||||
if (!pManifest->m_repository.commitPins.empty()) {
|
||||
// check commit pins
|
||||
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
|
||||
|
||||
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
||||
for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
|
||||
if (hl != HLVER.hash)
|
||||
continue;
|
||||
|
||||
|
@ -293,7 +293,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
progress.m_szCurrentMessage = "Building plugin(s)";
|
||||
progress.print();
|
||||
|
||||
for (auto& p : pManifest->m_vPlugins) {
|
||||
for (auto& p : pManifest->m_plugins) {
|
||||
std::string out;
|
||||
|
||||
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
|
||||
|
@ -336,11 +336,11 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
||||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
|
||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name;
|
||||
repo.url = url;
|
||||
repo.rev = rev;
|
||||
repo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_vPlugins) {
|
||||
for (auto const& p : pManifest->m_plugins) {
|
||||
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
|
||||
}
|
||||
DataState::addNewPluginRepo(repo);
|
||||
|
@ -566,13 +566,14 @@ bool CPluginManager::updateHeaders(bool force) {
|
|||
|
||||
ret = execAndGet(cmd);
|
||||
|
||||
cmd = std::format("cd {} && make installheaders && chmod -R 644 {} && find {} -type d -exec chmod o+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(),
|
||||
cmd = std::format("make -C '{}' installheaders && chmod -R 644 '{}' && find '{}' -type d -exec chmod o+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(),
|
||||
DataState::getHeadersPath());
|
||||
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(verboseString("install will run as sudo: {}", cmd));
|
||||
|
||||
ret = NSys::runAsSuperuser(cmd);
|
||||
// WORKINGDIR and headersPath should not contain anything unsafe. Usernames can't contain cmd exec parts.
|
||||
ret = NSys::root::runAsSuperuserUnsafe(cmd);
|
||||
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("installer returned: {}", ret));
|
||||
|
@ -701,17 +702,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!pManifest->m_bGood) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
|
||||
if (!pManifest->m_good) {
|
||||
std::println(stderr, "\n{}", failureString("The provided plugin repository has a bad manifest"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
|
||||
if (repo.rev.empty() && !pManifest->m_repository.commitPins.empty()) {
|
||||
// check commit pins unless a revision is specified
|
||||
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
|
||||
progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
|
||||
|
||||
for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
|
||||
for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
|
||||
if (hl != HLVER.hash)
|
||||
continue;
|
||||
|
||||
|
@ -721,7 +722,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& p : pManifest->m_vPlugins) {
|
||||
for (auto& p : pManifest->m_plugins) {
|
||||
std::string out;
|
||||
|
||||
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
|
||||
|
@ -763,7 +764,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
newrepo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_vPlugins) {
|
||||
for (auto const& p : pManifest->m_plugins) {
|
||||
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
|
||||
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <unistd.h>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
@ -20,7 +20,7 @@ inline constexpr std::array<std::string_view, 3> SUPERUSER_BINARIES = {
|
|||
"run0",
|
||||
};
|
||||
|
||||
static std::string fetchSuperuserBins() {
|
||||
static std::string validSubinsAsStr() {
|
||||
std::ostringstream oss;
|
||||
auto it = SUPERUSER_BINARIES.begin();
|
||||
if (it != SUPERUSER_BINARIES.end()) {
|
||||
|
@ -56,17 +56,30 @@ static bool executableExistsInPath(const std::string& exe) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static std::optional<std::pair<std::string, int>> execAndGet(std::string_view cmd, bool noRedirect = false) {
|
||||
std::string command = std::string{cmd};
|
||||
if (!noRedirect)
|
||||
command += " 2>&1";
|
||||
static std::string subin() {
|
||||
static std::string bin;
|
||||
static bool once = true;
|
||||
if (!once)
|
||||
return bin;
|
||||
|
||||
CProcess proc("/bin/sh", {"-c", command});
|
||||
if (!proc.runSync())
|
||||
// optional handles nullopt gracefully
|
||||
return std::nullopt;
|
||||
for (const auto& BIN : SUPERUSER_BINARIES) {
|
||||
if (!executableExistsInPath(std::string{BIN}))
|
||||
continue;
|
||||
|
||||
return {{proc.stdOut(), proc.exitCode()}};
|
||||
bin = BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
once = false;
|
||||
|
||||
if (bin.empty())
|
||||
Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr()));
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
static bool verifyStringValid(const std::string& s) {
|
||||
return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; });
|
||||
}
|
||||
|
||||
int NSys::getUID() {
|
||||
|
@ -85,39 +98,70 @@ bool NSys::isSuperuser() {
|
|||
return getuid() != geteuid() || geteuid() == 0;
|
||||
}
|
||||
|
||||
std::string NSys::runAsSuperuser(const std::string& cmd) {
|
||||
for (const auto& BIN : SUPERUSER_BINARIES) {
|
||||
if (!executableExistsInPath(std::string{BIN}))
|
||||
continue;
|
||||
|
||||
const auto result = execAndGet(std::string{BIN} + " /bin/sh -c \"" + cmd + "\"", true);
|
||||
if (!result.has_value() || result->second != 0)
|
||||
Debug::die("Failed to run a command as sudo. This could be due to an invalid password, or a hyprpm bug.");
|
||||
|
||||
return result->first;
|
||||
}
|
||||
|
||||
Debug::die("{} {}", "Failed to find a superuser binary. Supported: ", fetchSuperuserBins());
|
||||
return "";
|
||||
}
|
||||
|
||||
void NSys::cacheSudo() {
|
||||
void NSys::root::cacheSudo() {
|
||||
// "caches" the sudo so that the prompt later doesn't pop up in a weird spot
|
||||
// sudo will not ask us again for a moment
|
||||
runAsSuperuser("echo e > /dev/null");
|
||||
CProcess proc(subin(), {"echo", "hyprland"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
void NSys::dropSudo() {
|
||||
for (const auto& BIN : SUPERUSER_BINARIES) {
|
||||
if (!executableExistsInPath(std::string{BIN}))
|
||||
continue;
|
||||
|
||||
if (BIN == "sudo")
|
||||
execAndGet("sudo -k");
|
||||
else {
|
||||
// note the superuser binary that is being dropped
|
||||
std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", BIN));
|
||||
}
|
||||
void NSys::root::dropSudo() {
|
||||
if (subin() != "sudo") {
|
||||
std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin()));
|
||||
return;
|
||||
}
|
||||
|
||||
CProcess proc(subin(), {"-k"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
bool NSys::root::createDirectory(const std::string& path, const std::string& mode) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::removeRecursive(const std::string& path) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
std::error_code ec;
|
||||
const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec);
|
||||
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm"))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) {
|
||||
if (!verifyStringValid(what) || !verifyStringValid(where))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"install", "-m" + mode, "-o", "root", "-g", "root", what, where});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) {
|
||||
CProcess proc(subin(), {"/bin/sh", "-c", cmd});
|
||||
|
||||
if (!proc.runSync())
|
||||
return "";
|
||||
|
||||
return proc.stdOut();
|
||||
}
|
||||
|
|
|
@ -3,10 +3,21 @@
|
|||
#include <string>
|
||||
|
||||
namespace NSys {
|
||||
bool isSuperuser();
|
||||
int getUID();
|
||||
int getEUID();
|
||||
std::string runAsSuperuser(const std::string& cmd);
|
||||
void cacheSudo();
|
||||
void dropSudo();
|
||||
bool isSuperuser();
|
||||
int getUID();
|
||||
int getEUID();
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace root {
|
||||
void cacheSudo();
|
||||
void dropSudo();
|
||||
|
||||
//
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool createDirectory(const std::string& path, const std::string& mode);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool removeRecursive(const std::string& path);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool install(const std::string& what, const std::string& where, const std::string& mode);
|
||||
|
||||
// Do not use this unless absolutely necessary!
|
||||
std::string runAsSuperuserUnsafe(const std::string& cmd);
|
||||
};
|
||||
};
|
|
@ -101,8 +101,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
if (command.size() >= 3)
|
||||
rev = command[2];
|
||||
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
|
||||
} else if (command[0] == "remove") {
|
||||
|
@ -111,13 +111,13 @@ int main(int argc, char** argv, char** envp) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
||||
} else if (command[0] == "update") {
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||
bool headers = g_pPluginManager->updateHeaders(force);
|
||||
|
@ -152,8 +152,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
|
@ -173,8 +173,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
|
@ -200,8 +200,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
||||
}
|
||||
} else if (command[0] == "purge-cache") {
|
||||
NSys::cacheSudo();
|
||||
CScopeGuard x([] { NSys::dropSudo(); });
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
DataState::purgeAllCache();
|
||||
} else if (command[0] == "list") {
|
||||
g_pPluginManager->listAllPlugins();
|
||||
|
|
Loading…
Reference in a new issue