Skip to content
Snippets Groups Projects
Verified Commit 2881338e authored by Nik | Klampfradler's avatar Nik | Klampfradler
Browse files

[Cache] Fix all the types and lifetimes and whatever

parent ba5ad72f
No related branches found
No related tags found
No related merge requests found
...@@ -17,8 +17,6 @@ use crate::BASE_NAME; ...@@ -17,8 +17,6 @@ use crate::BASE_NAME;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::From;
use std::time::SystemTime;
use libc::{geteuid, seteuid, getpwnam, uid_t}; use libc::{geteuid, seteuid, getpwnam, uid_t};
use std::ffi::CString; use std::ffi::CString;
...@@ -29,100 +27,84 @@ use std::env; ...@@ -29,100 +27,84 @@ use std::env;
use std::fs; use std::fs;
use std::io; use std::io;
use std::path::PathBuf; 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; use serde_json;
const TOKEN_DEFAULT_EXPIRES: u64 = 24 * 60 * 60; const TOKEN_DEFAULT_EXPIRES: u64 = 24 * 60 * 60;
const USER_TOKEN_FILENAME: &str = "user_token.json"; 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 { struct Cache {
user_tokens: HashMap<String, UserToken>, user_tokens: HashMap<String, BasicTokenResponse>,
original_euid: uid_t, original_euid: uid_t,
} }
impl Cache { impl Cache {
pub fn new() -> Cache { pub fn new() -> Cache {
let euid;
unsafe {
euid = geteuid();
};
Cache { Cache {
user_tokens: HashMap::new(), user_tokens: HashMap::new(),
original_euid: geteuid() original_euid: euid
} }
} }
fn drop_privileges(&self, username: String) -> Result<uid_t, String> { fn drop_privileges(&self, username: &String) -> Result<uid_t, &str> {
let nam = match CString::new(username) { let nam = match CString::new(username.as_str()) {
Ok(nam) => nam, 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 { if target_euid == self.original_euid {
debug!("No need to drop privileges, already running as {}", username); debug!("No need to drop privileges, already running as {}", username);
return Ok(self.original_euid); return Ok(self.original_euid);
} else if self.original_euid == 0 { } else if self.original_euid == 0 {
let res = seteuid(target_euid); let res;
unsafe {
res = seteuid(target_euid);
};
if res == 0 { if res == 0 {
debug!("Successfully dropped privileges to {}", username); debug!("Successfully dropped privileges to {}", username);
return Ok(target_euid); return Ok(target_euid);
} else { } else {
error!("Could not drop privileges to {}", username); 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"); 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) { fn restore_privileges(&self) {
debug!("Restoring privileges"); 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 saved_home = env::var_os("HOME");
let nam = match CString::new(username) { let nam = match CString::new(username.as_str()) {
Ok(nam) => nam, Ok(nam) => nam,
Err(_) => CString::new("nobody").ok().unwrap() Err(_) => CString::new("nobody").ok().unwrap()
}; };
let user_home = CString::from_raw((*getpwnam(nam.as_ptr())).pw_dir).to_str().unwrap(); let user_home;
env::set_var("HOME", user_home); unsafe {
debug!("Home directory for {} is {}", username, user_home); 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)?; let base_dirs = BaseDirectories::with_prefix(BASE_NAME)?;
...@@ -150,51 +132,47 @@ impl Cache { ...@@ -150,51 +132,47 @@ impl Cache {
return Ok(base_dirs); 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) { match self.get_user_xdg_base_directories(username) {
Ok(b) => b.place_cache_file(filename), Ok(b) => b.place_cache_file(filename),
Err(e) => Err(io::Error::new(io::ErrorKind::NotFound, e)) Err(e) => Err(io::Error::new(io::ErrorKind::NotFound, e))
} }
} }
pub fn load_user_token(&self, owner: String) -> Option<&UserToken> { pub fn load_user_token(&mut self, owner: &String) -> Option<&BasicTokenResponse> {
let token = self.user_tokens.get(&owner); if !self.user_tokens.contains_key(owner) {
if token.is_none() || token.unwrap().is_expired() { debug!("No token for {} in memory, trying to load from file", owner);
debug!("No recent token for {} in memory, trying to load from file", owner);
self.user_tokens.remove(&owner);
self.drop_privileges(owner).ok(); self.drop_privileges(owner).ok();
let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) { let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME.to_string()) {
Ok(path) => match load_json::<UserToken>(path) { Ok(path) => match load_json(path) {
Ok(read_token) => { Ok(read_token) => Some(read_token),
if !read_token.is_expired() { Err(_) => None
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
}, },
Err(e) => None Err(_) => None
}; };
self.restore_privileges(); 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 { } else {
debug!("Found valid token for {} in memory", owner); debug!("Found token for {} in memory", owner);
token self.user_tokens.get(owner)
} }
} }
pub fn save_user_token(&self, owner: String, token: UserToken) -> Result<(), io::Error> { pub fn save_user_token(&mut self, owner: &String, token: BasicTokenResponse) -> Result<(), io::Error> {
self.user_tokens.insert(owner, token); self.user_tokens.insert(owner.to_string(), token.clone());
debug!("Saved token for {} in memory", owner); debug!("Saved token for {} in memory", owner);
// Try to write user's token cache file // Try to write user's token cache file
let res = match self.drop_privileges(owner) { 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) => { Ok(path) => {
debug!("Storing token for {} in cache file", owner); debug!("Storing token for {} in cache file", owner);
save_json(path, token) save_json(path, token)
...@@ -207,13 +185,13 @@ impl Cache { ...@@ -207,13 +185,13 @@ impl Cache {
return res; return res;
} }
pub fn delete_user_token(&self, owner: String) { pub fn delete_user_token(&mut self, owner: &String) {
self.user_tokens.remove(&owner); self.user_tokens.remove(owner);
debug!("Token for {} removed from memory", owner); debug!("Token for {} removed from memory", owner);
// Try to remove user's token cache file // Try to remove user's token cache file
self.drop_privileges(owner).ok(); 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) => { Ok(path) => {
debug!("Deleting cache file for {}", owner); debug!("Deleting cache file for {}", owner);
fs::remove_file(path) fs::remove_file(path)
...@@ -222,22 +200,14 @@ impl Cache { ...@@ -222,22 +200,14 @@ impl Cache {
}; };
self.restore_privileges(); 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> { fn load_json<O: DeserializeOwned>(path: PathBuf) -> Result<O, io::Error> {
let json = fs::read_to_string(path)?; let file = fs::File::open(path)?;
match serde_json::from_str(&json) { let reader = io::BufReader::new(file);
match serde_json::from_reader(reader) {
Ok(o) => Ok(o), 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))
} }
} }
......
...@@ -19,8 +19,6 @@ use serde::de::Deserialize; ...@@ -19,8 +19,6 @@ use serde::de::Deserialize;
extern crate config; 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 { pub fn get_config(conf_args: Option<config::Config>) -> config::Config {
// Preset default configuration // Preset default configuration
let mut conf = config::Config::default(); let mut conf = config::Config::default();
...@@ -32,7 +30,7 @@ pub fn get_config(conf_args: Option<config::Config>) -> config::Config { ...@@ -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 // Determine config file from args if provided and load config file
let config_file = match conf_args.get_str("config") { let config_file = match conf_args.get_str("config") {
Ok(filename) => filename.to_string(), 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(); conf.merge(config::File::with_name(&config_file)).ok();
......
...@@ -90,7 +90,7 @@ impl PamServiceModule for PamOidc { ...@@ -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) { match get_access_token_password(conf, "pam", username.to_string(), password.to_string(), PamError::SERVICE_ERR, PamError::AUTH_ERR) {
Ok(t) => { Ok(t) => {
info!("Authenticated {} using Resource Owner Password Grant", username); 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; return PamError::SUCCESS;
}, },
Err(e) => { Err(e) => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment