1
0

implement new auth method for server

This commit is contained in:
EAimTY 2023-02-05 22:26:31 +09:00
parent 2c1f8e1a64
commit 8c4171ba14
4 changed files with 49 additions and 28 deletions

View File

@ -13,10 +13,11 @@ register-count = { version = "0.1.0", default-features = false, features = ["std
rustls = { version = "0.20.8", default-features = false, features = ["quic"] } rustls = { version = "0.20.8", default-features = false, features = ["quic"] }
rustls-pemfile = { version = "1.0.2", default-features = false } rustls-pemfile = { version = "1.0.2", default-features = false }
serde = { version = "1.0.152", default-features = false, features = ["derive", "std"] } serde = { version = "1.0.152", default-features = false, features = ["derive", "std"] }
serde_json = { version = "1.0.91", default-features = false, features = ["std"] } serde_json = { version = "1.0.92", default-features = false, features = ["std"] }
socket2 = { version = "0.4.7", default-features = false } socket2 = { version = "0.4.7", default-features = false }
thiserror = { version = "1.0.38", default-features = false } thiserror = { version = "1.0.38", default-features = false }
tokio = { version = "1.25.0", default-features = false, features = ["macros", "net", "parking_lot", "rt-multi-thread", "time"] } tokio = { version = "1.25.0", default-features = false, features = ["macros", "net", "parking_lot", "rt-multi-thread", "time"] }
tokio-util = { version = "0.7.4", default-features = false, features = ["compat"] } tokio-util = { version = "0.7.4", default-features = false, features = ["compat"] }
tuic = { path = "../tuic", default-features = false } tuic = { version = "5.0.0-pre-alpha6", default-features = false }
tuic-quinn = { path = "../tuic-quinn", default-features = false } tuic-quinn = { version = "0.1.0-pre-alpha2", default-features = false }
uuid = { version = "1.3.0", default-features = false, features = ["serde", "std"] }

View File

@ -3,10 +3,11 @@ use lexopt::{Arg, Error as ArgumentError, Parser};
use serde::{de::Error as DeError, Deserialize, Deserializer}; use serde::{de::Error as DeError, Deserialize, Deserializer};
use serde_json::Error as SerdeError; use serde_json::Error as SerdeError;
use std::{ use std::{
env::ArgsOs, fmt::Display, fs::File, io::Error as IoError, net::SocketAddr, path::PathBuf, collections::HashMap, env::ArgsOs, fmt::Display, fs::File, io::Error as IoError,
str::FromStr, time::Duration, net::SocketAddr, path::PathBuf, str::FromStr, time::Duration,
}; };
use thiserror::Error; use thiserror::Error;
use uuid::Uuid;
const HELP_MSG: &str = r#" const HELP_MSG: &str = r#"
Usage tuic-server [arguments] Usage tuic-server [arguments]
@ -21,7 +22,8 @@ Arguments:
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Config { pub struct Config {
pub server: SocketAddr, pub server: SocketAddr,
pub token: String, #[serde(deserialize_with = "deserialize_users")]
pub users: HashMap<Uuid, String>,
pub certificate: PathBuf, pub certificate: PathBuf,
pub private_key: PathBuf, pub private_key: PathBuf,
#[serde( #[serde(
@ -130,6 +132,19 @@ where
T::from_str(&s).map_err(DeError::custom) T::from_str(&s).map_err(DeError::custom)
} }
pub fn deserialize_users<'de, D>(deserializer: D) -> Result<HashMap<Uuid, String>, D::Error>
where
D: Deserializer<'de>,
{
let map = HashMap::<Uuid, String>::deserialize(deserializer)?;
if map.is_empty() {
return Err(DeError::custom("users cannot be empty"));
}
Ok(map)
}
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ConfigError { pub enum ConfigError {
#[error(transparent)] #[error(transparent)]

View File

@ -8,6 +8,7 @@ use std::{env, io::Error as IoError, net::SocketAddr, process};
use thiserror::Error; use thiserror::Error;
use tuic::Address; use tuic::Address;
use tuic_quinn::Error as ModelError; use tuic_quinn::Error as ModelError;
use uuid::Uuid;
mod config; mod config;
mod server; mod server;
@ -52,8 +53,8 @@ pub enum Error {
DuplicatedAuth, DuplicatedAuth,
#[error("token length too short")] #[error("token length too short")]
ExportKeyingMaterial, ExportKeyingMaterial,
#[error("authentication failed")] #[error("authentication failed: {0}")]
AuthFailed, AuthFailed(Uuid),
#[error("received packet from unexpected source")] #[error("received packet from unexpected source")]
UnexpectedPacketSource, UnexpectedPacketSource,
#[error("{0} resolved to {1} but IPv6 UDP relay disabled")] #[error("{0} resolved to {1} but IPv6 UDP relay disabled")]

View File

@ -39,12 +39,13 @@ use tokio::{
use tokio_util::compat::FuturesAsyncReadCompatExt; use tokio_util::compat::FuturesAsyncReadCompatExt;
use tuic::Address; use tuic::Address;
use tuic_quinn::{side, Connect, Connection as Model, Packet, Task}; use tuic_quinn::{side, Connect, Connection as Model, Packet, Task};
use uuid::Uuid;
const DEFAULT_CONCURRENT_STREAMS: usize = 32; const DEFAULT_CONCURRENT_STREAMS: usize = 32;
pub struct Server { pub struct Server {
ep: Endpoint, ep: Endpoint,
token: Arc<[u8]>, users: Arc<HashMap<Uuid, Vec<u8>>>,
udp_relay_ipv6: bool, udp_relay_ipv6: bool,
zero_rtt_handshake: bool, zero_rtt_handshake: bool,
auth_timeout: Duration, auth_timeout: Duration,
@ -115,9 +116,15 @@ impl Server {
TokioRuntime, TokioRuntime,
)?; )?;
let users = cfg
.users
.into_iter()
.map(|(uuid, password)| (uuid, password.into_bytes()))
.collect();
Ok(Self { Ok(Self {
ep, ep,
token: Arc::from(cfg.token.into_bytes().into_boxed_slice()), users: Arc::new(users),
udp_relay_ipv6: cfg.udp_relay_ipv6, udp_relay_ipv6: cfg.udp_relay_ipv6,
zero_rtt_handshake: cfg.zero_rtt_handshake, zero_rtt_handshake: cfg.zero_rtt_handshake,
auth_timeout: cfg.auth_timeout, auth_timeout: cfg.auth_timeout,
@ -133,7 +140,7 @@ impl Server {
tokio::spawn(Connection::handle( tokio::spawn(Connection::handle(
conn, conn,
self.token.clone(), self.users.clone(),
self.udp_relay_ipv6, self.udp_relay_ipv6,
self.zero_rtt_handshake, self.zero_rtt_handshake,
self.auth_timeout, self.auth_timeout,
@ -149,7 +156,7 @@ impl Server {
struct Connection { struct Connection {
inner: QuinnConnection, inner: QuinnConnection,
model: Model<side::Server>, model: Model<side::Server>,
token: Arc<[u8]>, users: Arc<HashMap<Uuid, Vec<u8>>>,
udp_relay_ipv6: bool, udp_relay_ipv6: bool,
is_authed: IsAuthed, is_authed: IsAuthed,
udp_sessions: Arc<AsyncMutex<HashMap<u16, UdpSession>>>, udp_sessions: Arc<AsyncMutex<HashMap<u16, UdpSession>>>,
@ -165,7 +172,7 @@ struct Connection {
impl Connection { impl Connection {
async fn handle( async fn handle(
conn: Connecting, conn: Connecting,
token: Arc<[u8]>, users: Arc<HashMap<Uuid, Vec<u8>>>,
udp_relay_ipv6: bool, udp_relay_ipv6: bool,
zero_rtt_handshake: bool, zero_rtt_handshake: bool,
auth_timeout: Duration, auth_timeout: Duration,
@ -175,7 +182,7 @@ impl Connection {
) { ) {
match Self::init( match Self::init(
conn, conn,
token, users,
udp_relay_ipv6, udp_relay_ipv6,
zero_rtt_handshake, zero_rtt_handshake,
max_external_pkt_size, max_external_pkt_size,
@ -203,7 +210,7 @@ impl Connection {
async fn init( async fn init(
conn: Connecting, conn: Connecting,
token: Arc<[u8]>, users: Arc<HashMap<Uuid, Vec<u8>>>,
udp_relay_ipv6: bool, udp_relay_ipv6: bool,
zero_rtt_handshake: bool, zero_rtt_handshake: bool,
max_external_pkt_size: usize, max_external_pkt_size: usize,
@ -223,7 +230,7 @@ impl Connection {
Ok(Self { Ok(Self {
inner: conn.clone(), inner: conn.clone(),
model: Model::<side::Server>::new(conn), model: Model::<side::Server>::new(conn),
token, users,
udp_relay_ipv6, udp_relay_ipv6,
is_authed: IsAuthed::new(), is_authed: IsAuthed::new(),
udp_sessions: Arc::new(AsyncMutex::new(HashMap::new())), udp_sessions: Arc::new(AsyncMutex::new(HashMap::new())),
@ -263,21 +270,17 @@ impl Connection {
async fn pre_process(conn: &Connection, recv: RecvStream) -> Result<Task, Error> { async fn pre_process(conn: &Connection, recv: RecvStream) -> Result<Task, Error> {
let task = conn.model.accept_uni_stream(recv).await?; let task = conn.model.accept_uni_stream(recv).await?;
if let Task::Authenticate(token) = &task { if let Task::Authenticate(auth) = &task {
if conn.is_authed() { if conn.is_authed() {
return Err(Error::DuplicatedAuth); return Err(Error::DuplicatedAuth);
} else if conn
.users
.get(&auth.uuid())
.map_or(false, |password| auth.validate(password))
{
conn.set_authed();
} else { } else {
let mut buf = [0; 32]; return Err(Error::AuthFailed(auth.uuid()));
conn.inner
.export_keying_material(&mut buf, &conn.token, &conn.token)
.map_err(|_| Error::ExportKeyingMaterial)?;
if token == &buf {
conn.set_authed();
} else {
return Err(Error::AuthFailed);
}
} }
} }
@ -296,6 +299,7 @@ impl Connection {
} }
match pre_process(&self, recv).await { match pre_process(&self, recv).await {
Ok(Task::Authenticate(_)) => {}
Ok(Task::Packet(pkt)) => { Ok(Task::Packet(pkt)) => {
self.set_udp_relay_mode(UdpRelayMode::Quic); self.set_udp_relay_mode(UdpRelayMode::Quic);
match self.handle_packet(pkt).await { match self.handle_packet(pkt).await {