From 768ade88a8a8b79acdec4b7cc92a32cbc4cdae19 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PointerDilemma@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:21:13 +0000 Subject: [PATCH] pam: get username once (#974) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pam: fix username race by using getpwuid_r instead of getpwuid getpwuid() returns a pointer into a static buffer shared across all threads. Any getpw*/getpwent call from another thread — including those made internally by PAM modules during authentication — will overwrite it before pam_start() reads pw_name, causing hyprlock to authenticate as a random system user (root, bin, systemd-network) or fail with 'user unknown'. Replace with getpwuid_r(), which writes into a caller-supplied buffer, and copy pw_name into a std::string before calling pam_start(). * pam: get username once Instead of retrieving the username via getpwuid_r as in a69f526c95403abe9e7c861c05fcac4d272d95af, get the username once when initializing CPam and save it in a string. This should be sufficent for making sure there are no problems with the static buffer returned by getpwuid and is simpler. * misc: clang-format --------- Co-authored-by: mcgi5sr2 --- src/auth/Pam.cpp | 19 ++++++++++++------- src/auth/Pam.hpp | 3 ++- src/config/ConfigManager.cpp | 7 +++---- src/helpers/MiscFunctions.cpp | 21 +++++++++++++++++---- src/helpers/MiscFunctions.hpp | 1 + 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/auth/Pam.cpp b/src/auth/Pam.cpp index 9e1f2ac..2071ece 100644 --- a/src/auth/Pam.cpp +++ b/src/auth/Pam.cpp @@ -1,11 +1,11 @@ #include "Pam.hpp" +#include "../config/ConfigManager.hpp" #include "../core/hyprlock.hpp" #include "../helpers/Log.hpp" -#include "../config/ConfigManager.hpp" +#include "../helpers/MiscFunctions.hpp" #include #include -#include #include #if __has_include() #include @@ -69,6 +69,8 @@ CPam::CPam() { m_sPamModule = "su"; } + m_username = getUsernameForCurrentUid(); + m_sConversationState.waitForInput = [this]() { this->waitForInput(); }; } @@ -106,12 +108,15 @@ void CPam::init() { } bool CPam::auth() { - const pam_conv localConv = {.conv = conv, .appdata_ptr = (void*)&m_sConversationState}; - pam_handle_t* handle = nullptr; - auto uidPassword = getpwuid(getuid()); - RASSERT(uidPassword && uidPassword->pw_name, "Failed to get username (getpwuid)"); + const pam_conv localConv = {.conv = conv, .appdata_ptr = (void*)&m_sConversationState}; + pam_handle_t* handle = nullptr; - int ret = pam_start(m_sPamModule.c_str(), uidPassword->pw_name, &localConv, &handle); + if (m_username.empty()) { + m_sConversationState.failText = "Username not set"; + return false; + } + + int ret = pam_start(m_sPamModule.c_str(), m_username.c_str(), &localConv, &handle); if (ret != PAM_SUCCESS) { m_sConversationState.failText = "pam_start failed"; diff --git a/src/auth/Pam.hpp b/src/auth/Pam.hpp index 831fbf2..cb96cea 100644 --- a/src/auth/Pam.hpp +++ b/src/auth/Pam.hpp @@ -46,7 +46,8 @@ class CPam : public IAuthImplementation { bool m_bBlockInput = true; - std::string m_sPamModule; + std::string m_sPamModule = ""; + std::string m_username = ""; bool auth(); void resetConversation(); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 00365d7..6785ab7 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -369,11 +369,10 @@ std::vector CConfigManager::getWidgetConfigs() { #define SHADOWABLE(name) \ {"shadow_size", m_config.getSpecialConfigValue(name, "shadow_size", k.c_str())}, {"shadow_passes", m_config.getSpecialConfigValue(name, "shadow_passes", k.c_str())}, \ - {"shadow_color", m_config.getSpecialConfigValue(name, "shadow_color", k.c_str())}, { \ - "shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \ - } + {"shadow_color", m_config.getSpecialConfigValue(name, "shadow_color", k.c_str())}, {"shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str())} -#define CLICKABLE(name) {"onclick", m_config.getSpecialConfigValue(name, "onclick", k.c_str())} +#define CLICKABLE(name) \ + { "onclick", m_config.getSpecialConfigValue(name, "onclick", k.c_str()) } // auto keys = m_config.listKeysForSpecialCategory("background"); diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 33ac120..7be5073 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -1,11 +1,13 @@ -#include +#include "MiscFunctions.hpp" +#include "Log.hpp" + #include #include #include -#include "MiscFunctions.hpp" -#include "Log.hpp" -#include +#include #include +#include +#include #include using namespace Hyprutils::String; @@ -158,3 +160,14 @@ void spawnAsync(const std::string& cmd) { if (!proc.runAsync()) Debug::log(ERR, "Failed to start \"{}\"", cmd); } + +std::string getUsernameForCurrentUid() { + const uid_t UID = getuid(); + auto uidPassword = getpwuid(UID); + if (!uidPassword || !uidPassword->pw_name) { + Debug::log(ERR, "Failed to get username for uid {} (getpwuid)", UID); + return ""; + } + + return std::string{uidPassword->pw_name}; +} diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 6e7a259..0240155 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -9,3 +9,4 @@ int64_t configStringToInt(const std::string& VALUE); int createPoolFile(size_t size, std::string& name); std::string spawnSync(const std::string& cmd); void spawnAsync(const std::string& cmd); +std::string getUsernameForCurrentUid();