1
0
Fork 0
mirror of https://gitlab.redox-os.org/CoffeeCode/redox-ssh.git synced 2025-12-28 17:02:19 +01:00

Implement channels

This commit is contained in:
Thomas Gatzweiler 2017-07-20 17:25:46 +02:00
parent 8c911fec23
commit af8a1ffb77
9 changed files with 318 additions and 59 deletions

1
Cargo.lock generated
View file

@ -3,6 +3,7 @@ name = "redox-ssh"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.1.39 (git+https://github.com/rust-num/num)", "num-bigint 0.1.39 (git+https://github.com/rust-num/num)",
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -29,6 +29,9 @@ rust-crypto = "^0.2"
rand = "^0.3" rand = "^0.3"
num-bigint = { git = "https://github.com/rust-num/num" } num-bigint = { git = "https://github.com/rust-num/num" }
[target.'cfg(not(target_os = "redox"))'.dependencies]
libc = "^0.2.26"
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
redox_syscall = "0.1" redox_syscall = "0.1"

View file

@ -29,6 +29,7 @@ impl log::Log for StdErrLogger {
pub fn main() { pub fn main() {
let mut verbose = false; let mut verbose = false;
let mut foreground = false;
let key_pair = File::open("server.key").and_then( let key_pair = File::open("server.key").and_then(
|mut f| (ED25519.import)(&mut f), |mut f| (ED25519.import)(&mut f),
@ -51,6 +52,7 @@ pub fn main() {
match arg.as_ref() match arg.as_ref()
{ {
"-v" => verbose = true, "-v" => verbose = true,
"-f" => foreground = true,
"-p" => { "-p" => {
config.port = config.port =
u16::from_str( u16::from_str(
@ -68,6 +70,13 @@ pub fn main() {
}).unwrap(); }).unwrap();
} }
if !foreground {
use ssh::sys::fork;
if fork() != 0 {
process::exit(0);
}
}
let server = Server::with_config(config); let server = Server::with_config(config);
if let Err(err) = server.run() { if let Err(err) = server.run() {

131
src/channel.rs Normal file
View file

@ -0,0 +1,131 @@
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{self, Stdio};
use sys::{before_exec, getpty};
pub type ChannelId = u32;
#[derive(Debug)]
pub struct Channel {
id: ChannelId,
peer_id: ChannelId,
process: Option<process::Child>,
pty: Option<(RawFd, PathBuf)>,
master: Option<File>,
stdio: Option<(File, File, File)>,
window_size: u32,
peer_window_size: u32,
max_packet_size: u32,
}
#[derive(Debug)]
pub enum ChannelRequest {
Pty {
term: String,
char_width: u32,
row_height: u32,
pixel_width: u32,
pixel_height: u32,
modes: Vec<u8>,
},
Shell,
}
impl Channel {
pub fn new(
id: ChannelId, peer_id: ChannelId, peer_window_size: u32,
max_packet_size: u32
) -> Channel {
Channel {
id: id,
peer_id: peer_id,
process: None,
master: None,
pty: None,
stdio: None,
window_size: peer_window_size,
peer_window_size: peer_window_size,
max_packet_size: max_packet_size,
}
}
pub fn id(&self) -> ChannelId {
self.id
}
pub fn window_size(&self) -> u32 {
self.window_size
}
pub fn max_packet_size(&self) -> u32 {
self.max_packet_size
}
pub fn request(&mut self, request: ChannelRequest) {
match request
{
ChannelRequest::Pty { .. } => {
let (master_fd, tty_path) = getpty();
let stdin = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
let stdout = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
let stderr = OpenOptions::new()
.read(true)
.write(true)
.open(&tty_path)
.unwrap();
self.stdio = Some((stdin, stdout, stderr));
self.master = Some(unsafe { File::from_raw_fd(master_fd) });
}
ChannelRequest::Shell => {
if let Some((ref stdin, ref stdout, ref stderr)) = self.stdio {
process::Command::new("login")
.stdin(unsafe { Stdio::from_raw_fd(stdin.as_raw_fd()) })
.stdout(
unsafe { Stdio::from_raw_fd(stdout.as_raw_fd()) },
)
.stderr(
unsafe { Stdio::from_raw_fd(stderr.as_raw_fd()) },
)
.before_exec(|| before_exec())
.spawn()
.unwrap();
}
}
}
debug!("Channel Request: {:?}", request);
}
pub fn data(&mut self, data: &[u8]) -> io::Result<()> {
if let Some(ref mut master) = self.master {
master.write_all(data)?;
master.flush()
}
else {
Ok(())
}
}
pub fn read(&mut self) -> io::Result<Vec<u8>> {
if let Some(ref mut master) = self.master {
let mut buf = [0; 4096];
let count = master.read(&mut buf)?;
Ok(buf[0..count].to_vec())
}
else {
Ok(b"".to_vec())
}
}
}

View file

@ -1,8 +1,9 @@
use std::collections::VecDeque; use std::collections::{BTreeMap, VecDeque};
use std::io::{self, BufReader, Read, Write}; use std::io::{self, BufReader, Read, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::sync::Arc; use std::sync::Arc;
use channel::{Channel, ChannelId, ChannelRequest};
use encryption::{AesCtr, Decryptor, Encryption}; use encryption::{AesCtr, Decryptor, Encryption};
use error::{ConnectionError, ConnectionResult as Result}; use error::{ConnectionError, ConnectionResult as Result};
use key_exchange::{KexResult, KeyExchange}; use key_exchange::{KexResult, KeyExchange};
@ -41,6 +42,7 @@ pub struct Connection {
mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>, mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>,
seq: (u32, u32), seq: (u32, u32),
tx_queue: VecDeque<Packet>, tx_queue: VecDeque<Packet>,
channels: BTreeMap<ChannelId, Channel>,
} }
impl<'a> Connection { impl<'a> Connection {
@ -55,6 +57,7 @@ impl<'a> Connection {
mac: None, mac: None,
seq: (0, 0), seq: (0, 0),
tx_queue: VecDeque::new(), tx_queue: VecDeque::new(),
channels: BTreeMap::new(),
} }
} }
@ -103,7 +106,7 @@ impl<'a> Connection {
} }
} }
trace!("Packet {} received: {:?}", self.seq.0, packet); debug!("Packet {} received: {:?}", self.seq.0, packet);
// Count up the received packet sequence number // Count up the received packet sequence number
self.seq.0 = self.seq.0.wrapping_add(1); self.seq.0 = self.seq.0.wrapping_add(1);
@ -113,7 +116,7 @@ impl<'a> Connection {
fn send(&mut self, mut stream: &mut Write, packet: Packet) fn send(&mut self, mut stream: &mut Write, packet: Packet)
-> io::Result<()> { -> io::Result<()> {
trace!("Sending packet {}: {:?}", self.seq.1, packet); debug!("Sending packet {}: {:?}", self.seq.1, packet);
let packet = packet.to_raw()?; let packet = packet.to_raw()?;
@ -247,7 +250,10 @@ impl<'a> Connection {
let mut reader = packet.reader(); let mut reader = packet.reader();
let name = reader.read_string()?; let name = reader.read_string()?;
trace!("{:?}", ::std::str::from_utf8(&name.as_slice()).unwrap()); trace!(
"Service Request {:?}",
::std::str::from_utf8(&name.as_slice()).unwrap()
);
let mut res = Packet::new(MessageType::ServiceAccept); let mut res = Packet::new(MessageType::ServiceAccept);
res.with_writer(&|w| { res.with_writer(&|w| {
@ -292,64 +298,63 @@ impl<'a> Connection {
fn channel_open(&mut self, packet: Packet) -> Result<Option<Packet>> { fn channel_open(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader(); let mut reader = packet.reader();
let channel_type = reader.read_utf8()?; let channel_type = reader.read_utf8()?;
let sender_channel = reader.read_uint32()?; let peer_id = reader.read_uint32()?;
let window_size = reader.read_uint32()?; let window_size = reader.read_uint32()?;
let max_packet_size = reader.read_uint32()?; let max_packet_size = reader.read_uint32()?;
let id = if let Some((id, chan)) = self.channels.iter().next_back() {
id + 1
}
else {
0
};
let channel = Channel::new(id, peer_id, window_size, max_packet_size);
let mut res = Packet::new(MessageType::ChannelOpenConfirmation); let mut res = Packet::new(MessageType::ChannelOpenConfirmation);
res.with_writer(&|w| { res.with_writer(&|w| {
w.write_uint32(sender_channel)?; w.write_uint32(peer_id)?;
w.write_uint32(0)?; w.write_uint32(channel.id())?;
w.write_uint32(window_size)?; w.write_uint32(channel.window_size())?;
w.write_uint32(max_packet_size)?; w.write_uint32(channel.max_packet_size())?;
Ok(()) Ok(())
})?; })?;
debug!( debug!("Open {:?}", channel);
"Channel Open {:?}, {:?}, {:?}, {:?}",
channel_type, self.channels.insert(id, channel);
sender_channel,
window_size,
max_packet_size
);
Ok(Some(res)) Ok(Some(res))
} }
fn channel_request(&mut self, packet: Packet) -> Result<Option<Packet>> { fn channel_request(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader(); let mut reader = packet.reader();
let channel = reader.read_uint32()?; let channel_id = reader.read_uint32()?;
let request = reader.read_utf8()?; let name = reader.read_utf8()?;
let want_reply = reader.read_bool()?; let want_reply = reader.read_bool()?;
debug!(
"Channel Request {:?}, {:?}, {:?}",
channel,
request,
want_reply
);
if request == "pty-req" { let request = match &*name
let term = reader.read_utf8()?; {
let char_width = reader.read_uint32()?; "pty-req" => Some(ChannelRequest::Pty {
let row_height = reader.read_uint32()?; term: reader.read_utf8()?,
let pixel_width = reader.read_uint32()?; char_width: reader.read_uint32()?,
let pixel_height = reader.read_uint32()?; row_height: reader.read_uint32()?,
let modes = reader.read_string()?; pixel_width: reader.read_uint32()?,
pixel_height: reader.read_uint32()?,
modes: reader.read_string()?,
}),
"shell" => Some(ChannelRequest::Shell),
_ => None,
};
debug!(
"PTY request: {:?} {:?} {:?} {:?} {:?} {:?}", if let Some(request) = request {
term, let mut channel = self.channels.get_mut(&channel_id).unwrap();
char_width, channel.request(request);
row_height,
pixel_width,
pixel_height,
modes
);
} }
else {
if request == "shell" { warn!("Unkown channel request {}", name);
debug!("Shell request");
} }
if want_reply { if want_reply {
@ -364,24 +369,28 @@ impl<'a> Connection {
fn channel_data(&mut self, packet: Packet) -> Result<Option<Packet>> { fn channel_data(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader(); let mut reader = packet.reader();
let channel = reader.read_uint32()?; let channel_id = reader.read_uint32()?;
let data = reader.read_string()?; let data = reader.read_string()?;
let mut res = Packet::new(MessageType::ChannelData); let mut channel = self.channels.get_mut(&channel_id).unwrap();
res.with_writer(&|w| { channel.data(data.as_slice())?;
w.write_uint32(0)?;
w.write_bytes(data.as_slice())?;
Ok(())
})?;
debug!( let data = channel.read()?;
"Channel {} Data ({} bytes): {:?}",
channel, if data.len() > 0 {
data.len(), let mut res = Packet::new(MessageType::ChannelData);
data res.with_writer(&|w| {
); w.write_uint32(0)?;
w.write_bytes(data.as_slice())?;
Ok(())
})?;
Ok(Some(res))
}
else {
Ok(None)
}
Ok(Some(res))
} }
fn kex_init(&mut self, packet: Packet) -> Result<Option<Packet>> { fn kex_init(&mut self, packet: Packet) -> Result<Option<Packet>> {

View file

@ -15,12 +15,10 @@ impl AesCtr {
impl Encryption for AesCtr { impl Encryption for AesCtr {
fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) { fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) {
trace!("Encrypting {} -> {}", data.len(), buf.len());
self.cipher.process(data, buf); self.cipher.process(data, buf);
} }
fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) { fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) {
trace!("Decrypting {} -> {}", data.len(), buf.len());
self.cipher.process(data, buf); self.cipher.process(data, buf);
} }
} }

View file

@ -4,6 +4,10 @@ extern crate crypto;
extern crate num_bigint; extern crate num_bigint;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[cfg(target_os = "redox")]
extern crate syscall;
#[cfg(not(target_os = "redox"))]
extern crate libc;
mod error; mod error;
mod algorithm; mod algorithm;
@ -13,8 +17,17 @@ mod connection;
mod key_exchange; mod key_exchange;
mod encryption; mod encryption;
mod mac; mod mac;
mod channel;
pub mod public_key; pub mod public_key;
pub mod server; pub mod server;
#[cfg(target_os = "redox")]
#[path = "sys/redox.rs"]
pub mod sys;
#[cfg(not(target_os = "redox"))]
#[path = "sys/unix.rs"]
pub mod sys;
pub use self::server::{Server, ServerConfig}; pub use self::server::{Server, ServerConfig};

31
src/sys/redox.rs Normal file
View file

@ -0,0 +1,31 @@
use std::io::Result;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
pub fn before_exec() -> Result<()> {
Ok(())
}
pub fn fork() -> usize {
extern crate syscall;
unsafe { syscall::clone(0).unwrap() }
}
pub fn getpty() -> (RawFd, PathBuf) {
use syscall;
let master = syscall::open(
"pty:",
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK,
).unwrap();
let mut buf: [u8; 4096] = [0; 4096];
let count = syscall::fpath(master, &mut buf).unwrap();
(
master,
PathBuf::from(unsafe {
String::from_utf8_unchecked(Vec::from(&buf[..count]))
}),
)
}

64
src/sys/unix.rs Normal file
View file

@ -0,0 +1,64 @@
use std::io::Result;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
pub fn before_exec() -> Result<()> {
use libc;
unsafe {
libc::setsid();
libc::ioctl(0, libc::TIOCSCTTY, 1);
}
Ok(())
}
pub fn fork() -> usize {
extern crate libc;
unsafe { libc::fork() as usize }
}
pub fn getpty() -> (RawFd, PathBuf) {
use libc;
use std::ffi::CStr;
use std::fs::OpenOptions;
use std::io::Error;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::IntoRawFd;
const TIOCPKT: libc::c_ulong = 0x5420;
extern "C" {
fn ptsname(fd: libc::c_int) -> *const libc::c_char;
fn grantpt(fd: libc::c_int) -> libc::c_int;
fn unlockpt(fd: libc::c_int) -> libc::c_int;
fn ioctl(fd: libc::c_int, request: libc::c_ulong, ...) -> libc::c_int;
}
let master_fd = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NONBLOCK)
.open("/dev/ptmx")
.unwrap()
.into_raw_fd();
unsafe {
let mut flag: libc::c_int = 1;
if ioctl(master_fd, TIOCPKT, &mut flag as *mut libc::c_int) < 0 {
panic!("ioctl: {:?}", Error::last_os_error());
}
if grantpt(master_fd) < 0 {
panic!("grantpt: {:?}", Error::last_os_error());
}
if unlockpt(master_fd) < 0 {
panic!("unlockpt: {:?}", Error::last_os_error());
}
}
let tty_path = unsafe {
PathBuf::from(
CStr::from_ptr(ptsname(master_fd))
.to_string_lossy()
.into_owned(),
)
};
(master_fd, tty_path)
}