/* 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::logging::setup_log; use serde::de::Deserialize; use oauth2::{ AuthUrl, ClientId, ClientSecret, RequestTokenError, Scope, TokenUrl }; use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::reqwest::http_client; use libnss::interop::Response; use libnss::passwd::{PasswdHooks, Passwd}; fn get_or_nss_error<'de, T: Deserialize<'de>>(config: &Config, key: &str) -> Result<T, Response> { match get_optional(config, key) { Some(v) => { debug!("Configuration key found: {}", key); return Ok(v); }, None => { error!("Configuration key not found: {}", key); return Err(Response::Unavail); }, } } fn nss_hook_prepare() -> Config { let conf = get_config(None); let mut log_level = log::LevelFilter::Error; if conf.get_bool("debug").unwrap_or_default() || conf.get_bool("nss.debug").unwrap_or_default() { log_level = log::LevelFilter::Debug; } setup_log(log_level); return conf; } fn get_bearer_token(config: Config) -> Result<String, Response> { let client_id = ClientId::new(get_or_nss_error(&config, "nss.client_id")?); let client_secret = match get_optional(&config, "nss.client_secret") { Some(v) => Some(ClientSecret::new(v)), None => None, }; let auth_url = match AuthUrl::new(get_or_nss_error(&config, "nss.auth_url")?) { Ok(u) => u, _ => { error!("Could not parse authorization URL"); return Err(Response::Unavail); }, }; let token_url = match get_optional(&config, "nss.token_url") { Some(v) => match TokenUrl::new(v) { Ok(u) => Some(u), Err(_) => { error!("Could not parse token URL"); return Err(Response::Unavail); } }, None => None, }; let scopes: Vec<&str> = get_or_nss_error(&config, "nss.scopes")?; let client = BasicClient::new(client_id, client_secret, auth_url, token_url); let mut request = client.exchange_client_credentials(); for scope in scopes { request = request.add_scope(Scope::new(scope.to_string())); } let result = request.request(http_client); match result { Ok(t) => Ok(t), Err(e) => match e { RequestTokenError::Request(re) => match re { reqwest::Error::Reqwest(ree) => { error!("Request error requesting token: {}", ree); return Err(Response::Unavail); }, reqwest::Error::Http(he) => { error!("HTTP error requesting token: {}", he); return Err(Response::Unavail); }, reqwest::Error::Io(ioe) => { error!("IO error requesting token: {}", ioe); return Err(Response::Unavail); }, _ => { error!("Unknown error: {}", re); return Err(Response::Unavail); }, }, RequestTokenError::ServerResponse(t) => { error!("Authorization server returned error: {}", t); return Err(Response::Unavail); }, RequestTokenError::Parse(pe, _) => { error!("Error parsing response: {}", pe); return Err(Response::Unavail); }, _ => { error!("Unknown error: {}", e); return Err(Response::Unavail); }, }, } } fn do_json_request(config: Config, url: String) -> Result<String, Response> { let token = get_bearer_token(config)?; } struct OidcPasswd; impl PasswdHooks for OidcPasswd { fn get_all_entries() -> Vec<Passwd> { let config = nss_hook_prepare(); vec![ Passwd { name: "test".to_string(), passwd: "x".to_string(), uid: 1005, gid: 1005, gecos: "Test Account".to_string(), dir: "/home/test".to_string(), shell: "/bin/bash".to_string(), } ] } fn get_entry_by_uid(uid: libc::uid_t) -> Option<Passwd> { if uid == 1005 { return Some(Passwd { name: "test".to_string(), passwd: "x".to_string(), uid: 1005, gid: 1005, gecos: "Test Account".to_string(), dir: "/home/test".to_string(), shell: "/bin/bash".to_string(), }); } None } fn get_entry_by_name(name: String) -> Option<Passwd> { if name == "test" { return Some(Passwd { name: "test".to_string(), passwd: "x".to_string(), uid: 1005, gid: 1005, gecos: "Test Account".to_string(), dir: "/home/test".to_string(), shell: "/bin/bash".to_string(), }); } None } } libnss_passwd_hooks!(oidc, OidcPasswd);