mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-08 14:38:30 -04:00
fix(greeter): reimplement regression to hide debug logs during login
- Provide more status feedback
- Allow 2 fprint tries within 10 seconds for DMS-managed PAM
- Updated dms greeter docs
Note: distro-managed PAM takes precedence and may use different limits.
DMS auth changes remain conditional on greetd being installed.
Fixes #2853
Port 1.5
(cherry picked from commit 5b41d699fa)
This commit is contained in:
@@ -849,7 +849,7 @@ func syncGreeterPamConfigWithDeps(logFunc func(string), sudoPassword string, set
|
|||||||
if wantFprint || wantU2f {
|
if wantFprint || wantU2f {
|
||||||
blockLines := []string{GreeterPamManagedBlockStart}
|
blockLines := []string{GreeterPamManagedBlockStart}
|
||||||
if wantFprint {
|
if wantFprint {
|
||||||
blockLines = append(blockLines, "auth sufficient pam_fprintd.so max-tries=1 timeout=5")
|
blockLines = append(blockLines, "auth sufficient pam_fprintd.so max-tries=2 timeout=10")
|
||||||
}
|
}
|
||||||
if wantU2f {
|
if wantU2f {
|
||||||
blockLines = append(blockLines, "auth sufficient pam_u2f.so cue nouserok timeout=10")
|
blockLines = append(blockLines, "auth sufficient pam_u2f.so cue nouserok timeout=10")
|
||||||
|
|||||||
@@ -723,7 +723,7 @@ func TestSyncGreeterPamConfigWithDeps(t *testing.T) {
|
|||||||
got := readFileString(t, env.greetdPath)
|
got := readFileString(t, env.greetdPath)
|
||||||
for _, want := range []string{
|
for _, want := range []string{
|
||||||
GreeterPamManagedBlockStart,
|
GreeterPamManagedBlockStart,
|
||||||
"auth sufficient pam_fprintd.so max-tries=1 timeout=5",
|
"auth sufficient pam_fprintd.so max-tries=2 timeout=10",
|
||||||
"auth sufficient pam_u2f.so cue nouserok timeout=10",
|
"auth sufficient pam_u2f.so cue nouserok timeout=10",
|
||||||
GreeterPamManagedBlockEnd,
|
GreeterPamManagedBlockEnd,
|
||||||
} {
|
} {
|
||||||
@@ -840,7 +840,7 @@ func TestSyncAuthConfigWithDeps(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
greetd := readFileString(t, env.greetdPath)
|
greetd := readFileString(t, env.greetdPath)
|
||||||
if !strings.Contains(greetd, "auth sufficient pam_fprintd.so max-tries=1 timeout=5") {
|
if !strings.Contains(greetd, "auth sufficient pam_fprintd.so max-tries=2 timeout=10") {
|
||||||
t.Fatalf("expected greetd PAM to receive fingerprint auth block:\n%s", greetd)
|
t.Fatalf("expected greetd PAM to receive fingerprint auth block:\n%s", greetd)
|
||||||
}
|
}
|
||||||
if strings.Contains(greetd, "auth sufficient pam_u2f.so cue nouserok timeout=10") {
|
if strings.Contains(greetd, "auth sufficient pam_u2f.so cue nouserok timeout=10") {
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ Singleton {
|
|||||||
if (exitCode === 0)
|
if (exitCode === 0)
|
||||||
return "missing_enrollment";
|
return "missing_enrollment";
|
||||||
if (exitCode === 127 || (output || "").includes("__missing_command__"))
|
if (exitCode === 127 || (output || "").includes("__missing_command__"))
|
||||||
return "probe_failed";
|
return "missing_pam_support";
|
||||||
return pamFprintDetected ? "probe_failed" : "missing_pam_support";
|
return pamFprintDetected ? "probe_failed" : "missing_pam_support";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,12 +153,10 @@ Singleton {
|
|||||||
blockWrites: true
|
blockWrites: true
|
||||||
atomicWrites: false
|
atomicWrites: false
|
||||||
watchChanges: false
|
watchChanges: false
|
||||||
printErrors: true
|
printErrors: false
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
parseSessionConfig(sessionConfigFileView.text());
|
parseSessionConfig(sessionConfigFileView.text());
|
||||||
}
|
}
|
||||||
onLoadFailed: error => {
|
onLoadFailed: {}
|
||||||
log.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,12 +219,11 @@ Singleton {
|
|||||||
blockWrites: true
|
blockWrites: true
|
||||||
atomicWrites: false
|
atomicWrites: false
|
||||||
watchChanges: false
|
watchChanges: false
|
||||||
printErrors: true
|
printErrors: false
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
parseSettings(settingsFile.text());
|
parseSettings(settingsFile.text());
|
||||||
}
|
}
|
||||||
onLoadFailed: error => {
|
onLoadFailed: {
|
||||||
log.warn("Failed to load greetd settings:", error);
|
|
||||||
root.parseSettings("");
|
root.parseSettings("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ Item {
|
|||||||
property int passwordFailureCount: 0
|
property int passwordFailureCount: 0
|
||||||
property int passwordAttemptLimitHint: 0
|
property int passwordAttemptLimitHint: 0
|
||||||
property string authFeedbackMessage: ""
|
property string authFeedbackMessage: ""
|
||||||
|
property string authSuccessMessage: ""
|
||||||
property string greetdPamText: ""
|
property string greetdPamText: ""
|
||||||
property string systemAuthPamText: ""
|
property string systemAuthPamText: ""
|
||||||
property string commonAuthPamText: ""
|
property string commonAuthPamText: ""
|
||||||
@@ -67,11 +68,23 @@ Item {
|
|||||||
property bool fprintdProbeComplete: false
|
property bool fprintdProbeComplete: false
|
||||||
property bool fprintdHasDevice: false
|
property bool fprintdHasDevice: false
|
||||||
property bool autoLoginOnSuccess: false
|
property bool autoLoginOnSuccess: false
|
||||||
|
readonly property bool greeterPamStackHasFprint: greeterPamStackHasModule("pam_fprintd")
|
||||||
// Falls back to PAM-only detection until the fprintd D-Bus probe completes.
|
// Falls back to PAM-only detection until the fprintd D-Bus probe completes.
|
||||||
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd") && (!fprintdProbeComplete || fprintdHasDevice)
|
readonly property bool greeterPamHasFprint: greeterPamStackHasFprint && (!fprintdProbeComplete || fprintdHasDevice)
|
||||||
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
||||||
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
|
readonly property bool greeterExternalAuthAvailable: (greeterPamHasFprint && GreetdSettings.greeterEnableFprint) || (greeterPamHasU2f && GreetdSettings.greeterEnableU2f)
|
||||||
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
|
readonly property bool greeterPamHasExternalAuth: greeterPamStackHasFprint || greeterPamHasU2f
|
||||||
|
readonly property bool externalAuthInProgress: awaitingExternalAuth || (Greetd.state !== GreetdState.Inactive && passwordSubmitRequested && greeterPamHasExternalAuth && !pendingPasswordResponse)
|
||||||
|
readonly property string externalAuthStatusMessage: {
|
||||||
|
if (!externalAuthInProgress)
|
||||||
|
return "";
|
||||||
|
if (greeterPamStackHasFprint && greeterPamHasU2f)
|
||||||
|
return I18n.tr("Awaiting fingerprint or security key authentication");
|
||||||
|
if (greeterPamStackHasFprint)
|
||||||
|
return I18n.tr("Awaiting fingerprint authentication");
|
||||||
|
return I18n.tr("Awaiting security key authentication");
|
||||||
|
}
|
||||||
|
readonly property string authDisplayMessage: authFeedbackMessage || authSuccessMessage || externalAuthStatusMessage
|
||||||
readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
|
readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
|
||||||
readonly property bool multipleUsersAvailable: GreeterUsersService.loaded && GreeterUsersService.users.length > 1
|
readonly property bool multipleUsersAvailable: GreeterUsersService.loaded && GreeterUsersService.users.length > 1
|
||||||
// Single-user systems get the picker too when auto-login is available, so the
|
// Single-user systems get the picker too when auto-login is available, so the
|
||||||
@@ -238,7 +251,7 @@ Item {
|
|||||||
|
|
||||||
function isLikelyLockoutMessage(message) {
|
function isLikelyLockoutMessage(message) {
|
||||||
const lower = (message || "").toLowerCase();
|
const lower = (message || "").toLowerCase();
|
||||||
return lower.includes("account is locked") || lower.includes("too many") || lower.includes("maximum number of") || lower.includes("auth_err");
|
return lower.includes("account is locked") || lower.includes("too many") || lower.includes("maximum number of");
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentAuthMessage() {
|
function currentAuthMessage() {
|
||||||
@@ -251,11 +264,11 @@ Item {
|
|||||||
const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint));
|
const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint));
|
||||||
const remaining = Math.max(passwordAttemptLimitHint - attempt, 0);
|
const remaining = Math.max(passwordAttemptLimitHint - attempt, 0);
|
||||||
if (remaining > 0) {
|
if (remaining > 0) {
|
||||||
return I18n.tr("Incorrect password - attempt %1 of %2 (lockout may follow)").arg(attempt).arg(passwordAttemptLimitHint);
|
return I18n.tr("Authentication failed - attempt %1 of %2").arg(attempt).arg(passwordAttemptLimitHint);
|
||||||
}
|
}
|
||||||
return I18n.tr("Incorrect password - next failures may trigger account lockout");
|
return I18n.tr("Authentication failed - lockout can occur");
|
||||||
}
|
}
|
||||||
return I18n.tr("Incorrect password");
|
return I18n.tr("Authentication failed - try again");
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -263,6 +276,7 @@ Item {
|
|||||||
function clearAuthFeedback() {
|
function clearAuthFeedback() {
|
||||||
GreeterState.pamState = "";
|
GreeterState.pamState = "";
|
||||||
authFeedbackMessage = "";
|
authFeedbackMessage = "";
|
||||||
|
authSuccessMessage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPasswordSessionTransition(clearSubmitRequest) {
|
function resetPasswordSessionTransition(clearSubmitRequest) {
|
||||||
@@ -619,8 +633,8 @@ Item {
|
|||||||
pendingPasswordResponse = false;
|
pendingPasswordResponse = false;
|
||||||
passwordSubmitRequested = submitPassword;
|
passwordSubmitRequested = submitPassword;
|
||||||
awaitingExternalAuth = !submitPassword && !hasPasswordBuffer && root.greeterExternalAuthAvailable;
|
awaitingExternalAuth = !submitPassword && !hasPasswordBuffer && root.greeterExternalAuthAvailable;
|
||||||
// Use greeterExternalAuthAvailable so systems with pam_fprintd but no hardware don't incur the 30 s wait.
|
// Let the effective PAM stack finish external authentication.
|
||||||
const waitingOnPamExternalBeforePassword = submitPassword && root.greeterExternalAuthAvailable;
|
const waitingOnPamExternalBeforePassword = submitPassword && root.greeterPamHasExternalAuth;
|
||||||
authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
|
||||||
authTimeout.restart();
|
authTimeout.restart();
|
||||||
Greetd.createSession(GreeterState.username);
|
Greetd.createSession(GreeterState.username);
|
||||||
@@ -1406,16 +1420,16 @@ Item {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: root.authFeedbackMessage !== "" ? 38 : 0
|
Layout.preferredHeight: root.authDisplayMessage !== "" ? 38 : 0
|
||||||
Layout.topMargin: -Theme.spacingS
|
Layout.topMargin: -Theme.spacingS
|
||||||
Layout.bottomMargin: -Theme.spacingS
|
Layout.bottomMargin: -Theme.spacingS
|
||||||
text: root.authFeedbackMessage
|
text: root.authDisplayMessage
|
||||||
color: Theme.error
|
color: root.authFeedbackMessage !== "" ? Theme.error : (root.authSuccessMessage !== "" ? Theme.success : Theme.surfaceVariantText)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
opacity: root.authFeedbackMessage !== "" ? 1 : 0
|
opacity: root.authDisplayMessage !== "" ? 1 : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -1975,6 +1989,7 @@ Item {
|
|||||||
authTimeout.stop();
|
authTimeout.stop();
|
||||||
passwordFailureCount = 0;
|
passwordFailureCount = 0;
|
||||||
clearAuthFeedback();
|
clearAuthFeedback();
|
||||||
|
authSuccessMessage = I18n.tr("Authenticated - starting session");
|
||||||
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex];
|
const sessionCmd = GreeterState.selectedSession || GreeterState.sessionExecs[GreeterState.currentSessionIndex];
|
||||||
const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex];
|
const sessionPath = GreeterState.selectedSessionPath || GreeterState.sessionPaths[GreeterState.currentSessionIndex];
|
||||||
const sessionDesktopId = GreeterState.selectedSessionDesktopId || GreeterState.sessionDesktopIds[GreeterState.currentSessionIndex] || desktopIdFromPath(sessionPath);
|
const sessionDesktopId = GreeterState.selectedSessionDesktopId || GreeterState.sessionDesktopIds[GreeterState.currentSessionIndex] || desktopIdFromPath(sessionPath);
|
||||||
@@ -2055,12 +2070,8 @@ Item {
|
|||||||
pendingLaunchCommand = "";
|
pendingLaunchCommand = "";
|
||||||
pendingLaunchEnv = [];
|
pendingLaunchEnv = [];
|
||||||
const sessionArgs = sessionCommand.trim().split(/\s+/);
|
const sessionArgs = sessionCommand.trim().split(/\s+/);
|
||||||
const needsVoidDbusSession = Quickshell.env("DMS_VOID") === "1"
|
const needsVoidDbusSession = Quickshell.env("DMS_VOID") === "1" && !Quickshell.env("DBUS_SESSION_BUS_ADDRESS") && sessionArgs[0] !== "dbus-run-session";
|
||||||
&& !Quickshell.env("DBUS_SESSION_BUS_ADDRESS")
|
const launchArgs = needsVoidDbusSession ? ["dbus-run-session"].concat(sessionArgs) : sessionArgs;
|
||||||
&& sessionArgs[0] !== "dbus-run-session";
|
|
||||||
const launchArgs = needsVoidDbusSession
|
|
||||||
? ["dbus-run-session"].concat(sessionArgs)
|
|
||||||
: sessionArgs;
|
|
||||||
Greetd.launch(launchArgs, launchEnv);
|
Greetd.launch(launchArgs, launchEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,12 +51,44 @@ dms greeter enable
|
|||||||
dms greeter sync
|
dms greeter sync
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Fingerprint authentication is optional. Install your distribution's
|
||||||
|
fingerprint service/client and PAM integration before enabling it. Package
|
||||||
|
names vary by distribution; on Fedora the native stack is:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install fprintd fprintd-pam
|
||||||
|
fprintd-enroll
|
||||||
|
```
|
||||||
|
|
||||||
|
The distro `fprintd` package normally pulls in `libfprint` automatically. After
|
||||||
|
enrollment, enable fingerprint login in **Settings → Greeter**. The toggle
|
||||||
|
applies the shared PAM configuration automatically, so no additional sync is
|
||||||
|
needed after initial greeter setup. Use `dms auth sync` only to repair or apply
|
||||||
|
authentication manually without syncing the rest of the greeter.
|
||||||
|
|
||||||
|
greetd tries fingerprint and password sequentially. DMS-managed PAM allows two
|
||||||
|
scans for up to ten seconds before password fallback. Existing distro PAM
|
||||||
|
policy takes precedence, so fallback timing can differ.
|
||||||
|
|
||||||
|
Verify enrollment with `fprintd-list "$USER"`; pass a token such as
|
||||||
|
`fprintd-enroll -f right-thumb` to enroll a specific finger. Security keys
|
||||||
|
similarly require `pam_u2f` setup and key registration before enabling the
|
||||||
|
login toggle.
|
||||||
|
|
||||||
|
If `fprintd-enroll` reports **No devices available**, check the [libfprint
|
||||||
|
supported devices list](https://fprint.freedesktop.org/supported-devices.html).
|
||||||
|
Some Validity/Synaptics readers are not supported by the native stack,
|
||||||
|
regardless of distribution; the
|
||||||
|
[open-fprintd](https://github.com/uunicorn/open-fprintd) service with the
|
||||||
|
[python-validity](https://github.com/uunicorn/python-validity) driver may work
|
||||||
|
instead. Follow the driver's installation and firmware instructions, and do
|
||||||
|
not run the stock `fprintd` daemon alongside its replacement.
|
||||||
|
|
||||||
#### Syncing themes (Optional)
|
#### Syncing themes (Optional)
|
||||||
|
|
||||||
To sync your wallpaper and theme with the greeter login screen, follow the manual setup below:
|
To sync your wallpaper and theme with the greeter login screen, follow the manual setup below:
|
||||||
|
|
||||||
<details>
|
##### Manual theme syncing
|
||||||
<summary>Manual theme syncing</summary>
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add yourself to greeter group
|
# Add yourself to greeter group
|
||||||
|
|||||||
@@ -88,6 +88,19 @@ is_hyprland_lua_config() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_vt() {
|
||||||
|
local clear_sequence=$'\033[2J\033[H\033[3J\033[?25l'
|
||||||
|
|
||||||
|
# Clear retained console text on the controlling VT.
|
||||||
|
if printf '%s' "$clear_sequence" >/dev/tty 2>/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
printf '%s' "$clear_sequence"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
exec_compositor() {
|
exec_compositor() {
|
||||||
local log_tag="$1"
|
local log_tag="$1"
|
||||||
shift
|
shift
|
||||||
@@ -96,11 +109,14 @@ exec_compositor() {
|
|||||||
exec "$@"
|
exec "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
clear_vt
|
||||||
|
|
||||||
if command -v systemd-cat >/dev/null 2>&1; then
|
if command -v systemd-cat >/dev/null 2>&1; then
|
||||||
exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info) 2>&1
|
exec "$@" > >(systemd-cat -t "dms-greeter/$log_tag" -p info 2>/dev/null) 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec "$@"
|
# Preserve diagnostics on systems without journald.
|
||||||
|
exec "$@" >>"$CACHE_DIR/$log_tag.log" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
|
|||||||
Reference in New Issue
Block a user