use std::env;
use std::path::Path;
use picky_krb::constants::types::{NT_ENTERPRISE, NT_PRINCIPAL};
use crate::krb::Krb5Conf;
use crate::{Username, UsernameParts};
pub fn get_client_principal_name_type(username: &str, _domain: &str) -> u8 {
if username.contains('@') {
NT_ENTERPRISE
} else {
NT_PRINCIPAL
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ClientPrincipalName<'a> {
pub name: &'a str,
pub realm_domain: &'a str,
pub name_type: u8,
}
pub fn get_client_principal_name(username: &Username) -> ClientPrincipalName<'_> {
match username.parts() {
UsernameParts::UserPrincipalName(parts) => ClientPrincipalName {
name: parts.upn(),
realm_domain: parts.suffix(),
name_type: NT_ENTERPRISE,
},
UsernameParts::DownLevelLogonName(parts) => ClientPrincipalName {
name: parts.account_name(),
realm_domain: parts.netbios_domain().unwrap_or_default(),
name_type: NT_PRINCIPAL,
},
}
}
pub fn get_client_principal_realm(username: &str, domain: &str) -> String {
let krb5_config = env::var("KRB5_CONFIG").unwrap_or_else(|_| "/etc/krb5.conf:/usr/local/etc/krb5.conf".to_string());
let krb5_conf_paths = krb5_config.split(':').map(Path::new).collect::<Vec<&Path>>();
get_client_principal_realm_impl(&krb5_conf_paths, username, domain)
}
fn get_client_principal_realm_impl(krb5_conf_paths: &[&Path], username: &str, domain: &str) -> String {
let domain = if domain.is_empty() {
if let Some((_left, right)) = username.rsplit_once('@') {
right.to_string()
} else {
String::new()
}
} else {
domain.to_string()
};
for krb5_conf_path in krb5_conf_paths {
if !krb5_conf_path.exists() {
continue;
}
if let Some(krb5_conf) = Krb5Conf::new_from_file(krb5_conf_path)
&& let Some(mappings) = krb5_conf.get_values_in_section(&["domain_realm"])
{
for (mapping_domain, realm) in mappings {
if matches_domain(&domain, mapping_domain) {
return realm.to_owned();
}
}
}
}
domain.to_uppercase()
}
fn matches_domain(domain: &str, mapping_domain: &str) -> bool {
let domain = domain.to_lowercase();
let mapping_domain = mapping_domain.to_lowercase();
if mapping_domain.starts_with('.') {
domain.ends_with(&mapping_domain)
} else {
domain == mapping_domain || domain.ends_with(&format!(".{mapping_domain}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
const KRB5_CONFIG_FILE_PATH: &str = "test_assets/krb5.conf";
#[test]
fn client_principal_name_from_upn_is_enterprise() {
let username = Username::parse("user@example.com").expect("UPN");
let cname = get_client_principal_name(&username);
assert_eq!(cname.name, "user@example.com");
assert_eq!(cname.realm_domain, "example.com");
assert_eq!(cname.name_type, NT_ENTERPRISE);
}
#[test]
fn client_principal_name_from_down_level_is_principal() {
let username = Username::parse("EXAMPLE\\user").expect("down-level logon name");
let cname = get_client_principal_name(&username);
assert_eq!(cname.name, "user");
assert_eq!(cname.realm_domain, "EXAMPLE");
assert_eq!(cname.name_type, NT_PRINCIPAL);
}
#[test]
fn client_principal_name_from_bare_name_has_empty_realm_domain() {
let username = Username::parse("user").expect("bare name");
let cname = get_client_principal_name(&username);
assert_eq!(cname.name, "user");
assert_eq!(cname.realm_domain, "");
assert_eq!(cname.name_type, NT_PRINCIPAL);
}
#[test]
fn test_get_client_principal_realm_from_domain() {
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "", "TBT.COM");
assert_eq!(realm, "TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "", "C1.DEV.TBT.COM");
assert_eq!(realm, "TEST.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "", "P1.C2.DEV.TBT.COM");
assert_eq!(realm, "TEST.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "", "DEV.TBT.COM");
assert_eq!(realm, "DEV.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "", "TEST.TBT.COM");
assert_eq!(realm, "STAGE.TBT.COM");
}
#[test]
fn test_get_client_principal_realm_from_username() {
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@tbt.com", "");
assert_eq!(realm, "TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@c1.dev.tbt.com", "");
assert_eq!(realm, "TEST.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@p1.c2.dev.tbt.com", "");
assert_eq!(realm, "TEST.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@dev.tbt.com", "");
assert_eq!(realm, "DEV.TBT.COM");
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@test.tbt.com", "");
assert_eq!(realm, "STAGE.TBT.COM");
}
#[test]
fn realm_from_username_splits_on_last_at() {
let realm = get_client_principal_realm_impl(&[Path::new(KRB5_CONFIG_FILE_PATH)], "user@dept@tbt.com", "");
assert_eq!(realm, "TBT.COM");
}
}