diff --git a/async/src/agent.rs b/async/src/agent.rs index de3380b2311b3c97c4d9a14188a381f179aa747a..814eff98f4ada91d7d940c58e49990afaaf3076c 100644 --- a/async/src/agent.rs +++ b/async/src/agent.rs @@ -14,10 +14,9 @@ use sunset_sshwire_derive::*; use crate::*; use sunset::sshwire; -use sunset::{PubKey, AuthSigMsg, Signature}; +use sunset::{PubKey, AuthSigMsg, Signature,OwnedSig, SignKey}; use sshwire::{WireError, WireResult, BinString, TextString, Blob, SSHSink, SSHSource, SSHDecode, SSHEncode}; use sshwire::{SSHEncodeEnum, SSHDecodeEnum}; -use sunset::sign::{OwnedSig, SignKey}; use sunset::sshnames::*; // Must be sufficient for the list of all public keys @@ -105,12 +104,16 @@ impl<'de: 'a, 'a> SSHDecode<'de> for AgentIdentitiesAnswer<'a> { } } +/// A SSH Agent client pub struct AgentClient { conn: UnixStream, buf: Vec<u8>, } impl AgentClient { + /// Create a new client + /// + /// `path` is a Unix socket to a ssh-agent, such as that from `$SSH_AUTH_SOCK`. pub async fn new(path: impl AsRef<Path>) -> Result<Self, std::io::Error> { let conn = UnixStream::connect(path).await?; Ok(Self { diff --git a/async/src/cmdline_client.rs b/async/src/cmdline_client.rs index 7b45c7502eafe049f93f96d590e300634c5b4e7f..f7d67effa6d9cb30456bd41ed5f9172d30abb857 100644 --- a/async/src/cmdline_client.rs +++ b/async/src/cmdline_client.rs @@ -50,6 +50,9 @@ enum Msg { Exited, } +/// A commandline client session +/// +/// This opens a single channel and presents it to the stdin/stdout terminal. pub struct CmdlineClient { cmd: SessionCommand<String>, want_pty: bool, @@ -101,6 +104,11 @@ impl CmdlineClient { } } + /// Splits a `CmdlineClient` into hooks and the runner. + /// + /// `CmdlineRunner` should be awaited until the session completes. + /// `CmdlineHooks` can be used to exit early (and may in future provide + /// other functionality). pub fn split(&mut self) -> (CmdlineHooks, CmdlineRunner) { let pty = self.make_pty(); @@ -306,8 +314,10 @@ impl<'a> CmdlineRunner<'a> { Ok(()) } - /// Runs the `CmdlineClient` session. Requests a shell or command, performs - /// channel IO. + /// Runs the `CmdlineClient` session to completion. + /// + /// Performs authentication, requests a shell or command, performs channel IO. + /// Will return `Ok` after the session ends normally, or an error. pub async fn run(&mut self, cli: &'a SSHClient<'a, CmdlineHooks<'a>>) -> Result<()> { // chanio is only set once a channel is opened below let chanio = Fuse::terminated(); diff --git a/async/src/lib.rs b/async/src/lib.rs index 8702a4548a79ab5dba90d290f1bd06366e02f950..80ed96988a212c94d84e3cedd1dd6e799e452a40 100644 --- a/async/src/lib.rs +++ b/async/src/lib.rs @@ -11,11 +11,11 @@ mod agent; #[cfg(unix)] mod fdio; #[cfg(unix)] -pub use fdio::{stdin, stdout, stderr_out}; +use fdio::{stdin, stdout, stderr_out}; -pub use pty::{raw_pty, RawPtyGuard}; +use pty::{raw_pty, RawPtyGuard}; -pub use cmdline_client::CmdlineClient; +pub use cmdline_client::{CmdlineClient, CmdlineRunner, CmdlineHooks}; pub use agent::AgentClient; diff --git a/embassy/demos/common/src/server.rs b/embassy/demos/common/src/server.rs index 16b6da13771677fc7e36e5692824527930404490..3cd858f443fe411691875c60379da9c9117373a0 100644 --- a/embassy/demos/common/src/server.rs +++ b/embassy/demos/common/src/server.rs @@ -70,10 +70,10 @@ async fn session<S: DemoServer>(socket: &mut TcpSocket<'_>, config: &SunsetMutex let src = socket.remote_endpoint().unwrap(); info!("Connection from {}:{}", src.addr, src.port); - let s = S::new(init); + let demo = S::new(init); let conf = config.lock().await.clone(); - let app = ServerApp::new(&s, conf)?; + let app = ServerApp::new(&demo, conf)?; let app = Mutex::<NoopRawMutex, _>::new(app); let mut ssh_rxbuf = [0; 2000]; @@ -81,7 +81,7 @@ async fn session<S: DemoServer>(socket: &mut TcpSocket<'_>, config: &SunsetMutex let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf)?; let serv = &serv; - let session = s.run(serv); + let session = demo.run(serv); let (mut rsock, mut wsock) = socket.split(); diff --git a/embassy/src/embassy_channel.rs b/embassy/src/embassy_channel.rs index 0b20e2115bf8754cedd5ab9ebaf7bffba6d671c5..a9e3b83e242cbea85e9d1ffd5c91da48625005f4 100644 --- a/embassy/src/embassy_channel.rs +++ b/embassy/src/embassy_channel.rs @@ -72,7 +72,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Write for ChanIO<'a, C, S> { // Public wrappers for In only -/// An standard bidirectional SSH channel +/// A standard bidirectional SSH channel #[derive(Debug)] pub struct ChanInOut<'a, C: CliBehaviour, S: ServBehaviour>(ChanIO<'a, C, S>); diff --git a/src/auth.rs b/src/auth.rs index 66bbed6a91a5218b955e3c31b9716abe0bd415a4..90f9c76c9b0e73d9356965577f4eec4787e0ac99 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -19,8 +19,6 @@ use kex::SessId; /// The message to be signed in a pubkey authentication message, /// RFC4252 Section 7. -/// -/// The UserauthRequest's signature field None. #[derive(Debug)] pub struct AuthSigMsg<'a> { pub(crate) sess_id: BinString<'a>, @@ -40,7 +38,7 @@ impl SSHEncode for &AuthSigMsg<'_> { } impl<'a> AuthSigMsg<'a> { - pub fn new(u: &'a packets::UserauthRequest<'a>, sess_id: &'a SessId) -> Self { + pub(crate) fn new(u: &'a packets::UserauthRequest<'a>, sess_id: &'a SessId) -> Self { auth::AuthSigMsg { sess_id: BinString(sess_id.as_ref()), u, diff --git a/src/behaviour.rs b/src/behaviour.rs index d223dc1d6655eeaabea19e56b94b9ccde6a979dc..08fe38fc209ef08730706222656bbb8394b0a266 100644 --- a/src/behaviour.rs +++ b/src/behaviour.rs @@ -149,6 +149,9 @@ pub trait CliBehaviour { /// /// `key` is a key previously returned from `next_authkey()`, /// it will be one of the `Agent...` variants. + /// + /// The client can call [`msg.enc()`](crate::sshwire::SSHEncode::enc()) to obtain the + /// message to use for agent signing. #[allow(unused)] async fn agent_sign(&mut self, key: &sign::SignKey, msg: &AuthSigMsg<'_>) -> BhResult<sign::OwnedSig> { Err(BhError::Fail) diff --git a/src/error.rs b/src/error.rs index de25bf5eec980ca2f1ba4982a1d29b2eb88b283e..2b3ad4d7dbeda395834feff84fbf637b7f3f4a44 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,7 @@ use crate::channel::ChanNum; // TODO: can we make Snafu not require Debug? +/// The Sunset error type. #[non_exhaustive] #[derive(Snafu, Debug)] #[snafu(context(suffix(false)))] @@ -204,6 +205,7 @@ impl embedded_io::Error for Error { } } +/// A Sunset-specific Result type. pub type Result<T, E = Error> = core::result::Result<T, E>; pub trait TrapBug<T> { diff --git a/src/lib.rs b/src/lib.rs index b433b6a08913605cddf01a2955e9c8b9fe35a0b5..33d1018538b999bf107dcac401de9b276a58e662 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,17 +13,25 @@ // XXX unused_imports only during dev churn #![allow(unused_imports)] -// XXX decide what is public -pub mod conn; -pub mod encrypt; +pub mod sshwire; +pub mod packets; +pub mod sshnames; +pub mod config; +// exported so that UnusedCli can be used +pub mod behaviour; +// exported so that some Channel error variants can be created with .fail(). +// perhaps the ones of interest should be expored separately. pub mod error; -pub mod ident; -pub mod kex; -pub mod test; -pub mod namelist; +// perhaps don't need this, users could just use getrandom? pub mod random; -pub mod sshnames; -pub mod sign; + +mod conn; +mod encrypt; +mod ident; +mod kex; +mod test; +mod namelist; +mod sign; mod client; mod cliauth; @@ -31,24 +39,15 @@ mod cliauth; mod server; mod servauth; -// mod bhtokio; -// mod bhnostd; - -pub mod sunsetlog; +mod sunsetlog; mod auth; mod channel; mod runner; -// TODO only public for UnusedCli etc. -pub mod behaviour; mod termmodes; mod ssh_chapoly; mod traffic; mod noasync; -pub mod packets; -pub mod sshwire; -pub mod config; -pub mod prelude; // Application API pub use behaviour::{Behaviour, ServBehaviour, CliBehaviour, diff --git a/src/runner.rs b/src/runner.rs index 5f5750177faa7a955119a810cf4ed50a02d2b498..b74031c5a635d29138ae278246fb1a3ae849ec61 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -22,6 +22,10 @@ use conn::{Conn, Dispatched}; // was completed. The `ChanHandle` is consumed by `Runner::channel_done()`. // Internally sunset uses `ChanNum`, which is just a newtype around u32. +/// A SSH session instance +/// +/// An application provides network or channel data to `Runner` method calls, +/// and provides customisation callbacks via `CliBehaviour` or `ServBehaviour`. pub struct Runner<'a, C: CliBehaviour, S: ServBehaviour> { conn: Conn<C, S>, @@ -222,6 +226,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { } /// Send data from this application out the wire. + /// /// Returns `Ok(len)` consumed, `Err(Error::ChannelEof)` on EOF, /// or other errors. pub fn channel_send( @@ -254,6 +259,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { } /// Receive data coming from the wire into this application. + /// /// Returns `Ok(len)` received, `Err(Error::ChannelEof)` on EOF, /// or other errors. Ok(0) indicates no data available, ie pending. /// TODO: EOF is unimplemented @@ -286,7 +292,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { Ok(len) } - /// Receives input data, either dt or normal. + /// Receives input data, either normal or extended. pub fn channel_input_either( &mut self, chan: &ChanHandle, @@ -307,7 +313,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { /// Discards any channel input data pending for `chan`, regardless of whether - /// normal or `dt`. + /// normal or extended. pub fn discard_channel_input(&mut self, chan: &ChanHandle) -> Result<()> { let len = self.traf_in.discard_channel_input(chan.0); let wind_adjust = self.conn.channels.finished_input(chan.0, len)?; @@ -321,7 +327,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { /// Indicates when channel data is ready. /// /// When channel data is ready, returns a tuple - /// `Some((channel, dt, len))` + /// `Some((channel, data, len))` /// `len` is the amount of data ready remaining to read, will always be non-zero. /// Note that this returns a `ChanNum` index rather than a `ChanHandle` (which would /// be owned by the caller already. @@ -363,6 +369,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> Runner<'a, C, S> { } /// Returns `true` if the channel and `dt` are currently valid for writing. + /// /// Note that they may not be ready to send output. pub fn valid_channel_send(&self, chan: &ChanHandle, dt: ChanData) -> bool { self.conn.channels.valid_send(chan.0, dt) diff --git a/src/sshnames.rs b/src/sshnames.rs index d86fc5e1762e3717835c0671c6b88386f9b4aa5f..915ae9474f112de0abb26cdf476b46fed733cc7b 100644 --- a/src/sshnames.rs +++ b/src/sshnames.rs @@ -80,7 +80,7 @@ pub enum ChanFail { /// SSH agent message numbers /// -/// [draft-miller-ssh-agent-04](https://tools.ietf.org/html/draft-miller-ssh-agent-04) +/// [draft-miller-ssh-agent](https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-11#section-5.1) #[allow(non_camel_case_types)] #[derive(Debug, Copy, Clone)] pub enum AgentMessageNum { @@ -93,5 +93,5 @@ pub enum AgentMessageNum { } -/// [draft-miller-ssh-agent-04](https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04) +/// [draft-miller-ssh-agent](https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-11#section-5.3) pub const SSH_AGENT_FLAG_RSA_SHA2_256: u32 = 0x02;