From ba5ad72f2af2a8dd8b48ac35f6270419a88eae75 Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Sat, 8 May 2021 20:16:58 +0200
Subject: [PATCH] [Cache] Add comments and debug logging

Fixed a security issue on the go which would write the user cache file
as root if dropping privileges failed.
---
 src/cache.rs | 101 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 66 insertions(+), 35 deletions(-)

diff --git a/src/cache.rs b/src/cache.rs
index 951747f..6c25d66 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -89,17 +89,27 @@ impl Cache {
         };
         let target_euid = (*getpwnam(nam.as_ptr())).pw_uid;
 
+
         if target_euid == self.original_euid {
+            debug!("No need to drop privileges, already running as {}", username);
             return Ok(self.original_euid);
         } else if self.original_euid == 0 {
-            seteuid(target_euid);
-            return Ok(target_euid);
+            let res = seteuid(target_euid);
+            if res == 0 {
+                debug!("Successfully dropped privileges to {}", username);
+                return Ok(target_euid);
+            } else {
+                error!("Could not drop privileges to {}", username);
+                return Err("Failed to drop privileges".to_string());
+            }
         }
 
+        error!("Not running as root or target user, cannot drop privileges");
         return Err("Dropping privileges not supported".to_string());
     }
 
     fn restore_privileges(&self) {
+        debug!("Restoring privileges");
         seteuid(self.original_euid);
     }
 
@@ -112,6 +122,7 @@ impl Cache {
         };
         let user_home = CString::from_raw((*getpwnam(nam.as_ptr())).pw_dir).to_str().unwrap();
         env::set_var("HOME", user_home);
+        debug!("Home directory for {} is {}", username, user_home);
 
         let base_dirs = BaseDirectories::with_prefix(BASE_NAME)?;
 
@@ -122,7 +133,16 @@ impl Cache {
         }
 
         match self.drop_privileges(username) {
-            Ok(_) => base_dirs.create_cache_directory(BASE_NAME).ok(),
+            Ok(_) => match base_dirs.create_cache_directory(BASE_NAME) {
+                Ok(v) => {
+                    info!("Created XDG cache directory for user {}", username);
+                    Some(v)
+                },
+                Err(e) => {
+                    error!("Error creating XDG cache directory for user {}: {}", username, e);
+                    None
+                }
+            }
             Err(_) => None
         };
         self.restore_privileges();
@@ -138,56 +158,66 @@ impl Cache {
     }
 
     pub fn load_user_token(&self, owner: String) -> Option<&UserToken> {
-        match self.user_tokens.get(&owner) {
-            Some(t) => {
-                if t.is_expired() {
-                    // Try to load token from file
-                    self.drop_privileges(owner).ok();
-                    let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
-                        Ok(path) => match load_json(path) {
-                            Ok(read_token) => {
-                                self.user_tokens.insert(owner, read_token);
-                                Some(&read_token)
-                            },
-                            Err(e) => {
-                                self.user_tokens.remove(&owner);
-                                None
-                            }
-                        },
-                        Err(e) => {
-                            self.user_tokens.remove(&owner);
+        let token = self.user_tokens.get(&owner);
+        if token.is_none() || token.unwrap().is_expired() {
+            debug!("No recent token for {} in memory, trying to load from file", owner);
+            self.user_tokens.remove(&owner);
+
+            self.drop_privileges(owner).ok();
+            let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
+                Ok(path) => match load_json::<UserToken>(path) {
+                    Ok(read_token) => {
+                        if !read_token.is_expired() {
+                            debug!("Found valid token for {} in file", owner);
+                            self.user_tokens.insert(owner, read_token);
+                            Some(&read_token)
+                        } else {
+                            debug!("Token in file for {} is expired.", owner);
                             None
                         }
-                    };
-                    self.restore_privileges();
-                    new_token
-                } else {
-                    Some(t)
-                }
-            },
-            None => None
+                    },
+                    Err(e) => None
+                },
+                Err(e) => None
+            };
+            self.restore_privileges();
+            new_token
+        } else {
+            debug!("Found valid token for {} in memory", owner);
+            token
         }
     }
 
-    pub fn save_user_token(&self, owner: String, token: UserToken) {
+    pub fn save_user_token(&self, owner: String, token: UserToken) -> Result<(), io::Error> {
         self.user_tokens.insert(owner, token);
+        debug!("Saved token for {} in memory", owner);
 
         // Try to write user's token cache file
-        self.drop_privileges(owner).ok();
-        match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
-            Ok(path) => save_json(path, token),
-            Err(e) => Err(e)
+        let res = match self.drop_privileges(owner) {
+            Ok(_) => match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
+                Ok(path) => {
+                    debug!("Storing token for {} in cache file", owner);
+                    save_json(path, token)
+                },
+                Err(e) => Err(e)
+            },
+            Err(e) => Err(io::Error::new(io::ErrorKind::PermissionDenied, e))
         };
         self.restore_privileges();
+        return res;
     }
 
     pub fn delete_user_token(&self, owner: String) {
         self.user_tokens.remove(&owner);
+        debug!("Token for {} removed from memory", owner);
 
         // Try to remove user's token cache file
         self.drop_privileges(owner).ok();
         match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
-            Ok(path) => fs::remove_file(path),
+            Ok(path) => {
+                debug!("Deleting cache file for {}", owner);
+                fs::remove_file(path)
+            },
             Err(e) => Err(e)
         };
         self.restore_privileges();
@@ -197,6 +227,7 @@ impl Cache {
         for (owner, token) in self.user_tokens {
             if token.is_expired() {
                 self.delete_user_token(owner);
+                debug!("Deleted expired token for {}", owner);
             }
         }
     }
-- 
GitLab