use crate::core::forge::{new_nt_enterprise, new_nt_principal, new_nt_srv_inst};
use crate::core::KrbUser;
use crate::error::Error;
use kerberos_asn1::{Asn1Object, EncKrbCredPart, EncryptedData, KrbCred, KrbCredInfo, PrincipalName, Ticket};
use kerberos_constants::etypes::NO_ENCRYPTION;
use std::slice::Iter;
#[derive(Clone, Debug)]
pub struct TicketCreds
{
pub ticket_creds: Vec<TicketCred>,
}
impl TicketCreds
{
pub fn new(ticket_creds: Vec<TicketCred>) -> Self
{
Self { ticket_creds }
}
pub fn empty() -> Self
{
Self { ticket_creds: Vec::new() }
}
pub fn push(&mut self, ticket_info: TicketCred)
{
self.ticket_creds.push(ticket_info);
}
pub fn iter(&self) -> Iter<TicketCred>
{
return self.ticket_creds.iter();
}
pub fn is_empty(&self) -> bool
{
return self.ticket_creds.is_empty();
}
pub fn get(&self, index: usize) -> Option<&TicketCred>
{
return self.ticket_creds.get(index);
}
pub fn filter<P>(&self, predicate: P) -> Self
where P: Fn(&TicketCred) -> bool
{
self.iter().filter(|tci| predicate(tci)).cloned().collect::<Vec<TicketCred>>().into()
}
pub fn etype(&self, etype: i32) -> Self
{
self.filter(|tci| tci.cred_info.key.keytype == etype)
}
pub fn prealm(&self, realm: &str) -> Self
{
self.filter(|tci| {
if let Some(prealm) = &tci.cred_info.prealm
{
return prealm.to_lowercase() == realm.to_lowercase();
}
return false;
})
}
pub fn pname(&self, name: &PrincipalName) -> Self
{
self.filter(|tci| {
if let Some(pname) = &tci.cred_info.pname
{
return pname == name;
}
return false;
})
}
pub fn srealm(&self, realm: &str) -> Self
{
self.filter(|tci| {
if let Some(srealm) = &tci.cred_info.srealm
{
return srealm.to_lowercase() == realm.to_lowercase();
}
return false;
})
}
pub fn sname(&self, name: &PrincipalName) -> Self
{
self.filter(|tci| {
if let Some(sname) = &tci.cred_info.sname
{
return sname == name;
}
return false;
})
}
pub fn user_realm(&self, realm: &str) -> Self
{
return self.prealm(realm);
}
pub fn username(&self, name: &str) -> Self
{
let pname = new_nt_principal(name);
return self.pname(&pname);
}
pub fn user(&self, user: &KrbUser) -> Self
{
return self.user_realm(&user.realm).username(&user.name);
}
pub fn service_name(&self, name: &str) -> Self
{
let name_lower = name.to_lowercase();
self.filter(|tci| {
if let Some(sname) = &tci.cred_info.sname
{
return sname.name_string.iter().map(|s| s.to_lowercase()).any(|s| s.to_lowercase() == name_lower);
}
return false;
})
}
pub fn tgt(&self) -> Self
{
return self.service_name("krbtgt");
}
pub fn tgt_realm(&self, realm: &str) -> Self
{
let tgt_service = new_nt_srv_inst(&format!("krbtgt/{}", realm));
return self.sname(&tgt_service);
}
pub fn user_tgt_realm(&self, user: &KrbUser, realm: &str) -> Self
{
return self.tgt_realm(realm).user(user);
}
pub fn user_service(&self, client: &KrbUser, sname: &PrincipalName, srealm: &String) -> Self
{
return self.user(client).sname(sname).srealm(srealm);
}
pub fn s4u2self_tgss(&self, user: &KrbUser, impersonate_user: &KrbUser, user_service: Option<&String>) -> Self
{
let srealm = &user.realm;
let sname = match user_service
{
Some(user_service) => new_nt_srv_inst(user_service),
None => new_nt_enterprise(user),
};
return self.user_service(impersonate_user, &sname, srealm);
}
}
impl Into<KrbCred> for TicketCreds
{
fn into(self) -> KrbCred
{
let mut krb_cred = KrbCred::default();
let mut tickets = Vec::with_capacity(self.ticket_creds.len());
let mut cred_infos = Vec::with_capacity(self.ticket_creds.len());
for ticket_cred_info in self.ticket_creds
{
tickets.push(ticket_cred_info.ticket);
cred_infos.push(ticket_cred_info.cred_info);
}
krb_cred.tickets = tickets;
let mut cred_part = EncKrbCredPart::default();
cred_part.ticket_info = cred_infos;
krb_cred.enc_part = EncryptedData::new(NO_ENCRYPTION, None, cred_part.build());
return krb_cred;
}
}
impl TryFrom<KrbCred> for TicketCreds
{
type Error = Error;
fn try_from(krb_cred: KrbCred) -> Result<Self, Error>
{
if krb_cred.enc_part.etype != NO_ENCRYPTION
{
return Err(Error::DataError(format!("Unable to decrypt the credentials")));
}
let (_, cred_part) = EncKrbCredPart::parse(&krb_cred.enc_part.cipher).map_err(|_| {
Error::DataError(format!(
"Error parsing credentials: EncKrbCredPart"
))
})?;
let tickets = krb_cred.tickets;
let cred_infos = cred_part.ticket_info;
Ok((tickets, cred_infos).into())
}
}
impl From<(Vec<Ticket>, Vec<KrbCredInfo>)> for TicketCreds
{
fn from((tickets, cred_infos): (Vec<Ticket>, Vec<KrbCredInfo>)) -> Self
{
let mut ticket_cred_infos = Vec::with_capacity(tickets.len());
for (ticket, cred_info) in tickets.into_iter().zip(cred_infos.into_iter())
{
ticket_cred_infos.push(TicketCred::new(ticket, cred_info));
}
return Self::new(ticket_cred_infos);
}
}
impl From<Vec<TicketCred>> for TicketCreds
{
fn from(v: Vec<TicketCred>) -> Self
{
return Self::new(v);
}
}
impl From<TicketCred> for TicketCreds
{
fn from(ticket_info: TicketCred) -> Self
{
return Self::new(vec![ticket_info]);
}
}
#[derive(Debug, Clone)]
pub struct TicketCred
{
pub ticket: Ticket,
pub cred_info: KrbCredInfo,
}
impl TicketCred
{
pub fn new(ticket: Ticket, cred_info: KrbCredInfo) -> Self
{
return Self { ticket, cred_info };
}
pub fn is_tgt(&self) -> bool
{
if let Some(sname) = &self.cred_info.sname
{
if let Some(service) = sname.name_string.get(0)
{
return service == "krbtgt";
}
}
return false;
}
pub fn is_tgt_for_realm(&self, realm: &str) -> bool
{
if self.is_tgt()
{
if let Some(tgt_realm) = self.service_host()
{
return tgt_realm.to_lowercase() == realm.to_lowercase();
}
}
return false;
}
pub fn is_for_service(&self, service: &PrincipalName) -> bool
{
if let Some(srv) = self.service()
{
return srv == service;
}
return false;
}
pub fn service(&self) -> Option<&PrincipalName>
{
return self.cred_info.sname.as_ref();
}
pub fn service_host(&self) -> Option<&String>
{
if let Some(sname) = &self.cred_info.sname
{
return sname.name_string.get(1);
}
return None;
}
pub fn change_sname(&mut self, sname: PrincipalName)
{
self.ticket.sname = sname.clone();
self.cred_info.sname = Some(sname);
}
}
impl From<(Ticket, KrbCredInfo)> for TicketCred
{
fn from((t, kci): (Ticket, KrbCredInfo)) -> Self
{
return Self::new(t, kci);
}
}