cerbero-lib 0.1.0

Kerberos protocol attacker library
use chrono::{Duration, Utc};
use kerberos_asn1::{AsReq, Asn1Object, KdcReq, KerbPaPacRequest, KerberosTime, PaData, PrincipalName, TgsReq, Ticket};
use kerberos_constants::{kdc_options, pa_data_types, principal_names};
use kerberos_crypto::supported_etypes;
use rand::Rng;

pub struct KdcReqBuilder
{
	realm: String,
	sname: Option<PrincipalName>,
	etypes: Vec<i32>,
	kdc_options: u32,
	cname: Option<PrincipalName>,
	padatas: Vec<PaData>,
	nonce: u32,
	till: KerberosTime,
	rtime: Option<KerberosTime>,
	additional_tickets: Vec<Ticket>,
}

impl KdcReqBuilder
{
	pub fn new(realm: String) -> Self
	{
		Self { realm: realm.clone(),
		       // Linux krb5 kinit uses NT_SRV_INST here for the principal name
		       sname: Some(PrincipalName { name_type: principal_names::NT_SRV_INST,
		                                   name_string: vec!["krbtgt".into(), realm] }),
		       etypes: supported_etypes(),
		       kdc_options: kdc_options::FORWARDABLE
		                    | kdc_options::RENEWABLE
		                    | kdc_options::CANONICALIZE
		                    | kdc_options::RENEWABLE_OK,
		       cname: None,
		       padatas: Vec::new(),
		       nonce: rand::thread_rng().gen(),
		       till: Utc::now().checked_add_signed(Duration::days(1)).unwrap().into(),
		       rtime: Some(Utc::now().checked_add_signed(Duration::days(1)).unwrap().into()),
		       additional_tickets: Vec::new() }
	}

	pub fn add_kdc_option(mut self, kdc_option: u32) -> Self
	{
		self.kdc_options |= kdc_option;
		self
	}

	pub fn etypes(mut self, etypes: Vec<i32>) -> Self
	{
		self.etypes = etypes;
		self
	}

	pub fn cname(mut self, cname: Option<PrincipalName>) -> Self
	{
		self.cname = cname;
		self
	}

	pub fn sname(mut self, sname: Option<PrincipalName>) -> Self
	{
		self.sname = sname;
		self
	}

	pub fn username(self, username: String) -> Self
	{
		self.cname(Some(PrincipalName { name_type: principal_names::NT_PRINCIPAL, name_string: vec![username] }))
	}

	pub fn push_padata(mut self, padata: PaData) -> Self
	{
		self.padatas.push(padata);
		self
	}

	pub fn push_ticket(mut self, ticket: Ticket) -> Self
	{
		self.additional_tickets.push(ticket);
		self
	}

	pub fn request_pac(self) -> Self
	{
		self.push_padata(PaData::new(pa_data_types::PA_PAC_REQUEST, KerbPaPacRequest::new(true).build()))
	}

	pub fn build(self) -> KdcReq
	{
		let mut req = KdcReq::default();

		req.req_body.kdc_options = self.kdc_options.into();
		req.req_body.cname = self.cname;
		req.req_body.realm = self.realm;
		req.req_body.sname = self.sname;
		req.req_body.till = self.till;
		req.req_body.rtime = self.rtime;
		req.req_body.nonce = self.nonce;
		req.req_body.etypes = self.etypes;

		if !self.padatas.is_empty()
		{
			req.padata = Some(self.padatas);
		}

		if !self.additional_tickets.is_empty()
		{
			req.req_body.additional_tickets = Some(self.additional_tickets);
		}

		req
	}

	pub fn build_as_req(self) -> AsReq
	{
		self.build().into()
	}

	pub fn build_tgs_req(self) -> TgsReq
	{
		self.build().into()
	}
}