mirror of
https://gitlab.redox-os.org/CoffeeCode/redox-ssh.git
synced 2025-12-28 17:02:19 +01:00
Implement ConnectionError
This commit is contained in:
parent
cf7644a5ed
commit
86d7d48a29
7 changed files with 104 additions and 56 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use error::{ConnectionError, ConnectionResult};
|
||||||
|
|
||||||
/// Slice of implemented key exchange algorithms, ordered by preference
|
/// Slice of implemented key exchange algorithms, ordered by preference
|
||||||
pub static KEY_EXCHANGE: &[KeyExchangeAlgorithm] =
|
pub static KEY_EXCHANGE: &[KeyExchangeAlgorithm] =
|
||||||
&[
|
&[
|
||||||
|
|
@ -26,13 +28,16 @@ pub static COMPRESSION: &[CompressionAlgorithm] =
|
||||||
&[CompressionAlgorithm::None, CompressionAlgorithm::Zlib];
|
&[CompressionAlgorithm::None, CompressionAlgorithm::Zlib];
|
||||||
|
|
||||||
/// Find the best matching algorithm
|
/// Find the best matching algorithm
|
||||||
pub fn negotiate<A: PartialEq + Copy>(server: &[A], client: &[A]) -> Option<A> {
|
pub fn negotiate<A: PartialEq + Copy>(
|
||||||
|
server: &[A],
|
||||||
|
client: &[A],
|
||||||
|
) -> ConnectionResult<A> {
|
||||||
for algorithm in client.iter() {
|
for algorithm in client.iter() {
|
||||||
if server.iter().any(|a| a == algorithm) {
|
if server.iter().any(|a| a == algorithm) {
|
||||||
return Some(*algorithm);
|
return Ok(*algorithm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
Err(ConnectionError::NegotiationError)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::io::{self, BufRead, BufReader, Read, Write};
|
use std::io::{self, BufRead, BufReader, Read, Write};
|
||||||
|
|
||||||
|
use error::{ConnectionError, ConnectionResult};
|
||||||
use key_exchange::{self, KeyExchange, KeyExchangeResult};
|
use key_exchange::{self, KeyExchange, KeyExchangeResult};
|
||||||
use message::MessageType;
|
use message::MessageType;
|
||||||
use packet::{Packet, ReadPacketExt, WritePacketExt};
|
use packet::{Packet, ReadPacketExt, WritePacketExt};
|
||||||
use public_key::KeyPair;
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum ConnectionState {
|
enum ConnectionState {
|
||||||
|
|
@ -42,7 +42,7 @@ impl<W: Write> Connection<W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, mut stream: &mut Read) -> io::Result<()> {
|
pub fn run(&mut self, mut stream: &mut Read) -> ConnectionResult<()> {
|
||||||
self.stream.write(self.my_id.as_bytes())?;
|
self.stream.write(self.my_id.as_bytes())?;
|
||||||
self.stream.flush()?;
|
self.stream.flush()?;
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ impl<W: Write> Connection<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let packet = Packet::read_from(&mut stream).unwrap();
|
let packet = Packet::read_from(&mut stream)?;
|
||||||
println!("packet: {:?}", packet);
|
println!("packet: {:?}", packet);
|
||||||
self.process(&packet);
|
self.process(&packet);
|
||||||
}
|
}
|
||||||
|
|
@ -73,62 +73,64 @@ impl<W: Write> Connection<W> {
|
||||||
Ok(id.trim_right().to_owned())
|
Ok(id.trim_right().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, packet: &Packet) {
|
pub fn process(&mut self, packet: &Packet) -> ConnectionResult<()> {
|
||||||
match packet.msg_type()
|
match packet.msg_type()
|
||||||
{
|
{
|
||||||
MessageType::KexInit => {
|
MessageType::KexInit => {
|
||||||
println!("Starting Key Exchange!");
|
println!("Starting Key Exchange!");
|
||||||
self.kex_init(packet);
|
self.kex_init(packet)
|
||||||
}
|
}
|
||||||
MessageType::KeyExchange(_) => {
|
MessageType::KeyExchange(_) => {
|
||||||
if let Some(ref mut kex) = self.key_exchange {
|
let ref mut kex = self.key_exchange.as_mut().ok_or(
|
||||||
match kex.process(packet)
|
ConnectionError::KeyExchangeError,
|
||||||
{
|
)?;
|
||||||
KeyExchangeResult::Ok(Some(packet)) => {
|
|
||||||
packet.write_to(&mut self.stream);
|
match kex.process(packet)
|
||||||
|
{
|
||||||
|
KeyExchangeResult::Ok(Some(packet)) => {
|
||||||
|
packet.write_to(&mut self.stream)?;
|
||||||
|
}
|
||||||
|
KeyExchangeResult::Error(Some(packet)) => {
|
||||||
|
packet.write_to(&mut self.stream)?;
|
||||||
|
}
|
||||||
|
KeyExchangeResult::Done(packet) => {
|
||||||
|
if let Some(packet) = packet {
|
||||||
|
packet.write_to(&mut self.stream)?;
|
||||||
}
|
}
|
||||||
KeyExchangeResult::Error(Some(packet)) => {
|
self.state = ConnectionState::Established;
|
||||||
packet.write_to(&mut self.stream);
|
}
|
||||||
}
|
KeyExchangeResult::Ok(None) |
|
||||||
KeyExchangeResult::Done(Some(packet)) => {
|
KeyExchangeResult::Error(None) => {}
|
||||||
packet.write_to(&mut self.stream);
|
};
|
||||||
}
|
Ok(())
|
||||||
KeyExchangeResult::Ok(None) |
|
|
||||||
KeyExchangeResult::Error(None) |
|
|
||||||
KeyExchangeResult::Done(None) => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
warn!("Received KeyExchange packet without KexInit");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unhandled packet: {:?}", packet);
|
println!("Unhandled packet: {:?}", packet);
|
||||||
|
Err(ConnectionError::KeyExchangeError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kex_init(&mut self, packet: &Packet) {
|
pub fn kex_init(&mut self, packet: &Packet) -> ConnectionResult<()> {
|
||||||
use algorithm::*;
|
use algorithm::*;
|
||||||
let mut reader = packet.reader();
|
let mut reader = packet.reader();
|
||||||
|
|
||||||
let cookie = reader.read_bytes(16);
|
let _ = reader.read_bytes(16)?; // Cookie. Throw it away.
|
||||||
let kex_algos = reader.read_enum_list::<KeyExchangeAlgorithm>();
|
let kex_algos = reader.read_enum_list::<KeyExchangeAlgorithm>()?;
|
||||||
let srv_host_key_algos = reader.read_enum_list::<PublicKeyAlgorithm>();
|
let srv_host_key_algos = reader.read_enum_list::<PublicKeyAlgorithm>()?;
|
||||||
let enc_algos_c2s = reader.read_enum_list::<EncryptionAlgorithm>();
|
let enc_algos_c2s = reader.read_enum_list::<EncryptionAlgorithm>()?;
|
||||||
let enc_algos_s2c = reader.read_enum_list::<EncryptionAlgorithm>();
|
let enc_algos_s2c = reader.read_enum_list::<EncryptionAlgorithm>()?;
|
||||||
let mac_algos_c2s = reader.read_enum_list::<MacAlgorithm>();
|
let mac_algos_c2s = reader.read_enum_list::<MacAlgorithm>()?;
|
||||||
let mac_algos_s2c = reader.read_enum_list::<MacAlgorithm>();
|
let mac_algos_s2c = reader.read_enum_list::<MacAlgorithm>()?;
|
||||||
let comp_algos_c2s = reader.read_enum_list::<CompressionAlgorithm>();
|
let comp_algos_c2s = reader.read_enum_list::<CompressionAlgorithm>()?;
|
||||||
let comp_algos_s2c = reader.read_enum_list::<CompressionAlgorithm>();
|
let comp_algos_s2c = reader.read_enum_list::<CompressionAlgorithm>()?;
|
||||||
|
|
||||||
let kex_algo = negotiate(KEY_EXCHANGE, kex_algos.unwrap().as_slice());
|
let kex_algo = negotiate(KEY_EXCHANGE, kex_algos.as_slice())?;
|
||||||
let srv_host_key_algo =
|
let srv_host_key_algo =
|
||||||
negotiate(HOST_KEY, srv_host_key_algos.unwrap().as_slice());
|
negotiate(HOST_KEY, srv_host_key_algos.as_slice())?;
|
||||||
let enc_algo = negotiate(ENCRYPTION, enc_algos_s2c.unwrap().as_slice());
|
let enc_algo = negotiate(ENCRYPTION, enc_algos_s2c.as_slice())?;
|
||||||
let mac_algo = negotiate(MAC, mac_algos_s2c.unwrap().as_slice());
|
let mac_algo = negotiate(MAC, mac_algos_s2c.as_slice())?;
|
||||||
let comp_algo =
|
let comp_algo = negotiate(COMPRESSION, comp_algos_s2c.as_slice())?;
|
||||||
negotiate(COMPRESSION, comp_algos_s2c.unwrap().as_slice());
|
|
||||||
|
|
||||||
println!("Negotiated Kex Algorithm: {:?}", kex_algo);
|
println!("Negotiated Kex Algorithm: {:?}", kex_algo);
|
||||||
println!("Negotiated Host Key Algorithm: {:?}", srv_host_key_algo);
|
println!("Negotiated Host Key Algorithm: {:?}", srv_host_key_algo);
|
||||||
|
|
@ -137,7 +139,7 @@ impl<W: Write> Connection<W> {
|
||||||
println!("Negotiated Comp Algorithm: {:?}", comp_algo);
|
println!("Negotiated Comp Algorithm: {:?}", comp_algo);
|
||||||
|
|
||||||
use rand::{OsRng, Rng};
|
use rand::{OsRng, Rng};
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new()?;
|
||||||
let cookie: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
|
let cookie: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
|
||||||
|
|
||||||
let mut packet = Packet::new(MessageType::KexInit);
|
let mut packet = Packet::new(MessageType::KexInit);
|
||||||
|
|
@ -156,10 +158,11 @@ impl<W: Write> Connection<W> {
|
||||||
w.write_bool(false)?;
|
w.write_bool(false)?;
|
||||||
w.write_uint32(0)?;
|
w.write_uint32(0)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
})?;
|
||||||
|
|
||||||
self.state = ConnectionState::KeyExchange;
|
self.state = ConnectionState::KeyExchange;
|
||||||
self.key_exchange = Some(Box::new(key_exchange::Curve25519::new()));
|
self.key_exchange = Some(Box::new(key_exchange::Curve25519::new()));
|
||||||
packet.write_to(&mut self.stream);
|
packet.write_to(&mut self.stream)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
src/error.rs
Normal file
39
src/error.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
use std::convert::From;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub type ConnectionResult<T> = Result<T, ConnectionError>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConnectionError {
|
||||||
|
IoError(io::Error),
|
||||||
|
ProtocolError,
|
||||||
|
NegotiationError,
|
||||||
|
KeyExchangeError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConnectionError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "connection error: {}", (self as &Error).description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ConnectionError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
use self::ConnectionError::*;
|
||||||
|
match self
|
||||||
|
{
|
||||||
|
&IoError(_) => "io error",
|
||||||
|
&ProtocolError => "protocol error",
|
||||||
|
&NegotiationError => "negotiation error",
|
||||||
|
&KeyExchangeError => "key exchange error",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for ConnectionError {
|
||||||
|
fn from(err: io::Error) -> ConnectionError {
|
||||||
|
ConnectionError::IoError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ extern crate num_bigint;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
mod error;
|
||||||
mod algorithm;
|
mod algorithm;
|
||||||
mod packet;
|
mod packet;
|
||||||
mod message;
|
mod message;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||||
pub enum MessageType {
|
pub enum MessageType {
|
||||||
Disconnect,
|
Disconnect,
|
||||||
|
|
|
||||||
|
|
@ -109,11 +109,11 @@ pub trait ReadPacketExt: ReadBytesExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_utf8(&mut self) -> Result<String> {
|
fn read_utf8(&mut self) -> Result<String> {
|
||||||
Ok(
|
str::from_utf8(self.read_string()?.as_slice())
|
||||||
str::from_utf8(self.read_string()?.as_slice())
|
.map(|s| s.to_owned())
|
||||||
.unwrap_or("")
|
.map_err(|_| {
|
||||||
.to_owned(),
|
io::Error::new(io::ErrorKind::InvalidData, "invalid utf-8")
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bool(&mut self) -> Result<bool> {
|
fn read_bool(&mut self) -> Result<bool> {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::io::{self, Write};
|
use std::io;
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
|
|
||||||
use connection::{Connection, ConnectionType};
|
use connection::{Connection, ConnectionType};
|
||||||
use packet::Packet;
|
|
||||||
use public_key::KeyPair;
|
use public_key::KeyPair;
|
||||||
|
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
|
|
@ -41,7 +40,10 @@ impl Server {
|
||||||
stream.try_clone().unwrap(),
|
stream.try_clone().unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
connection.run(&mut stream);
|
let result = connection.run(&mut stream);
|
||||||
|
if let Some(error) = result.err() {
|
||||||
|
println!("sshd: {}", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue