use crate::asn1::{
authorization_data::AuthorizationData,
constants::authorization_data_types::AuthorizationDataType, enc_ticket_part::EncTicketPart,
encryption_key::EncryptionKey as KdcEncryptionKey,
};
use crate::proto::ms_pac::AdWin2kPac;
use crate::proto::reply::{EncKdcRepPart, KerberosReply, TransitedEncoding};
use crate::proto::request::TicketGrantRequest;
use crate::proto::time::{TicketGrantTimeBound, TicketRenewTimeBound};
use crate::proto::{
DerivedKey, EncTicket, EncryptedData, KdcPrimaryKey, KerberosTime, KrbError, Name, SessionKey,
Ticket, TicketFlags,
};
use der::asn1::OctetString;
use der::Encode;
#[derive(Debug)]
pub struct TicketGrantReply {
pub client_name: Name,
pub enc_part: EncryptedData,
pub ticket: EncTicket,
}
pub struct KerberosReplyTicketGrantBuilder {
nonce: i32,
service_name: Name,
sub_session_key: Option<SessionKey>,
pac: Option<AdWin2kPac>,
time_bounds: TicketGrantTimeBound,
ticket: Ticket,
flags: TicketFlags,
}
impl KerberosReplyTicketGrantBuilder {
pub fn new(
ticket_grant_request: TicketGrantRequest,
time_bounds: TicketGrantTimeBound,
) -> Self {
let TicketGrantRequest {
nonce,
service_name,
from: _,
until: _,
renew: _,
etypes: _,
sub_session_key,
client_time: _,
ticket,
} = ticket_grant_request;
let mut flags = TicketFlags::none();
if time_bounds.renew_until().is_some() {
flags |= TicketFlags::Renewable;
}
Self {
nonce,
service_name,
sub_session_key,
pac: None,
time_bounds,
ticket,
flags,
}
}
pub fn build(mut self, service_key: &DerivedKey) -> Result<KerberosReply, KrbError> {
let service_session_key = SessionKey::new();
let service_session_key: KdcEncryptionKey = service_session_key.try_into()?;
let (cname, crealm) = (&self.ticket.client_name).try_into()?;
let (server_name, server_realm) = (&self.service_name).try_into()?;
let auth_time = KerberosTime::from_system_time(self.ticket.auth_time)
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let start_time = Some(
KerberosTime::from_system_time(self.time_bounds.start_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?,
);
let end_time = KerberosTime::from_system_time(self.time_bounds.end_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let renew_till = if let Some(renew_until) = self.time_bounds.renew_until() {
self.flags |= TicketFlags::Renewable;
Some(
KerberosTime::from_system_time(renew_until)
.map_err(|_| KrbError::DerEncodeKerberosTime)?,
)
} else {
None
};
let enc_kdc_rep_part = EncKdcRepPart {
key: service_session_key.clone(),
last_req: Vec::with_capacity(0),
nonce: self.nonce,
key_expiration: None,
flags: self.flags,
auth_time,
start_time,
end_time,
renew_till,
server_realm,
server_name,
client_addresses: None,
encrypted_pa_data: None,
};
let enc_part = if let Some(sub_session_key) = self.sub_session_key {
sub_session_key.encrypt_tgs_rep_part(enc_kdc_rep_part, true)?
} else {
self.ticket
.session_key
.encrypt_tgs_rep_part(enc_kdc_rep_part, false)?
};
let authorization_data = if let Some(pac) = self.pac {
let pac_data_inner =
OctetString::new(pac.to_bytes()).map_err(|_| KrbError::DerEncodeOctetString)?;
let pac_data = AuthorizationData {
ad_type: AuthorizationDataType::AdWin2kPac.into(),
ad_data: pac_data_inner,
}
.to_der()
.and_then(OctetString::new)
.map_err(|_| KrbError::DerEncodeOctetString)?;
Some(vec![AuthorizationData {
ad_type: AuthorizationDataType::AdIfRelevant.into(),
ad_data: pac_data,
}])
} else {
None
};
let transited = TransitedEncoding {
tr_type: 1,
contents: OctetString::new(*b"").map_err(|_| KrbError::DerEncodeOctetString)?,
};
let ticket_inner = EncTicketPart {
flags: self.flags,
key: service_session_key,
crealm,
cname,
transited,
auth_time,
start_time,
end_time,
renew_till,
client_addresses: None,
authorization_data,
};
let ticket_enc_part = service_key.encrypt_tgs(ticket_inner)?;
let ticket = EncTicket {
tkt_vno: 5,
service: self.service_name,
enc_part: ticket_enc_part,
};
let client_name = self.ticket.client_name;
Ok(KerberosReply::TGS(TicketGrantReply {
client_name,
enc_part,
ticket,
}))
}
}
pub struct KerberosReplyTicketRenewBuilder {
nonce: i32,
service_name: Name,
sub_session_key: Option<SessionKey>,
time_bounds: TicketRenewTimeBound,
ticket: Ticket,
}
impl KerberosReplyTicketRenewBuilder {
pub fn new(
ticket_grant_request: TicketGrantRequest,
time_bounds: TicketRenewTimeBound,
) -> Self {
let TicketGrantRequest {
nonce,
service_name,
from: _,
until: _,
renew: _,
etypes: _,
sub_session_key,
client_time: _,
ticket,
} = ticket_grant_request;
Self {
nonce,
service_name,
sub_session_key,
time_bounds,
ticket,
}
}
pub fn build(self, primary_key: &KdcPrimaryKey) -> Result<KerberosReply, KrbError> {
let (cname, crealm) = (&self.ticket.client_name).try_into()?;
let (server_name, server_realm) = (&self.service_name).try_into()?;
let auth_time = KerberosTime::from_system_time(self.ticket.auth_time)
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let start_time = Some(
KerberosTime::from_system_time(self.time_bounds.start_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?,
);
let end_time = KerberosTime::from_system_time(self.time_bounds.end_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let renew_till = Some(
KerberosTime::from_system_time(self.time_bounds.renew_until())
.map_err(|_| KrbError::DerEncodeKerberosTime)?,
);
let session_key: KdcEncryptionKey = self.ticket.session_key.clone().try_into()?;
let enc_kdc_rep_part = EncKdcRepPart {
key: session_key.clone(),
last_req: Vec::with_capacity(0),
nonce: self.nonce,
key_expiration: None,
flags: self.ticket.flags,
auth_time,
start_time,
end_time,
renew_till,
server_realm,
server_name,
client_addresses: None,
encrypted_pa_data: None,
};
let enc_part = if let Some(sub_session_key) = self.sub_session_key {
sub_session_key.encrypt_tgs_rep_part(enc_kdc_rep_part, true)?
} else {
self.ticket
.session_key
.encrypt_tgs_rep_part(enc_kdc_rep_part, false)?
};
let authorization_data = None;
let transited = TransitedEncoding {
tr_type: 1,
contents: OctetString::new(*b"").map_err(|_| KrbError::DerEncodeOctetString)?,
};
let ticket_inner = EncTicketPart {
flags: self.ticket.flags,
key: session_key,
crealm,
cname,
transited,
auth_time,
start_time,
end_time,
renew_till,
client_addresses: None,
authorization_data,
};
let ticket_enc_part = primary_key.encrypt_tgs(ticket_inner)?;
let ticket = EncTicket {
tkt_vno: 5,
service: self.service_name,
enc_part: ticket_enc_part,
};
let client_name = self.ticket.client_name;
Ok(KerberosReply::TGS(TicketGrantReply {
client_name,
enc_part,
ticket,
}))
}
}