diff --git a/Cargo.toml b/Cargo.toml index 61bfbc2b..94ac5c9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.6.4" +version = "0.6.5" authors = [ "David Mulder " ] @@ -76,7 +76,7 @@ tracing-forest = "^0.1.6" rusqlite = "^0.32.0" hashbrown = { version = "0.14.0", features = ["serde", "inline-more", "ahash"] } lru = "^0.12.3" -kanidm_lib_crypto = { path = "./src/crypto", version = "0.6.4" } +kanidm_lib_crypto = { path = "./src/crypto", version = "0.6.5" } kanidm_utils_users = { path = "./src/users" } walkdir = "2" csv = "1.2.2" diff --git a/src/common/src/config.rs b/src/common/src/config.rs index bc44f695..d61638f8 100644 --- a/src/common/src/config.rs +++ b/src/common/src/config.rs @@ -5,11 +5,11 @@ use std::path::PathBuf; use tracing::{debug, error}; use crate::constants::{ - BROKER_APP_ID, DEFAULT_CACHE_TIMEOUT, DEFAULT_CONFIG_PATH, DEFAULT_CONN_TIMEOUT, - DEFAULT_DB_PATH, DEFAULT_HELLO_ENABLED, DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, - DEFAULT_HOME_PREFIX, DEFAULT_HSM_PIN_PATH, DEFAULT_ID_ATTR_MAP, DEFAULT_ODC_PROVIDER, - DEFAULT_SELINUX, DEFAULT_SFA_FALLBACK_ENABLED, DEFAULT_SHELL, DEFAULT_SOCK_PATH, - DEFAULT_TASK_SOCK_PATH, DEFAULT_USE_ETC_SKEL, SERVER_CONFIG_PATH, + BROKER_APP_ID, CN_NAME_MAPPING, DEFAULT_CACHE_TIMEOUT, DEFAULT_CONFIG_PATH, + DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_HELLO_ENABLED, DEFAULT_HOME_ALIAS, + DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX, DEFAULT_HSM_PIN_PATH, DEFAULT_ID_ATTR_MAP, + DEFAULT_ODC_PROVIDER, DEFAULT_SELINUX, DEFAULT_SFA_FALLBACK_ENABLED, DEFAULT_SHELL, + DEFAULT_SOCK_PATH, DEFAULT_TASK_SOCK_PATH, DEFAULT_USE_ETC_SKEL, SERVER_CONFIG_PATH, }; use crate::unix_config::{HomeAttr, HsmType}; use idmap::DEFAULT_IDMAP_RANGE; @@ -39,6 +39,8 @@ fn str_to_home_attr(attrib: &str) -> HomeAttr { return HomeAttr::Uuid; } else if attrib.to_lowercase() == "spn" { return HomeAttr::Spn; + } else if attrib.to_lowercase() == "cn" { + return HomeAttr::Cn; } HomeAttr::Uuid // Default to Uuid if the attrib can't be parsed } @@ -390,9 +392,13 @@ impl HimmelblauConfig { } pub fn get_debug(&self) -> bool { + match_bool(self.config.get("global", "debug"), false) + } + + pub fn get_cn_name_mapping(&self) -> bool { match_bool( - self.config.get("global", "debug"), - false, + self.config.get("global", "cn_name_mapping"), + CN_NAME_MAPPING, ) } } diff --git a/src/common/src/constants.rs b/src/common/src/constants.rs index 60339429..4860cd0e 100644 --- a/src/common/src/constants.rs +++ b/src/common/src/constants.rs @@ -25,3 +25,4 @@ pub const DEFAULT_SFA_FALLBACK_ENABLED: bool = false; pub const DEFAULT_ID_ATTR_MAP: IdAttr = IdAttr::Name; pub const BROKER_APP_ID: &str = "29d9ed98-a469-4536-ade2-f981bc1d605e"; pub const BROKER_CLIENT_IDENT: &str = "38aa3b87-a06d-4817-b275-7a316988d93b"; +pub const CN_NAME_MAPPING: bool = true; diff --git a/src/common/src/resolver.rs b/src/common/src/resolver.rs index e371dca7..f738207a 100644 --- a/src/common/src/resolver.rs +++ b/src/common/src/resolver.rs @@ -735,6 +735,7 @@ where HomeAttr::Uuid => token.uuid.hyphenated().to_string(), HomeAttr::Spn => token.spn.as_str().to_string(), HomeAttr::Name => token.name.as_str().to_string(), + HomeAttr::Cn => token.spn.split('@').collect::>()[0].to_string(), }) } @@ -744,6 +745,7 @@ where HomeAttr::Uuid => token.uuid.hyphenated().to_string(), HomeAttr::Spn => token.spn.as_str().to_string(), HomeAttr::Name => token.name.as_str().to_string(), + HomeAttr::Cn => token.spn.split('@').collect::>()[0].to_string(), } } diff --git a/src/common/src/unix_config.rs b/src/common/src/unix_config.rs index 97427217..3cfb6d21 100644 --- a/src/common/src/unix_config.rs +++ b/src/common/src/unix_config.rs @@ -4,6 +4,7 @@ use std::fmt::{Display, Formatter}; pub enum HomeAttr { Uuid, Spn, + Cn, Name, } @@ -15,6 +16,7 @@ impl Display for HomeAttr { match self { HomeAttr::Uuid => "UUID", HomeAttr::Spn => "SPN", + HomeAttr::Cn => "CN", HomeAttr::Name => "Name", } ) diff --git a/src/config/himmelblau.conf.example b/src/config/himmelblau.conf.example index f815fc18..80d66e12 100644 --- a/src/config/himmelblau.conf.example +++ b/src/config/himmelblau.conf.example @@ -37,6 +37,13 @@ # configured. This is disabled by default. # enable_sfa_fallback = false # +# CN to UPN mapping allows users to simply enter the short form of their +# username (`dave` instead of `dave@example.com`). Himmelblau will only map CNs +# to the primary domain (the first domain listed in the `domains` option +# above). WARNING: CN mapping could mask local users, depending on your PAM +# configuration. +# cn_name_mapping = true +# # authority_host = login.microsoftonline.com # # The location of the cache database @@ -50,9 +57,11 @@ # home_attr = UUID ; home directory attribute options: # ; UUID (default) # ; SPN +# ; CN # home_alias = SPN ; home directory alias options: # ; UUID # ; SPN (default) +# ; CN # shell = /bin/bash ; default shell for the user # idmap_range = 5000000-5999999 # connection_timeout = 2 diff --git a/src/glue/src/unix_config.rs b/src/glue/src/unix_config.rs index 4781fb54..9a205c64 100644 --- a/src/glue/src/unix_config.rs +++ b/src/glue/src/unix_config.rs @@ -2,25 +2,38 @@ use himmelblau_unix_common::config::HimmelblauConfig; use himmelblau_unix_common::constants::{DEFAULT_CONN_TIMEOUT, DEFAULT_SOCK_PATH}; pub struct KanidmUnixdConfig { + pub domains: Vec, pub unix_sock_timeout: u64, pub sock_path: String, + pub cn_name_mapping: bool, } impl KanidmUnixdConfig { pub fn new() -> Self { KanidmUnixdConfig { + domains: vec![], sock_path: DEFAULT_SOCK_PATH.to_string(), unix_sock_timeout: DEFAULT_CONN_TIMEOUT * 2, + cn_name_mapping: false, } } pub fn read_options_from_optional_config(self, config_path: &str) -> Result { let config: HimmelblauConfig = HimmelblauConfig::new(Some(config_path))?; Ok(KanidmUnixdConfig { + domains: config.get_configured_domains(), sock_path: config.get_socket_path(), unix_sock_timeout: config.get_connection_timeout() * 2, + cn_name_mapping: config.get_cn_name_mapping(), }) } + + pub fn map_cn_name(&self, account_id: &str) -> String { + if self.cn_name_mapping && !account_id.contains('@') && !self.domains.is_empty() { + return format!("{}@{}", account_id, self.domains[0]); + } + account_id.to_string() + } } impl Default for KanidmUnixdConfig { diff --git a/src/nss/src/implementation.rs b/src/nss/src/implementation.rs index 16619e5d..02f9f907 100644 --- a/src/nss/src/implementation.rs +++ b/src/nss/src/implementation.rs @@ -74,7 +74,8 @@ impl PasswdHooks for HimmelblauPasswd { return Response::Unavail; } }; - let req = ClientRequest::NssAccountByName(name); + let name = cfg.map_cn_name(&name); + let req = ClientRequest::NssAccountByName(name.clone()); let mut daemon_client = match DaemonClientBlocking::new(cfg.sock_path.as_str()) { Ok(dc) => dc, Err(_) => { @@ -86,8 +87,11 @@ impl PasswdHooks for HimmelblauPasswd { .call_and_wait(&req, cfg.unix_sock_timeout) .map(|r| match r { ClientResponse::NssAccount(opt) => opt - .map(passwd_from_nssuser) - .map(Response::Success) + .map(|nu| { + let mut passwd = passwd_from_nssuser(nu); + passwd.name = name; + Response::Success(passwd) + }) .unwrap_or_else(|| Response::NotFound), _ => Response::NotFound, }) diff --git a/src/pam/src/pam/mod.rs b/src/pam/src/pam/mod.rs index e26c4a4b..0458254a 100755 --- a/src/pam/src/pam/mod.rs +++ b/src/pam/src/pam/mod.rs @@ -262,6 +262,7 @@ impl PamHooks for PamKanidm { Ok(cfg) => cfg, Err(e) => return e, }; + let account_id = cfg.map_cn_name(&account_id); let req = ClientRequest::PamAccountAllowed(account_id); // PamResultCode::PAM_IGNORE @@ -331,6 +332,7 @@ impl PamHooks for PamKanidm { Ok(cfg) => cfg, Err(e) => return e, }; + let account_id = cfg.map_cn_name(&account_id); let mut timeout = cfg.unix_sock_timeout; let mut daemon_client = match DaemonClientBlocking::new(cfg.sock_path.as_str()) { @@ -553,6 +555,7 @@ impl PamHooks for PamKanidm { Ok(cfg) => cfg, Err(e) => return e, }; + let account_id = cfg.map_cn_name(&account_id); let req = ClientRequest::PamAccountBeginSession(account_id); let mut daemon_client = match DaemonClientBlocking::new(cfg.sock_path.as_str()) {