Skip to content
Snippets Groups Projects
Unverified Commit d233d138 authored by Nik | Klampfradler's avatar Nik | Klampfradler
Browse files

[PAM] Add syslog output and debug functionality

parent fdc142aa
No related branches found
No related tags found
No related merge requests found
......@@ -16,6 +16,8 @@ crate-type = [ "cdylib" ]
pamsm = { version = "^0.4.2", features = ["libpam"] }
oauth2 = "^4.0.0"
config = "^0.11.0"
log = "^0.4.11"
syslog = "^5.0.0"
[profile.release]
opt-level = 'z'
......
......@@ -42,10 +42,10 @@ pub fn get_config(conf_args: config::Config) -> config::Config {
return conf;
}
pub fn argv_to_config(argv: Vec<String>) -> config::Config {
pub fn argv_to_config(argv: &Vec<String>) -> config::Config {
let mut conf = config::Config::default();
for arg in &argv {
for arg in argv {
if arg.contains("=") {
let split: Vec<&str> = arg.split("=").collect();
conf.set(split[0], split[1]);
......
#[macro_use] extern crate pamsm;
#[macro_use] extern crate log;
mod logging;
mod config;
mod pam;
/* Copyright 2021 Nicolas Goy
*
* 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 log::{LevelFilter};
use syslog::{BasicLogger, Facility, Formatter3164};
pub fn setup_log(log_level: LevelFilter) {
let formatter = Formatter3164 {
facility: Facility::LOG_AUTHPRIV,
hostname: None,
process: "nss_pam_oidc".into(),
pid: 0,
};
let logger = syslog::unix(formatter).expect("could not connect to syslog");
log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
.map(|()| log::set_max_level(LevelFilter::Debug));
log::set_max_level(log_level);
}
......@@ -19,6 +19,8 @@ use crate::config::{
};
use config::Config;
use crate::logging::setup_log;
use oauth2::{
AuthUrl,
ClientId,
......@@ -38,8 +40,14 @@ use pamsm::{PamServiceModule, Pam, PamFlag, PamError, PamLibExt};
fn get_or_pam_error(config: &Config, key: &str) -> Result<String, PamError> {
match config.get_str(key) {
Ok(v) => Ok(v),
_ => Err(PamError::SERVICE_ERR),
Ok(v) => {
debug!("Configuration key found: {} = {}", key, v);
return Ok(v);
},
_ => {
error!("Configuration key not found: {}", key);
return Err(PamError::SERVICE_ERR);
},
}
}
......@@ -48,11 +56,17 @@ fn do_legacy_auth(username: String, password: String, config: Config) -> Result<
let client_secret = ClientSecret::new(get_or_pam_error(&config, "pam.client_secret")?);
let auth_url = match AuthUrl::new(get_or_pam_error(&config, "pam.auth_url")?) {
Ok(u) => u,
_ => return Err(PamError::SERVICE_ERR),
_ => {
error!("Could not parse authorization URL");
return Err(PamError::SERVICE_ERR);
},
};
let token_url = match TokenUrl::new(get_or_pam_error(&config, "pam.token_url")?){
Ok(u) => u,
_ => return Err(PamError::SERVICE_ERR),
_ => {
error!("Could not parse token URL");
return Err(PamError::SERVICE_ERR);
},
};
let scope = get_or_pam_error(&config, "pam.scope")?;
......@@ -65,41 +79,85 @@ fn do_legacy_auth(username: String, password: String, config: Config) -> Result<
.add_scope(Scope::new(scope.to_string()))
.request(http_client) {
Ok(t) => Ok(t),
_ => Err(PamError::AUTHINFO_UNAVAIL),
Err(e) => {
error!("Error requesting grant: {}", e);
return Err(PamError::AUTHINFO_UNAVAIL);
},
}
}
fn pam_sm_prepare(argv: &Vec<String>) -> Config {
let conf_args = argv_to_config(argv);
let conf = get_config(conf_args);
let mut log_level = log::LevelFilter::Error;
if conf.get_bool("debug").unwrap_or_default() || conf.get_bool("pam.debug").unwrap_or_default() {
log_level = log::LevelFilter::Debug;
}
setup_log(log_level);
return conf;
}
struct PamOidc;
impl PamServiceModule for PamOidc {
fn authenticate(pamh: Pam, _: PamFlag, argv: Vec<String>) -> PamError {
let conf_args = argv_to_config(argv);
let conf = get_config(conf_args);
let conf = pam_sm_prepare(&argv);
if conf.get_str("pam.flow").unwrap() == "password" {
debug!("Starting Resource Owner Password Credentials OAuth flow");
let username = match pamh.get_user(None) {
Ok(Some(u)) => match u.to_str() {
Ok(u) => u,
_ => return PamError::CRED_INSUFFICIENT,
_ => {
error!("Could not convert user name to string, aborting");
return PamError::CRED_INSUFFICIENT;
},
},
Ok(None) => {
error!("No username supplied");
return PamError::CRED_INSUFFICIENT;
},
Err(e) => {
error!("Error getting user name: {}", e);
return e;
},
Ok(None) => return PamError::CRED_INSUFFICIENT,
Err(e) => return e,
};
debug!("Successfully got user name");
let password = match pamh.get_authtok(None) {
Ok(Some(p)) => match p.to_str() {
Ok(p) => p,
_ => return PamError::CRED_INSUFFICIENT,
_ => {
error!("Could not convert password to string");
return PamError::CRED_INSUFFICIENT;
},
},
Ok(None) => {
error!("No password supplied");
return PamError::CRED_INSUFFICIENT;
},
Err(e) => {
error!("Error getting password: {}", e);
return e;
},
Ok(None) => return PamError::CRED_INSUFFICIENT,
Err(e) => return e,
};
debug!("Successfully got password");
match do_legacy_auth(username.to_string(), password.to_string(), conf) {
Ok(_) => return PamError::SUCCESS,
Err(e) => return e,
Ok(_) => {
info!("Authenticated {} using Resource Owner Password Grant", username);
return PamError::SUCCESS;
},
Err(e) => {
error!("Error in legacy authentication: {}", e);
return e;
},
};
}
error!("Unknown flow for authentication");
return PamError::SERVICE_ERR;
}
}
......
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