srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/platform
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-10-30 23:52:00 +0200
committersrdusr <[email protected]>2024-10-30 23:52:00 +0200
commit9748bca006cd2498c4c0f2730d8789c667bda52b (patch)
treef917af35cd32f9745c940253f2400ab0b92f0b0a /crates/platform
parent3509cf6127185d9fed5a63ceca18a003213a20d7 (diff)
downloadsrdwm-9748bca006cd2498c4c0f2730d8789c667bda52b.tar.gz
srdwm-9748bca006cd2498c4c0f2730d8789c667bda52b.zip
Add srdwm's own native session-lock screen (blur, PAM auth)
A real ext-session-lock-v1 implementation drawn by the compositor itself, not a hand-off to an external locker process: a live-blurred background, PAM authentication on a background thread, and its own config surface (lock_config.rs) rather than hardcoded appearance.
Diffstat (limited to 'crates/platform')
-rw-r--r--crates/platform/src/pam_auth.rs58
1 files changed, 58 insertions, 0 deletions
diff --git a/crates/platform/src/pam_auth.rs b/crates/platform/src/pam_auth.rs
new file mode 100644
index 0000000..bdad439
--- /dev/null
+++ b/crates/platform/src/pam_auth.rs
@@ -0,0 +1,58 @@
+//! PAM authentication for srdwm's own session-lock UI
+//! (`crates/wayland/src/native_lock.rs`) - the one piece of that feature
+//! that must never be "close enough": this is what stands between a typed
+//! password and actually unlocking the session.
+//!
+//! Uses `pam_client`'s application-side API (the same shape swaylock,
+//! gtklock, and every other real screen locker use) rather than reading
+//! `/etc/shadow` by hand - PAM already handles the privilege boundary
+//! correctly (on a normal distro, `pam_unix.so` shells out to the setuid
+//! `unix_chkpwd` helper to compare the password, so this process never
+//! needs elevated privileges or shadow-file access itself) and honors
+//! whatever the system's actual auth policy is (a YubiKey module, an
+//! account lockout policy, anything `/etc/pam.d/srdwm` enables), not just
+//! a plain password check.
+//!
+//! Needs `/etc/pam.d/srdwm` to exist (any distro's default `login`-derived
+//! policy works, e.g. `auth include login`) - a service with no PAM
+//! config file at all fails every authentication attempt, not falls back
+//! to some default. That file is a root-owned system config change,
+//! deliberately not written by this code.
+
+use pam_client::conv_mock::Conversation;
+use pam_client::{Context, Flag};
+
+/// The PAM service name - see `Context::new`'s own docs: this is what
+/// selects the policy from `/etc/pam.d/<service>`.
+const SERVICE: &str = "srdwm";
+
+/// Verifies `password` for `username` against the system's real PAM
+/// policy. `true` only for a genuine, complete authentication success
+/// (both `authenticate` *and* `acct_mgmt`, so a correct password on a
+/// locked or expired account still correctly fails) - every other
+/// outcome, including a PAM setup problem that has nothing to do with the
+/// password itself, resolves to `false`. Deliberately no distinction
+/// between "wrong password" and "something is broken" in the return value
+/// - fail secure means every non-success path stays locked, not just the
+/// ones that are the user's own fault. Logged at `warn` for whoever's
+/// debugging a setup problem, never at a level that would put the
+/// password itself in a log.
+pub fn authenticate(username: &str, password: &str) -> bool {
+ let conversation = Conversation::with_credentials(username, password);
+ let mut context = match Context::new(SERVICE, Some(username), conversation) {
+ Ok(ctx) => ctx,
+ Err(e) => {
+ log::warn!("session lock: failed to start PAM context for service '{SERVICE}': {e} ({:?})", e.code());
+ return false;
+ }
+ };
+ if let Err(e) = context.authenticate(Flag::NONE) {
+ log::warn!("session lock: PAM authentication failed: {e} ({:?})", e.code());
+ return false;
+ }
+ if let Err(e) = context.acct_mgmt(Flag::NONE) {
+ log::warn!("session lock: PAM account check failed: {e} ({:?})", e.code());
+ return false;
+ }
+ true
+}