radius_rust/server/
server.rs

1//! RADIUS Generic Server implementation
2
3
4use crate::protocol::host::Host;
5use crate::protocol::radius_packet::{ RadiusAttribute, RadiusMsgType, RadiusPacket, TypeCode };
6use crate::protocol::dictionary::Dictionary;
7use crate::protocol::error::RadiusError;
8
9use md5::{ Digest, Md5 };
10
11
12#[derive(Debug)]
13/// Represents RADIUS Generic Server instance
14pub struct Server {
15    host:          Host,
16    allowed_hosts: Vec<String>,
17    server:        String,
18    secret:        String,
19    retries:       u16,
20    timeout:       u16,
21}
22
23impl Server {
24    // === Builder for Server ===
25    /// Initialise Server instance with dictionary (other fields would be set to default values)
26    ///
27    /// To be called **first** when creating RADIUS Server instance
28    pub fn with_dictionary(dictionary: Dictionary) -> Server {
29        let host = Host::with_dictionary(dictionary);
30
31        Server {
32            host,
33            allowed_hosts: Vec::new(),
34            server:        String::from(""),
35            secret:        String::from(""),
36            retries:       1,
37            timeout:       2,
38        }
39    }
40
41    /// **Required**
42    ///
43    /// Sets hostname to which server would bind
44    pub fn set_server(mut self, server: String) -> Server {
45        self.server = server;
46        self
47    }
48
49    /// **Required**
50    ///
51    /// Sets secret which is used to encode/decode RADIUS packet
52    pub fn set_secret(mut self, secret: String) -> Server {
53        self.secret = secret;
54        self
55    }
56
57    /// **Required**
58    ///
59    /// Sets allowed hosts, from where Server would be allowed to accept RADIUS requests
60    pub fn set_allowed_hosts(mut self, allowed_hosts: Vec<String>) -> Server {
61        self.allowed_hosts = allowed_hosts;
62        self
63    }
64
65    /// **Required/Optional**
66    ///
67    /// Sets remote port, that responsible for specific RADIUS Message Type
68    pub fn set_port(mut self, msg_type: RadiusMsgType, port: u16) -> Server {
69        self.host.set_port(msg_type, port);
70        self
71    }
72
73    /// **Optional**
74    ///
75    /// Sets socket retries, otherwise you would have a default value of 1
76    pub fn set_retries(mut self, retries: u16) -> Server {
77        self.retries = retries;
78        self
79    }
80
81    /// **Optional**
82    ///
83    /// Sets socket timeout, otherwise you would have a default value of 2
84    pub fn set_timeout(mut self, timeout: u16) -> Server {
85        self.timeout = timeout;
86        self
87    }
88    // ===================
89
90    /// Returns port of RADIUS server, that receives given type of RADIUS message/packet
91   pub fn port(&self, code: &TypeCode) -> Option<u16> {
92        self.host.port(code)
93    }
94
95    /// Returns hostname/FQDN of RADIUS Server
96    pub fn server(&self) -> &str {
97        &self.server
98    }
99
100    /// Returns retries
101    pub fn retries(&self) -> u16 {
102        self.retries
103    }
104
105    /// Returns timeout
106    pub fn timeout(&self) -> u16 {
107        self.timeout
108    }
109
110    /// Returns allowed hosts list
111    pub fn allowed_hosts(&self) -> &[String] {
112        &self.allowed_hosts
113    }
114
115    /// Creates RADIUS packet attribute by name, that is defined in dictionary file
116    ///
117    /// For example, see [Client](crate::client::client::Client::create_attribute_by_name)
118    pub fn create_attribute_by_name(&self, attribute_name: &str, value: Vec<u8>) -> Result<RadiusAttribute, RadiusError> {
119        self.host.create_attribute_by_name(attribute_name, value)
120    }
121
122    /// Creates RADIUS packet attribute by id, that is defined in dictionary file
123    ///
124    /// For example, see [Client](crate::client::client::Client::create_attribute_by_id)
125    pub fn create_attribute_by_id(&self, attribute_id: u8, value: Vec<u8>) -> Result<RadiusAttribute, RadiusError> {
126        self.host.create_attribute_by_id(attribute_id, value)
127    }
128
129    /// Creates reply RADIUS packet
130    ///
131    /// Similar to [Client's create_packet()](crate::client::client::Client::create_packet), however also sets correct packet ID and authenticator
132    pub fn create_reply_packet(&self, reply_code: TypeCode, attributes: Vec<RadiusAttribute>, request: &mut [u8]) -> RadiusPacket {
133        let mut reply_packet = RadiusPacket::initialise_packet(reply_code);
134        reply_packet.set_attributes(attributes);
135
136        // We can only create new authenticator after we set reply packet ID to the request's ID
137        reply_packet.override_id(request[1]);
138
139        let authenticator = self.create_reply_authenticator(&reply_packet.to_bytes(), &request[4..20]);
140        reply_packet.override_authenticator(authenticator);
141
142        reply_packet
143    }
144
145    fn create_reply_authenticator(&self, raw_reply_packet: &[u8], request_authenticator: &[u8]) -> Vec<u8> {
146        // We need to create authenticator as MD5 hash (similar to how client verifies server reply)
147        let mut md5_hasher    = Md5::new();
148
149        md5_hasher.update(&raw_reply_packet[0..4]); // Append reply's   type code, reply ID and reply length
150        md5_hasher.update(&request_authenticator);  // Append request's authenticator
151        md5_hasher.update(&raw_reply_packet[20..]); // Append reply's   attributes
152        md5_hasher.update(&self.secret.as_bytes()); // Append server's  secret. Possibly it should be client's secret, which sould be stored together with allowed hostnames ?
153        // ----------------
154
155        md5_hasher.finalize().to_vec()
156    }
157
158    /// Verifies incoming RADIUS packet:
159    ///
160    /// Server would try to build RadiusPacket from raw bytes, and if it succeeds then packet is
161    /// valid, otherwise would return RadiusError
162    pub fn verify_request(&self, request: &[u8]) -> Result<(), RadiusError> {
163        match RadiusPacket::initialise_packet_from_bytes(&self.host.dictionary(), request) {
164            Err(err) => Err(err),
165            _        => Ok(())
166        }
167    }
168
169    /// Verifies RadiusAttributes's values of incoming RADIUS packet:
170    ///
171    /// Server would try to build RadiusPacket from raw bytes, and then it would try to restore
172    /// RadiusAttribute original value from bytes, based on the attribute data type, see [SupportedAttributeTypes](crate::protocol::dictionary::SupportedAttributeTypes)
173    pub fn verify_request_attributes(&self, request: &[u8]) -> Result<(), RadiusError> {
174        self.host.verify_packet_attributes(&request)
175    }
176
177    /// Initialises RadiusPacket from bytes
178    ///
179    /// Unlike [verify_request](Server::verify_request), on success this function would return
180    /// RadiusPacket
181    pub fn initialise_packet_from_bytes(&self, request: &[u8]) -> Result<RadiusPacket, RadiusError> {
182        self.host.initialise_packet_from_bytes(request)
183    }
184
185    /// Checks if host from where Server received RADIUS request is allowed host, meaning RADIUS
186    /// Server can process such request
187    pub fn host_allowed(&self, remote_host: &std::net::SocketAddr) -> bool {
188        let remote_host_name            = remote_host.to_string();
189        let remote_host_name: Vec<&str> = remote_host_name.split(':').collect();
190
191        self.allowed_hosts.iter().any(|host| host==remote_host_name[0]) 
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_add_allowed_hosts_and_add_request_handler() {
201        let dictionary = Dictionary::from_file("./dict_examples/integration_dict").unwrap();
202        let server     = Server::with_dictionary(dictionary)
203            .set_server(String::from("0.0.0.0"))
204            .set_secret(String::from("secret"))
205            .set_allowed_hosts(vec![String::from("127.0.0.1")]);
206
207        assert_eq!(server.allowed_hosts().len(), 1);
208    }
209}