mirror of
https://github.com/warp-tech/warpgate.git
synced 2024-11-15 04:21:59 +08:00
fixed #1053 - prevent repeated consumption of the ticket uses within the same SSH session
This commit is contained in:
parent
9b599ed1e2
commit
1f597a88a5
1 changed files with 43 additions and 5 deletions
|
@ -65,6 +65,11 @@ enum KeyboardInteractiveState {
|
||||||
WebAuthRequested(broadcast::Receiver<AuthResult>),
|
WebAuthRequested(broadcast::Receiver<AuthResult>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CachedSuccessfulTicketAuth {
|
||||||
|
ticket: Secret<String>,
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ServerSession {
|
pub struct ServerSession {
|
||||||
pub id: SessionId,
|
pub id: SessionId,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
|
@ -90,6 +95,7 @@ pub struct ServerSession {
|
||||||
channel_writer: ChannelWriter,
|
channel_writer: ChannelWriter,
|
||||||
auth_state: Option<Arc<Mutex<AuthState>>>,
|
auth_state: Option<Arc<Mutex<AuthState>>>,
|
||||||
keyboard_interactive_state: KeyboardInteractiveState,
|
keyboard_interactive_state: KeyboardInteractiveState,
|
||||||
|
cached_successful_ticket_auth: Option<CachedSuccessfulTicketAuth>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn session_debug_tag(id: &SessionId, remote_address: &SocketAddr) -> String {
|
fn session_debug_tag(id: &SessionId, remote_address: &SocketAddr) -> String {
|
||||||
|
@ -147,6 +153,7 @@ impl ServerSession {
|
||||||
channel_writer: ChannelWriter::new(),
|
channel_writer: ChannelWriter::new(),
|
||||||
auth_state: None,
|
auth_state: None,
|
||||||
keyboard_interactive_state: KeyboardInteractiveState::None,
|
keyboard_interactive_state: KeyboardInteractiveState::None,
|
||||||
|
cached_successful_ticket_auth: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut so_rx = this.service_output.subscribe();
|
let mut so_rx = this.service_output.subscribe();
|
||||||
|
@ -1216,7 +1223,7 @@ impl ServerSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
let selector: AuthSelector = ssh_username.expose_secret().into();
|
let selector: AuthSelector = ssh_username.expose_secret().into();
|
||||||
match self.try_auth(&selector, None).await {
|
match self.try_auth_lazy(&selector, None).await {
|
||||||
Ok(AuthResult::Need(kinds)) => russh::server::Auth::Reject {
|
Ok(AuthResult::Need(kinds)) => russh::server::Auth::Reject {
|
||||||
proceed_with_methods: Some(self.get_remaining_auth_methods(kinds)),
|
proceed_with_methods: Some(self.get_remaining_auth_methods(kinds)),
|
||||||
},
|
},
|
||||||
|
@ -1244,7 +1251,7 @@ impl ServerSession {
|
||||||
let mut result = Ok(AuthResult::Rejected);
|
let mut result = Ok(AuthResult::Rejected);
|
||||||
for key in keys {
|
for key in keys {
|
||||||
result = self
|
result = self
|
||||||
.try_auth(
|
.try_auth_lazy(
|
||||||
&selector,
|
&selector,
|
||||||
Some(AuthCredential::PublicKey {
|
Some(AuthCredential::PublicKey {
|
||||||
kind: key.name().to_string(),
|
kind: key.name().to_string(),
|
||||||
|
@ -1283,7 +1290,7 @@ impl ServerSession {
|
||||||
info!("Password auth as {:?}", selector);
|
info!("Password auth as {:?}", selector);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.try_auth(&selector, Some(AuthCredential::Password(password)))
|
.try_auth_lazy(&selector, Some(AuthCredential::Password(password)))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept,
|
Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept,
|
||||||
|
@ -1327,7 +1334,7 @@ impl ServerSession {
|
||||||
|
|
||||||
self.keyboard_interactive_state = KeyboardInteractiveState::None;
|
self.keyboard_interactive_state = KeyboardInteractiveState::None;
|
||||||
|
|
||||||
match self.try_auth(&selector, cred).await {
|
match self.try_auth_lazy(&selector, cred).await {
|
||||||
Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept,
|
Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept,
|
||||||
Ok(AuthResult::Rejected) => russh::server::Auth::Reject {
|
Ok(AuthResult::Rejected) => russh::server::Auth::Reject {
|
||||||
proceed_with_methods: None,
|
proceed_with_methods: None,
|
||||||
|
@ -1448,7 +1455,38 @@ impl ServerSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_auth(
|
/// As try_auth_lazy is called multiple times, this memoization prevents
|
||||||
|
/// consuming the ticket multiple times, depleting its uses.
|
||||||
|
async fn try_auth_lazy(
|
||||||
|
&mut self,
|
||||||
|
selector: &AuthSelector,
|
||||||
|
credential: Option<AuthCredential>,
|
||||||
|
) -> Result<AuthResult> {
|
||||||
|
if let AuthSelector::Ticket { secret } = selector {
|
||||||
|
if let Some(ref csta) = self.cached_successful_ticket_auth {
|
||||||
|
// Only if the client hasn't maliciously changed the username
|
||||||
|
// between auth attempts
|
||||||
|
if &csta.ticket == secret {
|
||||||
|
return Ok(AuthResult::Accepted {
|
||||||
|
username: csta.username.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self.try_auth_eager(selector, credential).await?;
|
||||||
|
if let AuthResult::Accepted { ref username } = result {
|
||||||
|
self.cached_successful_ticket_auth = Some(CachedSuccessfulTicketAuth {
|
||||||
|
ticket: secret.clone(),
|
||||||
|
username: username.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
self.try_auth_eager(selector, credential).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_auth_eager(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &AuthSelector,
|
selector: &AuthSelector,
|
||||||
credential: Option<AuthCredential>,
|
credential: Option<AuthCredential>,
|
||||||
|
|
Loading…
Reference in a new issue