Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Scaffold for EAP Messages #34

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ path = "server.rs"
name = "client"
path = "client.rs"

[[example]]
name = "eap_server"
path = "eap_server.rs"

169 changes: 169 additions & 0 deletions examples/eap_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#[macro_use]
extern crate log;

use std::net::SocketAddr;
use std::{io, process};

use async_trait::async_trait;
use tokio::net::UdpSocket;
use tokio::signal;

use radius::core::code::Code;
use radius::core::packet::Packet;
use radius::core::request::Request;
use radius::core::eap::{EAP, EAPType, EAPCode};
use radius::core::rfc2865;
use radius::core::rfc2869;
use radius::server::{RequestHandler, SecretProvider, SecretProviderError, Server};

#[tokio::main]
async fn main() {
env_logger::init();

let req_handler = MyRequestHandler {
secret_provider: MySecretProvider {}
};
// start UDP listening
let mut server = Server::listen("0.0.0.0", 1812, req_handler, MySecretProvider {})
.await
.unwrap();
server.set_buffer_size(1500); // default value: 1500
server.set_skip_authenticity_validation(false); // default value: false

// once it has reached here, a RADIUS server is now ready
info!(
"serve is now ready: {}",
server.get_listen_address().unwrap()
);

// start the loop to handle the RADIUS requests
let result = server.run(signal::ctrl_c()).await;
info!("{:?}", result);
if result.is_err() {
process::exit(1);
}
}

struct MyRequestHandler {
secret_provider: MySecretProvider
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] is this secret provider needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-pasta, with pesto :)

}

impl MyRequestHandler {
// Authenticating peer NAS RADIUS server
// ------------------- --- -------------
// <- EAP-Request/
// Identity
// EAP-Response/
// Identity (MyID) ->
// RADIUS Access-Request/
// EAP-Message/EAP-Response/
// (MyID) ->
// <- RADIUS
// Access-Challenge/
// EAP-Message/EAP-Request
// OTP/OTP Challenge
// <- EAP-Request/
// OTP/OTP Challenge
// EAP-Response/
// OTP, OTPpw ->
// RADIUS Access-Request/
// EAP-Message/EAP-Response/
// OTP, OTPpw ->
// <- RADIUS
// Access-Accept/
// EAP-Message/EAP-Success
// (other attributes)
// <- EAP-Success
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a question: does this flow diagram has the original document? For example, RFC document.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, all of the dry stuff in the comments strewn about is copy-pasted from RFCs. This bloody process is described in like 3 - the two i mentioned and the actual EAP RFC, plus HMAC-MD5 has its own (stdby for proto-code)

pub async fn handle_eap_identity(&self, req_packet: &Packet, conn: &UdpSocket, req: &Request, ieap: &EAP ) -> Result<usize, io::Error> {
let mut eap = EAP::new(); //ieap.clone();
let mut ac_packet = req_packet.make_response_packet(Code::AccessChallenge);
eap.code = EAPCode::Response;
eap.typ = EAPType::MD5Challenge;
eap.id = ieap.id;
eap.len = eap.recalc_len();
println!("Response EAP: {}", &eap);
rfc2869::add_eap_message(&mut ac_packet, &eap.to_bytes()[..]);
let ac_bytes = &ac_packet.encode().unwrap();
let authenticator = Packet::create_respose_authenticator(&ac_bytes[..], &req_packet.encode().unwrap()[..], &req_packet.get_secret());
rfc2869::add_message_authenticator(&mut ac_packet, &authenticator);
let result = conn.send_to(&ac_packet.encode().unwrap(), req.get_remote_addr()).await.unwrap();
Ok(result)
}
}

#[async_trait]
impl RequestHandler<(), io::Error> for MyRequestHandler {
async fn handle_radius_request(
&self,
conn: &UdpSocket,
req: &Request,
) -> Result<(), io::Error> {
let req_packet = req.get_packet();
// println!("Req:\n{:#?}", req_packet.clone());
let maybe_user_name_attr = rfc2865::lookup_user_name(req_packet);
let maybe_user_password_attr = rfc2865::lookup_user_password(req_packet);
info!("maybe usr pass looked up");
let maybe_eap_message = rfc2869::lookup_eap_message(req_packet);
info!("maybe eap looked up");
match maybe_eap_message {
Some(e) => {
let ieap = EAP::from_bytes(&e);
println!("EAP Message: {}", &ieap);
let result = self.handle_eap_identity(req_packet, conn, req, &ieap).await.unwrap();
println!("Sent challenge-response {} bytes", &result);
ieap
},
None => {
println!("No eap message found");
EAP::new()
}
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the result of this match expression is discarded. What does this code intend?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but it satisfies the constraints of the match (maybe thats not needed anymore, been doing it this way for a few years).

let maybe_message_authenticator = rfc2869::lookup_message_authenticator(req_packet);
let message_authenticator = match maybe_message_authenticator {
Some(m) => m.to_vec(),
None => {
println!("No authenticator found");
vec![0u8;16]
}
};
println!("Message Authenticator: {:#?}", &message_authenticator);

let user_name = maybe_user_name_attr.unwrap().unwrap();
let user_password = match maybe_user_password_attr {
Some(e) => match e {
Ok(m) => String::from_utf8(m).unwrap(),
Err(e) => {
error!("Could not decode user password due to:\n{}\n", e);
"".to_owned()
}
},
None => {
info!("No user password found");
"".to_owned()
}
};

let code = if user_name == "admin" && user_password == "p@ssw0rd" {
Code::AccessAccept
} else {
Code::AccessReject
};
info!("response => {:?} to {}", code, req.get_remote_addr());

conn.send_to(
&req_packet.make_response_packet(code).encode().unwrap(),
req.get_remote_addr(),
)
.await?;
Ok(())
}
}

struct MySecretProvider {}

impl SecretProvider for MySecretProvider {
fn fetch_secret(&self, _remote_addr: SocketAddr) -> Result<Vec<u8>, SecretProviderError> {
let bs = b"secret".to_vec();
Ok(bs)
}
}
3 changes: 3 additions & 0 deletions radius/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ thiserror = "1.0"
log = "0.4.14"
tokio = { version = "1.6.1", features = ["full"] }
async-trait = "0.1.50"

[dev-dependencies]
hex = "0.4"
Loading