Skip to content

Commit

Permalink
fixed #1053 - prevent repeated consumption of the ticket uses within …
Browse files Browse the repository at this point in the history
…the same SSH session
  • Loading branch information
Eugeny committed Sep 27, 2024
1 parent 9b599ed commit 1f597a8
Showing 1 changed file with 43 additions and 5 deletions.
48 changes: 43 additions & 5 deletions warpgate-protocol-ssh/src/server/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ enum KeyboardInteractiveState {
WebAuthRequested(broadcast::Receiver<AuthResult>),
}

struct CachedSuccessfulTicketAuth {
ticket: Secret<String>,
username: String,
}

pub struct ServerSession {
pub id: SessionId,
username: Option<String>,
Expand All @@ -90,6 +95,7 @@ pub struct ServerSession {
channel_writer: ChannelWriter,
auth_state: Option<Arc<Mutex<AuthState>>>,
keyboard_interactive_state: KeyboardInteractiveState,
cached_successful_ticket_auth: Option<CachedSuccessfulTicketAuth>,
}

fn session_debug_tag(id: &SessionId, remote_address: &SocketAddr) -> String {
Expand Down Expand Up @@ -147,6 +153,7 @@ impl ServerSession {
channel_writer: ChannelWriter::new(),
auth_state: None,
keyboard_interactive_state: KeyboardInteractiveState::None,
cached_successful_ticket_auth: None,
};

let mut so_rx = this.service_output.subscribe();
Expand Down Expand Up @@ -1216,7 +1223,7 @@ impl ServerSession {
}

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 {
proceed_with_methods: Some(self.get_remaining_auth_methods(kinds)),
},
Expand Down Expand Up @@ -1244,7 +1251,7 @@ impl ServerSession {
let mut result = Ok(AuthResult::Rejected);
for key in keys {
result = self
.try_auth(
.try_auth_lazy(
&selector,
Some(AuthCredential::PublicKey {
kind: key.name().to_string(),
Expand Down Expand Up @@ -1283,7 +1290,7 @@ impl ServerSession {
info!("Password auth as {:?}", selector);

match self
.try_auth(&selector, Some(AuthCredential::Password(password)))
.try_auth_lazy(&selector, Some(AuthCredential::Password(password)))
.await
{
Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept,
Expand Down Expand Up @@ -1327,7 +1334,7 @@ impl ServerSession {

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::Rejected) => russh::server::Auth::Reject {
proceed_with_methods: None,
Expand Down Expand Up @@ -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,
selector: &AuthSelector,
credential: Option<AuthCredential>,
Expand Down

0 comments on commit 1f597a8

Please sign in to comment.