mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 05:19:48 +01:00
auth: add an interface for different authentication methods (#578)
* auth: add an interface for different authentication methods * auth: pick inline feedback based on last active implementation * config: move auth options to auth:<auth_impl> BREAKING: - general:pam_module -> auth:pam:module - general:enable_fingerprint -> auth:fingerprint:enabled - general:fingerprint_ready_message -> auth:fingerprint:ready_message - general:fingerprint_present_message -> auth:fingerprint:present_message * auth: don't clear password input for fingerprint auth check * fingerprint: checkAuthenticated when handling verfiy status * Revert conditionally clearing the password input buffer Makes sure the input field can show the fail text for fingerprint auth. * auth: virtual instead of override, remove braces * pam: join the thread * auth: remove isAuthenticated and switch to a control flow based unlock * auth: initialize authentication before aquiring the session lock
This commit is contained in:
parent
4681f8f7f3
commit
a4b0562749
12 changed files with 355 additions and 174 deletions
116
src/auth/Auth.cpp
Normal file
116
src/auth/Auth.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "Auth.hpp"
|
||||
#include "Pam.hpp"
|
||||
#include "Fingerprint.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "src/helpers/Log.hpp"
|
||||
|
||||
#include <hyprlang.hpp>
|
||||
#include <memory>
|
||||
|
||||
CAuth::CAuth() {
|
||||
static auto* const PENABLEPAM = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:pam:enabled");
|
||||
if (**PENABLEPAM)
|
||||
m_vImpls.push_back(std::make_shared<CPam>());
|
||||
static auto* const PENABLEFINGERPRINT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("auth:fingerprint:enabled");
|
||||
if (**PENABLEFINGERPRINT)
|
||||
m_vImpls.push_back(std::make_shared<CFingerprint>());
|
||||
|
||||
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
|
||||
}
|
||||
|
||||
void CAuth::start() {
|
||||
for (const auto& i : m_vImpls) {
|
||||
i->init();
|
||||
}
|
||||
}
|
||||
|
||||
void CAuth::submitInput(const std::string& input) {
|
||||
for (const auto& i : m_vImpls) {
|
||||
i->handleInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
bool CAuth::checkWaiting() {
|
||||
for (const auto& i : m_vImpls) {
|
||||
if (i->checkWaiting())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string CAuth::getInlineFeedback() {
|
||||
std::optional<std::string> firstFeedback = std::nullopt;
|
||||
for (const auto& i : m_vImpls) {
|
||||
const auto FEEDBACK = (m_bDisplayFailText) ? i->getLastFailText() : i->getLastPrompt();
|
||||
if (!FEEDBACK.has_value())
|
||||
continue;
|
||||
|
||||
if (!firstFeedback.has_value())
|
||||
firstFeedback = FEEDBACK;
|
||||
|
||||
if (i->getImplType() == m_eLastActiveImpl)
|
||||
return FEEDBACK.value();
|
||||
}
|
||||
|
||||
return firstFeedback.value_or("Ups, no authentication feedack");
|
||||
}
|
||||
|
||||
std::optional<std::string> CAuth::getFailText(eAuthImplementations implType) {
|
||||
for (const auto& i : m_vImpls) {
|
||||
if (i->getImplType() == implType)
|
||||
return i->getLastFailText();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CAuth::getPrompt(eAuthImplementations implType) {
|
||||
for (const auto& i : m_vImpls) {
|
||||
if (i->getImplType() == implType)
|
||||
return i->getLastPrompt();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<IAuthImplementation> CAuth::getImpl(eAuthImplementations implType) {
|
||||
for (const auto& i : m_vImpls) {
|
||||
if (i->getImplType() == implType)
|
||||
return i;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CAuth::terminate() {
|
||||
for (const auto& i : m_vImpls) {
|
||||
i->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
|
||||
g_pHyprlock->clearPasswordBuffer();
|
||||
g_pAuth->m_iFailedAttempts++;
|
||||
Debug::log(LOG, "Failed attempts: {}", g_pAuth->m_iFailedAttempts);
|
||||
|
||||
g_pAuth->m_bDisplayFailText = true;
|
||||
g_pHyprlock->enqueueForceUpdateTimers();
|
||||
|
||||
g_pHyprlock->renderAllOutputs();
|
||||
}
|
||||
|
||||
static void passwordUnlockCallback(std::shared_ptr<CTimer> self, void* data) {
|
||||
g_pHyprlock->unlock();
|
||||
}
|
||||
|
||||
void CAuth::enqueueFail() {
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordFailCallback, nullptr);
|
||||
}
|
||||
|
||||
void CAuth::enqueueUnlock() {
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordUnlockCallback, nullptr);
|
||||
}
|
||||
|
||||
void CAuth::postActivity(eAuthImplementations implType) {
|
||||
m_eLastActiveImpl = implType;
|
||||
}
|
61
src/auth/Auth.hpp
Normal file
61
src/auth/Auth.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
enum eAuthImplementations {
|
||||
AUTH_IMPL_PAM = 0,
|
||||
AUTH_IMPL_FINGERPRINT = 1,
|
||||
};
|
||||
|
||||
class IAuthImplementation {
|
||||
public:
|
||||
virtual ~IAuthImplementation() = default;
|
||||
|
||||
virtual eAuthImplementations getImplType() = 0;
|
||||
virtual void init() = 0;
|
||||
virtual void handleInput(const std::string& input) = 0;
|
||||
virtual bool checkWaiting() = 0;
|
||||
virtual std::optional<std::string> getLastFailText() = 0;
|
||||
virtual std::optional<std::string> getLastPrompt() = 0;
|
||||
virtual void terminate() = 0;
|
||||
|
||||
friend class CAuth;
|
||||
};
|
||||
|
||||
class CAuth {
|
||||
public:
|
||||
CAuth();
|
||||
|
||||
void start();
|
||||
|
||||
void submitInput(const std::string& input);
|
||||
bool checkWaiting();
|
||||
|
||||
// Used by the PasswordInput field. We are constraint to a single line for the authentication feedback there.
|
||||
// Based on m_bDisplayFailText, this will return either the fail text or the prompt.
|
||||
// Based on m_eLastActiveImpl, it will select the implementation.
|
||||
std::string getInlineFeedback();
|
||||
|
||||
std::optional<std::string> getFailText(eAuthImplementations implType);
|
||||
std::optional<std::string> getPrompt(eAuthImplementations implType);
|
||||
|
||||
std::shared_ptr<IAuthImplementation> getImpl(eAuthImplementations implType);
|
||||
|
||||
void terminate();
|
||||
|
||||
// Should only be set via the main thread
|
||||
bool m_bDisplayFailText = false;
|
||||
size_t m_iFailedAttempts = 0;
|
||||
|
||||
void enqueueUnlock();
|
||||
void enqueueFail();
|
||||
void postActivity(eAuthImplementations implType);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<IAuthImplementation>> m_vImpls;
|
||||
std::optional<eAuthImplementations> m_eLastActiveImpl = std::nullopt;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CAuth> g_pAuth;
|
|
@ -1,13 +1,13 @@
|
|||
#include "Fingerprint.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
static const auto FPRINT = sdbus::ServiceName{"net.reactivated.Fprint"};
|
||||
static const auto DEVICE = sdbus::ServiceName{"net.reactivated.Fprint.Device"};
|
||||
|
@ -37,18 +37,17 @@ static std::map<std::string, MatchResult> s_mapStringToTestType = {{"verify-no-m
|
|||
{"verify-unknown-error", MATCH_UNKNOWN_ERROR}};
|
||||
|
||||
CFingerprint::CFingerprint() {
|
||||
static auto* const PFINGERPRINTREADY = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("general:fingerprint_ready_message"));
|
||||
static auto* const PFINGERPRINTREADY = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("auth:fingerprint:ready_message"));
|
||||
m_sFingerprintReady = *PFINGERPRINTREADY;
|
||||
static auto* const PFINGERPRINTPRESENT = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("general:fingerprint_present_message"));
|
||||
static auto* const PFINGERPRINTPRESENT = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("auth:fingerprint:present_message"));
|
||||
m_sFingerprintPresent = *PFINGERPRINTPRESENT;
|
||||
static auto* const PENABLEFINGERPRINT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:enable_fingerprint");
|
||||
m_bEnabled = **PENABLEFINGERPRINT;
|
||||
}
|
||||
|
||||
std::shared_ptr<sdbus::IConnection> CFingerprint::start() {
|
||||
if (!m_bEnabled)
|
||||
return {};
|
||||
CFingerprint::~CFingerprint() {
|
||||
;
|
||||
}
|
||||
|
||||
void CFingerprint::init() {
|
||||
m_sDBUSState.connection = sdbus::createSystemBusConnection();
|
||||
m_sDBUSState.login = sdbus::createProxy(*m_sDBUSState.connection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"});
|
||||
m_sDBUSState.login->getPropertyAsync("PreparingForSleep").onInterface(LOGIN_MANAGER).uponReplyInvoke([this](std::optional<sdbus::Error> e, sdbus::Variant preparingForSleep) {
|
||||
|
@ -75,22 +74,33 @@ std::shared_ptr<sdbus::IConnection> CFingerprint::start() {
|
|||
startVerify();
|
||||
}
|
||||
});
|
||||
return m_sDBUSState.connection;
|
||||
}
|
||||
|
||||
bool CFingerprint::isAuthenticated() {
|
||||
return m_bAuthenticated;
|
||||
void CFingerprint::handleInput(const std::string& input) {
|
||||
;
|
||||
}
|
||||
|
||||
std::optional<std::string> CFingerprint::getLastMessage() {
|
||||
std::optional<std::string> CFingerprint::getLastFailText() {
|
||||
return m_sDBUSState.message.empty() ? std::nullopt : std::optional(m_sDBUSState.message);
|
||||
}
|
||||
|
||||
std::optional<std::string> CFingerprint::getLastPrompt() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool CFingerprint::checkWaiting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CFingerprint::terminate() {
|
||||
if (!m_sDBUSState.abort)
|
||||
releaseDevice();
|
||||
}
|
||||
|
||||
std::shared_ptr<sdbus::IConnection> CFingerprint::getConnection() {
|
||||
return m_sDBUSState.connection;
|
||||
}
|
||||
|
||||
void CFingerprint::inhibitSleep() {
|
||||
m_sDBUSState.login->callMethodAsync("Inhibit")
|
||||
.onInterface(LOGIN_MANAGER)
|
||||
|
@ -139,8 +149,10 @@ bool CFingerprint::createDeviceProxy() {
|
|||
}
|
||||
|
||||
void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
|
||||
g_pAuth->postActivity(AUTH_IMPL_FINGERPRINT);
|
||||
Debug::log(LOG, "fprint: handling status {}", result);
|
||||
auto matchResult = s_mapStringToTestType[result];
|
||||
auto matchResult = s_mapStringToTestType[result];
|
||||
bool authenticated = false;
|
||||
if (m_sDBUSState.sleeping && matchResult != MATCH_DISCONNECTED)
|
||||
return;
|
||||
switch (matchResult) {
|
||||
|
@ -148,21 +160,22 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
|
|||
case MATCH_NO_MATCH:
|
||||
stopVerify();
|
||||
if (m_sDBUSState.retries >= 3) {
|
||||
m_sDBUSState.message = "Fingerprint auth disabled: too many failed attempts";
|
||||
m_sDBUSState.message = "Fingerprint auth disabled (too many failed attempts)";
|
||||
} else {
|
||||
done = false;
|
||||
startVerify(true);
|
||||
m_sDBUSState.message = "Fingerprint not matched";
|
||||
}
|
||||
break;
|
||||
case MATCH_UNKNOWN_ERROR:
|
||||
stopVerify();
|
||||
m_sDBUSState.message = "Unknown fingerprint error, disabling fingerprint auth";
|
||||
m_sDBUSState.message = "Fingerprint auth disabled (unknown error)";
|
||||
break;
|
||||
case MATCH_MATCHED:
|
||||
stopVerify();
|
||||
m_bAuthenticated = true;
|
||||
m_sDBUSState.message = "";
|
||||
g_pHyprlock->unlock();
|
||||
authenticated = true;
|
||||
g_pAuth->enqueueUnlock();
|
||||
break;
|
||||
case MATCH_RETRY: m_sDBUSState.message = "Please retry fingerprint scan"; break;
|
||||
case MATCH_SWIPE_TOO_SHORT: m_sDBUSState.message = "Swipe too short - try again"; break;
|
||||
|
@ -173,7 +186,10 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
|
|||
m_sDBUSState.abort = true;
|
||||
break;
|
||||
}
|
||||
g_pHyprlock->enqueueForceUpdateTimers();
|
||||
|
||||
if (!authenticated)
|
||||
g_pAuth->enqueueFail();
|
||||
|
||||
if (done || m_sDBUSState.abort)
|
||||
m_sDBUSState.done = true;
|
||||
}
|
||||
|
@ -203,7 +219,7 @@ void CFingerprint::startVerify(bool isRetry) {
|
|||
if (e) {
|
||||
Debug::log(WARN, "fprint: could not start verifying, {}", e->what());
|
||||
if (isRetry)
|
||||
m_sDBUSState.message = "Fingerprint auth disabled: could not restart verification";
|
||||
m_sDBUSState.message = "Fingerprint auth disabled (failed to restart)";
|
||||
} else {
|
||||
Debug::log(LOG, "fprint: started verifying");
|
||||
if (isRetry) {
|
|
@ -1,20 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "hyprlock.hpp"
|
||||
#include "Auth.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
class CFingerprint {
|
||||
class CFingerprint : public IAuthImplementation {
|
||||
public:
|
||||
CFingerprint();
|
||||
|
||||
std::shared_ptr<sdbus::IConnection> start();
|
||||
bool isAuthenticated();
|
||||
std::optional<std::string> getLastMessage();
|
||||
void terminate();
|
||||
virtual ~CFingerprint();
|
||||
virtual eAuthImplementations getImplType() {
|
||||
return AUTH_IMPL_FINGERPRINT;
|
||||
}
|
||||
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();
|
||||
|
||||
std::shared_ptr<sdbus::IConnection> getConnection();
|
||||
|
||||
private:
|
||||
struct SDBUSState {
|
||||
|
@ -33,8 +41,6 @@ class CFingerprint {
|
|||
|
||||
std::string m_sFingerprintReady;
|
||||
std::string m_sFingerprintPresent;
|
||||
bool m_bAuthenticated = false;
|
||||
bool m_bEnabled = false;
|
||||
|
||||
void handleVerifyStatus(const std::string& result, const bool done);
|
||||
|
||||
|
@ -46,5 +52,3 @@ class CFingerprint {
|
|||
bool stopVerify();
|
||||
bool releaseDevice();
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CFingerprint> g_pFingerprint;
|
|
@ -1,7 +1,7 @@
|
|||
#include "Auth.hpp"
|
||||
#include "hyprlock.hpp"
|
||||
#include "Pam.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "src/config/ConfigManager.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
|
@ -15,7 +15,7 @@
|
|||
#include <thread>
|
||||
|
||||
int conv(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) {
|
||||
const auto CONVERSATIONSTATE = (CAuth::SPamConversationState*)appdata_ptr;
|
||||
const auto CONVERSATIONSTATE = (CPam::SPamConversationState*)appdata_ptr;
|
||||
struct pam_response* pamReply = (struct pam_response*)calloc(num_msg, sizeof(struct pam_response));
|
||||
bool initialPrompt = true;
|
||||
|
||||
|
@ -34,7 +34,7 @@ int conv(int num_msg, const struct pam_message** msg, struct pam_response** resp
|
|||
// When the prompt is the same as the last one, I guess our answer can be the same.
|
||||
if (!initialPrompt && PROMPTCHANGED) {
|
||||
CONVERSATIONSTATE->prompt = PROMPT;
|
||||
g_pAuth->waitForInput();
|
||||
CONVERSATIONSTATE->waitForInput();
|
||||
}
|
||||
|
||||
// Needed for unlocks via SIGUSR1
|
||||
|
@ -60,44 +60,52 @@ int conv(int num_msg, const struct pam_message** msg, struct pam_response** resp
|
|||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
CAuth::CAuth() {
|
||||
static auto* const PPAMMODULE = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("general:pam_module"));
|
||||
CPam::CPam() {
|
||||
static auto* const PPAMMODULE = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("auth:pam:module"));
|
||||
m_sPamModule = *PPAMMODULE;
|
||||
|
||||
if (!std::filesystem::exists(std::filesystem::path("/etc/pam.d/") / m_sPamModule)) {
|
||||
Debug::log(ERR, "Pam module \"/etc/pam.d/{}\" does not exist! Falling back to \"/etc/pam.d/su\"", m_sPamModule);
|
||||
m_sPamModule = "su";
|
||||
}
|
||||
|
||||
m_sConversationState.waitForInput = [this]() { this->waitForInput(); };
|
||||
}
|
||||
|
||||
static void passwordCheckTimerCallback(std::shared_ptr<CTimer> self, void* data) {
|
||||
g_pHyprlock->onPasswordCheckTimer();
|
||||
CPam::~CPam() {
|
||||
;
|
||||
}
|
||||
|
||||
void CAuth::start() {
|
||||
std::thread([this]() {
|
||||
resetConversation();
|
||||
void CPam::init() {
|
||||
m_thread = std::thread([this]() {
|
||||
while (true) {
|
||||
resetConversation();
|
||||
|
||||
// Initial input
|
||||
m_sConversationState.prompt = "Password: ";
|
||||
waitForInput();
|
||||
// Initial input
|
||||
m_sConversationState.prompt = "Password: ";
|
||||
waitForInput();
|
||||
|
||||
// For grace or SIGUSR1 unlocks
|
||||
if (g_pHyprlock->isUnlocked())
|
||||
return;
|
||||
// For grace or SIGUSR1 unlocks
|
||||
if (g_pHyprlock->isUnlocked())
|
||||
return;
|
||||
|
||||
const auto AUTHENTICATED = auth();
|
||||
m_bAuthenticated = AUTHENTICATED;
|
||||
const auto AUTHENTICATED = auth();
|
||||
|
||||
// For SIGUSR1 unlocks
|
||||
if (g_pHyprlock->isUnlocked())
|
||||
return;
|
||||
// For SIGUSR1 unlocks
|
||||
if (g_pHyprlock->isUnlocked())
|
||||
return;
|
||||
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(1), passwordCheckTimerCallback, nullptr);
|
||||
}).detach();
|
||||
if (!AUTHENTICATED)
|
||||
g_pAuth->enqueueFail();
|
||||
else {
|
||||
g_pAuth->enqueueUnlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool CAuth::auth() {
|
||||
bool CPam::auth() {
|
||||
const pam_conv localConv = {conv, (void*)&m_sConversationState};
|
||||
pam_handle_t* handle = NULL;
|
||||
auto uidPassword = getpwuid(getuid());
|
||||
|
@ -115,6 +123,7 @@ bool CAuth::auth() {
|
|||
handle = nullptr;
|
||||
|
||||
m_sConversationState.waitingForPamAuth = false;
|
||||
g_pAuth->postActivity(AUTH_IMPL_PAM);
|
||||
|
||||
if (ret != PAM_SUCCESS) {
|
||||
if (!m_sConversationState.failTextFromPam)
|
||||
|
@ -129,16 +138,12 @@ bool CAuth::auth() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CAuth::isAuthenticated() {
|
||||
return m_bAuthenticated;
|
||||
}
|
||||
|
||||
// clearing the input must be done from the main thread
|
||||
static void clearInputTimerCallback(std::shared_ptr<CTimer> self, void* data) {
|
||||
g_pHyprlock->clearPasswordBuffer();
|
||||
}
|
||||
|
||||
void CAuth::waitForInput() {
|
||||
void CPam::waitForInput() {
|
||||
g_pHyprlock->addTimer(std::chrono::milliseconds(1), clearInputTimerCallback, nullptr);
|
||||
|
||||
std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex);
|
||||
|
@ -149,7 +154,8 @@ void CAuth::waitForInput() {
|
|||
m_bBlockInput = true;
|
||||
}
|
||||
|
||||
void CAuth::submitInput(std::string input) {
|
||||
void CPam::handleInput(const std::string& input) {
|
||||
g_pAuth->postActivity(AUTH_IMPL_PAM);
|
||||
std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex);
|
||||
|
||||
if (!m_sConversationState.inputRequested)
|
||||
|
@ -161,23 +167,25 @@ void CAuth::submitInput(std::string input) {
|
|||
m_sConversationState.inputSubmittedCondition.notify_all();
|
||||
}
|
||||
|
||||
std::optional<std::string> CAuth::getLastFailText() {
|
||||
std::optional<std::string> CPam::getLastFailText() {
|
||||
return m_sConversationState.failText.empty() ? std::nullopt : std::optional(m_sConversationState.failText);
|
||||
}
|
||||
|
||||
std::optional<std::string> CAuth::getLastPrompt() {
|
||||
std::optional<std::string> CPam::getLastPrompt() {
|
||||
return m_sConversationState.prompt.empty() ? std::nullopt : std::optional(m_sConversationState.prompt);
|
||||
}
|
||||
|
||||
bool CAuth::checkWaiting() {
|
||||
bool CPam::checkWaiting() {
|
||||
return m_bBlockInput || m_sConversationState.waitingForPamAuth;
|
||||
}
|
||||
|
||||
void CAuth::terminate() {
|
||||
void CPam::terminate() {
|
||||
m_sConversationState.inputSubmittedCondition.notify_all();
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void CAuth::resetConversation() {
|
||||
void CPam::resetConversation() {
|
||||
m_sConversationState.input = "";
|
||||
m_sConversationState.waitingForPamAuth = false;
|
||||
m_sConversationState.inputRequested = false;
|
52
src/auth/Pam.hpp
Normal file
52
src/auth/Pam.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "Auth.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
class CPam : public IAuthImplementation {
|
||||
public:
|
||||
struct SPamConversationState {
|
||||
std::string input = "";
|
||||
std::string prompt = "";
|
||||
std::string failText = "";
|
||||
|
||||
std::mutex inputMutex;
|
||||
std::condition_variable inputSubmittedCondition;
|
||||
|
||||
bool waitingForPamAuth = false;
|
||||
bool inputRequested = false;
|
||||
bool failTextFromPam = false;
|
||||
std::function<void()> waitForInput = []() {};
|
||||
};
|
||||
|
||||
CPam();
|
||||
|
||||
void waitForInput();
|
||||
|
||||
virtual ~CPam();
|
||||
virtual eAuthImplementations getImplType() {
|
||||
return AUTH_IMPL_PAM;
|
||||
}
|
||||
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:
|
||||
std::thread m_thread;
|
||||
SPamConversationState m_sConversationState;
|
||||
|
||||
bool m_bBlockInput = true;
|
||||
|
||||
std::string m_sPamModule;
|
||||
|
||||
bool auth();
|
||||
void resetConversation();
|
||||
};
|
|
@ -171,11 +171,13 @@ void CConfigManager::init() {
|
|||
m_config.addConfigValue("general:no_fade_out", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:pam_module", Hyprlang::STRING{"hyprlock"});
|
||||
m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2});
|
||||
m_config.addConfigValue("general:enable_fingerprint", Hyprlang::INT{0});
|
||||
m_config.addConfigValue("general:fingerprint_ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"});
|
||||
m_config.addConfigValue("general:fingerprint_present_message", Hyprlang::STRING{"Scanning fingerprint"});
|
||||
|
||||
m_config.addConfigValue("auth:pam:enabled", Hyprlang::INT{1});
|
||||
m_config.addConfigValue("auth:pam:module", Hyprlang::STRING{"hyprlock"});
|
||||
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.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
class CAuth {
|
||||
public:
|
||||
struct SPamConversationState {
|
||||
std::string input = "";
|
||||
std::string prompt = "";
|
||||
std::string failText = "";
|
||||
|
||||
std::mutex inputMutex;
|
||||
std::condition_variable inputSubmittedCondition;
|
||||
|
||||
bool waitingForPamAuth = false;
|
||||
bool inputRequested = false;
|
||||
bool failTextFromPam = false;
|
||||
};
|
||||
|
||||
CAuth();
|
||||
|
||||
void start();
|
||||
bool auth();
|
||||
bool isAuthenticated();
|
||||
|
||||
void waitForInput();
|
||||
void submitInput(std::string input);
|
||||
|
||||
std::optional<std::string> getLastFailText();
|
||||
std::optional<std::string> getLastPrompt();
|
||||
|
||||
bool checkWaiting();
|
||||
|
||||
void terminate();
|
||||
|
||||
// Should only be set via the main thread
|
||||
bool m_bDisplayFailText = false;
|
||||
|
||||
private:
|
||||
SPamConversationState m_sConversationState;
|
||||
|
||||
bool m_bBlockInput = true;
|
||||
bool m_bAuthenticated = false;
|
||||
|
||||
std::string m_sPamModule;
|
||||
|
||||
void resetConversation();
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CAuth> g_pAuth;
|
|
@ -2,9 +2,9 @@
|
|||
#include "../helpers/Log.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../renderer/Renderer.hpp"
|
||||
#include "Auth.hpp"
|
||||
#include "../auth/Auth.hpp"
|
||||
#include "../auth/Fingerprint.hpp"
|
||||
#include "Egl.hpp"
|
||||
#include "Fingerprint.hpp"
|
||||
#include "linux-dmabuf-unstable-v1-protocol.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
|
@ -388,6 +388,8 @@ void CHyprlock::run() {
|
|||
wl_display_roundtrip(m_sWaylandState.display);
|
||||
|
||||
g_pRenderer = std::make_unique<CRenderer>();
|
||||
g_pAuth = std::make_unique<CAuth>();
|
||||
g_pAuth->start();
|
||||
|
||||
static auto* const PNOFADEOUT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:no_fade_out");
|
||||
const bool NOFADEOUT = **PNOFADEOUT;
|
||||
|
@ -419,11 +421,8 @@ void CHyprlock::run() {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
g_pAuth = std::make_unique<CAuth>();
|
||||
g_pAuth->start();
|
||||
|
||||
g_pFingerprint = std::make_unique<CFingerprint>();
|
||||
std::shared_ptr<sdbus::IConnection> conn = g_pFingerprint->start();
|
||||
const auto fingerprintAuth = g_pAuth->getImpl(AUTH_IMPL_FINGERPRINT);
|
||||
const auto dbusConn = (fingerprintAuth) ? ((CFingerprint*)fingerprintAuth.get())->getConnection() : nullptr;
|
||||
|
||||
registerSignalAction(SIGUSR1, handleUnlockSignal, SA_RESTART);
|
||||
registerSignalAction(SIGUSR2, handleForceUpdateSignal);
|
||||
|
@ -438,13 +437,13 @@ void CHyprlock::run() {
|
|||
.fd = wl_display_get_fd(m_sWaylandState.display),
|
||||
.events = POLLIN,
|
||||
};
|
||||
if (conn) {
|
||||
if (dbusConn) {
|
||||
pollfds[1] = {
|
||||
.fd = conn->getEventLoopPollData().fd,
|
||||
.fd = dbusConn->getEventLoopPollData().fd,
|
||||
.events = POLLIN,
|
||||
};
|
||||
}
|
||||
size_t fdcount = conn ? 2 : 1;
|
||||
size_t fdcount = dbusConn ? 2 : 1;
|
||||
|
||||
std::thread pollThr([this, &pollfds, fdcount]() {
|
||||
while (!m_bTerminate) {
|
||||
|
@ -524,7 +523,7 @@ void CHyprlock::run() {
|
|||
m_sLoopState.event = false;
|
||||
|
||||
if (pollfds[1].revents & POLLIN /* dbus */) {
|
||||
while (conn && conn->processPendingEvent()) {
|
||||
while (dbusConn && dbusConn->processPendingEvent()) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -592,7 +591,6 @@ void CHyprlock::run() {
|
|||
pthread_kill(pollThr.native_handle(), SIGRTMIN);
|
||||
|
||||
g_pAuth->terminate();
|
||||
g_pFingerprint->terminate();
|
||||
|
||||
// wait for threads to exit cleanly to avoid a coredump
|
||||
pollThr.join();
|
||||
|
@ -811,24 +809,6 @@ static const ext_session_lock_v1_listener sessionLockListener = {
|
|||
|
||||
// end session_lock
|
||||
|
||||
void CHyprlock::onPasswordCheckTimer() {
|
||||
// check result
|
||||
if (g_pAuth->isAuthenticated()) {
|
||||
unlock();
|
||||
} else {
|
||||
m_sPasswordState.passBuffer = "";
|
||||
m_sPasswordState.failedAttempts += 1;
|
||||
Debug::log(LOG, "Failed attempts: {}", m_sPasswordState.failedAttempts);
|
||||
|
||||
g_pAuth->m_bDisplayFailText = true;
|
||||
forceUpdateTimers();
|
||||
|
||||
g_pAuth->start();
|
||||
|
||||
renderAllOutputs();
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprlock::clearPasswordBuffer() {
|
||||
if (m_sPasswordState.passBuffer.empty())
|
||||
return;
|
||||
|
@ -1087,10 +1067,6 @@ size_t CHyprlock::getPasswordBufferDisplayLen() {
|
|||
return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });
|
||||
}
|
||||
|
||||
size_t CHyprlock::getPasswordFailedAttempts() {
|
||||
return m_sPasswordState.failedAttempts;
|
||||
}
|
||||
|
||||
std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data,
|
||||
bool force) {
|
||||
std::lock_guard<std::mutex> lg(m_sLoopState.timersMutex);
|
||||
|
|
|
@ -68,7 +68,6 @@ class CHyprlock {
|
|||
|
||||
size_t getPasswordBufferLen();
|
||||
size_t getPasswordBufferDisplayLen();
|
||||
size_t getPasswordFailedAttempts();
|
||||
|
||||
ext_session_lock_manager_v1* getSessionLockMgr();
|
||||
ext_session_lock_v1* getSessionLock();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "IWidget.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#include "../../core/hyprlock.hpp"
|
||||
#include "../../core/Auth.hpp"
|
||||
#include "../../core/Fingerprint.hpp"
|
||||
#include "../../auth/Auth.hpp"
|
||||
#include "../../auth/Fingerprint.hpp"
|
||||
#include <chrono>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
@ -58,7 +58,7 @@ Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size,
|
|||
|
||||
static void replaceAllAttempts(std::string& str) {
|
||||
|
||||
const size_t ATTEMPTS = g_pHyprlock->getPasswordFailedAttempts();
|
||||
const size_t ATTEMPTS = g_pAuth->m_iFailedAttempts;
|
||||
const std::string STR = std::to_string(ATTEMPTS);
|
||||
size_t pos = 0;
|
||||
|
||||
|
@ -138,7 +138,8 @@ static std::string getTime12h() {
|
|||
|
||||
const auto HRS = hhmmss.hours().count();
|
||||
const auto MINS = hhmmss.minutes().count();
|
||||
return (HRS == 12 || HRS == 0 ? "12" : (HRS % 12 < 10 ? "0" : "") + std::to_string(HRS % 12)) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS) + (HRS < 12 ? " AM" : " PM");
|
||||
return (HRS == 12 || HRS == 0 ? "12" : (HRS % 12 < 10 ? "0" : "") + std::to_string(HRS % 12)) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS) +
|
||||
(HRS < 12 ? " AM" : " PM");
|
||||
}
|
||||
|
||||
IWidget::SFormatResult IWidget::formatString(std::string in) {
|
||||
|
@ -169,14 +170,14 @@ IWidget::SFormatResult IWidget::formatString(std::string in) {
|
|||
}
|
||||
|
||||
if (in.contains("$FAIL")) {
|
||||
const auto FAIL = g_pAuth->getLastFailText();
|
||||
replaceInString(in, "$FAIL", FAIL.has_value() ? FAIL.value() : "");
|
||||
const auto FAIL = g_pAuth->getFailText(AUTH_IMPL_PAM);
|
||||
replaceInString(in, "$FAIL", FAIL.value_or(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
if (in.contains("$PROMPT")) {
|
||||
const auto PROMPT = g_pAuth->getLastPrompt();
|
||||
replaceInString(in, "$PROMPT", PROMPT.has_value() ? PROMPT.value() : "");
|
||||
const auto PROMPT = g_pAuth->getPrompt(AUTH_IMPL_PAM);
|
||||
replaceInString(in, "$PROMPT", PROMPT.value_or(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -191,8 +192,8 @@ IWidget::SFormatResult IWidget::formatString(std::string in) {
|
|||
}
|
||||
|
||||
if (in.contains("$FPRINTMESSAGE")) {
|
||||
const auto FPRINTMESSAGE = g_pFingerprint->getLastMessage();
|
||||
replaceInString(in, "$FPRINTMESSAGE", FPRINTMESSAGE.has_value() ? FPRINTMESSAGE.value() : "");
|
||||
const auto FPRINTMESSAGE = g_pAuth->getFailText(AUTH_IMPL_FINGERPRINT);
|
||||
replaceInString(in, "$FPRINTMESSAGE", FPRINTMESSAGE.value_or(""));
|
||||
result.allowForceUpdate = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "PasswordInputField.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include "../../core/hyprlock.hpp"
|
||||
#include "../../core/Auth.hpp"
|
||||
#include "../../auth/Auth.hpp"
|
||||
#include "../../config/ConfigDataValues.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
@ -334,13 +334,13 @@ void CPasswordInputField::updatePlaceholder() {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto AUTHFEEDBACK = g_pAuth->m_bDisplayFailText ? g_pAuth->getLastFailText().value_or("Ups, no fail text?") : g_pAuth->getLastPrompt().value_or("Ups, no prompt?");
|
||||
const auto AUTHFEEDBACK = g_pAuth->getInlineFeedback();
|
||||
const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont;
|
||||
|
||||
if (!ALLOWCOLORSWAP && placeholder.lastAuthFeedback == AUTHFEEDBACK && g_pHyprlock->getPasswordFailedAttempts() == placeholder.failedAttempts)
|
||||
if (!ALLOWCOLORSWAP && placeholder.lastAuthFeedback == AUTHFEEDBACK && g_pAuth->m_iFailedAttempts == placeholder.failedAttempts)
|
||||
return;
|
||||
|
||||
placeholder.failedAttempts = g_pHyprlock->getPasswordFailedAttempts();
|
||||
placeholder.failedAttempts = g_pAuth->m_iFailedAttempts;
|
||||
placeholder.lastAuthFeedback = AUTHFEEDBACK;
|
||||
|
||||
placeholder.asset = nullptr;
|
||||
|
|
Loading…
Reference in a new issue