mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-20 18:15:24 -04:00
fix(lock): show the faillock lockout reason instead of "incorrect password" (#2669)
When pam_faillock locks the account (DMS authenticates through the system PAM stack: /etc/pam.d/login -> system-auth), the lock screen kept showing the generic "Incorrect password - try again" even though the real cause is a lockout, so a correct password looks rejected and only a reboot (which clears the tmpfs /run/faillock tally) appears to help. See #2647. The previous onMessageChanged only matched the *English* faillock strings ("The account is locked ...") and then wiped that text again on the trailing pam_unix "Password:" prompt. On a non-English system (e.g. German) the strings never matched, so the lockout was never surfaced at all. Detect the notice by position rather than by text: pam emits its informational messages within an attempt before the password prompt. Collect every non-prompt info message and, once the prompt arrives, surface the collected lines (minus the prompt itself) as lockMessage. If the stack short-circuits without ever prompting (e.g. pam_faillock preauth configured as requisite), the notice is surfaced on completion instead. This is locale-independent. A per-attempt flag keeps the message stable across repeated locked attempts and retires it when an attempt completes without a lockout (faillock reset / unlock_time elapsed). Fixes #2647
This commit is contained in:
@@ -23,6 +23,9 @@ Scope {
|
||||
property string u2fPendingMode
|
||||
property string buffer
|
||||
|
||||
property var attemptInfoMessages: []
|
||||
property bool lockoutAnnouncedThisAttempt: false
|
||||
|
||||
signal flashMsg
|
||||
signal unlockRequested
|
||||
|
||||
@@ -118,23 +121,37 @@ Scope {
|
||||
configDirectory: (dankshellConfigWatcher.loaded || nixosMarker.loaded || root.runningFromNixStore) ? "/etc/pam.d" : Quickshell.shellDir + "/assets/pam"
|
||||
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked")) {
|
||||
root.lockMessage = message;
|
||||
} else if (root.lockMessage && message.endsWith(" left to unlock)")) {
|
||||
root.lockMessage += "\n" + message;
|
||||
} else if (root.lockMessage && message && message.length > 0) {
|
||||
root.lockMessage = "";
|
||||
}
|
||||
// collected by position, not text, so it works in any locale
|
||||
if (message.length > 0 && !responseRequired)
|
||||
root.attemptInfoMessages = root.attemptInfoMessages.concat([message]);
|
||||
}
|
||||
|
||||
onResponseRequiredChanged: {
|
||||
if (!responseRequired)
|
||||
return;
|
||||
|
||||
const notice = root.attemptInfoMessages.filter(m => m !== message);
|
||||
if (notice.length > 0) {
|
||||
root.lockMessage = notice.join("\n");
|
||||
root.lockoutAnnouncedThisAttempt = true;
|
||||
}
|
||||
root.attemptInfoMessages = [];
|
||||
|
||||
respond(root.buffer);
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
// requisite preauth can lock without ever prompting; surface it here too
|
||||
if (!root.lockoutAnnouncedThisAttempt) {
|
||||
if (root.attemptInfoMessages.length > 0) {
|
||||
root.lockMessage = root.attemptInfoMessages.join("\n");
|
||||
root.lockoutAnnouncedThisAttempt = true;
|
||||
} else {
|
||||
root.lockMessage = "";
|
||||
}
|
||||
root.attemptInfoMessages = [];
|
||||
}
|
||||
|
||||
if (res === PamResult.Success) {
|
||||
if (!root.unlockInProgress) {
|
||||
fprint.abort();
|
||||
@@ -168,6 +185,8 @@ Scope {
|
||||
|
||||
function onActiveChanged() {
|
||||
if (passwd.active) {
|
||||
root.attemptInfoMessages = [];
|
||||
root.lockoutAnnouncedThisAttempt = false;
|
||||
passwdActiveTimeout.restart();
|
||||
} else {
|
||||
passwdActiveTimeout.running = false;
|
||||
@@ -393,6 +412,8 @@ Scope {
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.lockMessage = "";
|
||||
root.attemptInfoMessages = [];
|
||||
root.lockoutAnnouncedThisAttempt = false;
|
||||
root.resetAuthFlows();
|
||||
fprint.checkAvail();
|
||||
u2f.checkAvail();
|
||||
|
||||
Reference in New Issue
Block a user