diff --git a/ssh-transport/src/channel.rs b/ssh-transport/src/channel.rs index b7da62c..3bd89da 100644 --- a/ssh-transport/src/channel.rs +++ b/ssh-transport/src/channel.rs @@ -1,15 +1,29 @@ -use tracing::debug; +use std::collections::VecDeque; +use tracing::{debug, warn}; -use crate::packet::Packet; -use crate::parse::Parser; -use crate::Result; use crate::client_error; +use crate::packet::Packet; +use crate::parse::{Parser, Writer}; +use crate::Result; -pub(crate) struct ServerChannelsState {} +pub(crate) struct ServerChannelsState { + packets_to_send: VecDeque, + channels: Vec, +} + +struct SessionChannel { + peer_channel: u32, + has_pty: bool, + has_shell: bool, + sent_bytes: Vec, +} impl ServerChannelsState { pub(crate) fn new() -> Self { - ServerChannelsState {} + ServerChannelsState { + packets_to_send: VecDeque::new(), + channels: Vec::new(), + } } pub(crate) fn on_packet(&mut self, packet_type: u8, mut payload: Parser<'_>) -> Result<()> { @@ -25,9 +39,114 @@ impl ServerChannelsState { match channel_type { "session" => { - todo!("open session") + let our_number = self.channels.len() as u32; + + let mut confirm = Writer::new(); + confirm.u8(Packet::SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + confirm.u32(our_number); + confirm.u32(sender_channel); + confirm.u32(initial_window_size); + confirm.u32(max_packet_size); + + self.channels.push(SessionChannel { + peer_channel: sender_channel, + has_pty: false, + has_shell: false, + sent_bytes: Vec::new(), + }); + + self.packets_to_send.push_back(Packet { + payload: confirm.finish(), + }); + + debug!(?channel_type, ?our_number, "Successfully opened channel"); + } + _ => { + let mut failure = Writer::new(); + failure.u8(Packet::SSH_MSG_CHANNEL_OPEN_FAILURE); + failure.u32(sender_channel); + failure.u32(3); // SSH_OPEN_UNKNOWN_CHANNEL_TYPE + failure.string(b"unknown channel type"); + failure.string(b"en_US"); + + self.packets_to_send.push_back(Packet { + payload: failure.finish(), + }); + } + } + } + Packet::SSH_MSG_CHANNEL_DATA => { + let our_channel = payload.u32()?; + let data = payload.string()?; + + let channel = self.channel(our_channel)?; + channel.recv_bytes(data); + + let mut reply = Writer::new(); + reply.u8(Packet::SSH_MSG_CHANNEL_DATA); + reply.u32(channel.peer_channel); + reply.string(data); + self.packets_to_send.push_back(Packet { + payload: reply.finish(), + }); + } + Packet::SSH_MSG_CHANNEL_REQUEST => { + let our_channel = payload.u32()?; + let request_type = payload.utf8_string()?; + let want_reply = payload.bool()?; + + debug!(?our_channel, ?request_type, "Got channel request"); + + let channel = self.channel(our_channel)?; + let peer_channel = channel.peer_channel; + + match request_type { + "pty-req" => { + let term = payload.utf8_string()?; + let width_chars = payload.u32()?; + let height_rows = payload.u32()?; + let _width_px = payload.u32()?; + let _height_px = payload.u32()?; + let _term_modes = payload.string()?; + + debug!( + ?our_channel, + ?term, + ?width_chars, + ?height_rows, + "Trying to open a terminal" + ); + + // Faithfully allocate the PTY. + channel.has_pty = true; + + if want_reply { + self.send_channel_success(peer_channel); + } + } + "shell" => { + if !channel.has_pty { + self.send_channel_failure(peer_channel); + } + + // Sure! (reborrow) + let channel = self.channel(our_channel)?; + channel.has_shell = true; + + debug!(?our_channel, "Opening shell"); + + if want_reply { + self.send_channel_success(peer_channel); + } + } + "signal" => { + debug!(?our_channel, "Received signal"); + // Ignore signals, something we can do. + } + _ => { + warn!(?request_type, ?our_channel, "Unknown channel request"); + self.send_channel_failure(peer_channel); } - _ => todo!("response with SSH_MSG_CHANNEL_OPEN_FAILURE"), } } _ => { @@ -37,4 +156,38 @@ impl ServerChannelsState { Ok(()) } + + pub(crate) fn packets_to_send(&mut self) -> impl Iterator + '_ { + self.packets_to_send.drain(..) + } + + fn send_channel_success(&mut self, recipient_channel: u32) { + let mut failure = Writer::new(); + failure.u8(Packet::SSH_MSG_CHANNEL_SUCCESS); + failure.u32(recipient_channel); + self.packets_to_send.push_back(Packet { + payload: failure.finish(), + }); + } + + fn send_channel_failure(&mut self, recipient_channel: u32) { + let mut failure = Writer::new(); + failure.u8(Packet::SSH_MSG_CHANNEL_FAILURE); + failure.u32(recipient_channel); + self.packets_to_send.push_back(Packet { + payload: failure.finish(), + }); + } + + fn channel(&mut self, number: u32) -> Result<&mut SessionChannel> { + self.channels + .get_mut(number as usize) + .ok_or_else(|| client_error!("unknown channel: {number}")) + } +} + +impl SessionChannel { + fn recv_bytes(&mut self, bytes: &[u8]) { + self.sent_bytes.extend_from_slice(bytes); + } } diff --git a/ssh-transport/src/lib.rs b/ssh-transport/src/lib.rs index fee639f..975fbd4 100644 --- a/ssh-transport/src/lib.rs +++ b/ssh-transport/src/lib.rs @@ -406,8 +406,8 @@ impl ServerConnection { let mut banner = Writer::new(); banner.u8(Packet::SSH_MSG_USERAUTH_BANNER); - banner.string(b"this system ONLY allows catgirls to enter.\r\nall other attempts WILL be prosecuted to the full extent of the rawr!!\r\n"); - banner.string(b"en-US"); + banner.string(b"!! this system ONLY allows catgirls to enter !!\r\n!! all other attempts WILL be prosecuted to the full extent of the rawr !!\r\n"); + banner.string(b"en_US"); self.packet_transport.queue_packet(Packet { payload: banner.finish(), }); @@ -431,6 +431,9 @@ impl ServerConnection { // Connection-related packets 90..128 => { con.on_packet(packet_type, payload)?; + for packet in con.packets_to_send() { + self.packet_transport.queue_packet(packet); + } } Packet::SSH_MSG_GLOBAL_REQUEST => { let request_name = payload.utf8_string()?;