Skip to content

Commit

Permalink
Implement command and environment substituion in shell subsystem
Browse files Browse the repository at this point in the history
This change also implements foundations for stream redirection as well as
pipes.
  • Loading branch information
w4 committed Feb 4, 2024
1 parent 62439b2 commit 51d3c21
Show file tree
Hide file tree
Showing 12 changed files with 952 additions and 38 deletions.
114 changes: 114 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pisshoff-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pisshoff-types = { path = "../pisshoff-types" }

anyhow = "1.0"
async-trait = "0.1"
atoi = "2.0"
bitflags = "2.3"
bytes = "1.4"
clap = { version = "4.3", features = ["derive", "env", "cargo"] }
Expand All @@ -18,6 +19,7 @@ parking_lot = "0.12"
fastrand = "1.9"
itertools = "0.10"
nom = "7.1"
nom-supreme = "0.8"
nix = { version = "0.26", features = ["hostname"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand All @@ -30,6 +32,7 @@ toml = "0.7"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
yoke = { version = "0.7", features = ["derive"] }

[dev-dependencies]
mockall = "0.11"
Expand Down
2 changes: 1 addition & 1 deletion pisshoff-server/src/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn start_audit_writer(
_ = &mut shutdown_recv => {
shutdown = true;
}
_ = tokio::time::sleep(Duration::from_secs(5)), if !writer.buffer().is_empty() => {
() = tokio::time::sleep(Duration::from_secs(5)), if !writer.buffer().is_empty() => {
debug!("Flushing audits to disk");
writer.flush().await?;
}
Expand Down
65 changes: 49 additions & 16 deletions pisshoff-server/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ mod whoami;
use crate::server::{ConnectionState, ThrusshSession};
use async_trait::async_trait;
use itertools::Either;
use std::borrow::Cow;
use std::fmt::Debug;
use thrussh::{server::Session, ChannelId};
use thrussh::ChannelId;

#[derive(Debug)]
pub enum CommandResult<T> {
/// Wait for stdin
ReadStdin(T),
/// Exit process
Exit(u32),
/// Close session
Close(u32),
}

Expand Down Expand Up @@ -55,6 +59,34 @@ pub trait Command: Sized {
) -> CommandResult<Self>;
}

#[derive(PartialEq, Eq, Debug)]
pub struct PartialCommand<'a> {
exec: Option<Cow<'a, [u8]>>,
params: Vec<Cow<'a, [u8]>>,
}

impl<'a> PartialCommand<'a> {
pub fn new(exec: Option<Cow<'a, [u8]>>, params: Vec<Cow<'a, [u8]>>) -> Self {
Self { exec, params }
}

pub async fn into_concrete_command<S: ThrusshSession + Send>(
self,
connection: &mut ConnectionState,
channel: ChannelId,
session: &mut S,
) -> CommandResult<ConcreteCommand> {
// TODO: make commands take byte slices
let args = self
.params
.iter()
.map(|v| String::from_utf8_lossy(v).to_string())
.collect::<Vec<_>>();

ConcreteCommand::new(connection, self.exec.as_deref(), &args, channel, session).await
}
}

macro_rules! define_commands {
($($name:ident($ty:ty) = $command:expr),*) => {
#[derive(Debug, Clone)]
Expand All @@ -63,35 +95,36 @@ macro_rules! define_commands {
}

impl ConcreteCommand {
pub async fn new(
pub async fn new<S: ThrusshSession + Send>(
connection: &mut ConnectionState,
exec: Option<&[u8]>,
params: &[String],
channel: ChannelId,
session: &mut Session,
session: &mut S,
) -> CommandResult<Self> {
let Some(command) = params.get(0) else {
let Some(command) = exec else {
return CommandResult::Exit(0);
};

match command.as_str() {
$($command => <$ty as Command>::new(connection, &params[1..], channel, session).await.map(Self::$name),)*
match command {
$($command => <$ty as Command>::new(connection, &params, channel, session).await.map(Self::$name),)*
other => {
// TODO: fix stderr displaying out of order
session.data(
channel,
format!("bash: {other}: command not found\n").into(),
format!("bash: {}: command not found\n", String::from_utf8_lossy(other)).into(),
);
CommandResult::Exit(1)
}
}
}

pub async fn stdin(
pub async fn stdin<S: ThrusshSession + Send>(
self,
connection: &mut ConnectionState,
channel: ChannelId,
data: &[u8],
session: &mut Session,
session: &mut S,
) -> CommandResult<Self> {
match self {
$(Self::$name(cmd) => {
Expand All @@ -107,13 +140,13 @@ macro_rules! define_commands {
}

define_commands! {
Echo(echo::Echo) = "echo",
Exit(exit::Exit) = "exit",
Ls(ls::Ls) = "ls",
Pwd(pwd::Pwd) = "pwd",
Scp(scp::Scp) = "scp",
Uname(uname::Uname) = "uname",
Whoami(whoami::Whoami) = "whoami"
Echo(echo::Echo) = b"echo",
Exit(exit::Exit) = b"exit",
Ls(ls::Ls) = b"ls",
Pwd(pwd::Pwd) = b"pwd",
Scp(scp::Scp) = b"scp",
Uname(uname::Uname) = b"uname",
Whoami(whoami::Whoami) = b"whoami"
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down
9 changes: 8 additions & 1 deletion pisshoff-server/src/command/echo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ impl Command for Echo {
channel: ChannelId,
session: &mut S,
) -> CommandResult<Self> {
session.data(channel, format!("{}\n", params.iter().join(" ")).into());
let suffix = if session.redirected() { "" } else { "\n" };

session.data(
channel,
format!("{}{suffix}", params.iter().join(" ")).into(),
);

CommandResult::Exit(0)
}
Expand Down Expand Up @@ -58,6 +63,8 @@ mod test {
.with(always(), eq_string(output))
.returning(|_, _| ());

session.expect_redirected().returning(|| false);

let out = Echo::new(
&mut ConnectionState::mock(),
params
Expand Down
2 changes: 1 addition & 1 deletion pisshoff-server/src/command/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl Command for Exit {
_session: &mut S,
) -> CommandResult<Self> {
let exit_status = params
.get(0)
.first()
.map(String::as_str)
.map_or(Ok(0), u32::from_str)
.unwrap_or(2);
Expand Down
2 changes: 1 addition & 1 deletion pisshoff-server/src/command/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Command for Ls {
} else if params.len() == 1 {
connection
.file_system()
.ls(Some(params.get(0).unwrap()))
.ls(Some(params.first().unwrap()))
.join(" ")
} else {
let mut out = String::new();
Expand Down
Loading

0 comments on commit 51d3c21

Please sign in to comment.