diff --git a/src/cache.rs b/src/cache.rs index 6a77b707e8271da5ec6398c7400e9b063c43e493..d6e5dadc385c6cd08086f724147230735219854c 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -21,6 +21,7 @@ use crate::BASE_NAME; use crate::unix::{Passwd, getpwnam_safe, getpwuid_safe}; use lazy_static::lazy_static; +use std::cell::RefCell; use std::sync::{Mutex, MutexGuard}; use libc::{geteuid, seteuid, uid_t}; @@ -54,34 +55,16 @@ pub struct UserInfo<'a> { access_token: Option<BasicTokenResponse> } -/// In-memory structure to hold global process state -/// Needed because the PAM and NSS components might be used chained (if NSS -/// resolution is necessary to complete PAM authentication), and we need to -/// remember state between the calls. -pub struct Cache<'a> { - /// The user currently calling the library - /// For NSS, this will be the process owner; for PAM, this will be the user - /// logging in (after successful authentication) - pub context_user: UserInfo<'a> -} - -impl Cache<'_> { - pub fn new<'a>() -> Cache<'a> { - let euid = unsafe { - geteuid() - }; - Cache { - context_user: UserInfo { - uid: None, - username: None, - passwd: None, - access_token: None - } +impl <'a>UserInfo<'a> { + pub fn new() -> UserInfo<'a> { + Self { + uid: None, + username: None, + passwd: None, + access_token: None } } -} -impl <'a>UserInfo<'a> { /// Set the information of this user object to that of the process owner // FIXME Move to Cache, with a from_current_user generator method here pub fn set_current_user(&mut self) { @@ -400,9 +383,11 @@ fn get_original_euid() -> uid_t { } } -lazy_static! { - static ref CACHE: Mutex<Cache<'static>> = Mutex::new(Cache::new()); +thread_local! { + static CONTEXT_USER: RefCell<UserInfo<'static>> = RefCell::new(UserInfo::new()); } -pub fn get_cache() -> MutexGuard<'static, Cache<'static>> { - CACHE.lock().unwrap() +pub fn with_context_user<R>(f: impl FnOnce(UserInfo<'static>) -> R) -> R { + CONTEXT_USER.with(|rc| { + f(rc.into_inner()) + }) } diff --git a/src/nss.rs b/src/nss.rs index dca4b9fd8a3f120426a30f705f33c2e0450c6167..a15202738ca526d741fe010124d6b44ccbc8d68a 100644 --- a/src/nss.rs +++ b/src/nss.rs @@ -18,7 +18,7 @@ use crate::config::{ get_optional, }; use config::Config; -use crate::cache::get_cache; +use crate::cache::with_context_user; use crate::logging::setup_log; @@ -53,9 +53,11 @@ fn nss_hook_prepare() -> Config { // Set the context user to the current user, but only if not already set // When doing PAM, we might get called back into by libc to do some NSS // lookup, and we want to keep the PAM login user context in that case - if !get_cache().context_user.is_initialized() { - get_cache().context_user.set_current_user(); - } + with_context_user(|mut cu| { + if !cu.is_initialized() { + cu.set_current_user(); + } + }); return conf; } @@ -67,99 +69,102 @@ impl PasswdHooks for OidcPasswd { let conf = nss_hook_prepare(); info!("[NSS] passwd.get_all_entries called"); - let mut cache = get_cache(); - let user_token_res = cache.context_user.get_access_token(); - // FIXME Implement caching of system token - let system_token_res = get_access_token_client(&conf, "nss", "", ""); - let system_token_res = system_token_res.as_ref(); - let token = match user_token_res { - Some(t) => t, - None => { - debug!("Could not find a user token to request NSS data; trying client credentials"); - match system_token_res { - Ok(ct) => ct, - Err(e) => { - error!("Failed to get access token with client credentials: {}", e); - return Response::Unavail; + with_context_user(|mut cu| { + let user_token_res = cu.get_access_token(); + // FIXME Implement caching of system token + let system_token_res = get_access_token_client(&conf, "nss", "", ""); + let system_token_res = system_token_res; + let token = match user_token_res { + Some(t) => t, + None => { + debug!("Could not find a user token to request NSS data; trying client credentials"); + match system_token_res.as_ref() { + Ok(ct) => ct, + Err(e) => { + error!("Failed to get access token with client credentials: {}", e); + return Response::Unavail; + } } } - } - }; - - let data: Vec<PasswdHelper> = match get_data_jq(&conf, "nss", "passwd.list", "".to_string(), &token, true) { - Ok(d) => d, - Err(e) => { - error!("Could not load JSON data for passwd: {}", e); - return Response::Unavail; - } - }; - Response::Success(data.into_iter().map(|p| p.0).collect()) + }; + + let data: Vec<PasswdHelper> = match get_data_jq(&conf, "nss", "passwd.list", "".to_string(), &token, true) { + Ok(d) => d, + Err(e) => { + error!("Could not load JSON data for passwd: {}", e); + return Response::Unavail; + } + }; + Response::Success(data.into_iter().map(|p| p.0).collect()) + }) } fn get_entry_by_uid(uid: libc::uid_t) -> Response<Passwd> { let conf = nss_hook_prepare(); info!("[NSS] passwd.get_entry_by_uid called for {}", uid); - let mut cache = get_cache(); - let user_token_res = cache.context_user.get_access_token(); - // FIXME Implement caching of system token - let system_token_res = get_access_token_client(&conf, "nss", "", ""); - let system_token_res = system_token_res.as_ref(); - let token = match user_token_res { - Some(t) => t, - None => { - debug!("Could not find a user token to request NSS data; trying client credentials"); - match system_token_res { - Ok(ct) => ct, - Err(e) => { - error!("Failed to get access token with client credentials: {}", e); - return Response::Unavail; + with_context_user(|mut cu| { + let user_token_res = cu.get_access_token(); + // FIXME Implement caching of system token + let system_token_res = get_access_token_client(&conf, "nss", "", ""); + let system_token_res = system_token_res; + let token = match user_token_res { + Some(t) => t, + None => { + debug!("Could not find a user token to request NSS data; trying client credentials"); + match system_token_res.as_ref() { + Ok(ct) => ct, + Err(e) => { + error!("Failed to get access token with client credentials: {}", e); + return Response::Unavail; + } } } - } - }; - - let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_uid", uid, &token, false).map(|PasswdHelper(p)| p) { - Ok(d) => d, - Err(e) => { - error!("Could not load JSON data for passwd: {}", e); - return Response::NotFound; - } - }; - Response::Success(data) + }; + + let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_uid", uid, &token, false).map(|PasswdHelper(p)| p) { + Ok(d) => d, + Err(e) => { + error!("Could not load JSON data for passwd: {}", e); + return Response::NotFound; + } + }; + Response::Success(data) + }) } fn get_entry_by_name(name: String) -> Response<Passwd> { let conf = nss_hook_prepare(); info!("[NSS] passwd.get_entry_by_name called for {}", name); - let mut cache = get_cache(); - let user_token_res = cache.context_user.get_access_token(); - // FIXME Implement caching of system token - let system_token_res = get_access_token_client(&conf, "nss", "", ""); - let system_token_res = system_token_res.as_ref(); - let token = match user_token_res { - Some(t) => t, - None => { - debug!("Could not find a user token to request NSS data; trying client credentials"); - match system_token_res { - Ok(ct) => ct, - Err(e) => { - error!("Failed to get access token with client credentials: {}", e); - return Response::Unavail; + with_context_user(|mut cu| { + let user_token_res = cu.get_access_token(); + // FIXME Implement caching of system token + let system_token_res = get_access_token_client(&conf, "nss", "", ""); + let system_token_res = system_token_res; + let token = match user_token_res { + Some(t) => t, + None => { + debug!("Could not find a user token to request NSS data; trying client credentials"); + match system_token_res.as_ref() { + Ok(ct) => ct, + Err(e) => { + error!("Failed to get access token with client credentials: {}", e); + return Response::Unavail; + } } } - } - }; - - let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_name", name, &token, false).map(|PasswdHelper(p)| p) { - Ok(d) => d, - Err(e) => { - error!("Could not load JSON data for passwd: {}", e); - return Response::NotFound; - } - }; - Response::Success(data) + }; + + let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_name", &name, &token, false).map(|PasswdHelper(p)| p) { + Ok(d) => d, + Err(e) => { + error!("Could not load JSON data for passwd: {}", e); + return Response::NotFound; + } + }; + Response::Success(data) + }) } } diff --git a/src/pam.rs b/src/pam.rs index 02b26a773ed5862f761609021207ecd8de94753f..b4f112636e1fe9f296c7b25543d69fbbfba978a3 100644 --- a/src/pam.rs +++ b/src/pam.rs @@ -24,7 +24,7 @@ use crate::oauth::get_access_token_password; use crate::logging::setup_log; -use crate::cache::{get_cache, set_is_getpwnam_safe}; +use crate::cache::{set_is_getpwnam_safe, with_context_user}; use pamsm::{PamServiceModule, Pam, PamFlag, PamError, PamLibExt}; @@ -91,8 +91,10 @@ impl PamServiceModule for PamOidc { Ok(t) => { info!("Authenticated {} using Resource Owner Password Grant", username); set_is_getpwnam_safe(false); - get_cache().context_user.set_username(username.to_string()); - get_cache().context_user.set_access_token(t); + with_context_user(|mut cu| { + cu.set_username(username.to_string()); + cu.set_access_token(t); + }); set_is_getpwnam_safe(true); return PamError::SUCCESS; },