disconnect after ctrl c

This commit is contained in:
nora 2024-08-11 20:39:05 +02:00
parent bc7e12e50b
commit b75db7c21f
3 changed files with 78 additions and 13 deletions

View file

@ -7,7 +7,7 @@ use tokio::{
}; };
use tracing::{error, info}; use tracing::{error, info};
use ssh_transport::{ServerConnection, SshError, ThreadRngRand}; use ssh_transport::{ServerConnection, SshStatus, ThreadRngRand};
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
#[tokio::main] #[tokio::main]
@ -67,13 +67,16 @@ async fn handle_connection(next: (TcpStream, SocketAddr)) -> Result<()> {
if let Err(err) = state.recv_bytes(&buf[..read]) { if let Err(err) = state.recv_bytes(&buf[..read]) {
match err { match err {
SshError::ClientError(err) => { SshStatus::ClientError(err) => {
info!(?err, "disconnecting client after invalid operation"); info!(?err, "disconnecting client after invalid operation");
return Ok(()); return Ok(());
} }
SshError::ServerError(err) => { SshStatus::ServerError(err) => {
return Err(err); return Err(err);
} }
SshStatus::Disconnect => {
return Ok(());
}
} }
} }

View file

@ -12,6 +12,8 @@ pub(crate) struct ServerChannelsState {
} }
struct SessionChannel { struct SessionChannel {
/// Whether our side has closed this channel.
we_closed: bool,
peer_channel: u32, peer_channel: u32,
has_pty: bool, has_pty: bool,
has_shell: bool, has_shell: bool,
@ -49,6 +51,7 @@ impl ServerChannelsState {
confirm.u32(max_packet_size); confirm.u32(max_packet_size);
self.channels.push(SessionChannel { self.channels.push(SessionChannel {
we_closed: false,
peer_channel: sender_channel, peer_channel: sender_channel,
has_pty: false, has_pty: false,
has_shell: false, has_shell: false,
@ -67,7 +70,7 @@ impl ServerChannelsState {
failure.u32(sender_channel); failure.u32(sender_channel);
failure.u32(3); // SSH_OPEN_UNKNOWN_CHANNEL_TYPE failure.u32(3); // SSH_OPEN_UNKNOWN_CHANNEL_TYPE
failure.string(b"unknown channel type"); failure.string(b"unknown channel type");
failure.string(b"en_US"); failure.string(b"");
self.packets_to_send.push_back(Packet { self.packets_to_send.push_back(Packet {
payload: failure.finish(), payload: failure.finish(),
@ -80,15 +83,54 @@ impl ServerChannelsState {
let data = payload.string()?; let data = payload.string()?;
let channel = self.channel(our_channel)?; let channel = self.channel(our_channel)?;
let peer = channel.peer_channel;
channel.recv_bytes(data); channel.recv_bytes(data);
let mut reply = Writer::new(); let mut reply = Writer::new();
reply.u8(Packet::SSH_MSG_CHANNEL_DATA); reply.u8(Packet::SSH_MSG_CHANNEL_DATA);
reply.u32(channel.peer_channel); reply.u32(channel.peer_channel);
reply.string(data); reply.string(data); // echo :3
self.packets_to_send.push_back(Packet { self.packets_to_send.push_back(Packet {
payload: reply.finish(), payload: reply.finish(),
}); });
if data.contains(&0x03 /*EOF, Ctrl-C*/) {
debug!(?our_channel, "Received EOF, closing channel");
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
let mut eof = Writer::new();
eof.u8(Packet::SSH_MSG_CHANNEL_EOF);
eof.u32(peer);
self.packets_to_send.push_back(Packet {
payload: eof.finish(),
});
let mut close = Writer::new();
close.u8(Packet::SSH_MSG_CHANNEL_CLOSE);
close.u32(peer);
self.packets_to_send.push_back(Packet {
payload: close.finish(),
});
let channel = self.channel(our_channel)?;
channel.we_closed = true;
}
}
Packet::SSH_MSG_CHANNEL_CLOSE => {
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
let our_channel = payload.u32()?;
let channel = self.channel(our_channel)?;
if !channel.we_closed {
let mut close = Writer::new();
close.u8(Packet::SSH_MSG_CHANNEL_CLOSE);
close.u32(channel.peer_channel);
self.packets_to_send.push_back(Packet {
payload: close.finish(),
});
}
self.channels.remove(our_channel as usize);
debug!("Channel has been closed");
} }
Packet::SSH_MSG_CHANNEL_REQUEST => { Packet::SSH_MSG_CHANNEL_REQUEST => {
let our_channel = payload.u32()?; let our_channel = payload.u32()?;

View file

@ -21,7 +21,10 @@ use x25519_dalek::{EphemeralSecret, PublicKey};
pub use packet::Msg; pub use packet::Msg;
#[derive(Debug)] #[derive(Debug)]
pub enum SshError { pub enum SshStatus {
/// The client has sent a disconnect request, close the connection.
/// This is not an error.
Disconnect,
/// The client did something wrong. /// The client did something wrong.
/// The connection should be closed and a notice may be logged, /// The connection should be closed and a notice may be logged,
/// but this does not require operator intervention. /// but this does not require operator intervention.
@ -32,9 +35,9 @@ pub enum SshError {
ServerError(eyre::Report), ServerError(eyre::Report),
} }
pub type Result<T, E = SshError> = std::result::Result<T, E>; pub type Result<T, E = SshStatus> = std::result::Result<T, E>;
impl From<eyre::Report> for SshError { impl From<eyre::Report> for SshStatus {
fn from(value: eyre::Report) -> Self { fn from(value: eyre::Report) -> Self {
Self::ServerError(value) Self::ServerError(value)
} }
@ -140,6 +143,23 @@ impl ServerConnection {
while let Some(packet) = self.packet_transport.recv_next_packet() { while let Some(packet) = self.packet_transport.recv_next_packet() {
trace!(packet_type = ?packet.payload.get(0), packet_len = ?packet.payload.len(), "Received packet"); trace!(packet_type = ?packet.payload.get(0), packet_len = ?packet.payload.len(), "Received packet");
// Handle some packets ignoring the state.
match packet.payload.get(0).copied() {
Some(Packet::SSH_MSG_DISCONNECT) => {
// <https://datatracker.ietf.org/doc/html/rfc4253#section-11.1>
let mut disconnect = Parser::new(&packet.payload[1..]);
let reason = disconnect.u32()?;
let description = disconnect.utf8_string()?;
let _language_tag = disconnect.utf8_string()?;
info!(?reason, ?description, "Client disconnecting");
return Ok(());
}
_ => {}
}
match &mut self.state { match &mut self.state {
ServerState::ProtoExchange { .. } => unreachable!("handled above"), ServerState::ProtoExchange { .. } => unreachable!("handled above"),
ServerState::KeyExchangeInit { ServerState::KeyExchangeInit {
@ -407,7 +427,7 @@ impl ServerConnection {
let mut banner = Writer::new(); let mut banner = Writer::new();
banner.u8(Packet::SSH_MSG_USERAUTH_BANNER); banner.u8(Packet::SSH_MSG_USERAUTH_BANNER);
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"!! 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"); banner.string(b"");
self.packet_transport.queue_packet(Packet { self.packet_transport.queue_packet(Packet {
payload: banner.finish(), payload: banner.finish(),
}); });
@ -442,9 +462,9 @@ impl ServerConnection {
let mut failure = Writer::new(); let mut failure = Writer::new();
failure.u8(Packet::SSH_MSG_REQUEST_FAILURE); failure.u8(Packet::SSH_MSG_REQUEST_FAILURE);
//self.packet_transport.queue_packet(Packet { self.packet_transport.queue_packet(Packet {
// payload: failure.finish(), payload: failure.finish(),
//}); });
} }
_ => { _ => {
todo!("packet: {packet_type}"); todo!("packet: {packet_type}");
@ -485,7 +505,7 @@ const PRIVKEY_BYTES: &[u8; 32] = &[
macro_rules! client_error { macro_rules! client_error {
($($tt:tt)*) => { ($($tt:tt)*) => {
$crate::SshError::ClientError(::std::format!($($tt)*)) $crate::SshStatus::ClientError(::std::format!($($tt)*))
}; };
} }
use client_error; use client_error;