Skip to content

Commit

Permalink
auth: add an interface for different authentication methods (#578)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
PaideiaDilemma authored Dec 16, 2024
1 parent 4681f8f commit a4b0562
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 174 deletions.
116 changes: 116 additions & 0 deletions src/auth/Auth.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions src/auth/Auth.hpp
Original file line number Diff line number Diff line change
@@ -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;
56 changes: 36 additions & 20 deletions src/core/Fingerprint.cpp → src/auth/Fingerprint.cpp
Original file line number Diff line number Diff line change
@@ -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"};
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -139,30 +149,33 @@ 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) {
case MATCH_INVALID: Debug::log(WARN, "fprint: unknown status: {}", result); break;
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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 14 additions & 10 deletions src/core/Fingerprint.hpp → src/auth/Fingerprint.hpp
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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);

Expand All @@ -46,5 +52,3 @@ class CFingerprint {
bool stopVerify();
bool releaseDevice();
};

inline std::unique_ptr<CFingerprint> g_pFingerprint;
Loading

0 comments on commit a4b0562

Please sign in to comment.