From 9adc28cb988a162b7a0db7d3642de6406ae69424 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler Date: Mon, 16 Dec 2024 16:13:48 +0100 Subject: [PATCH] auth: add hyprlock-setpwhash target for setting the password hash --- CMakeLists.txt | 6 +- setpwhash/main.cpp | 150 ++++++++++++++++++++++++++++++++++++++ src/auth/SodiumPWHash.cpp | 23 ------ src/auth/SodiumPWHash.hpp | 2 - 4 files changed, 155 insertions(+), 26 deletions(-) create mode 100644 setpwhash/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 408bee3b..aa923015 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,8 +116,12 @@ protocol("protocols/wlr-screencopy-unstable-v1.xml" protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) +# hyprlock-setpwhash +add_executable(hyprlock-setpwhash "setpwhash/main.cpp") +target_link_libraries(hyprlock-setpwhash PRIVATE sodium hyprutils) + # Installation -install(TARGETS hyprlock) +install(TARGETS hyprlock hyprlock-setpwhash) install(FILES ${CMAKE_SOURCE_DIR}/pam/hyprlock DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d) diff --git a/setpwhash/main.cpp b/setpwhash/main.cpp new file mode 100644 index 00000000..9db75a5b --- /dev/null +++ b/setpwhash/main.cpp @@ -0,0 +1,150 @@ +#include "../src/helpers/Log.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::filesystem::perms; + +void setStdinEcho(bool enable = true) { + struct termios tty; + tcgetattr(STDIN_FILENO, &tty); + if (!enable) + tty.c_lflag &= ~ECHO; + else + tty.c_lflag |= ECHO; + RASSERT(tcsetattr(STDIN_FILENO, TCSANOW, &tty) == 0, "Failed to set terminal attributes"); +} + +void readPW(std::string& pw) { + setStdinEcho(false); + std::string input; + std::getline(std::cin, pw); + setStdinEcho(true); +} + +enum HashCost { INTERACTIVE, MODERATE, SENSITIVE }; + +unsigned int getOpsLimit(HashCost cost) { + switch (cost) { + case HashCost::INTERACTIVE: + return crypto_pwhash_OPSLIMIT_INTERACTIVE; + case HashCost::MODERATE: + return crypto_pwhash_OPSLIMIT_MODERATE; + case HashCost::SENSITIVE: + return crypto_pwhash_OPSLIMIT_SENSITIVE; + default: + return crypto_pwhash_OPSLIMIT_MODERATE; + } + std::unreachable(); +} + +unsigned int getMemLimit(HashCost cost) { + switch (cost) { + case HashCost::INTERACTIVE: + return crypto_pwhash_MEMLIMIT_INTERACTIVE; + case HashCost::MODERATE: + return crypto_pwhash_MEMLIMIT_MODERATE; + case HashCost::SENSITIVE: + return crypto_pwhash_MEMLIMIT_SENSITIVE; + default: + return crypto_pwhash_MEMLIMIT_MODERATE; + } + std::unreachable(); +} + +void help() { + std::cout << "Usage: hyprlock-setpwhash [hashing cost]" << std::endl; + std::cout << "Set the password hash for hyprlock" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help Show this help message and exit" << std::endl; + std::cout << " [hashing cost] How computationally expensive should the hashing be?" << std::endl; + std::cout << " interactive - fast checking, least security" << std::endl; + std::cout << " moderate (default) - moderate checking speed, moderate security" << std::endl; + std::cout << " sensitive - slow checking speed, high security" << std::endl; +} + + +int main(int argc, char** argv, char** envp) { + std::vector args(argv, argv + argc); + + RASSERT(sodium_init() >= 0, "Failed to initialize libsodium"); + + auto hashCost = HashCost::MODERATE; + for (std::size_t i = 1; i < args.size(); ++i) { + const std::string arg = argv[i]; + + if (arg == "--help" || arg == "-h") { + help(); + return 0; + } + + if (arg == "interactive") + hashCost = HashCost::INTERACTIVE; + else if (arg == "moderate") + hashCost = HashCost::MODERATE; + else if (arg == "sensitive") + hashCost = HashCost::SENSITIVE; + else { + std::cerr << "Unknown argument: " << arg << std::endl; + help(); + return 1; + } + } + + static const auto [SECRETSCONF, DOTDIR] = Hyprutils::Path::findConfig("hyprlock_pwhash"); + if (SECRETSCONF.has_value()) { + // check permissions + std::cout << SECRETSCONF.value() << " already exists" << std::endl; + std::cout << "Do you want to overwrite it? [y/N] "; + char C; + std::cin >> C; + std::cin.ignore(10, '\n'); + + if (C != 'y' && C != 'Y') { + std::cout << "Keeping existing secrets!" << std::endl; + + const auto PERMS = std::filesystem::status(SECRETSCONF.value()).permissions(); + if ((PERMS & perms::group_read) != perms::none || (PERMS & perms::group_write) != perms::none || (PERMS & perms::others_read) != perms::none || + (PERMS & perms::others_write) != perms::none) { + std::cout << "Setting permissions of " << SECRETSCONF.value() << " to -rw-------" << std::endl; + // set perms to -rw------- + std::filesystem::permissions(SECRETSCONF.value(), perms::owner_read | perms::owner_write); + } + return 0; + } + } + + RASSERT(DOTDIR.has_value(), "Failed to find config directory!"); + const auto DEST = DOTDIR.value() + "/hypr/hyprlock_pwhash.conf"; + + std::string pw; + std::cout << "Enter password: "; + + readPW(pw); + + std::cout << "\r"; + + char hash[crypto_pwhash_STRBYTES]; + if (crypto_pwhash_str(hash, pw.c_str(), pw.size(), getOpsLimit(hashCost), getMemLimit(hashCost)) != 0) { + Debug::log(ERR, "[Sodium] Failed to hash password"); + return 1; + } + + std::cout << "🔒 Writing password hash to " << DEST << std::endl; + + std::ofstream out(DEST); + out << "pw_hash = " << hash << std::endl; + out.close(); + + // set perms to -rw------- + std::filesystem::permissions(DEST, perms::owner_read | perms::owner_write); + return 0; +} diff --git a/src/auth/SodiumPWHash.cpp b/src/auth/SodiumPWHash.cpp index c2b18c21..83120417 100644 --- a/src/auth/SodiumPWHash.cpp +++ b/src/auth/SodiumPWHash.cpp @@ -73,30 +73,9 @@ void CSodiumPWHash::terminate() { m_checkerThread.join(); } -void CSodiumPWHash::rehash(std::string& input) { - const auto CONFIGPATH = getSecretsConfigPath(); - - char hash[crypto_pwhash_STRBYTES]; - if (crypto_pwhash_str(hash, input.c_str(), input.size(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) { - Debug::log(ERR, "[Sodium] Failed to hash password"); - return; - } - - std::ofstream out(CONFIGPATH); - out << "hyprlock {\n pw_hash = " << hash << "\n}\n"; - out.close(); - - // set perms to -rw------- - using std::filesystem::perms; - std::filesystem::permissions(CONFIGPATH, perms::owner_read | perms::owner_write); -} - void CSodiumPWHash::checkerLoop() { static auto* const PPWHASH = (Hyprlang::STRING*)getConfigValuePtr("pw_hash"); const auto PWHASH = std::string(*PPWHASH); - const bool NEEDSREHASH = crypto_pwhash_str_needs_rehash(PWHASH.c_str(), crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0; - if (NEEDSREHASH) - Debug::log(WARN, "[Sodium] Password hash needs rehashing"); while (true) { std::unique_lock lk(m_sCheckerState.requestMutex); @@ -110,8 +89,6 @@ void CSodiumPWHash::checkerLoop() { Debug::log(ERR, "[SodiumAuth] Invalid password hash set in secrets.conf"); g_pAuth->enqueueFail(); } else if (crypto_pwhash_str_verify(PWHASH.c_str(), m_sCheckerState.input.c_str(), m_sCheckerState.input.length()) == 0) { - if (NEEDSREHASH) - rehash(m_sCheckerState.input); g_pAuth->enqueueUnlock(); } else { g_pAuth->enqueueFail(); diff --git a/src/auth/SodiumPWHash.hpp b/src/auth/SodiumPWHash.hpp index f84bf17b..a29fd853 100644 --- a/src/auth/SodiumPWHash.hpp +++ b/src/auth/SodiumPWHash.hpp @@ -37,7 +37,5 @@ class CSodiumPWHash : public IAuthImplementation { std::thread m_checkerThread; void checkerLoop(); - void rehash(std::string& input); - Hyprlang::CConfig m_config; };