mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 16:35:06 +01:00
disconnect after ctrl c
This commit is contained in:
parent
bc7e12e50b
commit
b75db7c21f
3 changed files with 78 additions and 13 deletions
|
|
@ -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(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()?;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue