Skip to main content

cerbero_lib/communication/
mod.rs

1//! Module to provide means to transport Kerberos messages
2//!
3
4mod channel_trait;
5pub use channel_trait::KrbChannel;
6
7mod tcp_channel;
8use tcp_channel::TcpChannel;
9
10mod udp_channel;
11use udp_channel::UdpChannel;
12
13use crate::Result;
14use std::collections::HashMap;
15use std::net::{IpAddr, SocketAddr};
16use trust_dns_resolver::config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts};
17use trust_dns_resolver::Resolver;
18
19/// Transport protocols available to send Kerberos messages
20#[derive(Debug, PartialEq, Clone, Copy)]
21pub enum TransportProtocol
22{
23	TCP,
24	UDP,
25}
26
27/// Struct to package KDC's
28#[derive(Debug, Clone)]
29pub struct Kdcs
30{
31	kdcs: HashMap<String, IpAddr>,
32}
33
34impl Default for Kdcs
35{
36	fn default() -> Self
37	{
38		Self::new()
39	}
40}
41
42impl Kdcs
43{
44	pub fn new() -> Self
45	{
46		Self { kdcs: HashMap::new() }
47	}
48
49	/// # Examples
50	///
51	/// ```
52	/// let mut kdcs = Kdcs::new();
53	/// let realm_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
54	/// kdcs.insert("realm.com", realm_ip);
55	/// ```
56	pub fn insert(&mut self, realm: String, ip: IpAddr)
57	{
58		self.kdcs.insert(realm.to_lowercase(), ip);
59	}
60
61	pub fn get(&self, realm: &str) -> Option<&IpAddr>
62	{
63		self.kdcs.get(&realm.to_lowercase())
64	}
65
66	pub fn ips(&self) -> Vec<&IpAddr>
67	{
68		self.kdcs.values().collect()
69	}
70
71	pub fn get_clone(&self, realm: &str) -> Option<IpAddr>
72	{
73		self.get(realm).copied()
74	}
75}
76
77/// Struct to package the KDC's and the protocol to communicate with them
78#[derive(Clone)]
79pub struct KdcComm
80{
81	kdcs: Kdcs,
82	protocol: TransportProtocol,
83}
84
85impl KdcComm
86{
87	/// Create a new KdcComm struct
88	///
89	/// # Examples
90	/// ```
91	/// let mut kdcs = Kdcs::new();
92	/// let kdc_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
93	/// kdcs.insert("DOMAIN.COM".to_string(), kdc_ip);
94	///
95	/// let kdccomm = KdcComm::new(kdcs, TransportProtocol::TCP);
96	/// ```
97	pub fn new(kdcs: Kdcs, protocol: TransportProtocol) -> Self
98	{
99		Self { kdcs, protocol }
100	}
101
102	/// Create a KrbChannel for the KDC specified by it's realm
103	///
104	/// # Examples
105	///
106	/// ```
107	/// let mut kdcs = Kdcs::new();
108	/// let kdc_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
109	/// kdcs.insert("DOMAIN.COM".to_string(), kdc_ip);
110	///
111	/// let kdccomm = KdcComm::new(kdcs, TransportProtocol::TCP);
112	/// let channel = kdccomm.create_channel("DOMAIN.COM")?;
113	/// ```
114	pub fn create_channel(&mut self, realm: &str) -> Result<Box<dyn KrbChannel>>
115	{
116		resolve_krb_channel(realm, &mut self.kdcs, self.protocol)
117	}
118}
119
120const KERBEROS_PORT: u16 = 88;
121/// Generates a transporter given and address and transport protocol
122pub fn new_krb_channel(dst_ip: IpAddr, transport_protocol: TransportProtocol) -> Box<dyn KrbChannel>
123{
124	let dst_address = SocketAddr::new(dst_ip, KERBEROS_PORT);
125	match transport_protocol
126	{
127		TransportProtocol::TCP => Box::new(TcpChannel::new(dst_address)),
128		TransportProtocol::UDP => Box::new(UdpChannel::new(dst_address)),
129	}
130}
131
132pub fn resolve_krb_channel(realm: &str,
133                           kdcs: &mut Kdcs,
134                           channel_protocol: TransportProtocol)
135                           -> Result<Box<dyn KrbChannel>>
136{
137	let kdc_ip = resolve_kdc_ip(realm, kdcs)?;
138	kdcs.insert(realm.to_string(), kdc_ip);
139
140	Ok(new_krb_channel(kdc_ip, channel_protocol))
141}
142
143pub fn resolve_kdc_ip(realm: &str, kdcs: &Kdcs) -> Result<IpAddr>
144{
145	Ok(match kdcs.get_clone(realm)
146	{
147		Some(ip) => ip,
148		None =>
149		{
150			let dns_servers = kdcs.ips().iter().map(|ip| SocketAddr::new(**ip, 53)).collect();
151			resolve_host(realm, dns_servers)?
152		},
153	})
154}
155
156pub fn resolve_host(realm: &str, dns_servers: Vec<SocketAddr>) -> Result<IpAddr>
157{
158	let resolver = if dns_servers.is_empty()
159	{
160		Resolver::from_system_conf().map_err(|err| format!("Unable to use dns system configuration: {}", err))?
161	}
162	else
163	{
164		let mut resolver_config = ResolverConfig::new();
165		for server in dns_servers
166		{
167			resolver_config.add_name_server(NameServerConfig { socket_addr: server,
168			                                                   protocol: Protocol::Tcp,
169			                                                   tls_dns_name: None,
170			                                                   trust_nx_responses: false });
171		}
172		Resolver::new(resolver_config, ResolverOpts::default()).unwrap()
173	};
174
175	let ips = resolver.lookup_ip(realm).map_err(|err| format!("Error resolving '{}' : '{}'", realm, err))?;
176
177	let ip = ips.iter().next().ok_or(format!("Error resolving '{}': No entries found", realm))?;
178
179	Ok(ip)
180}