From 28fa58620042f873cbec45458848888e936366a1 Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Mon, 17 May 2021 18:56:00 +0200
Subject: [PATCH] [PAM] Fix and properly document PAM token handling code

---
 src/cache.rs |  4 ++++
 src/pam.rs   | 18 +++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/cache.rs b/src/cache.rs
index dcdaeb3..5698a92 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -398,6 +398,7 @@ fn get_original_euid() -> uid_t {
     unsafe {
         if !original_euid_set {
             original_euid = geteuid();
+            debug!("Original EUID stored as {}", original_euid);
             original_euid_set = true;
         }
         original_euid
@@ -412,5 +413,8 @@ lazy_static! {
     static ref CONTEXT_USER: Mutex<UserInfo> = Mutex::new(UserInfo::new());
 }
 pub fn get_context_user() -> MutexGuard<'static, UserInfo> {
+    // Ensure original EUID is set before first action on context user
+    get_original_euid();
+
     CONTEXT_USER.lock().unwrap()
 }
diff --git a/src/pam.rs b/src/pam.rs
index d7637ed..487caae 100644
--- a/src/pam.rs
+++ b/src/pam.rs
@@ -92,12 +92,28 @@ impl PamServiceModule for PamOidc {
             match get_access_token_password(&conf, "pam", username.to_string(), password.to_string(), PamError::SERVICE_ERR, PamError::AUTH_ERR) {
                 Ok(t) => {
                     info!("Authenticated {} using Resource Owner Password Grant", username);
+
+                    // Cast a a butt-backwards somersault to get the token where it belongs
+                    // This is because we cannot store the token in the user's home directory
+                    // without resolving the user through getpwnam, and we (probably) cannot
+                    // do getpwnam without having the user's access token, and we (certainly)
+                    // cannot do getpwnam form inside the UserInfo cache object while having
+                    // the UserInfo cache object locked for PAM. Therefore...
+
+                    // 1. ...mark getpwnam unsafe (prevent cache code from calling it)
                     set_is_getpwnam_safe(false);
+                    // 2. ...store the access token (will not go through to $HOME, as getpwnam
+                    //    is locked)
+                    get_context_user().set_access_token(t.clone());
+                    // 3. ...call getpwnam ourselves without having the cache object locked
                     let passwd = getpwnam_safe(username.to_string());
                     if passwd.is_ok() {
+                        // 4. ...if getpwnam was successful, store the token again (this time,
+                        //    modulo other errors, it will go through to $HOME)
                         get_context_user().set_passwd(passwd.unwrap());
+                        get_context_user().set_access_token(t.clone());
                     }
-                    get_context_user().set_access_token(t);
+                    // 5. ...unlock getpwnam again (somewhat unnecessary)
                     set_is_getpwnam_safe(true);
                     return PamError::SUCCESS;
                 },
-- 
GitLab