Commit af8a1ffb authored by Thomas Gatzweiler's avatar Thomas Gatzweiler
Browse files

Implement channels

parent 8c911fec
......@@ -3,6 +3,7 @@ name = "redox-ssh"
version = "0.1.0"
dependencies = [
"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)",
"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)",
......
......@@ -29,6 +29,9 @@ rust-crypto = "^0.2"
rand = "^0.3"
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]
redox_syscall = "0.1"
......@@ -29,6 +29,7 @@ impl log::Log for StdErrLogger {
pub fn main() {
let mut verbose = false;
let mut foreground = false;
let key_pair = File::open("server.key").and_then(
|mut f| (ED25519.import)(&mut f),
......@@ -51,6 +52,7 @@ pub fn main() {
match arg.as_ref()
{
"-v" => verbose = true,
"-f" => foreground = true,
"-p" => {
config.port =
u16::from_str(
......@@ -68,6 +70,13 @@ pub fn main() {
}).unwrap();
}
if !foreground {
use ssh::sys::fork;
if fork() != 0 {
process::exit(0);
}
}
let server = Server::with_config(config);
if let Err(err) = server.run() {
......
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())
}
}
}
use std::collections::VecDeque;
use std::collections::{BTreeMap, VecDeque};
use std::io::{self, BufReader, Read, Write};
use std::net::TcpStream;
use std::sync::Arc;
use channel::{Channel, ChannelId, ChannelRequest};
use encryption::{AesCtr, Decryptor, Encryption};
use error::{ConnectionError, ConnectionResult as Result};
use key_exchange::{KexResult, KeyExchange};
......@@ -41,6 +42,7 @@ pub struct Connection {
mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>,
seq: (u32, u32),
tx_queue: VecDeque<Packet>,
channels: BTreeMap<ChannelId, Channel>,
}
impl<'a> Connection {
......@@ -55,6 +57,7 @@ impl<'a> Connection {
mac: None,
seq: (0, 0),
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
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)
-> io::Result<()> {
trace!("Sending packet {}: {:?}", self.seq.1, packet);
debug!("Sending packet {}: {:?}", self.seq.1, packet);
let packet = packet.to_raw()?;
......@@ -247,7 +250,10 @@ impl<'a> Connection {
let mut reader = packet.reader();
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);
res.with_writer(&|w| {
......@@ -292,64 +298,63 @@ impl<'a> Connection {
fn channel_open(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader();
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 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);
res.with_writer(&|w| {
w.write_uint32(sender_channel)?;
w.write_uint32(0)?;
w.write_uint32(window_size)?;
w.write_uint32(max_packet_size)?;
w.write_uint32(peer_id)?;
w.write_uint32(channel.id())?;
w.write_uint32(channel.window_size())?;
w.write_uint32(channel.max_packet_size())?;
Ok(())
})?;
debug!(
"Channel Open {:?}, {:?}, {:?}, {:?}",
channel_type,
sender_channel,
window_size,
max_packet_size
);
debug!("Open {:?}", channel);
self.channels.insert(id, channel);
Ok(Some(res))
}
fn channel_request(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader();
let channel = reader.read_uint32()?;
let request = reader.read_utf8()?;
let channel_id = reader.read_uint32()?;
let name = reader.read_utf8()?;
let want_reply = reader.read_bool()?;
debug!(
"Channel Request {:?}, {:?}, {:?}",
channel,
request,
want_reply
);
if request == "pty-req" {
let term = reader.read_utf8()?;
let char_width = reader.read_uint32()?;
let row_height = reader.read_uint32()?;
let pixel_width = reader.read_uint32()?;
let pixel_height = reader.read_uint32()?;
let modes = reader.read_string()?;
debug!(
"PTY request: {:?} {:?} {:?} {:?} {:?} {:?}",
term,
char_width,
row_height,
pixel_width,
pixel_height,
modes
);
}
let request = match &*name
{
"pty-req" => Some(ChannelRequest::Pty {
term: reader.read_utf8()?,
char_width: reader.read_uint32()?,
row_height: reader.read_uint32()?,
pixel_width: reader.read_uint32()?,
pixel_height: reader.read_uint32()?,
modes: reader.read_string()?,
}),
"shell" => Some(ChannelRequest::Shell),
_ => None,
};
if request == "shell" {
debug!("Shell request");
if let Some(request) = request {
let mut channel = self.channels.get_mut(&channel_id).unwrap();
channel.request(request);
}
else {
warn!("Unkown channel request {}", name);
}
if want_reply {
......@@ -364,24 +369,28 @@ impl<'a> Connection {
fn channel_data(&mut self, packet: Packet) -> Result<Option<Packet>> {
let mut reader = packet.reader();
let channel = reader.read_uint32()?;
let channel_id = reader.read_uint32()?;
let data = reader.read_string()?;
let mut res = Packet::new(MessageType::ChannelData);
res.with_writer(&|w| {
w.write_uint32(0)?;
w.write_bytes(data.as_slice())?;
Ok(())
})?;
let mut channel = self.channels.get_mut(&channel_id).unwrap();
channel.data(data.as_slice())?;
debug!(
"Channel {} Data ({} bytes): {:?}",
channel,
data.len(),
data
);
let data = channel.read()?;
if data.len() > 0 {
let mut res = Packet::new(MessageType::ChannelData);
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>> {
......
......@@ -15,12 +15,10 @@ impl AesCtr {
impl Encryption for AesCtr {
fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) {
trace!("Encrypting {} -> {}", data.len(), buf.len());
self.cipher.process(data, buf);
}
fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) {
trace!("Decrypting {} -> {}", data.len(), buf.len());
self.cipher.process(data, buf);
}
}
......@@ -4,6 +4,10 @@ extern crate crypto;
extern crate num_bigint;
#[macro_use]
extern crate log;
#[cfg(target_os = "redox")]
extern crate syscall;
#[cfg(not(target_os = "redox"))]
extern crate libc;
mod error;
mod algorithm;
......@@ -13,8 +17,17 @@ mod connection;
mod key_exchange;
mod encryption;
mod mac;
mod channel;
pub mod public_key;
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};
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]))
}),
)
}
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)
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment