1use super::dictionary::{ Dictionary, DictionaryAttribute, DictionaryValue };
5use super::error::RadiusError;
6use super::radius_packet::{ RadiusAttribute, RadiusMsgType, RadiusPacket, TypeCode };
7
8use hmac::{ Hmac, Mac };
9use md5::Md5;
10
11
12const IGNORE_VERIFY_ATTRIBUTE: &str = "Message-Authenticator";
13type HmacMd5 = Hmac<Md5>;
14
15
16#[derive(Debug)]
17pub struct Host {
19 auth_port: u16,
20 acct_port: u16,
21 coa_port: u16,
22 dictionary: Dictionary
23}
24
25impl Host{
26 pub fn with_dictionary(dictionary: Dictionary) -> Host {
29 Host {
30 auth_port: 0,
31 acct_port: 0,
32 coa_port: 0,
33 dictionary
34 }
35 }
36
37 pub fn set_port(&mut self, msg_type: RadiusMsgType, port: u16) {
39 match msg_type {
40 RadiusMsgType::AUTH => self.auth_port = port,
41 RadiusMsgType::ACCT => self.acct_port = port,
42 RadiusMsgType::COA => self.coa_port = port,
43 }
44 }
45
46 #[allow(dead_code)]
47 pub fn initialise_host(auth_port: u16, acct_port: u16, coa_port: u16, dictionary: Dictionary) -> Host {
49 Host { auth_port, acct_port, coa_port, dictionary }
50 }
51
52
53 pub fn create_attribute_by_name(&self, attribute_name: &str, value: Vec<u8>) -> Result<RadiusAttribute, RadiusError> {
55 RadiusAttribute::create_by_name(&self.dictionary, attribute_name, value).ok_or(RadiusError::MalformedAttributeError { error: format!("Failed to create: {:?} attribute. Check if attribute exists in provided dictionary file", attribute_name) })
56 }
57
58 pub fn create_attribute_by_id(&self, attribute_id: u8, value: Vec<u8>) -> Result<RadiusAttribute, RadiusError> {
60 RadiusAttribute::create_by_id(&self.dictionary, attribute_id, value).ok_or(RadiusError::MalformedAttributeError { error: format!("Failed to create: attribute with ID {}. Check if attribute exists in provided dictionary file", attribute_id) })
61 }
62
63 pub fn port(&self, code: &TypeCode) -> Option<u16> {
65 match code {
66 TypeCode::AccessRequest => Some(self.auth_port),
67 TypeCode::AccountingRequest => Some(self.acct_port),
68 TypeCode::CoARequest => Some(self.coa_port),
69 _ => None
70 }
71 }
72
73 pub fn dictionary(&self) -> &Dictionary {
75 &self.dictionary
76 }
77
78 #[allow(dead_code)]
79 pub fn dictionary_value_by_attr_and_value_name(&self, attr_name: &str, value_name: &str) -> Option<&DictionaryValue> {
81 self.dictionary.values().iter().find(|&value| value.name() == value_name && value.attribute_name() == attr_name)
82 }
83
84 pub fn dictionary_attribute_by_id(&self, packet_attr_id: u8) -> Option<&DictionaryAttribute> {
86 self.dictionary.attributes().iter().find(|&attr| attr.code() == packet_attr_id)
87 }
88
89 #[allow(dead_code)]
90 pub fn dictionary_attribute_by_name(&self, packet_attr_name: &str) -> Option<&DictionaryAttribute> {
92 self.dictionary.attributes().iter().find(|&attr| attr.name() == packet_attr_name)
93 }
94
95 pub fn initialise_packet_from_bytes(&self, packet: &[u8]) -> Result<RadiusPacket, RadiusError> {
97 RadiusPacket::initialise_packet_from_bytes(&self.dictionary, packet)
98 }
99
100 pub fn verify_packet_attributes(&self, packet: &[u8]) -> Result<(), RadiusError> {
105 let _packet_tmp = RadiusPacket::initialise_packet_from_bytes(&self.dictionary, &packet)?;
106
107 for packet_attr in _packet_tmp.attributes().iter().filter(|&attr| attr.name() != IGNORE_VERIFY_ATTRIBUTE) {
108 match self.dictionary_attribute_by_id(packet_attr.id()) {
109 None => return Err( RadiusError::ValidationError {error: format!("Attribute with ID {} may not exist in provided dictionary file, thus verification failed", packet_attr.id())} ),
110 Some(_dict_attr) => {
111 let _dict_attr_data_type = _dict_attr.code_type();
112 match packet_attr.verify_original_value(_dict_attr_data_type) {
113 Err(err) => return Err( RadiusError::ValidationError {error: err.to_string()} ),
114 _ => continue
115 }
116 }
117 }
118 }
119 Ok(())
120 }
121
122 pub fn verify_message_authenticator(&self, secret: &str, packet: &[u8]) -> Result<(), RadiusError> {
124 let mut _packet_tmp = RadiusPacket::initialise_packet_from_bytes(&self.dictionary, &packet)?;
126 let original_msg_auth = _packet_tmp.message_authenticator()?.to_vec();
127
128 let zeroed_authenticator = [0; 16];
130 _packet_tmp.override_message_authenticator(zeroed_authenticator.to_vec())?;
131
132 let mut calculated_msg_auth = HmacMd5::new_from_slice(secret.as_bytes()).map_err(|error| RadiusError::ValidationError { error: error.to_string() })?;
134 calculated_msg_auth.update(&_packet_tmp.to_bytes());
135
136 match calculated_msg_auth.verify_slice(&original_msg_auth) {
138 Ok(()) => Ok(()),
139 Err(_) => Err( RadiusError::ValidationError {error: String::from("Packet Message-Authenticator mismatch")} )
140 }
141 }
142}
143
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use crate::protocol::dictionary::SupportedAttributeTypes;
149
150 #[test]
151 fn test_get_dictionary_value_by_attr_and_value_name() {
152 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
153 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
154
155 let dict_value = host.dictionary_value_by_attr_and_value_name("Service-Type", "Login-User").unwrap();
156
157 assert_eq!("Service-Type", dict_value.attribute_name());
158 assert_eq!("Login-User", dict_value.name());
159 assert_eq!("1", dict_value.value());
160 }
161
162 #[test]
163 fn test_get_dictionary_value_by_attr_and_value_name_error() {
164 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
165 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
166
167 let dict_value = host.dictionary_value_by_attr_and_value_name("Service-Type", "Lin-User");
168 assert_eq!(None, dict_value);
169 }
170
171 #[test]
172 fn test_get_dictionary_attribute_by_id() {
173 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
174 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
175
176 let dict_attr = host.dictionary_attribute_by_id(80).unwrap();
177
178 assert_eq!("Message-Authenticator", dict_attr.name());
179 assert_eq!(80, dict_attr.code());
180 assert_eq!(&Some(SupportedAttributeTypes::ByteString), dict_attr.code_type());
181 }
182
183 #[test]
184 fn test_get_dictionary_attribute_by_id_error() {
185 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
186 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
187
188 let dict_attr = host.dictionary_attribute_by_id(255);
189 assert_eq!(None, dict_attr);
190 }
191
192 #[test]
193 fn test_verify_packet_attributes() {
194 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
195 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
196
197 let packet_bytes = [4, 43, 0, 86, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 4, 6, 192, 168, 1, 10, 5, 6, 0, 0, 0, 0, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
198
199 match host.verify_packet_attributes(&packet_bytes) {
200 Err(_err) => {
201 assert!(false)
202 },
203 _ => assert!(true)
204 }
205 }
206
207 #[test]
208 fn test_verify_packet_attributes_fail() {
209 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
210 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
211
212 let packet_bytes = [4, 43, 0, 85, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 4, 5, 192, 168, 10, 5, 6, 0, 0, 0, 0, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
213
214 match host.verify_packet_attributes(&packet_bytes) {
215 Err(err) => {
216 assert_eq!(err.to_string(), String::from("Verification failed for incoming Radius packet: Attribute in Radius packet is malformed: invalid IPv4 bytes"))
217 },
218 _ => assert!(false)
219 }
220 }
221
222 #[test]
223 fn test_verify_message_authenticator_valid() {
224 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
225 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
226 let secret = "secret";
227
228 let packet_bytes = [1, 120, 0, 185, 49, 79, 108, 150, 27, 203, 166, 51, 193, 68, 15, 76, 208, 114, 171, 48, 1, 9, 116, 101, 115, 116, 105, 110, 103, 80, 18, 164, 201, 132, 0, 209, 101, 200, 189, 252, 251, 120, 224, 74, 190, 232, 197, 2, 66, 85, 125, 163, 190, 40, 210, 235, 231, 112, 96, 7, 94, 27, 95, 241, 63, 23, 81, 25, 136, 36, 209, 238, 119, 131, 113, 118, 14, 160, 16, 94, 184, 143, 37, 193, 138, 124, 238, 85, 197, 21, 17, 206, 158, 87, 132, 239, 59, 82, 183, 175, 54, 124, 138, 5, 245, 166, 195, 181, 106, 41, 31, 129, 183, 4, 6, 192, 168, 1, 10, 5, 6, 0, 0, 0, 0, 6, 6, 0, 0, 0, 2, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
229
230 match host.verify_message_authenticator(&secret, &packet_bytes) {
231 Err(_err) => {
232 assert!(false)
233 },
234 _ => assert!(true)
235 }
236 }
237
238 #[test]
239 fn test_verify_message_authenticator_wo_authenticator() {
240 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
241 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
242 let secret = "secret";
243
244 let packet_bytes = [4, 43, 0, 86, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 4, 6, 192, 168, 1, 10, 5, 6, 0, 0, 0, 0, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
245
246 match host.verify_message_authenticator(&secret, &packet_bytes) {
247 Err(err) => {
248 assert_eq!(err.to_string(), String::from("Radius packet is malformed: Message-Authenticator attribute not found in packet"))
249 },
250 _ => assert!(false)
251 }
252 }
253
254 #[test]
255 fn test_verify_message_authenticator_invalid() {
256 let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
257 let host = Host::initialise_host(1812, 1813, 3799, dictionary);
258 let secret = "secret";
259
260 let packet_bytes = [1, 94, 0, 190, 241, 228, 181, 142, 185, 194, 157, 205, 159, 0, 91, 199, 171, 119, 68, 44, 1, 9, 116, 101, 115, 116, 105, 110, 103, 80, 23, 109, 101, 115, 115, 97, 103, 101, 45, 97, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 111, 114, 2, 66, 167, 81, 185, 84, 173, 104, 91, 10, 145, 109, 156, 169, 227, 109, 100, 76, 86, 227, 61, 253, 129, 35, 109, 115, 54, 140, 66, 106, 193, 70, 145, 39, 106, 105, 142, 215, 21, 166, 142, 80, 145, 217, 202, 252, 172, 33, 17, 12, 159, 105, 157, 144, 221, 221, 94, 48, 158, 22, 62, 191, 16, 177, 137, 131, 4, 6, 192, 168, 1, 10, 5, 6, 0, 0, 0, 0, 6, 6, 0, 0, 0, 2, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
261
262 match host.verify_message_authenticator(&secret, &packet_bytes) {
263 Err(err) => {
264 assert_eq!(err.to_string(), String::from("Verification failed for incoming Radius packet: Packet Message-Authenticator mismatch"))
265 },
266 _ => assert!(false)
267 }
268 }
269}