pam: get username once (#974)

* 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 a69f526c95,
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 <mcgi5sr2@gmail.com>
This commit is contained in:
Maximilian Seidler 2026-03-23 16:21:13 +00:00 committed by GitHub
parent d7079a1248
commit 768ade88a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 16 deletions

View file

@ -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 <filesystem>
#include <unistd.h>
#include <pwd.h>
#include <security/pam_appl.h>
#if __has_include(<security/pam_misc.h>)
#include <security/pam_misc.h>
@ -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";

View file

@ -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();

View file

@ -369,11 +369,10 @@ std::vector<CConfigManager::SWidgetConfig> 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");

View file

@ -1,11 +1,13 @@
#include <filesystem>
#include "MiscFunctions.hpp"
#include "Log.hpp"
#include <algorithm>
#include <cmath>
#include <fcntl.h>
#include "MiscFunctions.hpp"
#include "Log.hpp"
#include <hyprutils/string/String.hpp>
#include <filesystem>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/string/String.hpp>
#include <pwd.h>
#include <unistd.h>
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};
}

View file

@ -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();