diff --git a/turn/Cargo.toml b/turn/Cargo.toml index fca1b93b1..61674e6ef 100644 --- a/turn/Cargo.toml +++ b/turn/Cargo.toml @@ -47,6 +47,7 @@ criterion = "0.5" [features] metrics = [] +async-auth = [] [[bench]] name = "bench" diff --git a/turn/examples/turn_server_udp.rs b/turn/examples/turn_server_udp.rs index ae8f88c00..0e51bad34 100644 --- a/turn/examples/turn_server_udp.rs +++ b/turn/examples/turn_server_udp.rs @@ -3,6 +3,8 @@ use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; use std::sync::Arc; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use clap::{App, AppSettings, Arg}; use tokio::net::UdpSocket; use tokio::signal; @@ -24,6 +26,7 @@ impl MyAuthHandler { } } +#[cfg(not(feature = "async-auth"))] impl AuthHandler for MyAuthHandler { fn auth_handle( &self, @@ -39,6 +42,23 @@ impl AuthHandler for MyAuthHandler { } } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for MyAuthHandler { + async fn auth_handle( + &self, + username: &str, + _realm: &str, + _src_addr: SocketAddr, + ) -> Result, Error> { + if let Some(pw) = self.cred_map.get(username) { + //log::debug!("username={}, password={:?}", username, pw); + Ok(pw.to_vec()) + } else { + Err(Error::ErrFakeErr) + } + } +} // RUST_LOG=trace cargo run --color=always --package turn --example turn_server_udp -- --public-ip 0.0.0.0 --users user=pass diff --git a/turn/src/allocation/allocation_manager/allocation_manager_test.rs b/turn/src/allocation/allocation_manager/allocation_manager_test.rs index c994d6e55..f3df6f4ae 100644 --- a/turn/src/allocation/allocation_manager/allocation_manager_test.rs +++ b/turn/src/allocation/allocation_manager/allocation_manager_test.rs @@ -1,6 +1,8 @@ use std::net::{IpAddr, Ipv4Addr}; use std::str::FromStr; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use stun::attributes::ATTR_USERNAME; use stun::textattrs::TextAttribute; use tokio::net::UdpSocket; @@ -397,11 +399,24 @@ async fn test_delete_allocation_by_username() -> Result<()> { } struct TestAuthHandler; +#[cfg(not(feature = "async-auth"))] impl AuthHandler for TestAuthHandler { fn auth_handle(&self, username: &str, realm: &str, _src_addr: SocketAddr) -> Result> { Ok(generate_auth_key(username, realm, "pass")) } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for TestAuthHandler { + async fn auth_handle( + &self, + username: &str, + realm: &str, + _src_addr: SocketAddr, + ) -> Result> { + Ok(generate_auth_key(username, realm, "pass")) + } +} async fn create_server( alloc_close_notify: Option>, diff --git a/turn/src/auth/mod.rs b/turn/src/auth/mod.rs index 537983d7d..ef9482d7f 100644 --- a/turn/src/auth/mod.rs +++ b/turn/src/auth/mod.rs @@ -4,6 +4,8 @@ mod auth_test; use std::net::SocketAddr; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use base64::prelude::BASE64_STANDARD; use base64::Engine; use md5::{Digest, Md5}; @@ -11,10 +13,22 @@ use ring::hmac; use crate::error::*; +#[cfg(not(feature = "async-auth"))] pub trait AuthHandler { fn auth_handle(&self, username: &str, realm: &str, src_addr: SocketAddr) -> Result>; } +#[cfg(feature = "async-auth")] +#[async_trait] +pub trait AuthHandler { + async fn auth_handle( + &self, + username: &str, + realm: &str, + src_addr: SocketAddr, + ) -> Result>; +} + /// `generate_long_term_credentials()` can be used to create credentials valid for `duration` time/ pub fn generate_long_term_credentials( shared_secret: &str, @@ -48,6 +62,7 @@ pub struct LongTermAuthHandler { shared_secret: String, } +#[cfg(not(feature = "async-auth"))] impl AuthHandler for LongTermAuthHandler { fn auth_handle(&self, username: &str, realm: &str, src_addr: SocketAddr) -> Result> { log::trace!( @@ -68,6 +83,33 @@ impl AuthHandler for LongTermAuthHandler { Ok(generate_auth_key(username, realm, &password)) } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for LongTermAuthHandler { + async fn auth_handle( + &self, + username: &str, + realm: &str, + src_addr: SocketAddr, + ) -> Result> { + log::trace!( + "Authentication username={} realm={} src_addr={}", + username, + realm, + src_addr + ); + + let t = Duration::from_secs(username.parse::()?); + if t < SystemTime::now().duration_since(UNIX_EPOCH)? { + return Err(Error::Other(format!( + "Expired time-windowed username {username}" + ))); + } + + let password = long_term_credentials(username, &self.shared_secret); + Ok(generate_auth_key(username, realm, &password)) + } +} impl LongTermAuthHandler { /// https://tools.ietf.org/search/rfc5389#section-10.2 diff --git a/turn/src/client/client_test.rs b/turn/src/client/client_test.rs index 20516bd25..85113013f 100644 --- a/turn/src/client/client_test.rs +++ b/turn/src/client/client_test.rs @@ -1,5 +1,7 @@ use std::net::IpAddr; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use tokio::net::UdpSocket; use tokio::time::Duration; use util::vnet::net::*; @@ -120,11 +122,24 @@ async fn test_client_with_stun_send_binding_request_to_timeout() -> Result<()> { } struct TestAuthHandler; +#[cfg(not(feature = "async-auth"))] impl AuthHandler for TestAuthHandler { fn auth_handle(&self, username: &str, realm: &str, _src_addr: SocketAddr) -> Result> { Ok(generate_auth_key(username, realm, "pass")) } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for TestAuthHandler { + async fn auth_handle( + &self, + username: &str, + realm: &str, + _src_addr: SocketAddr, + ) -> Result> { + Ok(generate_auth_key(username, realm, "pass")) + } +} // Create an allocation, and then delete all nonces // The subsequent Write on the allocation will cause a CreatePermission diff --git a/turn/src/server/request.rs b/turn/src/server/request.rs index be6db47c1..784e99f1b 100644 --- a/turn/src/server/request.rs +++ b/turn/src/server/request.rs @@ -201,6 +201,7 @@ impl Request { return Ok(None); } + #[cfg(not(feature = "async-auth"))] let our_key = match self.auth_handler.auth_handle( &username_attr.to_string(), &realm_attr.to_string(), @@ -218,6 +219,28 @@ impl Request { return Ok(None); } }; + #[cfg(feature = "async-auth")] + let our_key = match self + .auth_handler + .auth_handle( + &username_attr.to_string(), + &realm_attr.to_string(), + self.src_addr, + ) + .await + { + Ok(key) => key, + Err(_) => { + build_and_send_err( + &self.conn, + self.src_addr, + bad_request_msg, + Error::ErrNoSuchUser, + ) + .await?; + return Ok(None); + } + }; let mi = MessageIntegrity(our_key); if let Err(err) = mi.check(&mut m.clone()) { diff --git a/turn/src/server/request/request_test.rs b/turn/src/server/request/request_test.rs index cfe012055..b26dda934 100644 --- a/turn/src/server/request/request_test.rs +++ b/turn/src/server/request/request_test.rs @@ -1,6 +1,8 @@ use std::net::IpAddr; use std::str::FromStr; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use tokio::net::UdpSocket; use tokio::time::{Duration, Instant}; use util::vnet::net::*; @@ -50,11 +52,24 @@ async fn test_allocation_lifetime_overflow() -> Result<()> { } struct TestAuthHandler; +#[cfg(not(feature = "async-auth"))] impl AuthHandler for TestAuthHandler { fn auth_handle(&self, _username: &str, _realm: &str, _src_addr: SocketAddr) -> Result> { Ok(STATIC_KEY.as_bytes().to_vec()) } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for TestAuthHandler { + async fn auth_handle( + &self, + _username: &str, + _realm: &str, + _src_addr: SocketAddr, + ) -> Result> { + Ok(STATIC_KEY.as_bytes().to_vec()) + } +} #[tokio::test] async fn test_allocation_lifetime_deletion_zero_lifetime() -> Result<()> { diff --git a/turn/src/server/server_test.rs b/turn/src/server/server_test.rs index 1505a12da..f01ab6535 100644 --- a/turn/src/server/server_test.rs +++ b/turn/src/server/server_test.rs @@ -1,6 +1,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::str::FromStr; +#[cfg(feature = "async-auth")] +use async_trait::async_trait; use tokio::net::UdpSocket; use tokio::sync::mpsc; use util::vnet::router::Nic; @@ -30,6 +32,7 @@ impl TestAuthHandler { } } +#[cfg(not(feature = "async-auth"))] impl AuthHandler for TestAuthHandler { fn auth_handle(&self, username: &str, _realm: &str, _src_addr: SocketAddr) -> Result> { if let Some(pw) = self.cred_map.get(username) { @@ -39,6 +42,22 @@ impl AuthHandler for TestAuthHandler { } } } +#[cfg(feature = "async-auth")] +#[async_trait] +impl AuthHandler for TestAuthHandler { + async fn auth_handle( + &self, + username: &str, + _realm: &str, + _src_addr: SocketAddr, + ) -> Result> { + if let Some(pw) = self.cred_map.get(username) { + Ok(pw.to_vec()) + } else { + Err(Error::ErrFakeErr) + } + } +} #[tokio::test] async fn test_server_simple() -> Result<()> {