mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 21:39:47 +01:00
auth: add sodium pwhash authentication
This commit is contained in:
parent
d12b4a7fba
commit
3f20cc2679
7 changed files with 177 additions and 0 deletions
|
@ -58,6 +58,7 @@ pkg_check_modules(
|
|||
gbm
|
||||
hyprutils>=0.2.6
|
||||
sdbus-c++>=2.0.0
|
||||
libsodium
|
||||
hyprgraphics)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
libdrm,
|
||||
libGL,
|
||||
libjpeg,
|
||||
libsodium,
|
||||
libwebp,
|
||||
libxkbcommon,
|
||||
mesa,
|
||||
|
@ -38,6 +39,7 @@ stdenv.mkDerivation {
|
|||
buildInputs = [
|
||||
cairo
|
||||
file
|
||||
libsodium
|
||||
libdrm
|
||||
libGL
|
||||
libjpeg
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "Auth.hpp"
|
||||
#include "Pam.hpp"
|
||||
#include "Fingerprint.hpp"
|
||||
#include "SodiumPWHash.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "src/helpers/Log.hpp"
|
||||
|
@ -15,7 +16,11 @@ CAuth::CAuth() {
|
|||
static auto* const PENABLEFINGERPRINT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:fingerprint:enabled");
|
||||
if (**PENABLEFINGERPRINT)
|
||||
m_vImpls.push_back(std::make_shared<CFingerprint>());
|
||||
static auto* const PENABLESODIUM = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:sodium:enabled");
|
||||
if (**PENABLESODIUM)
|
||||
m_vImpls.push_back(std::make_shared<CSodiumPWHash>());
|
||||
|
||||
RASSERT(!(**PENABLEPAM && **PENABLESODIUM), "Pam and sodium hash authentication are mutually exclusive! Please enable one or the other.");
|
||||
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
enum eAuthImplementations {
|
||||
AUTH_IMPL_PAM = 0,
|
||||
AUTH_IMPL_FINGERPRINT = 1,
|
||||
AUTH_IMPL_SODIUM = 2,
|
||||
};
|
||||
|
||||
class IAuthImplementation {
|
||||
|
|
124
src/auth/SodiumPWHash.cpp
Normal file
124
src/auth/SodiumPWHash.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "SodiumPWHash.hpp"
|
||||
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <hyprutils/path/Path.hpp>
|
||||
#include <sodium.h>
|
||||
|
||||
static std::string getSecretsConfigPath() {
|
||||
static const auto [PWHASHPATH, DOTDIR] = Hyprutils::Path::findConfig("hyprlock_pwhash");
|
||||
(void)DOTDIR;
|
||||
|
||||
RASSERT(PWHASHPATH.has_value(), "[SodiumAuth] Failed to find hyprlock_pwhash.conf. Please use \"hyprlock-setpwhash\" to generate it!");
|
||||
// check permissions
|
||||
using std::filesystem::perms;
|
||||
const auto PERMS = std::filesystem::status(PWHASHPATH.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) {
|
||||
RASSERT(false, "[SodiumAuth] hyprlock_pwhash.conf has insecure permissions");
|
||||
}
|
||||
return PWHASHPATH.value();
|
||||
}
|
||||
|
||||
void* const* CSodiumPWHash::getConfigValuePtr(const std::string& name) {
|
||||
return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr();
|
||||
}
|
||||
|
||||
CSodiumPWHash::CSodiumPWHash() : m_config(getSecretsConfigPath().c_str(), {}) {
|
||||
m_config.addConfigValue("pw_hash", Hyprlang::STRING{""});
|
||||
m_config.commence();
|
||||
auto result = m_config.parse();
|
||||
|
||||
if (result.error)
|
||||
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());
|
||||
|
||||
m_checkerThread = std::thread([this]() { checkerLoop(); });
|
||||
}
|
||||
|
||||
CSodiumPWHash::~CSodiumPWHash() {
|
||||
;
|
||||
}
|
||||
|
||||
void CSodiumPWHash::init() {
|
||||
RASSERT(sodium_init() >= 0, "Failed to initialize libsodium");
|
||||
}
|
||||
|
||||
void CSodiumPWHash::handleInput(const std::string& input) {
|
||||
std::lock_guard<std::mutex> lk(m_sCheckerState.requestMutex);
|
||||
|
||||
m_sCheckerState.input = input;
|
||||
m_sCheckerState.requested = true;
|
||||
|
||||
m_sCheckerState.requestCV.notify_all();
|
||||
}
|
||||
|
||||
bool CSodiumPWHash::checkWaiting() {
|
||||
return m_sCheckerState.requested;
|
||||
}
|
||||
|
||||
std::optional<std::string> CSodiumPWHash::getLastFailText() {
|
||||
return m_sCheckerState.failText.empty() ? std::nullopt : std::optional<std::string>(m_sCheckerState.failText);
|
||||
}
|
||||
|
||||
std::optional<std::string> CSodiumPWHash::getLastPrompt() {
|
||||
return "Password: ";
|
||||
}
|
||||
|
||||
void CSodiumPWHash::terminate() {
|
||||
m_sCheckerState.requestCV.notify_all();
|
||||
if (m_checkerThread.joinable())
|
||||
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<std::mutex> lk(m_sCheckerState.requestMutex);
|
||||
m_sCheckerState.requestCV.wait(lk, [this]() { return m_sCheckerState.requested || g_pHyprlock->m_bTerminate; });
|
||||
|
||||
if (g_pHyprlock->isUnlocked())
|
||||
return;
|
||||
|
||||
if (PWHASH.empty() || PWHASH.size() > crypto_pwhash_STRBYTES) {
|
||||
m_sCheckerState.failText = "Invalid password hash";
|
||||
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();
|
||||
m_sCheckerState.failText = "Failed to authenticate";
|
||||
Debug::log(LOG, "[SodiumAuth] Failed to authenticate");
|
||||
}
|
||||
|
||||
m_sCheckerState.input.clear();
|
||||
m_sCheckerState.requested = false;
|
||||
}
|
||||
}
|
43
src/auth/SodiumPWHash.hpp
Normal file
43
src/auth/SodiumPWHash.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "Auth.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <hyprlang.hpp>
|
||||
#include <thread>
|
||||
|
||||
class CSodiumPWHash : public IAuthImplementation {
|
||||
public:
|
||||
CSodiumPWHash();
|
||||
|
||||
virtual ~CSodiumPWHash();
|
||||
virtual eAuthImplementations getImplType() {
|
||||
return AUTH_IMPL_SODIUM;
|
||||
}
|
||||
virtual void init();
|
||||
virtual void handleInput(const std::string& input);
|
||||
virtual bool checkWaiting();
|
||||
virtual std::optional<std::string> getLastFailText();
|
||||
virtual std::optional<std::string> getLastPrompt();
|
||||
virtual void terminate();
|
||||
|
||||
private:
|
||||
void* const* getConfigValuePtr(const std::string& name);
|
||||
|
||||
struct {
|
||||
std::condition_variable requestCV;
|
||||
std::string input;
|
||||
bool requested = false;
|
||||
std::mutex requestMutex;
|
||||
std::string failText;
|
||||
} m_sCheckerState;
|
||||
|
||||
std::thread m_checkerThread;
|
||||
void checkerLoop();
|
||||
|
||||
void rehash(std::string& input);
|
||||
|
||||
Hyprlang::CConfig m_config;
|
||||
};
|
|
@ -178,6 +178,7 @@ void CConfigManager::init() {
|
|||
m_config.addConfigValue("auth:fingerprint:enabled", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("auth:fingerprint:ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"});
|
||||
m_config.addConfigValue("auth:fingerprint:present_message", Hyprlang::STRING{"Scanning fingerprint"});
|
||||
m_config.addConfigValue("auth:sodium:enabled", Hyprlang::INT{1});
|
||||
|
||||
m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
|
||||
|
|
Loading…
Reference in a new issue