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
|
gbm
|
||||||
hyprutils>=0.2.6
|
hyprutils>=0.2.6
|
||||||
sdbus-c++>=2.0.0
|
sdbus-c++>=2.0.0
|
||||||
|
libsodium
|
||||||
hyprgraphics)
|
hyprgraphics)
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
libdrm,
|
libdrm,
|
||||||
libGL,
|
libGL,
|
||||||
libjpeg,
|
libjpeg,
|
||||||
|
libsodium,
|
||||||
libwebp,
|
libwebp,
|
||||||
libxkbcommon,
|
libxkbcommon,
|
||||||
mesa,
|
mesa,
|
||||||
|
@ -38,6 +39,7 @@ stdenv.mkDerivation {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
cairo
|
cairo
|
||||||
file
|
file
|
||||||
|
libsodium
|
||||||
libdrm
|
libdrm
|
||||||
libGL
|
libGL
|
||||||
libjpeg
|
libjpeg
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Auth.hpp"
|
#include "Auth.hpp"
|
||||||
#include "Pam.hpp"
|
#include "Pam.hpp"
|
||||||
#include "Fingerprint.hpp"
|
#include "Fingerprint.hpp"
|
||||||
|
#include "SodiumPWHash.hpp"
|
||||||
#include "../config/ConfigManager.hpp"
|
#include "../config/ConfigManager.hpp"
|
||||||
#include "../core/hyprlock.hpp"
|
#include "../core/hyprlock.hpp"
|
||||||
#include "src/helpers/Log.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");
|
static auto* const PENABLEFINGERPRINT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:fingerprint:enabled");
|
||||||
if (**PENABLEFINGERPRINT)
|
if (**PENABLEFINGERPRINT)
|
||||||
m_vImpls.push_back(std::make_shared<CFingerprint>());
|
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!");
|
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
enum eAuthImplementations {
|
enum eAuthImplementations {
|
||||||
AUTH_IMPL_PAM = 0,
|
AUTH_IMPL_PAM = 0,
|
||||||
AUTH_IMPL_FINGERPRINT = 1,
|
AUTH_IMPL_FINGERPRINT = 1,
|
||||||
|
AUTH_IMPL_SODIUM = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class IAuthImplementation {
|
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:enabled", Hyprlang::INT{0});
|
||||||
m_config.addConfigValue("auth:fingerprint:ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"});
|
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: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.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||||
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
|
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
|
||||||
|
|
Loading…
Reference in a new issue