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
+48 -26
View File
@@ -2,7 +2,7 @@ use std::convert::TryInto;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use chrono::{DateTime, Utc, TimeZone}; use chrono::{DateTime, TimeZone, Utc};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Attribute(pub(crate) Vec<u8>); pub struct Attribute(pub(crate) Vec<u8>);
@@ -28,9 +28,16 @@ impl Attribute {
Attribute(v.octets().to_vec()) 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 { 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() { if secret.is_empty() {
@@ -38,7 +45,10 @@ impl Attribute {
} }
if request_authenticator.len() != 16 { 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(); let mut enc: Vec<u8> = Vec::new();
@@ -60,7 +70,8 @@ impl Attribute {
enc.extend(digest.to_vec()); enc.extend(digest.to_vec());
let mut j = 0; 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; enc[i + j] ^= b;
j += 1; 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 { if self.0.len() < 16 || self.0.len() > 128 {
return Err(format!("invalid attribute length {}", self.0.len())); return Err(format!("invalid attribute length {}", self.0.len()));
} }
@@ -132,7 +147,10 @@ impl Attribute {
} }
if request_authenticator.len() != 16 { 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(); let mut dec: Vec<u8> = Vec::new();
@@ -158,7 +176,8 @@ impl Attribute {
dec.extend(digest.to_vec()); dec.extend(digest.to_vec());
let mut j = 0; 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; dec[i + j] ^= b;
if dec[i + j] == 0 && maybe_first_zero_byte_idx.is_none() { if dec[i + j] == 0 && maybe_first_zero_byte_idx.is_none() {
maybe_first_zero_byte_idx = Option::Some(i + j) maybe_first_zero_byte_idx = Option::Some(i + j)
@@ -171,7 +190,7 @@ impl Attribute {
match maybe_first_zero_byte_idx { match maybe_first_zero_byte_idx {
None => Ok(dec), None => Ok(dec),
Some(idx) => Ok(dec[..idx].to_vec()) Some(idx) => Ok(dec[..idx].to_vec()),
} }
} }
@@ -204,7 +223,10 @@ mod tests {
#[test] #[test]
fn it_should_convert_attribute_to_string() -> Result<(), FromUtf8Error> { fn it_should_convert_attribute_to_string() -> Result<(), FromUtf8Error> {
assert_eq!( 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" "Hello, World"
); );
Ok(()) Ok(())
@@ -214,21 +236,17 @@ mod tests {
fn it_should_convert_ipv4() -> Result<(), String> { fn it_should_convert_ipv4() -> Result<(), String> {
let given_ipv4 = Ipv4Addr::new(192, 0, 2, 1); let given_ipv4 = Ipv4Addr::new(192, 0, 2, 1);
let ipv4_attr = Attribute::from_ipv4(&given_ipv4); let ipv4_attr = Attribute::from_ipv4(&given_ipv4);
assert_eq!( assert_eq!(ipv4_attr.to_ipv4()?, given_ipv4,);
ipv4_attr.to_ipv4()?,
given_ipv4,
);
Ok(()) Ok(())
} }
#[test] #[test]
fn it_should_convert_ipv6() -> Result<(), String> { fn it_should_convert_ipv6() -> Result<(), String> {
let given_ipv6 = Ipv6Addr::new(0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001); let given_ipv6 = Ipv6Addr::new(
let ipv6_attr = Attribute::from_ipv6(&given_ipv6); 0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
assert_eq!(
ipv6_attr.to_ipv6()?,
given_ipv6,
); );
let ipv6_attr = Attribute::from_ipv6(&given_ipv6);
assert_eq!(ipv6_attr.to_ipv6()?, given_ipv6,);
Ok(()) Ok(())
} }
@@ -237,14 +255,21 @@ mod tests {
let plain_text = b"texttexttexttexttexttexttexttext".to_vec(); let plain_text = b"texttexttexttexttexttexttexttext".to_vec();
let secret = b"secret".to_vec(); let secret = b"secret".to_vec();
let request_authenticator = b"0123456789abcdef".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(); let user_password_attr = user_password_attr_result.unwrap();
assert_eq!( assert_eq!(
user_password_attr.0, 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!( assert_eq!(
user_password_attr.to_user_password(&secret, &request_authenticator).unwrap(), user_password_attr
.to_user_password(&secret, &request_authenticator)
.unwrap(),
plain_text, plain_text,
); );
} }
@@ -253,10 +278,7 @@ mod tests {
fn it_should_convert_date() -> Result<(), String> { fn it_should_convert_date() -> Result<(), String> {
let now = Utc::now(); let now = Utc::now();
let attr = Attribute::from_date(&now); let attr = Attribute::from_date(&now);
assert_eq!( assert_eq!(attr.to_date()?.timestamp(), now.timestamp(),);
attr.to_date()?.timestamp(),
now.timestamp(),
);
Ok(()) Ok(())
} }
} }
+1 -4
View File
@@ -44,10 +44,7 @@ impl Attributes {
} }
pub fn add(&mut self, typ: Type, attribute: Attribute) { pub fn add(&mut self, typ: Type, attribute: Attribute) {
self.0.push(AVP { self.0.push(AVP { typ, attribute })
typ,
attribute,
})
} }
pub fn attributes_encoded_len(&self) -> Result<u16, String> { pub fn attributes_encoded_len(&self) -> Result<u16, String> {
+21 -8
View File
@@ -3,7 +3,10 @@ use std::net::SocketAddr;
use thiserror::Error; use thiserror::Error;
use tokio::net::UdpSocket; 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; use crate::packet::Packet;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -27,14 +30,19 @@ pub struct Client {}
impl Client { impl Client {
const MAX_DATAGRAM_SIZE: usize = 65507; 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 // TODO retransmission
let local_addr: SocketAddr = if remote_addr.is_ipv4() { let local_addr: SocketAddr = if remote_addr.is_ipv4() {
"0.0.0.0:0" "0.0.0.0:0"
} else { } else {
"[::]:0" "[::]:0"
}.parse().unwrap(); }
.parse()
.unwrap();
let conn = match UdpSocket::bind(local_addr).await { let conn = match UdpSocket::bind(local_addr).await {
Ok(conn) => conn, Ok(conn) => conn,
@@ -42,28 +50,33 @@ impl Client {
}; };
match conn.connect(remote_addr).await { match conn.connect(remote_addr).await {
Ok(_) => {} 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() { let request_data = match request_packet.encode() {
Ok(encoded) => encoded, Ok(encoded) => encoded,
Err(e) => return Err(FailedRadiusPacketEncoding(e)) Err(e) => return Err(FailedRadiusPacketEncoding(e)),
}; };
match conn.send(request_data.as_slice()).await { match conn.send(request_data.as_slice()).await {
Ok(_) => {} 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 mut buf = vec![0; Self::MAX_DATAGRAM_SIZE];
let len = match conn.recv(&mut buf).await { let len = match conn.recv(&mut buf).await {
Ok(len) => len, 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()) { match Packet::parse(&buf[..len].to_vec(), request_packet.get_secret()) {
Ok(response_packet) => Ok(response_packet), Ok(response_packet) => Ok(response_packet),
Err(e) => Err(FailedParsingUDPResponse(e)) Err(e) => Err(FailedParsingUDPResponse(e)),
} }
} }
} }
+1 -1
View File
@@ -48,7 +48,7 @@ impl Code {
pub fn from(value: u8) -> Self { pub fn from(value: u8) -> Self {
match Code::try_from(value) { match Code::try_from(value) {
Ok(code) => code, Ok(code) => code,
Err(_) => Code::Invalid Err(_) => Code::Invalid,
} }
} }
} }
+6 -6
View File
@@ -1,12 +1,12 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
pub mod code;
pub mod attribute; pub mod attribute;
pub mod attributes; 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 client;
pub mod code;
pub mod packet;
pub mod request;
pub mod request_handler;
pub mod secret_provider;
pub mod server;
+37 -17
View File
@@ -33,7 +33,8 @@ impl Packet {
self.identifier self.identifier
} }
pub fn get_secret(&self) -> &Vec<u8> { // TODO pub fn get_secret(&self) -> &Vec<u8> {
// TODO
&self.secret &self.secret
} }
@@ -82,14 +83,23 @@ impl Packet {
match self.code { match self.code {
Code::AccessRequest | Code::StatusServer => Ok(bs), 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 // TODO length checking
let mut buf: Vec<u8> = bs[..4].to_vec(); let mut buf: Vec<u8> = bs[..4].to_vec();
match self.code { match self.code {
Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => { Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => {
buf.extend(vec![ 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); buf.extend(&self.secret);
Ok(md5::compute(buf).to_vec()) 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> { pub fn marshal_binary(&self) -> Result<Vec<u8>, String> {
let attributes_len = match self.attributes.attributes_encoded_len() { let attributes_len = match self.attributes.attributes_encoded_len() {
Ok(attributes_len) => attributes_len, Ok(attributes_len) => attributes_len,
Err(e) => return Err(e) Err(e) => return Err(e),
}; };
let size = 20 + attributes_len; let size = 20 + attributes_len;
@@ -128,12 +138,17 @@ impl Packet {
return false; return false;
} }
md5::compute([ md5::compute(
[
&response[..4], &response[..4],
&request[4..20], &request[4..20],
&response[20..], // TODO length &response[20..], // TODO length
&secret, &secret,
].concat()).to_vec().eq(&response[4..20].to_vec()) ]
.concat(),
)
.to_vec()
.eq(&response[4..20].to_vec())
} }
pub fn is_authentic_request(request: &[u8], secret: &[u8]) -> bool { pub fn is_authentic_request(request: &[u8], secret: &[u8]) -> bool {
@@ -144,17 +159,22 @@ impl Packet {
match Code::from(request[0]) { match Code::from(request[0]) {
Code::AccessRequest | Code::StatusServer => true, Code::AccessRequest | Code::StatusServer => true,
Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => { Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => {
md5::compute([ md5::compute(
[
&request[..4], &request[..4],
&[ &[
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,
], ],
&request[20..], // TODO length &request[20..], // TODO length
&secret, &secret,
].concat()).to_vec().eq(&request[4..20].to_vec()) ]
.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 secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
let request: Vec<u8> = vec![ let request: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83, 0xd5, 0xcb, 0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83,
0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12, 0x0d, 0xbe, 0x70, 0x8d, 0xd5, 0xcb, 0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12,
0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a, 0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x0d, 0xbe, 0x70, 0x8d, 0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a,
0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03, 0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
]; ];
let packet = Packet::parse(&request, &secret)?; let packet = Packet::parse(&request, &secret)?;
+1 -1
View File
@@ -5,7 +5,7 @@ use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum SecretProviderError { pub enum SecretProviderError {
#[error("failed to fetch a secret value => `{0}`")] #[error("failed to fetch a secret value => `{0}`")]
FailedFetching(String) FailedFetching(String),
} }
pub trait SecretProvider: 'static + Sync + Send { pub trait SecretProvider: 'static + Sync + Send {
+18 -6
View File
@@ -64,7 +64,10 @@ impl Server {
let local_addr = match conn.local_addr() { let local_addr = match conn.local_addr() {
Ok(addr) => addr, Ok(addr) => addr,
Err(e) => { 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; continue;
} }
}; };
@@ -81,7 +84,8 @@ impl Server {
request_handler, request_handler,
secret_provider, secret_provider,
skip_authenticity_validation, skip_authenticity_validation,
).await; )
.await;
}); });
} }
} }
@@ -99,7 +103,10 @@ impl Server {
let secret: Vec<u8> = match secret_provider.fetch_secret(remote_addr) { let secret: Vec<u8> = match secret_provider.fetch_secret(remote_addr) {
Ok(secret) => secret, Ok(secret) => secret,
Err(e) => { 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; return;
} }
}; };
@@ -116,7 +123,10 @@ impl Server {
let packet = match Packet::parse(request_data, &secret) { let packet = match Packet::parse(request_data, &secret) {
Ok(packet) => packet, Ok(packet) => packet,
Err(e) => { 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); debug!("failed request data => {:?}", request_data);
// TODO error handler? // TODO error handler?
return; return;
@@ -137,7 +147,10 @@ impl Server {
undergoing_requests.insert(key); 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(); let mut undergoing_requests = undergoing_requests_lock.write().unwrap();
undergoing_requests.remove(&key_for_remove); undergoing_requests.remove(&key_for_remove);
@@ -149,4 +162,3 @@ struct RequestKey {
ip: String, ip: String,
identifier: u8, identifier: u8,
} }