use std::env;
use std::fs;
use std::error::Error;
use std::path::Path;
pub fn get_default_keytab_path() -> String {
if let Ok(keytab_path) = env::var("KRB5_KTNAME") {
if Path::new(&keytab_path).exists() {
return keytab_path;
}
}
if let Ok(config_keytab) = get_keytab_from_config() {
if Path::new(&config_keytab).exists() {
return config_keytab;
}
}
let default_paths = vec![
"/etc/krb5.keytab".to_string(),
"/etc/krb5/krb5.keytab".to_string(),
"C:\\Windows\\krb5.keytab".to_string(),
format!("{}\\krb5.keytab", env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string())),
];
for path in default_paths {
if Path::new(&path).exists() {
return path;
}
}
"/etc/krb5.keytab".to_string()
}
pub fn get_keytab_from_config() -> Result<String, Box<dyn Error>> {
let config_paths = if let Ok(krb5_config) = env::var("KRB5_CONFIG") {
vec![krb5_config]
} else {
vec![
"/etc/krb5.conf".to_string(),
"/etc/krb5/krb5.conf".to_string(),
"C:\\Windows\\krb5.ini".to_string(),
format!("{}\\krb5.ini", env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string())),
]
};
for config_path in config_paths {
if let Ok(config_content) = fs::read_to_string(&config_path) {
if let Some(keytab_path) = parse_krb5_conf_for_keytab(&config_content) {
return Ok(keytab_path);
}
}
}
Err("Could not find keytab path in any krb5.conf file".into())
}
pub fn parse_krb5_conf_for_keytab(config_content: &str) -> Option<String> {
for line in config_content.lines() {
let line = line.trim();
if line.starts_with("default_keytab_name") || line.contains("default_keytab_name =") {
if let Some(keytab_part) = line.split('=').nth(1) {
let keytab = keytab_part.trim().trim_matches('"').trim_matches('\'');
if !keytab.is_empty() {
return Some(keytab.to_string());
}
}
}
}
None
}
pub fn get_kdc_ip_from_config(realm: &str) -> Result<String, Box<dyn Error>> {
let config_paths = if let Ok(krb5_config) = env::var("KRB5_CONFIG") {
vec![krb5_config]
} else {
vec![
"/etc/krb5.conf".to_string(),
"/etc/krb5/krb5.conf".to_string(),
"C:\\Windows\\krb5.ini".to_string(),
format!("{}\\krb5.ini", env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string())),
]
};
for config_path in config_paths {
if let Ok(config_content) = fs::read_to_string(&config_path) {
if let Some(kdc_ip) = parse_krb5_conf_for_kdc(&config_content, realm) {
return Ok(kdc_ip);
}
}
}
Err(format!("Could not find KDC for realm {} in any krb5.conf file", realm).into())
}
pub fn parse_krb5_conf_for_kdc(config_content: &str, realm: &str) -> Option<String> {
let mut in_realms_section = false;
let mut current_realm = String::new();
for line in config_content.lines() {
let line = line.trim();
if line.starts_with('[') && line.ends_with(']') {
let section = line[1..line.len()-1].trim();
in_realms_section = section == "realms" || section.ends_with("realms");
continue;
}
if !in_realms_section {
continue;
}
if line.starts_with(&format!("{} =", realm)) || line.starts_with(&format!("{}=", realm)) {
current_realm = realm.to_string();
continue;
}
if !current_realm.is_empty() {
if line.contains('{') {
continue;
}
if line.contains('}') {
current_realm.clear();
continue;
}
if line.starts_with("kdc") || line.contains("kdc =") {
if let Some(kdc_part) = line.split('=').nth(1) {
let kdc = kdc_part.trim().trim_matches('"').trim_matches('\'');
if !kdc.is_empty() {
return Some(kdc.to_string());
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_krb5_conf_for_kdc() {
let config_content = r#"
[libdefaults]
default_realm = EXAMPLE.COM
[realms]
EXAMPLE.COM = {
kdc = kdc.example.com
admin_server = kdc.example.com
}
OTHER.REALM = {
kdc = 192.168.1.100
admin_server = 192.168.1.100
}
"#;
let kdc = parse_krb5_conf_for_kdc(config_content, "EXAMPLE.COM");
assert_eq!(kdc, Some("kdc.example.com".to_string()));
let kdc2 = parse_krb5_conf_for_kdc(config_content, "OTHER.REALM");
assert_eq!(kdc2, Some("192.168.1.100".to_string()));
let kdc3 = parse_krb5_conf_for_kdc(config_content, "NONEXISTENT.REALM");
assert_eq!(kdc3, None);
}
}