gardesk/gardm / c1683bc

Browse files

auth: Prepare authentication for child process PAM

Wayland compositors need proper logind session registration for device
access (DRM, input). This requires PAM open_session() to be called in
the process that will become the user session, not in the daemon.

Changes:
- Store password in Authenticated state for session spawner to use
- Rename pam_authenticate to pam_verify_only (no open_session)
- Export PAM_SERVICE_NAME constant for session module
- Update take_authenticated to return (username, password) tuple

The actual PAM open_session() call is now deferred to the forked
child process in the session module.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
c1683bc52dd691742e03290df2b32f197e7ae32e
Parents
fc6c115
Tree
7647f32

1 changed file

StatusFile+-
M gardmd/src/auth.rs 33 24
gardmd/src/auth.rsmodified
@@ -16,8 +16,8 @@ pub enum AuthState {
1616
     Idle,
1717
     /// Waiting for password after CreateSession
1818
     AwaitingPassword { username: String },
19
-    /// Authentication succeeded
20
-    Authenticated { username: String },
19
+    /// Authentication succeeded - stores password for session PAM
20
+    Authenticated { username: String, password: String },
2121
 }
2222
 
2323
 impl Default for AuthState {
@@ -73,6 +73,9 @@ impl AuthSession {
7373
     /// Attempt authentication with provided password
7474
     ///
7575
     /// This runs PAM authentication in a blocking thread since PAM is synchronous.
76
+    /// The password is stored temporarily for use by the session spawner, which
77
+    /// will re-authenticate with PAM in the child process to properly register
78
+    /// the session with logind.
7679
     pub async fn authenticate(&mut self, password: &str) -> AuthResponse {
7780
         let username = match &self.state {
7881
             AuthState::AwaitingPassword { username } => username.clone(),
@@ -83,18 +86,24 @@ impl AuthSession {
8386
             }
8487
         };
8588
 
86
-        // Run PAM in blocking thread
87
-        let password = password.to_string();
89
+        // Run PAM in blocking thread - this just verifies credentials
90
+        let password_str = password.to_string();
8891
         let username_for_pam = username.clone();
92
+        tracing::debug!("Spawning PAM authentication task");
8993
         let result = tokio::task::spawn_blocking(move || {
90
-            pam_authenticate(&username_for_pam, &password)
94
+            pam_verify_only(&username_for_pam, &password_str)
9195
         })
9296
         .await;
97
+        tracing::debug!(?result, "PAM task completed");
9398
 
9499
         match result {
95100
             Ok(Ok(())) => {
96101
                 tracing::info!(%username, "Authentication succeeded");
97
-                self.state = AuthState::Authenticated { username };
102
+                // Store password for session spawner to use with PAM open_session
103
+                self.state = AuthState::Authenticated {
104
+                    username,
105
+                    password: password.to_string(),
106
+                };
98107
                 AuthResponse::Success
99108
             }
100109
             Ok(Err(e)) => {
@@ -116,7 +125,7 @@ impl AuthSession {
116125
 
117126
     /// Cancel the current authentication session
118127
     pub fn cancel(&mut self) {
119
-        if let AuthState::AwaitingPassword { username } | AuthState::Authenticated { username } =
128
+        if let AuthState::AwaitingPassword { username } | AuthState::Authenticated { username, .. } =
120129
             &self.state
121130
         {
122131
             tracing::info!(username, "Session cancelled");
@@ -127,26 +136,29 @@ impl AuthSession {
127136
     /// Check if user is authenticated and ready to start session
128137
     pub fn is_authenticated(&self) -> Option<&str> {
129138
         match &self.state {
130
-            AuthState::Authenticated { username } => Some(username),
139
+            AuthState::Authenticated { username, .. } => Some(username),
131140
             _ => None,
132141
         }
133142
     }
134143
 
135
-    /// Consume the authenticated state and return the username
136
-    pub fn take_authenticated(&mut self) -> Option<String> {
144
+    /// Consume the authenticated state and return (username, password)
145
+    /// The password is needed for the session spawner to re-authenticate with PAM
146
+    pub fn take_authenticated(&mut self) -> Option<(String, String)> {
137147
         if matches!(self.state, AuthState::Authenticated { .. }) {
138148
             let old = std::mem::replace(&mut self.state, AuthState::Idle);
139
-            if let AuthState::Authenticated { username } = old {
140
-                return Some(username);
149
+            if let AuthState::Authenticated { username, password } = old {
150
+                return Some((username, password));
141151
             }
142152
         }
143153
         None
144154
     }
145155
 }
146156
 
147
-/// Perform PAM authentication (blocking)
148
-fn pam_authenticate(username: &str, password: &str) -> Result<()> {
149
-    tracing::debug!(%username, password_len = password.len(), "Starting PAM authentication");
157
+/// Verify PAM credentials only (no open_session)
158
+/// This is used for quick credential verification. The actual session opening
159
+/// happens in the spawned child process.
160
+fn pam_verify_only(username: &str, password: &str) -> Result<()> {
161
+    tracing::debug!(%username, password_len = password.len(), "Verifying PAM credentials");
150162
 
151163
     // Create client with PasswordConv (non-interactive, uses provided password)
152164
     let mut client = Client::with_password(PAM_SERVICE)
@@ -159,22 +171,19 @@ fn pam_authenticate(username: &str, password: &str) -> Result<()> {
159171
 
160172
     tracing::debug!("PAM client created, calling authenticate");
161173
 
162
-    // Authenticate
174
+    // Authenticate only - don't open session here
175
+    // open_session will be called in the spawned child process
163176
     client
164177
         .authenticate()
165178
         .map_err(|e| anyhow!("PAM authentication failed: {:?}", e))?;
166179
 
167
-    tracing::debug!("PAM authenticate succeeded, opening session");
168
-
169
-    // Open session (also does account validation)
170
-    client
171
-        .open_session()
172
-        .map_err(|e| anyhow!("Failed to open PAM session: {:?}", e))?;
173
-
174
-    tracing::debug!("PAM session opened successfully");
180
+    tracing::debug!("PAM credential verification complete");
175181
     Ok(())
176182
 }
177183
 
184
+/// Service name for PAM - exported for use by session module
185
+pub const PAM_SERVICE_NAME: &str = PAM_SERVICE;
186
+
178187
 #[cfg(test)]
179188
 mod tests {
180189
     use super::*;