From 2881338e7a65a228655772e420fa8966ed7caf12 Mon Sep 17 00:00:00 2001 From: Dominik George <dominik.george@teckids.org> Date: Sun, 9 May 2021 01:00:23 +0200 Subject: [PATCH] [Cache] Fix all the types and lifetimes and whatever --- src/cache.rs | 158 ++++++++++++++++++++------------------------------ src/config.rs | 4 +- src/pam.rs | 2 +- 3 files changed, 66 insertions(+), 98 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index 6c25d66..752fc45 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -17,8 +17,6 @@ use crate::BASE_NAME; use lazy_static::lazy_static; use std::collections::HashMap; -use std::convert::From; -use std::time::SystemTime; use libc::{geteuid, seteuid, getpwnam, uid_t}; use std::ffi::CString; @@ -29,100 +27,84 @@ use std::env; use std::fs; use std::io; use std::path::PathBuf; -use xdg::{BaseDirectories,BaseDirectoriesError}; +use xdg::{BaseDirectories, BaseDirectoriesError}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; +use serde::de::DeserializeOwned; use serde_json; const TOKEN_DEFAULT_EXPIRES: u64 = 24 * 60 * 60; const USER_TOKEN_FILENAME: &str = "user_token.json"; -#[derive(Serialize, Deserialize)] -struct UserToken { - access_token: String, - expires_at: u64, - refresh_token: Option<String>, -} - -impl UserToken { - fn is_expired(&self) -> bool { - match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - Ok(d) => d.as_secs() >= self.expires_at, - Err(_) => true - } - } -} - -impl From<BasicTokenResponse> for UserToken { - fn from(response: BasicTokenResponse) -> Self { - UserToken { - access_token: response.access_token.secret().to_string(), - expires_at: match response.expires_in { - Some(duration) => duration, - None => TOKEN_DEFAULT_EXPIRES - } + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).ok().unwrap().as_secs(), - refresh_token: match response.refresh_token { - Some(t) => Some(t.secret().to_string()), - None => None - } - } - } -} - struct Cache { - user_tokens: HashMap<String, UserToken>, + user_tokens: HashMap<String, BasicTokenResponse>, original_euid: uid_t, } impl Cache { pub fn new() -> Cache { + let euid; + unsafe { + euid = geteuid(); + }; Cache { user_tokens: HashMap::new(), - original_euid: geteuid() + original_euid: euid } } - fn drop_privileges(&self, username: String) -> Result<uid_t, String> { - let nam = match CString::new(username) { + fn drop_privileges(&self, username: &String) -> Result<uid_t, &str> { + let nam = match CString::new(username.as_str()) { Ok(nam) => nam, - Err(_) => return Err("Invalid username in lookup".to_string()) + Err(_) => return Err("Invalid username in lookup") + }; + let target_euid; + unsafe { + target_euid = (*getpwnam(nam.as_ptr())).pw_uid; }; - 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 { - let res = seteuid(target_euid); + let res; + unsafe { + 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()); + return Err("Failed to drop privileges"); } } error!("Not running as root or target user, cannot drop privileges"); - return Err("Dropping privileges not supported".to_string()); + return Err("Dropping privileges not supported"); } fn restore_privileges(&self) { debug!("Restoring privileges"); - seteuid(self.original_euid); + unsafe { + seteuid(self.original_euid); + } } - fn get_user_xdg_base_directories(&self, username: String) -> Result<BaseDirectories, BaseDirectoriesError> { + fn get_user_xdg_base_directories(&self, username: &String) -> Result<BaseDirectories, BaseDirectoriesError> { let saved_home = env::var_os("HOME"); - let nam = match CString::new(username) { + let nam = match CString::new(username.as_str()) { Ok(nam) => nam, Err(_) => CString::new("nobody").ok().unwrap() }; - 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 user_home; + unsafe { + user_home = CString::from_raw((*getpwnam(nam.as_ptr())).pw_dir); + }; + + env::set_var("HOME", user_home.to_str().unwrap()); + debug!("Home directory for {} is {}", username, user_home.to_str().unwrap()); let base_dirs = BaseDirectories::with_prefix(BASE_NAME)?; @@ -150,51 +132,47 @@ impl Cache { return Ok(base_dirs); } - fn place_user_cache_file(&self, username: String, filename: &str) -> Result<PathBuf, io::Error> { + fn place_user_cache_file(&self, username: &String, filename: String) -> Result<PathBuf, io::Error> { match self.get_user_xdg_base_directories(username) { Ok(b) => b.place_cache_file(filename), Err(e) => Err(io::Error::new(io::ErrorKind::NotFound, e)) } } - pub fn load_user_token(&self, owner: String) -> Option<&UserToken> { - 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); + pub fn load_user_token(&mut self, owner: &String) -> Option<&BasicTokenResponse> { + if !self.user_tokens.contains_key(owner) { + debug!("No token for {} in memory, trying to load from file", 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 - } - }, - Err(e) => None + let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME.to_string()) { + Ok(path) => match load_json(path) { + Ok(read_token) => Some(read_token), + Err(_) => None }, - Err(e) => None + Err(_) => None }; self.restore_privileges(); - new_token + + match new_token { + Some(t) => { + self.user_tokens.insert(owner.to_string(), t); + self.user_tokens.get(owner) + }, + None => None + } } else { - debug!("Found valid token for {} in memory", owner); - token + debug!("Found token for {} in memory", owner); + self.user_tokens.get(owner) } } - pub fn save_user_token(&self, owner: String, token: UserToken) -> Result<(), io::Error> { - self.user_tokens.insert(owner, token); + pub fn save_user_token(&mut self, owner: &String, token: BasicTokenResponse) -> Result<(), io::Error> { + self.user_tokens.insert(owner.to_string(), token.clone()); debug!("Saved token for {} in memory", owner); // Try to write user's token cache file let res = match self.drop_privileges(owner) { - Ok(_) => match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) { + Ok(_) => match self.place_user_cache_file(owner, USER_TOKEN_FILENAME.to_string()) { Ok(path) => { debug!("Storing token for {} in cache file", owner); save_json(path, token) @@ -207,13 +185,13 @@ impl Cache { return res; } - pub fn delete_user_token(&self, owner: String) { - self.user_tokens.remove(&owner); + pub fn delete_user_token(&mut 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) { + match self.place_user_cache_file(owner, USER_TOKEN_FILENAME.to_string()) { Ok(path) => { debug!("Deleting cache file for {}", owner); fs::remove_file(path) @@ -222,22 +200,14 @@ impl Cache { }; self.restore_privileges(); } - - pub fn cleanup_tokens(&self) { - for (owner, token) in self.user_tokens { - if token.is_expired() { - self.delete_user_token(owner); - debug!("Deleted expired token for {}", owner); - } - } - } } -fn load_json<'de, O: Deserialize<'de>>(path: PathBuf) -> Result<O, io::Error> { - let json = fs::read_to_string(path)?; - match serde_json::from_str(&json) { +fn load_json<O: DeserializeOwned>(path: PathBuf) -> Result<O, io::Error> { + let file = fs::File::open(path)?; + let reader = io::BufReader::new(file); + match serde_json::from_reader(reader) { Ok(o) => Ok(o), - Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)) + Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)) } } diff --git a/src/config.rs b/src/config.rs index 9009b2d..ca3b3be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,8 +19,6 @@ use serde::de::Deserialize; extern crate config; -const DEFAULT_CONFIG_FILE: &str = ("/etc/".to_string() + BASE_NAME).as_str(); - pub fn get_config(conf_args: Option<config::Config>) -> config::Config { // Preset default configuration let mut conf = config::Config::default(); @@ -32,7 +30,7 @@ pub fn get_config(conf_args: Option<config::Config>) -> config::Config { // Determine config file from args if provided and load config file let config_file = match conf_args.get_str("config") { Ok(filename) => filename.to_string(), - Err(_) => DEFAULT_CONFIG_FILE.to_string(), + Err(_) => "/etc/".to_string() + BASE_NAME, }; conf.merge(config::File::with_name(&config_file)).ok(); diff --git a/src/pam.rs b/src/pam.rs index 31d5b35..32e44e5 100644 --- a/src/pam.rs +++ b/src/pam.rs @@ -90,7 +90,7 @@ 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); - CACHE.save_user_token(username.to_string(), t.into()); + CACHE.save_user_token(&username.to_string(), t.into()); return PamError::SUCCESS; }, Err(e) => { -- GitLab