/* 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::config::{ get_config, get_optional, }; use config::Config; use crate::cache::get_cache; use crate::logging::setup_log; use crate::oauth::{get_access_token_client, get_data_jq}; use serde::{Serialize, Deserialize}; use libnss::interop::Response; use libnss::passwd::{PasswdHooks, Passwd}; #[derive(Serialize, Deserialize)] #[serde(remote = "Passwd")] struct PasswdDef { name: String, passwd: String, uid: libc::uid_t, gid: libc::gid_t, gecos: String, dir: String, shell: String } #[derive(Deserialize)] struct PasswdHelper(#[serde(with = "PasswdDef")] Passwd); fn nss_hook_prepare() -> Config { let conf = get_config(None); let mut log_level = log::LevelFilter::Error; if get_optional(&conf, "nss.debug").unwrap_or_default() { log_level = log::LevelFilter::Debug; } setup_log(log_level); // 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(); } return conf; } struct OidcPasswd; impl PasswdHooks for OidcPasswd { fn get_all_entries() -> Response<Vec<Passwd>> { let conf = nss_hook_prepare(); 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; } } } }; 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(); 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; } } } }; let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_uid", uid.to_string(), &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(); 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; } } } }; 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) } } libnss_passwd_hooks!(oidc, OidcPasswd);