mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-24 23:04:43 +01:00
fmt
This commit is contained in:
@@ -2,7 +2,7 @@ use std::convert::TryInto;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use chrono::{DateTime, Utc, TimeZone};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Attribute(pub(crate) Vec<u8>);
|
||||
@@ -28,9 +28,16 @@ impl Attribute {
|
||||
Attribute(v.octets().to_vec())
|
||||
}
|
||||
|
||||
pub fn from_user_password(plain_text: &[u8], secret: &[u8], request_authenticator: &[u8]) -> Result<Self, String> {
|
||||
pub fn from_user_password(
|
||||
plain_text: &[u8],
|
||||
secret: &[u8],
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<Self, String> {
|
||||
if plain_text.len() > 128 {
|
||||
return Err("the length of plain_text has to be within 128, but the given value is longer".to_owned());
|
||||
return Err(
|
||||
"the length of plain_text has to be within 128, but the given value is longer"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
@@ -38,7 +45,10 @@ impl Attribute {
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err("request_authenticator has to have 16-bytes payload, but the given value doesn't".to_owned());
|
||||
return Err(
|
||||
"request_authenticator has to have 16-bytes payload, but the given value doesn't"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut enc: Vec<u8> = Vec::new();
|
||||
@@ -60,7 +70,8 @@ impl Attribute {
|
||||
enc.extend(digest.to_vec());
|
||||
|
||||
let mut j = 0;
|
||||
for b in &plain_text[i..i + 16] { // TODO this has to be 16 bounds, is this correct?
|
||||
for b in &plain_text[i..i + 16] {
|
||||
// TODO this has to be 16 bounds, is this correct?
|
||||
enc[i + j] ^= b;
|
||||
j += 1;
|
||||
}
|
||||
@@ -122,7 +133,11 @@ impl Attribute {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_user_password(&self, secret: &[u8], request_authenticator: &[u8]) -> Result<Vec<u8>, String> {
|
||||
pub fn to_user_password(
|
||||
&self,
|
||||
secret: &[u8],
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<Vec<u8>, String> {
|
||||
if self.0.len() < 16 || self.0.len() > 128 {
|
||||
return Err(format!("invalid attribute length {}", self.0.len()));
|
||||
}
|
||||
@@ -132,7 +147,10 @@ impl Attribute {
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err("request_authenticator has to have 16-bytes payload, but the given value doesn't".to_owned());
|
||||
return Err(
|
||||
"request_authenticator has to have 16-bytes payload, but the given value doesn't"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut dec: Vec<u8> = Vec::new();
|
||||
@@ -158,7 +176,8 @@ impl Attribute {
|
||||
dec.extend(digest.to_vec());
|
||||
|
||||
let mut j = 0;
|
||||
for b in &self.0[i..i + 16] { // TODO this has to be 16 bounds, is this correct?
|
||||
for b in &self.0[i..i + 16] {
|
||||
// TODO this has to be 16 bounds, is this correct?
|
||||
dec[i + j] ^= b;
|
||||
if dec[i + j] == 0 && maybe_first_zero_byte_idx.is_none() {
|
||||
maybe_first_zero_byte_idx = Option::Some(i + j)
|
||||
@@ -171,7 +190,7 @@ impl Attribute {
|
||||
|
||||
match maybe_first_zero_byte_idx {
|
||||
None => Ok(dec),
|
||||
Some(idx) => Ok(dec[..idx].to_vec())
|
||||
Some(idx) => Ok(dec[..idx].to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +223,10 @@ mod tests {
|
||||
#[test]
|
||||
fn it_should_convert_attribute_to_string() -> Result<(), FromUtf8Error> {
|
||||
assert_eq!(
|
||||
Attribute(vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64]).to_string()?,
|
||||
Attribute(vec![
|
||||
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64
|
||||
])
|
||||
.to_string()?,
|
||||
"Hello, World"
|
||||
);
|
||||
Ok(())
|
||||
@@ -214,21 +236,17 @@ mod tests {
|
||||
fn it_should_convert_ipv4() -> Result<(), String> {
|
||||
let given_ipv4 = Ipv4Addr::new(192, 0, 2, 1);
|
||||
let ipv4_attr = Attribute::from_ipv4(&given_ipv4);
|
||||
assert_eq!(
|
||||
ipv4_attr.to_ipv4()?,
|
||||
given_ipv4,
|
||||
);
|
||||
assert_eq!(ipv4_attr.to_ipv4()?, given_ipv4,);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_ipv6() -> Result<(), String> {
|
||||
let given_ipv6 = Ipv6Addr::new(0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);
|
||||
let ipv6_attr = Attribute::from_ipv6(&given_ipv6);
|
||||
assert_eq!(
|
||||
ipv6_attr.to_ipv6()?,
|
||||
given_ipv6,
|
||||
let given_ipv6 = Ipv6Addr::new(
|
||||
0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
|
||||
);
|
||||
let ipv6_attr = Attribute::from_ipv6(&given_ipv6);
|
||||
assert_eq!(ipv6_attr.to_ipv6()?, given_ipv6,);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -237,14 +255,21 @@ mod tests {
|
||||
let plain_text = b"texttexttexttexttexttexttexttext".to_vec();
|
||||
let secret = b"secret".to_vec();
|
||||
let request_authenticator = b"0123456789abcdef".to_vec();
|
||||
let user_password_attr_result = Attribute::from_user_password(&plain_text, &secret, &request_authenticator);
|
||||
let user_password_attr_result =
|
||||
Attribute::from_user_password(&plain_text, &secret, &request_authenticator);
|
||||
let user_password_attr = user_password_attr_result.unwrap();
|
||||
assert_eq!(
|
||||
user_password_attr.0,
|
||||
vec![0xb7, 0xb0, 0xcb, 0x5d, 0x4f, 0x96, 0xd4, 0x75, 0x1c, 0xea, 0x3a, 0xb6, 0xf, 0xc, 0xea, 0xa5, 0xc9, 0x22, 0xac, 0x26, 0x28, 0x23, 0x93, 0xef, 0x19, 0x67, 0xcc, 0xeb, 0x9d, 0x33, 0xd7, 0x46],
|
||||
vec![
|
||||
0xb7, 0xb0, 0xcb, 0x5d, 0x4f, 0x96, 0xd4, 0x75, 0x1c, 0xea, 0x3a, 0xb6, 0xf, 0xc,
|
||||
0xea, 0xa5, 0xc9, 0x22, 0xac, 0x26, 0x28, 0x23, 0x93, 0xef, 0x19, 0x67, 0xcc, 0xeb,
|
||||
0x9d, 0x33, 0xd7, 0x46
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
user_password_attr.to_user_password(&secret, &request_authenticator).unwrap(),
|
||||
user_password_attr
|
||||
.to_user_password(&secret, &request_authenticator)
|
||||
.unwrap(),
|
||||
plain_text,
|
||||
);
|
||||
}
|
||||
@@ -253,10 +278,7 @@ mod tests {
|
||||
fn it_should_convert_date() -> Result<(), String> {
|
||||
let now = Utc::now();
|
||||
let attr = Attribute::from_date(&now);
|
||||
assert_eq!(
|
||||
attr.to_date()?.timestamp(),
|
||||
now.timestamp(),
|
||||
);
|
||||
assert_eq!(attr.to_date()?.timestamp(), now.timestamp(),);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,7 @@ impl Attributes {
|
||||
}
|
||||
|
||||
pub fn add(&mut self, typ: Type, attribute: Attribute) {
|
||||
self.0.push(AVP {
|
||||
typ,
|
||||
attribute,
|
||||
})
|
||||
self.0.push(AVP { typ, attribute })
|
||||
}
|
||||
|
||||
pub fn attributes_encoded_len(&self) -> Result<u16, String> {
|
||||
|
||||
@@ -3,7 +3,10 @@ use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::client::ClientError::{FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding, FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding};
|
||||
use crate::client::ClientError::{
|
||||
FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding,
|
||||
FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding,
|
||||
};
|
||||
use crate::packet::Packet;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -27,14 +30,19 @@ pub struct Client {}
|
||||
impl Client {
|
||||
const MAX_DATAGRAM_SIZE: usize = 65507;
|
||||
|
||||
pub async fn send_packet(remote_addr: &SocketAddr, request_packet: &Packet) -> Result<Packet, ClientError> {
|
||||
pub async fn send_packet(
|
||||
remote_addr: &SocketAddr,
|
||||
request_packet: &Packet,
|
||||
) -> Result<Packet, ClientError> {
|
||||
// TODO retransmission
|
||||
|
||||
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
}.parse().unwrap();
|
||||
}
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let conn = match UdpSocket::bind(local_addr).await {
|
||||
Ok(conn) => conn,
|
||||
@@ -42,28 +50,33 @@ impl Client {
|
||||
};
|
||||
match conn.connect(remote_addr).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(FailedConnection(remote_addr.to_string(), e.to_string()))
|
||||
Err(e) => return Err(FailedConnection(remote_addr.to_string(), e.to_string())),
|
||||
};
|
||||
|
||||
let request_data = match request_packet.encode() {
|
||||
Ok(encoded) => encoded,
|
||||
Err(e) => return Err(FailedRadiusPacketEncoding(e))
|
||||
Err(e) => return Err(FailedRadiusPacketEncoding(e)),
|
||||
};
|
||||
|
||||
match conn.send(request_data.as_slice()).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string()))
|
||||
Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string())),
|
||||
};
|
||||
|
||||
let mut buf = vec![0; Self::MAX_DATAGRAM_SIZE];
|
||||
let len = match conn.recv(&mut buf).await {
|
||||
Ok(len) => len,
|
||||
Err(e) => return Err(FailedReceivingResponse(remote_addr.to_string(), e.to_string()))
|
||||
Err(e) => {
|
||||
return Err(FailedReceivingResponse(
|
||||
remote_addr.to_string(),
|
||||
e.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match Packet::parse(&buf[..len].to_vec(), request_packet.get_secret()) {
|
||||
Ok(response_packet) => Ok(response_packet),
|
||||
Err(e) => Err(FailedParsingUDPResponse(e))
|
||||
Err(e) => Err(FailedParsingUDPResponse(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ impl Code {
|
||||
pub fn from(value: u8) -> Self {
|
||||
match Code::try_from(value) {
|
||||
Ok(code) => code,
|
||||
Err(_) => Code::Invalid
|
||||
Err(_) => Code::Invalid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/lib.rs
12
src/lib.rs
@@ -1,12 +1,12 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod code;
|
||||
pub mod attribute;
|
||||
pub mod attributes;
|
||||
pub mod packet;
|
||||
pub mod server;
|
||||
pub mod secret_provider;
|
||||
pub mod request_handler;
|
||||
pub mod request;
|
||||
pub mod client;
|
||||
pub mod code;
|
||||
pub mod packet;
|
||||
pub mod request;
|
||||
pub mod request_handler;
|
||||
pub mod secret_provider;
|
||||
pub mod server;
|
||||
|
||||
@@ -33,7 +33,8 @@ impl Packet {
|
||||
self.identifier
|
||||
}
|
||||
|
||||
pub fn get_secret(&self) -> &Vec<u8> { // TODO
|
||||
pub fn get_secret(&self) -> &Vec<u8> {
|
||||
// TODO
|
||||
&self.secret
|
||||
}
|
||||
|
||||
@@ -82,14 +83,23 @@ impl Packet {
|
||||
|
||||
match self.code {
|
||||
Code::AccessRequest | Code::StatusServer => Ok(bs),
|
||||
Code::AccessAccept | Code::AccessReject | Code::AccountingRequest | Code::AccessChallenge | Code::DisconnectRequest | Code::DisconnectACK | Code::DisconnectNAK | Code::CoARequest | Code::CoAACK | Code::CoANAK => {
|
||||
Code::AccessAccept
|
||||
| Code::AccessReject
|
||||
| Code::AccountingRequest
|
||||
| Code::AccessChallenge
|
||||
| Code::DisconnectRequest
|
||||
| Code::DisconnectACK
|
||||
| Code::DisconnectNAK
|
||||
| Code::CoARequest
|
||||
| Code::CoAACK
|
||||
| Code::CoANAK => {
|
||||
// TODO length checking
|
||||
let mut buf: Vec<u8> = bs[..4].to_vec();
|
||||
match self.code {
|
||||
Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => {
|
||||
buf.extend(vec![
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]);
|
||||
}
|
||||
_ => {
|
||||
@@ -100,14 +110,14 @@ impl Packet {
|
||||
buf.extend(&self.secret);
|
||||
Ok(md5::compute(buf).to_vec())
|
||||
}
|
||||
_ => Err("unknown packet code".to_owned())
|
||||
_ => Err("unknown packet code".to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn marshal_binary(&self) -> Result<Vec<u8>, String> {
|
||||
let attributes_len = match self.attributes.attributes_encoded_len() {
|
||||
Ok(attributes_len) => attributes_len,
|
||||
Err(e) => return Err(e)
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let size = 20 + attributes_len;
|
||||
@@ -128,12 +138,17 @@ impl Packet {
|
||||
return false;
|
||||
}
|
||||
|
||||
md5::compute([
|
||||
&response[..4],
|
||||
&request[4..20],
|
||||
&response[20..], // TODO length
|
||||
&secret,
|
||||
].concat()).to_vec().eq(&response[4..20].to_vec())
|
||||
md5::compute(
|
||||
[
|
||||
&response[..4],
|
||||
&request[4..20],
|
||||
&response[20..], // TODO length
|
||||
&secret,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.to_vec()
|
||||
.eq(&response[4..20].to_vec())
|
||||
}
|
||||
|
||||
pub fn is_authentic_request(request: &[u8], secret: &[u8]) -> bool {
|
||||
@@ -144,17 +159,22 @@ impl Packet {
|
||||
match Code::from(request[0]) {
|
||||
Code::AccessRequest | Code::StatusServer => true,
|
||||
Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => {
|
||||
md5::compute([
|
||||
&request[..4],
|
||||
&[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
],
|
||||
&request[20..], // TODO length
|
||||
&secret,
|
||||
].concat()).to_vec().eq(&request[4..20].to_vec())
|
||||
md5::compute(
|
||||
[
|
||||
&request[..4],
|
||||
&[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
],
|
||||
&request[20..], // TODO length
|
||||
&secret,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.to_vec()
|
||||
.eq(&request[4..20].to_vec())
|
||||
}
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,10 +190,10 @@ mod tests {
|
||||
|
||||
let secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
|
||||
let request: Vec<u8> = vec![
|
||||
0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83, 0xd5, 0xcb,
|
||||
0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12, 0x0d, 0xbe, 0x70, 0x8d,
|
||||
0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a, 0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8,
|
||||
0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
|
||||
0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83,
|
||||
0xd5, 0xcb, 0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12,
|
||||
0x0d, 0xbe, 0x70, 0x8d, 0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a,
|
||||
0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
|
||||
];
|
||||
|
||||
let packet = Packet::parse(&request, &secret)?;
|
||||
|
||||
@@ -5,7 +5,7 @@ use thiserror::Error;
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SecretProviderError {
|
||||
#[error("failed to fetch a secret value => `{0}`")]
|
||||
FailedFetching(String)
|
||||
FailedFetching(String),
|
||||
}
|
||||
|
||||
pub trait SecretProvider: 'static + Sync + Send {
|
||||
|
||||
@@ -64,7 +64,10 @@ impl Server {
|
||||
let local_addr = match conn.local_addr() {
|
||||
Ok(addr) => addr,
|
||||
Err(e) => {
|
||||
error!("failed to get a local address from from a connection; {}", e);
|
||||
error!(
|
||||
"failed to get a local address from from a connection; {}",
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -81,7 +84,8 @@ impl Server {
|
||||
request_handler,
|
||||
secret_provider,
|
||||
skip_authenticity_validation,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -99,7 +103,10 @@ impl Server {
|
||||
let secret: Vec<u8> = match secret_provider.fetch_secret(remote_addr) {
|
||||
Ok(secret) => secret,
|
||||
Err(e) => {
|
||||
error!("failed to fetch secret binary vector from the secret provider; {}", e);
|
||||
error!(
|
||||
"failed to fetch secret binary vector from the secret provider; {}",
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -116,7 +123,10 @@ impl Server {
|
||||
let packet = match Packet::parse(request_data, &secret) {
|
||||
Ok(packet) => packet,
|
||||
Err(e) => {
|
||||
error!("failed to parse given request data to pack into the RADIUS packet; {}", e);
|
||||
error!(
|
||||
"failed to parse given request data to pack into the RADIUS packet; {}",
|
||||
e
|
||||
);
|
||||
debug!("failed request data => {:?}", request_data);
|
||||
// TODO error handler?
|
||||
return;
|
||||
@@ -137,7 +147,10 @@ impl Server {
|
||||
undergoing_requests.insert(key);
|
||||
}
|
||||
|
||||
request_handler.handle_radius_request(conn.borrow(), &Request::new(local_addr, remote_addr, packet));
|
||||
request_handler.handle_radius_request(
|
||||
conn.borrow(),
|
||||
&Request::new(local_addr, remote_addr, packet),
|
||||
);
|
||||
|
||||
let mut undergoing_requests = undergoing_requests_lock.write().unwrap();
|
||||
undergoing_requests.remove(&key_for_remove);
|
||||
@@ -149,4 +162,3 @@ struct RequestKey {
|
||||
ip: String,
|
||||
identifier: u8,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user