Newer
Older
/* Copyright 2021 Dominik George <dominik.george@teckids.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::BASE_NAME;
use libc::{geteuid, seteuid, getpwnam, uid_t};
use std::ffi::{CStr, CString};
use oauth2::basic::BasicTokenResponse;
use std::io;
use std::path::PathBuf;
use xdg::{BaseDirectories, BaseDirectoriesError};
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json;
const USER_TOKEN_FILENAME: &str = "user_token.json";
user_tokens: HashMap<String, BasicTokenResponse>,
}
impl Cache {
let euid;
unsafe {
euid = geteuid();
};
user_tokens: HashMap::new(),
original_euid: euid,
prefix: prefix.to_string()
fn drop_privileges(&self, username: &String) -> Result<uid_t, &str> {
let current_euid;
unsafe {
current_euid = geteuid();
};
let nam = match CString::new(username.as_str()) {
Ok(nam) => nam,
Err(_) => return Err("Invalid username in lookup")
};
let pw;
unsafe {
pw = getpwnam(nam.as_ptr());
};
if pw.is_null() {
error!("Failed to lookup user {}", username);
return Err("Failed to lookup user");
}
let target_euid;
unsafe {
target_euid = (*pw).pw_uid;
if target_euid == current_euid {
debug!("No need to drop privileges, already running as {}", username);
return Ok(self.original_euid);
} else if self.original_euid == 0 {
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");
error!("Not running as root or target user, cannot drop privileges");
return Err("Dropping privileges not supported");
}
fn restore_privileges(&self) {
current_euid = geteuid();
};
if current_euid != self.original_euid {
debug!("Restoring privileges");
unsafe {
seteuid(self.original_euid);
};
} else {
debug!("No need to restore privileges, already running as original user");
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.as_str()) {
Ok(nam) => nam,
Err(_) => CString::new("nobody").ok().unwrap()
};
let user_home;
unsafe {
user_home = CStr::from_ptr((*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)?;
if saved_home != None {
env::set_var("HOME", saved_home.unwrap());
} else {
env::remove_var("HOME");
}
return Ok(base_dirs);
}
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(&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.to_string()) {
Ok(path) => match load_json(path) {
Ok(read_token) => Some(read_token),
Err(_) => None
Err(_) => None
};
self.restore_privileges();
match new_token {
Some(t) => {
self.user_tokens.insert(owner.to_string(), t);
self.user_tokens.get(owner)
},
None => None
}
debug!("Found token for {} in memory", owner);
self.user_tokens.get(owner)
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.to_string()) {
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();
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.to_string()) {
Ok(path) => {
debug!("Deleting cache file for {}", owner);
fs::remove_file(path).ok();
()
self.restore_privileges();
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) {
Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e))
}
}
fn save_json<O: Serialize>(path: PathBuf, obj: O) -> Result<(), io::Error> {
let json = match serde_json::to_string(&obj) {
Ok(j) => j,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e))
};
fs::write(path, json)
}