This commit is contained in:
moznion
2020-11-24 01:55:03 +09:00
parent d1af19f5f0
commit 6c7012c609
8 changed files with 142 additions and 78 deletions

View File

@@ -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(())
}
}

View File

@@ -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> {

View File

@@ -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)),
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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;

View File

@@ -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)?;

View File

@@ -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 {

View File

@@ -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,
}