many improvements

This commit is contained in:
nora 2024-08-12 16:46:27 +02:00
parent 7a129eba2e
commit 193f762ae9
6 changed files with 188 additions and 46 deletions

37
Cargo.lock generated
View file

@ -291,6 +291,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -567,6 +573,12 @@ dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -599,6 +611,18 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "sha2"
version = "0.10.8"
@ -796,6 +820,16 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
@ -806,12 +840,15 @@ dependencies = [
"nu-ansi-term",
"once_cell",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]

View file

@ -13,4 +13,4 @@ ssh-protocol = { path = "./ssh-protocol" }
tokio = { version = "1.39.2", features = ["full"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] }

View file

@ -8,7 +8,7 @@ use tokio::{
use tracing::{debug, error, info, info_span, Instrument};
use ssh_protocol::{
connection::{ChannelOpen, ChannelOperationKind, ChannelRequestKind},
connection::{ChannelOpen, ChannelOperationKind, ChannelRequest},
transport::{self, ThreadRngRand},
ChannelUpdateKind, ServerConnection, SshStatus,
};
@ -16,11 +16,16 @@ use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() -> eyre::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.init();
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
if std::env::var("FAKESSH_JSON_LOGS").is_ok_and(|v| v != "0") {
tracing_subscriber::fmt()
.json()
.with_env_filter(env_filter)
.init();
} else {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
}
let addr = std::env::var("FAKESSH_LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0:2222".to_owned());
@ -43,7 +48,7 @@ async fn main() -> eyre::Result<()> {
error!(?err, "error handling connection");
}
info!(data = ?String::from_utf8_lossy(&total_sent_data), "Finished connection");
info!(stdin = ?String::from_utf8_lossy(&total_sent_data), "Finished connection");
}
.instrument(span),
);
@ -108,23 +113,47 @@ async fn handle_connection(
}
},
ChannelUpdateKind::Request(req) => {
match req.kind {
ChannelRequestKind::PtyReq { .. } => {}
ChannelRequestKind::Shell => {}
ChannelRequestKind::Exec { command } => {
if command == b"uname -s -v -n -r -m" {
state.do_operation(update.number.construct_op(ChannelOperationKind::Data(
b"Linux nixos 6.6.35 #1-NixOS SMP PREEMPT_DYNAMIC Fri Jun 21 12:38:50 UTC 2024 x86_64\r\n".to_vec()
)));
let success = update.number.construct_op(ChannelOperationKind::Success);
match req {
ChannelRequest::PtyReq { want_reply, .. } => {
if want_reply {
state.do_operation(success);
}
}
ChannelRequestKind::Env { .. } => {}
ChannelRequest::Shell { want_reply } => {
if want_reply {
state.do_operation(success);
}
}
ChannelRequest::Exec {
want_reply,
command,
} => {
if want_reply {
state.do_operation(success);
}
let result = execute_command(&command);
state.do_operation(
update
.number
.construct_op(ChannelOperationKind::Data(result.stdout)),
);
state.do_operation(update.number.construct_op(
ChannelOperationKind::Request(ChannelRequest::ExitStatus {
status: result.status,
}),
));
state.do_operation(
update.number.construct_op(ChannelOperationKind::Eof),
);
state.do_operation(
update.number.construct_op(ChannelOperationKind::Close),
);
}
ChannelRequest::ExitStatus { .. } => {}
ChannelRequest::Env { .. } => {}
};
if req.want_reply {
state.do_operation(
update.number.construct_op(ChannelOperationKind::Success),
);
}
}
ChannelUpdateKind::Data { data } => {
let is_eof = data.contains(&0x03 /*EOF, Ctrl-C*/);
@ -158,3 +187,46 @@ async fn handle_connection(
}
}
}
struct ProcessOutput {
status: u32,
stdout: Vec<u8>,
}
const UNAME_SVNRM: &[u8] =
b"Linux ubuntu 5.15.0-105-generic #115-Ubuntu SMP Mon Apr 15 09:52:04 UTC 2024 x86_64\r\n";
const UNAME_A: &[u8] =
b"Linux ubuntu 5.15.0-105-generic #115-Ubuntu SMP Mon Apr 15 09:52:04 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux\r\n";
const CPUINFO_UNAME_A: &[u8] = b" 4 AMD EPYC 7282 16-Core Processor\r\n\
Linux vps2 5.15.0-105-generic #115-Ubuntu SMP Mon Apr 15 09:52:04 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux\r\n";
fn execute_command(command: &[u8]) -> ProcessOutput {
let Ok(command) = std::str::from_utf8(command) else {
return ProcessOutput {
status: 1,
stdout: b"what the hell".to_vec(),
};
};
match command {
"uname -s -v -n -r -m" => ProcessOutput {
status: 0,
stdout: UNAME_SVNRM.to_vec(),
},
"uname -a" => ProcessOutput {
status: 0,
stdout: UNAME_A.to_vec(),
},
"cat /proc/cpuinfo|grep name|cut -f2 -d':'|uniq -c ; uname -a" => ProcessOutput {
status: 0,
stdout: CPUINFO_UNAME_A.to_vec(),
},
_ => {
let argv0 = command.split_ascii_whitespace().next().unwrap_or("");
ProcessOutput {
status: 127,
stdout: format!("bash: line 1: {argv0}: command not found\r\n").into_bytes(),
}
}
}
}

View file

@ -43,13 +43,10 @@ pub enum ChannelOpen {
Session,
}
pub struct ChannelRequest {
pub want_reply: bool,
pub kind: ChannelRequestKind,
}
pub enum ChannelRequestKind {
pub enum ChannelRequest {
PtyReq {
want_reply: bool,
term: String,
width_chars: u32,
height_rows: u32,
@ -57,14 +54,23 @@ pub enum ChannelRequestKind {
height_px: u32,
term_modes: Vec<u8>,
},
Shell,
Shell {
want_reply: bool,
},
Exec {
want_reply: bool,
command: Vec<u8>,
},
Env {
want_reply: bool,
name: String,
value: Vec<u8>,
},
ExitStatus {
status: u32,
},
}
impl ChannelNumber {
@ -85,6 +91,8 @@ pub enum ChannelOperationKind {
Success,
Failure,
Data(Vec<u8>),
Request(ChannelRequest),
Eof,
Close,
}
@ -216,7 +224,7 @@ impl ServerChannelsState {
let channel = self.channel(our_channel)?;
let peer_channel = channel.peer_channel;
let channel_request_kind = match request_type {
let channel_request = match request_type {
"pty-req" => {
let term = packet.utf8_string()?;
let width_chars = packet.u32()?;
@ -233,7 +241,8 @@ impl ServerChannelsState {
"Trying to open a terminal"
);
ChannelRequestKind::PtyReq {
ChannelRequest::PtyReq {
want_reply,
term: term.to_owned(),
width_chars,
height_rows,
@ -244,12 +253,13 @@ impl ServerChannelsState {
}
"shell" => {
info!(?our_channel, "Opening shell");
ChannelRequestKind::Shell
ChannelRequest::Shell { want_reply }
}
"exec" => {
let command = packet.string()?;
info!(?our_channel, command = ?String::from_utf8_lossy(command), "Executing command");
ChannelRequestKind::Exec {
ChannelRequest::Exec {
want_reply,
command: command.to_owned(),
}
}
@ -259,7 +269,8 @@ impl ServerChannelsState {
info!(?our_channel, ?name, value = ?String::from_utf8_lossy(value), "Setting environment variable");
ChannelRequestKind::Env {
ChannelRequest::Env {
want_reply,
name: name.to_owned(),
value: value.to_owned(),
}
@ -278,10 +289,7 @@ impl ServerChannelsState {
self.channel_updates.push_back(ChannelUpdate {
number: our_channel,
kind: ChannelUpdateKind::Request(ChannelRequest {
want_reply,
kind: channel_request_kind,
}),
kind: ChannelUpdateKind::Request(channel_request),
})
}
_ => {
@ -312,6 +320,27 @@ impl ServerChannelsState {
self.packets_to_send
.push_back(Packet::new_msg_channel_data(peer, &data));
}
ChannelOperationKind::Request(req) => {
let packet = match req {
ChannelRequest::PtyReq { .. } => todo!("pty-req"),
ChannelRequest::Shell { .. } => todo!("shell"),
ChannelRequest::Exec { .. } => todo!("exec"),
ChannelRequest::Env { .. } => todo!("env"),
ChannelRequest::ExitStatus { status } => {
Packet::new_msg_channel_request_exit_status(
peer,
b"exit-status",
false,
status,
)
}
};
self.packets_to_send.push_back(packet);
}
ChannelOperationKind::Eof => {
self.packets_to_send
.push_back(Packet::new_msg_channel_eof(peer));
}
ChannelOperationKind::Close => {
// <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>
self.packets_to_send

View file

@ -162,16 +162,17 @@ impl ServerConnection {
} => {
let kex = KeyExchangeInitPacket::parse(&packet.payload)?;
let require_algorithm =
|expected: &'static str, list: NameList<'_>| -> Result<&'static str> {
if list.iter().any(|alg| alg == expected) {
Ok(expected)
} else {
Err(client_error!(
let require_algorithm = |expected: &'static str,
list: NameList<'_>|
-> Result<&'static str> {
if list.iter().any(|alg| alg == expected) {
Ok(expected)
} else {
Err(client_error!(
"client does not supported algorithm {expected}. supported: {list:?}",
))
}
};
}
};
let key_algorithm = require_algorithm("curve25519-sha256", kex.kex_algorithms)?;
let server_host_key_algorithm =

View file

@ -88,6 +88,9 @@ ctors! {
fn new_msg_channel_eof(SSH_MSG_CHANNEL_EOF; recipient_channel: u32);
fn new_msg_channel_close(SSH_MSG_CHANNEL_CLOSE; recipient_channel: u32);
fn new_msg_channel_request_exit_status(SSH_MSG_CHANNEL_REQUEST; recipient_channel: u32, kind_exit_status: string, false_: bool, exit_status: u32);
fn new_msg_channel_success(SSH_MSG_CHANNEL_SUCCESS; recipient_channel: u32);
fn new_msg_channel_failure(SSH_MSG_CHANNEL_FAILURE; recipient_channel: u32);
}