diff --git a/demo/common/src/server.rs b/demo/common/src/server.rs index 8ca98157..3bf95215 100644 --- a/demo/common/src/server.rs +++ b/demo/common/src/server.rs @@ -116,6 +116,10 @@ impl DemoCommon { ServEvent::FirstAuth(a) => self.handle_firstauth(a), ServEvent::PasswordAuth(a) => self.handle_password(a), ServEvent::PubkeyAuth(a) => self.handle_pubkey(a), + ServEvent::Authenticated => { + info!("Auth success"); + Ok(()) + } ServEvent::OpenSession(a) => self.open_session(a), ServEvent::SessionPty(a) => a.succeed(), ServEvent::SessionEnv(a) => a.succeed(), diff --git a/fuzz/src/server.rs b/fuzz/src/server.rs index 9c5470c7..949c5e80 100644 --- a/fuzz/src/server.rs +++ b/fuzz/src/server.rs @@ -72,7 +72,6 @@ fn serv_event(input: &mut FuzzInput, ev: Event, state: &mut State) -> Result<()> } if input.chance(0.9)? { h.allow()?; - state.authed = true; } else if input.chance(0.4)? { h.reject()?; } @@ -88,15 +87,15 @@ fn serv_event(input: &mut FuzzInput, ev: Event, state: &mut State) -> Result<()> h.enable_pubkey_auth(false).unwrap(); } if input.chance(0.9)? { - let real = h.real(); h.allow()?; - if real { - state.authed = true; - } } else if input.chance(0.4)? { h.reject()?; } } + ServEvent::Authenticated => { + assert!(state.authed); + state.authed = true; + } ServEvent::OpenSession(h) => { assert!(state.authed); diff --git a/src/conn.rs b/src/conn.rs index e7c99cae..27266884 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -15,6 +15,7 @@ use { use crate::*; use channel::{Channels, CliSessionExit}; use client::Client; +use event::{CliEventId, ServEventId}; use kex::{AlgoConfig, Kex, SessId}; use packets::{Packet, ParseContext}; use server::Server; @@ -64,8 +65,8 @@ enum ConnState { pub(crate) enum DispatchEvent { /// Incoming channel data Data(channel::DataIn), - CliEvent(event::CliEventId), - ServEvent(event::ServEventId), + CliEvent(CliEventId), + ServEvent(ServEventId), /// NewKeys was received, wake any output channels in case they were waiting. KexDone, /// Connection state has changed, should poll again @@ -683,13 +684,15 @@ impl Conn { &mut self, allow: bool, s: &mut TrafSend, - ) -> Result<()> { + ) -> Result { let auth = &mut self.mut_server()?.auth; auth.resume_request(allow, s)?; if auth.authed && matches!(self.state, ConnState::PreAuth) { self.state = ConnState::Authed; + Ok(DispatchEvent::ServEvent(ServEventId::Authenticated)) + } else { + Ok(DispatchEvent::None) } - Ok(()) } pub(crate) fn resume_servauth_pkok( diff --git a/src/event.rs b/src/event.rs index a9842a91..974a33aa 100644 --- a/src/event.rs +++ b/src/event.rs @@ -273,8 +273,14 @@ pub enum ServEvent<'g, 'a> { /// /// Note that this event may be emitted multiple times, /// since the client first queries acceptable public keys, - /// and then later sends an actual signature. + /// and then later sends an actual signature. The application + /// should give consistent `allow()`/`deny()` (default) responses + /// for requests of the same key. PubkeyAuth(ServPubkeyAuth<'g, 'a>), + /// Authentication success. + /// + /// Emitted when a client first successfully authenticates. + Authenticated, /// Client's request for a session channel. /// /// After accepting a channel the [`ChanHandle`] will be returned. @@ -313,6 +319,7 @@ impl Debug for ServEvent<'_, '_> { Self::PasswordAuth(_) => "PasswordAuth", Self::PubkeyAuth(_) => "PubkeyAuth", Self::FirstAuth(_) => "FirstAuth", + Self::Authenticated => "Authenticated", Self::OpenSession(_) => "OpenSession", Self::SessionShell(_) => "SessionShell", Self::SessionExec(_) => "SessionExec", @@ -473,14 +480,6 @@ impl<'g, 'a> ServPubkeyAuth<'g, 'a> { self.runner.fetch_servpubkey() } - /// Whether this is an pubkey auth attempt. - /// - /// `real()` will be `false` for a pubkey key query (no signature attemp), - /// or `true` for the actual login attempt with signature. - pub fn real(&self) -> bool { - self.real_sig - } - /// Accept the presented public key. pub fn allow(mut self) -> Result<()> { self.done = true; @@ -913,6 +912,7 @@ pub(crate) enum ServEventId { real_sig: bool, }, FirstAuth, + Authenticated, OpenSession { num: ChanNum, }, @@ -964,6 +964,13 @@ impl ServEventId { debug_assert!(matches!(p, Some(Packet::UserauthRequest(_)))); Ok(ServEvent::FirstAuth(ServFirstAuth::new(runner))) } + Self::Authenticated => { + // TODO: Doesn't actually need Packet::UserauthRequest + // since it's not using data from it. But it fits the current + // flow. + debug_assert!(matches!(p, Some(Packet::UserauthRequest(_)))); + Ok(ServEvent::Authenticated) + } Self::OpenSession { num } => { debug_assert!(matches!(p, Some(Packet::ChannelOpen(_)))); Ok(ServEvent::OpenSession(ServOpenSession::new(runner, num))) @@ -996,7 +1003,7 @@ impl ServEventId { // Used for internal correctness checks. pub(crate) fn needs_resume(&self) -> bool { match self { - Self::Defunct => false, + Self::Defunct | Self::Authenticated => false, Self::Hostkeys | Self::FirstAuth | Self::PasswordAuth diff --git a/src/kex.rs b/src/kex.rs index dde7e287..b731ad2f 100644 --- a/src/kex.rs +++ b/src/kex.rs @@ -1049,6 +1049,7 @@ impl KexMlkemX25519 { #[cfg(test)] mod tests { use crate::encrypt::{self, KeyState, KeysRecv, KeysSend, SSH_PAYLOAD_START}; + use crate::event::CliEventId; use crate::ident::RemoteVersion; use crate::kex; use crate::kex::*; diff --git a/src/lib.rs b/src/lib.rs index 446e8fad..88cdfa5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,6 @@ mod termmodes; mod traffic; use conn::DispatchEvent; -use event::CliEventId; // Application API pub use sshwire::TextString; diff --git a/src/runner.rs b/src/runner.rs index fb65c2d0..8d9afcae 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -226,21 +226,18 @@ impl<'a> Runner<'a, server::Server> { let prev_event = self.resume_event.take(); // auth packets have passwords self.traf_in.zeroize_payload(); - debug_assert!( - matches!( - prev_event, - DispatchEvent::ServEvent(ServEventId::PasswordAuth) - ) || matches!( - prev_event, - DispatchEvent::ServEvent(ServEventId::PubkeyAuth { .. }) - ) || matches!( - prev_event, - DispatchEvent::ServEvent(ServEventId::FirstAuth) + debug_assert!(matches!( + prev_event, + DispatchEvent::ServEvent( + ServEventId::PasswordAuth + | ServEventId::PubkeyAuth { .. } + | ServEventId::FirstAuth ) - ); + )); let mut s = self.traf_out.sender(&mut self.keys); - self.conn.resume_servauth(allow, &mut s) + self.resume_event = self.conn.resume_servauth(allow, &mut s)?; + Ok(()) } pub(crate) fn resume_servauth_pkok(&mut self) -> Result<()> {