diff --git a/Cargo.lock b/Cargo.lock index 7c9b58b..a4c595b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,55 +4,29 @@ version = "0.1.0" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "byteorder" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "coco" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "foreign-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "gcc" version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "lazy_static" -version = "0.2.8" +name = "kernel32-sys" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "libc" @@ -64,57 +38,6 @@ name = "log" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "memchr" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nom" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "openssl" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "openssl-sys" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pkg-config" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rand" version = "0.3.15" @@ -123,75 +46,59 @@ dependencies = [ "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rayon" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_syscall" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "ring" -version = "0.11.0" +name = "rust-crypto" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "scopeguard" -version = "0.3.2" +name = "rustc-serialize" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "untrusted" -version = "0.5.0" +name = "time" +version = "0.1.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" -"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" -"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" -"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" -"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a" "checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" -"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "30885bcb161cf67054244d10d4a7f4835ffd58773bc72e07d35fecf472295503" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" -"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" -"checksum nom 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f609d6a10aad859fdfccb134ba1022bbdfac52dd759acc2ab75171874056237" -"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" -"checksum openssl 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)" = "11ba043cb65fc9af71a431b8a36ffe8686cd4751cdf70a473ec1d01066ac7e41" -"checksum openssl-sys 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)" = "236c718c2e2c2b58a546d86ffea5194400bb15dbe01ca85325ffd357b03cf66c" -"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" -"checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" -"checksum rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7febc28567082c345f10cddc3612c6ea020fc3297a1977d472cf9fdb73e6e493" "checksum redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "9df6a71a1e67be2104410736b2389fb8e383c1d7e9e792d629ff13c02867147a" -"checksum ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2a6dc7fc06a05e6de183c5b97058582e9da2de0c136eafe49609769c507724" -"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" -"checksum untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b65243989ef6aacd9c0d6bd2b822765c3361d8ed352185a6f3a41f3a718c673" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index ca5cb19..7c12d4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ path = "src/bin/sshd.rs" [dependencies] byteorder = "^1.0" log = "^0.3" -nom = "^3.1" -ring = "^0.11.0" +rust-crypto = "^0.2" +rand = "^0.3" [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.1" diff --git a/src/algorithm.rs b/src/algorithm.rs new file mode 100644 index 0000000..fd26546 --- /dev/null +++ b/src/algorithm.rs @@ -0,0 +1,258 @@ +use std::str::FromStr; +use std::fmt; + +/// Slice of implemented key exchange algorithms, ordered by preference +pub static KEY_EXCHANGE: &[KeyExchangeAlgorithm] = &[ + KeyExchangeAlgorithm::CURVE25519_SHA256 +]; + +/// Slice of implemented host key algorithms, ordered by preference +pub static HOST_KEY: &[PublicKeyAlgorithm] = &[ + PublicKeyAlgorithm::SSH_ED25519 +]; + +/// Slice of implemented encryption algorithms, ordered by preference +pub static ENCRYPTION: &[EncryptionAlgorithm] = &[ + EncryptionAlgorithm::AES256_CTR +]; + +/// Slice of implemented MAC algorithms, ordered by preference +pub static MAC: &[MacAlgorithm] = &[ + MacAlgorithm::HMAC_SHA2_512 +]; + +/// Slice of implemented compression algorithms, ordered by preference +pub static COMPRESSION: &[CompressionAlgorithm] = &[ + CompressionAlgorithm::None, + CompressionAlgorithm::Zlib +]; + +/// Find the best matching algorithm +pub fn negotiate(server: &[A], client: &[A]) -> Option { + for algorithm in client.iter() { + if server.iter().any(|a| a == algorithm) { + return Some(*algorithm); + } + } + None +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(C)] +#[allow(non_camel_case_types)] +pub enum KeyExchangeAlgorithm { + CURVE25519_SHA256, + ECDH_SHA2_NISTP256, + ECDH_SHA2_NISTP384, + ECDH_SHA2_NISTP521, + DH_GROUP_EXCHANGE_SHA256, + DH_GROUP_EXCHANGE_SHA1, + DH_GROUP16_SHA512, + DH_GROUP18_SHA512, + DH_GROUP14_SHA256, + DH_GROUP14_SHA1, + EXT_INFO_C, +} + +impl FromStr for KeyExchangeAlgorithm { + type Err = (); + fn from_str(s: &str) -> Result { + use self::KeyExchangeAlgorithm::*; + match s { + "curve25519-sha256" => Ok(CURVE25519_SHA256), + "ecdh-sha2-nistp256" => Ok(ECDH_SHA2_NISTP256), + "ecdh-sha2-nistp384" => Ok(ECDH_SHA2_NISTP384), + "ecdh-sha2-nistp521" => Ok(ECDH_SHA2_NISTP521), + "diffie-hellman-group-exchange-sha256" => Ok(DH_GROUP_EXCHANGE_SHA256), + "diffie-hellman-group-exchange-sha1" => Ok(DH_GROUP_EXCHANGE_SHA1), + "diffie-hellman-group16-sha512" => Ok(DH_GROUP16_SHA512), + "diffie-hellman-group18-sha512" => Ok(DH_GROUP18_SHA512), + "diffie-hellman-group14-sha256" => Ok(DH_GROUP14_SHA256), + "diffie-hellman-group14-sha1" => Ok(DH_GROUP14_SHA1), + "ext-info-c" => Ok(EXT_INFO_C), + _ => { + debug!("Unknown kex algorithm: {}", s); + Err(()) + } + } + } +} + +impl fmt::Display for KeyExchangeAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::KeyExchangeAlgorithm::*; + f.write_str(match self { + &CURVE25519_SHA256 => "curve25519-sha256", + &ECDH_SHA2_NISTP256 => "ecdh-sha2-nistp256", + &ECDH_SHA2_NISTP384 => "ecdh-sha2-nistp384", + &ECDH_SHA2_NISTP521 => "ecdh-sha2-nistp521", + &DH_GROUP_EXCHANGE_SHA256 => "diffie-hellman-group-exchange-sha256", + &DH_GROUP_EXCHANGE_SHA1 => "diffie-hellman-group-exchange-sha1", + &DH_GROUP16_SHA512 => "diffie-hellman-group16-sha512", + &DH_GROUP18_SHA512 => "diffie-hellman-group18-sha512", + &DH_GROUP14_SHA256 => "diffie-hellman-group14-sha256", + &DH_GROUP14_SHA1 => "diffie-hellman-group14-sha1", + &EXT_INFO_C => "ext-info-c", + }) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[allow(non_camel_case_types)] +pub enum PublicKeyAlgorithm { + SSH_RSA, + RSA_SHA2_256, + RSA_SHA2_512, + ECDSA_SHA2_NISTP256, + ECDSA_SHA2_NISTP384, + ECDSA_SHA2_NISTP521, + SSH_ED25519, +} + +impl FromStr for PublicKeyAlgorithm { + type Err = (); + fn from_str(s: &str) -> Result { + use self::PublicKeyAlgorithm::*; + match s { + "ssh-rsa" => Ok(SSH_RSA), + "rsa-sha2-256" => Ok(RSA_SHA2_256), + "rsa-sha2-512" => Ok(RSA_SHA2_512), + "ecdsa-sha2-nistp256" => Ok(ECDSA_SHA2_NISTP256), + "ecdsa-sha2-nistp384" => Ok(ECDSA_SHA2_NISTP384), + "ecdsa-sha2-nistp521" => Ok(ECDSA_SHA2_NISTP521), + "ssh-ed25519" => Ok(SSH_ED25519), + _ => { + debug!("Unknown host key algorithm: {}", s); + Err(()) + } + } + } +} + +impl fmt::Display for PublicKeyAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::PublicKeyAlgorithm::*; + f.write_str(match self { + &SSH_RSA => "ssh-rsa", + &RSA_SHA2_256 => "rsa-sha2-256", + &RSA_SHA2_512 => "rsa-sha2-512", + &ECDSA_SHA2_NISTP256 => "ecdsa-sha2-nistp256", + &ECDSA_SHA2_NISTP384 => "ecdsa-sha2-nistp384", + &ECDSA_SHA2_NISTP521 => "ecdsa-sha2-nistp521", + &SSH_ED25519 => "ssh-ed25519", + }) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[allow(non_camel_case_types)] +pub enum EncryptionAlgorithm { + AES128_CTR, + AES128_CBC, + AES192_CTR, + AES192_CBC, + AES256_CTR, + AES256_CBC, + None, +} + +impl FromStr for EncryptionAlgorithm { + type Err = (); + fn from_str(s: &str) -> Result { + use self::EncryptionAlgorithm::*; + match s { + "aes128-ctr" => Ok(AES128_CTR), + "aes128-cbc" => Ok(AES128_CBC), + "aes192-ctr" => Ok(AES192_CTR), + "aes192-cbc" => Ok(AES192_CBC), + "aes256-ctr" => Ok(AES256_CTR), + "aes256-cbc" => Ok(AES256_CBC), + "none" => Ok(EncryptionAlgorithm::None), + _ => { + println!("Unknown encryption algorithm: `{}`", s); + Err(()) + } + } + } +} + +impl fmt::Display for EncryptionAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::EncryptionAlgorithm::*; + f.write_str(match self { + &AES128_CTR => "aes128-ctr", + &AES128_CBC => "aes128-cbc", + &AES192_CTR => "aes192-ctr", + &AES192_CBC => "aes192-cbc", + &AES256_CTR => "aes256-ctr", + &AES256_CBC => "aes256-cbc", + &EncryptionAlgorithm::None => "none", + }) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[allow(non_camel_case_types)] +pub enum MacAlgorithm { + HMAC_SHA1, + HMAC_SHA2_256, + HMAC_SHA2_512, + None, +} + +impl FromStr for MacAlgorithm { + type Err = (); + fn from_str(s: &str) -> Result { + use self::MacAlgorithm::*; + match s { + "hmac-sha1" => Ok(MacAlgorithm::HMAC_SHA1), + "hmac-sha2-256" => Ok(MacAlgorithm::HMAC_SHA2_256), + "hmac-sha2-512" => Ok(MacAlgorithm::HMAC_SHA2_512), + _ => { + println!("Unknown mac algorithm: {}", s); + Err(()) + } + } + } +} + +impl fmt::Display for MacAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::MacAlgorithm::*; + f.write_str(match self { + &HMAC_SHA1 => "hmac-sha1", + &HMAC_SHA2_256 => "hmac-sha2-256", + &HMAC_SHA2_512 => "hmac-sha2-512", + &MacAlgorithm::None => "none" + }) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum CompressionAlgorithm { + Zlib, + None, +} + +impl FromStr for CompressionAlgorithm { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "zlib" => Ok(CompressionAlgorithm::Zlib), + "none" => Ok(CompressionAlgorithm::None), + _ => { + println!("Unknown compression algorithm: {}", s); + Err(()) + } + } + } +} + +impl fmt::Display for CompressionAlgorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + &CompressionAlgorithm::Zlib => "zlib", + &CompressionAlgorithm::None => "none" + }) + } +} diff --git a/src/auth.rs b/src/auth.rs deleted file mode 100644 index 9b30812..0000000 --- a/src/auth.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub enum AuthMethod { - PublicKey, Password, HostBased, None -} - -pub struct AuthRequest { - user: String, - service: String, - method: AuthMethod -} diff --git a/src/bin/sshd.rs b/src/bin/sshd.rs index 63349e9..f42ddec 100644 --- a/src/bin/sshd.rs +++ b/src/bin/sshd.rs @@ -1,10 +1,52 @@ +#![cfg_attr(not(target_os = "redox"), feature(libc))] + extern crate ssh; +use std::io::{self, Write}; +use std::str::FromStr; use std::env; +use std::process; use ssh::{Server, ServerConfig}; pub fn main() { - let config = ServerConfig { host: String::from("0.0.0.0:22222") }; + let mut foreground = false; + let mut quiet = false; + + let mut config = ServerConfig::default(); + + let mut args = env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_ref() { + "-f" => foreground = true, + "-q" => quiet = true, + "-p" => { + config.port = u16::from_str(&args.next().expect("sshd: no argument to -p option")) + .expect("sshd: invalid port number to -p option"); + } + _ => () + } + } + let server = Server::with_config(config); - server.start(); + + if !foreground && fork() != 0 { + process::exit(0); + } + + if let Err(err) = server.run() { + writeln!(io::stderr(), "sshd: {}", err).unwrap(); + process::exit(1); + } +} + +#[cfg(target_os = "redox")] +fn fork() -> usize { + extern crate syscall; + unsafe { syscall::clone(0).unwrap() } +} + +#[cfg(not(target_os = "redox"))] +fn fork() -> usize { + extern crate libc; + unsafe { libc::fork() as usize } } diff --git a/src/key_exchange/curve25519.rs b/src/key_exchange/curve25519.rs new file mode 100644 index 0000000..e9683f8 --- /dev/null +++ b/src/key_exchange/curve25519.rs @@ -0,0 +1,3 @@ +pub struct Curve25519 { + +} diff --git a/src/key_exchange/mod.rs b/src/key_exchange/mod.rs new file mode 100644 index 0000000..19d6ad0 --- /dev/null +++ b/src/key_exchange/mod.rs @@ -0,0 +1,9 @@ +mod curve25519; + +pub use self::curve25519::Curve25519; + +use packet::Packet; + +pub trait KeyExchange { + fn process(&self, packet: &Packet); +} diff --git a/src/lib.rs b/src/lib.rs index b444d6e..1104cd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ extern crate byteorder; +extern crate rand; +extern crate crypto; #[macro_use] extern crate log; -#[macro_use] -extern crate nom; +pub mod algorithm; pub mod protocol; pub mod server; pub mod packet; -pub mod parser; pub mod message; +pub mod session; +pub mod key_exchange; pub use self::server::{Server, ServerConfig}; diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..9180cab --- /dev/null +++ b/src/message.rs @@ -0,0 +1,108 @@ +use std::fmt::Debug; + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum MessageType { + Disconnect, + Ignore, + Unimplemented, + Debug, + ServiceRequest, + ServiceAccept, + KexInit, + NewKeys, + KeyExchange(u8), + UserAuthRequest, + UserAuthFailure, + UserAuthSuccess, + UserAuthBanner, + UserAuth(u8), + GlobalRequest, + RequestSuccess, + RequestFailure, + ChannelOpen, + ChannelOpenConfirmation, + ChannelOpenFailure, + ChannelWindowAdjust, + ChannelData, + ChannelExtendedData, + ChannelEOF, + ChannelClose, + ChannelRequest, + ChannelSuccess, + ChannelFailure, + Unknown +} + +impl From for MessageType { + fn from(id: u8) -> Self { + use self::MessageType::*; + match id { + 1 => Disconnect, + 2 => Ignore, + 3 => Unimplemented, + 4 => Debug, + 5 => ServiceRequest, + 6 => ServiceAccept, + 20 => KexInit, + 21 => NewKeys, + 30...49 => KeyExchange(id), + 50 => UserAuthRequest, + 51 => UserAuthFailure, + 52 => UserAuthSuccess, + 53 => UserAuthBanner, + 60...79 => UserAuth(id), + 80 => GlobalRequest, + 81 => RequestSuccess, + 82 => RequestFailure, + 90 => ChannelOpen, + 91 => ChannelOpenConfirmation, + 92 => ChannelOpenFailure, + 93 => ChannelWindowAdjust, + 94 => ChannelData, + 95 => ChannelExtendedData, + 96 => ChannelEOF, + 97 => ChannelClose, + 98 => ChannelRequest, + 99 => ChannelSuccess, + 100 => ChannelFailure, + _ => Unknown + } + } +} + +impl Into for MessageType { + fn into(self) -> u8 { + use self::MessageType::*; + match self { + Disconnect => 1, + Ignore => 2, + Unimplemented => 3, + Debug => 4, + ServiceRequest => 5, + ServiceAccept => 6, + KexInit => 20, + NewKeys => 21, + KeyExchange(id) => id, + UserAuthRequest => 50, + UserAuthFailure => 51, + UserAuthSuccess => 52, + UserAuthBanner => 53, + UserAuth(id) => id, + GlobalRequest => 80, + RequestSuccess => 81, + RequestFailure => 82, + ChannelOpen => 90, + ChannelOpenConfirmation => 91, + ChannelOpenFailure => 92, + ChannelWindowAdjust => 93, + ChannelData => 94, + ChannelExtendedData => 95, + ChannelEOF => 96, + ChannelClose => 97, + ChannelRequest => 98, + ChannelSuccess => 99, + ChannelFailure => 100, + Unknown => 255 + } + } +} diff --git a/src/message/kex.rs b/src/message/kex.rs deleted file mode 100644 index 7d73e69..0000000 --- a/src/message/kex.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::str::FromStr; - -#[derive(Debug)] -pub struct KeyExchangeInit { - pub cookie: Vec, - pub kex_algorithms: Vec, - pub server_host_key_algorithms: Vec, - pub encryption_algorithms_client_to_server: Vec, - pub encryption_algorithms_server_to_client: Vec, - pub mac_algorithms_client_to_server: Vec, - pub mac_algorithms_server_to_client: Vec, - pub compression_algorithms_client_to_server: Vec, - pub compression_algorithms_server_to_client: Vec, - pub languages_client_to_server: Vec, - pub languages_server_to_client: Vec, - pub first_kex_packet_follows: bool -} - -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum KeyExchangeAlgorithm { - CURVE25519_SHA256, - ECDH_SHA2_NISTP256, - ECDH_SHA2_NISTP384, - ECDH_SHA2_NISTP521, - DH_GROUP_EXCHANGE_SHA256, - DH_GROUP_EXCHANGE_SHA1, - DH_GROUP16_SHA512, - DH_GROUP18_SHA512, - DH_GROUP14_SHA256, - DH_GROUP14_SHA1, - EXT_INFO_C -} - -impl FromStr for KeyExchangeAlgorithm { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "curve25519-sha256" => - Ok(KeyExchangeAlgorithm::CURVE25519_SHA256), - "ecdh-sha2-nistp256" => - Ok(KeyExchangeAlgorithm::ECDH_SHA2_NISTP256), - "ecdh-sha2-nistp384" => - Ok(KeyExchangeAlgorithm::ECDH_SHA2_NISTP384), - "ecdh-sha2-nistp521" => - Ok(KeyExchangeAlgorithm::ECDH_SHA2_NISTP521), - "diffie-hellman-group16-sha512" => - Ok(KeyExchangeAlgorithm::DH_GROUP16_SHA512), - "diffie-hellman-group18-sha512" => - Ok(KeyExchangeAlgorithm::DH_GROUP18_SHA512), - "diffie-hellman-group14-sha256" => - Ok(KeyExchangeAlgorithm::DH_GROUP14_SHA256), - "diffie-hellman-group14-sha1" => - Ok(KeyExchangeAlgorithm::DH_GROUP14_SHA1), - "diffie-hellman-group-exchange-sha256" => - Ok(KeyExchangeAlgorithm::DH_GROUP_EXCHANGE_SHA256), - "diffie-hellman-group-exchange-sha1" => - Ok(KeyExchangeAlgorithm::DH_GROUP_EXCHANGE_SHA1), - "ext-info-c" => - Ok(KeyExchangeAlgorithm::EXT_INFO_C), - _ => { println!("Unknown kex algorithm: {}", s); Err(()) } - } - } -} - - -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum HostKeyAlgorithm { - SSH_RSA, - RSA_SHA2_256, - RSA_SHA2_512, - ECDSA_SHA2_NISTP256, - ECDSA_SHA2_NISTP384, - ECDSA_SHA2_NISTP521, - SSH_ED25519 -} - -impl FromStr for HostKeyAlgorithm { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "ssh-rsa" => Ok(HostKeyAlgorithm::SSH_RSA), - "rsa-sha2-256" => Ok(HostKeyAlgorithm::RSA_SHA2_256), - "rsa-sha2-512" => Ok(HostKeyAlgorithm::RSA_SHA2_512), - "ecdsa-sha2-nistp256" => Ok(HostKeyAlgorithm::ECDSA_SHA2_NISTP256), - "ecdsa-sha2-nistp384" => Ok(HostKeyAlgorithm::ECDSA_SHA2_NISTP384), - "ecdsa-sha2-nistp521" => Ok(HostKeyAlgorithm::ECDSA_SHA2_NISTP521), - "ssh-ed25519" => Ok(HostKeyAlgorithm::SSH_ED25519), - _ => { println!("Unknown host key algorithm: {}", s); Err(()) } - } - } -} - -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum EncryptionAlgorithm { - AES128_CTR, - AES128_CBC, - AES192_CTR, - AES192_CBC, - AES256_CTR, - AES256_CBC, - None -} - -impl FromStr for EncryptionAlgorithm { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "aes128-ctr" => Ok(EncryptionAlgorithm::AES128_CTR), - "aes128-cbc" => Ok(EncryptionAlgorithm::AES128_CBC), - "aes192-ctr" => Ok(EncryptionAlgorithm::AES192_CTR), - "aes192-cbc" => Ok(EncryptionAlgorithm::AES192_CBC), - "aes256-ctr" => Ok(EncryptionAlgorithm::AES256_CTR), - "aes256-cbc" => Ok(EncryptionAlgorithm::AES256_CBC), - "none" => Ok(EncryptionAlgorithm::None), - _ => { println!("Unknown encryption algorithm: `{}`", s); Err(()) } - } - } -} - -#[derive(Debug)] -#[allow(non_camel_case_types)] -pub enum MacAlgorithm { - HMAC_SHA1, - HMAC_SHA2_256, - HMAC_SHA2_512, - None -} - -impl FromStr for MacAlgorithm { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "hmac-sha1" => Ok(MacAlgorithm::HMAC_SHA1), - "hmac-sha2-256" => Ok(MacAlgorithm::HMAC_SHA2_256), - "hmac-sha2-512" => Ok(MacAlgorithm::HMAC_SHA2_512), - _ => { println!("Unknown mac algorithm: {}", s); Err(()) } - } - } -} - -#[derive(Debug)] -pub enum CompressionAlgorithm { - Zlib, - None -} - -impl FromStr for CompressionAlgorithm { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "zlib" => Ok(CompressionAlgorithm::Zlib), - "none" => Ok(CompressionAlgorithm::None), - _ => { println!("Unknown compression algorithm: {}", s); Err(()) } - } - } -} - -#[derive(Debug)] -pub struct Language(pub String); - diff --git a/src/message/mod.rs b/src/message/mod.rs deleted file mode 100644 index 3303006..0000000 --- a/src/message/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -pub mod kex; - -#[derive(Debug)] -pub enum Message { - Disconnect, - Ignore, - Unimplemented, - Debug, - ServiceRequest, - ServiceAccept, - KexInit(kex::KeyExchangeInit), - NewKeys, - UserAuthRequest, - UserAuthFailure, - UserAuthSuccess, - UserAuthBanner, - GlobalRequest, - RequestSuccess, - RequestFailure, - ChannelOpen, - ChannelOpenConfirmation, - ChannelOpenFailure, - ChannelWindowAdjust, - ChannelData, - ChannelExtendedData, - ChannelEOF, - ChannelClose, - ChannelRequest, - ChannelSuccess, - ChannelFailure, - Unknown -} - diff --git a/src/packet.rs b/src/packet.rs index 3326425..8a2287b 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,21 +1,159 @@ use std::fmt; -use std::str; -use parser; +use std::str::{self, FromStr}; +use std::string::ToString; +use std::io::{self, BufReader, Write, Read, Result}; +use message::MessageType; +use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; pub struct Packet { - pub payload: Vec, - pub mac: Vec + payload: Vec } impl Packet { - pub fn parse(&self) { - let result = parser::parse_packet(&self.payload.as_slice()); - println!("{:?}", result); + pub fn new(msg_type: MessageType) -> Packet { + Packet { payload: (&[msg_type.into()]).to_vec() } + } + + pub fn msg_type(&self) -> MessageType { + self.payload[0].into() + } + + pub fn read_from(stream: &mut R) -> Result { + let mac_len = 0; + + let packet_len = stream.read_u32::()? as usize; + let padding_len = stream.read_u8()? as usize; + let payload_len = packet_len - padding_len - 1; + + // TODO: Prevent packets that are too large + + let mut payload = Vec::with_capacity(payload_len); + let mut padding = Vec::with_capacity(padding_len); +// let mut mac = Vec::with_capacity(mac_len); + + stream.take(payload_len as u64).read_to_end(&mut payload)?; + stream.take(padding_len as u64).read_to_end(&mut padding)?; + +// if mac_len > 0 { +// stream.take(mac_len as u64).read_to_end(&mut mac); +// } + + Ok(Packet { payload: payload }) + } + + pub fn write_to(&self, stream: &mut W) -> Result<()> { + let padding_len = self.padding_len(); + let packet_len = self.payload.len() + padding_len + 1; + + stream.write_u32::(packet_len as u32)?; + stream.write_u8(padding_len as u8)?; + stream.write(&self.payload)?; + stream.write(&[0u8;255][..padding_len])?; + + Ok(()) + } + + pub fn writer<'a>(&'a mut self) -> &'a mut Write { + &mut self.payload + } + + pub fn with_writer(&mut self, f: &Fn(&mut Write) -> ()) { + f(&mut self.payload); + } + + pub fn reader<'a>(&'a self) -> BufReader<&'a [u8]> { + BufReader::new(self.payload.as_slice()) + } + + pub fn padding_len(&self) -> usize { + // Calculate the padding to reach a multiple of 8 bytes + let padding_len = 8 - ((self.payload.len() + 5) % 8); + + // The padding has to be at least 4 bytes long + if padding_len < 4 { padding_len + 8 } + else { padding_len } } } -impl fmt::Debug for Packet { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Packet({} bytes)", self.payload.len()) +pub trait ReadPacketExt: ReadBytesExt { + fn read_msg_type(&mut self) -> Result { + Ok(self.read_u8()?.into()) + } + + fn read_string(&mut self) -> Result> { + let len = self.read_u32::()?; + self.read_bytes(len as usize) + } + + fn read_mpint(&mut self) -> Result> { + let len = self.read_u32::()?; + self.read_bytes(len as usize) + } + + fn read_bytes(&mut self, len: usize) -> Result> { + let mut buffer = Vec::with_capacity(len); + self.take(len as u64).read_to_end(&mut buffer)?; + Ok(buffer) + } + + fn read_utf8(&mut self) -> Result { + Ok(str::from_utf8(self.read_string()?.as_slice()).unwrap_or("").to_owned()) + } + + fn read_bool(&mut self) -> Result { + self.read_u8().map(|i| i != 0) + } + + fn read_enum_list(&mut self) -> Result> { + let string = self.read_utf8()?; + Ok(string.split(",").filter_map(|l| T::from_str(&l).ok()).collect()) + } + + fn read_name_list(&mut self) -> Result> { + let string = self.read_utf8()?; + Ok(string.split(",").map(|l| l.to_owned()).collect()) + } +} + +impl ReadPacketExt for R {} + +pub trait WritePacketExt: WriteBytesExt { + fn write_msg_type(&mut self, msg_type: MessageType) -> Result<()> { + self.write_u8(msg_type.into()) + } + + fn write_string(&mut self, s: &str) -> Result<()> { + let bytes = s.as_bytes(); + self.write_u32::(bytes.len() as u32)?; + self.write_all(bytes) + } + + fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> { + self.write_all(bytes) + } + + fn write_bool(&mut self, value: bool) -> Result<()> { + self.write_u8(if value { 1 } else { 0 }) + } + + fn write_list(&mut self, list: &[T]) -> Result<()> { + let mut string = String::new(); + let mut iter = list.iter(); + + while let Some(item) = iter.next() { + if !string.is_empty() { + string += ","; + } + string += &*item.to_string(); + } + self.write_string(&*string) + } +} + +impl WritePacketExt for R {} + +impl fmt::Debug for Packet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Packet({:?}, {} bytes)", self.msg_type(), self.payload.len()) } } diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index 57fcd6b..0000000 --- a/src/parser.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::str; -use std::str::FromStr; -use nom::{IResult, Endianness}; -use message::*; - -macro_rules! enum_list ( - ($i:expr, $name:ty) => ( - parse_enum_list::<$name>($i) - ); -); - -named!(pub parse_packet, - map!(parse_keyx_init, |m| Message::KexInit(m)) -); - -named!(parse_keyx_init<&[u8], kex::KeyExchangeInit>, do_parse!(tag!(&[20]) >> - cookie: take!(16) >> - kex_algos: enum_list!(kex::KeyExchangeAlgorithm) >> - server_host_key_algos: enum_list!(kex::HostKeyAlgorithm) >> - enc_algos_c2s: enum_list!(kex::EncryptionAlgorithm) >> - enc_algos_s2c: enum_list!(kex::EncryptionAlgorithm) >> - mac_algos_c2s: enum_list!(kex::MacAlgorithm) >> - mac_algos_s2c: enum_list!(kex::MacAlgorithm) >> - comp_algos_c2s: enum_list!(kex::CompressionAlgorithm) >> - comp_algos_s2c: enum_list!(kex::CompressionAlgorithm) >> - langs_c2s: parse_name_list >> - langs_s2c: parse_name_list >> - first_kex_packet_follows: parse_bool >> - reserved: u32!(Endianness::Big) >> - (kex::KeyExchangeInit { - cookie: cookie.to_vec(), - kex_algorithms: kex_algos, - server_host_key_algorithms: server_host_key_algos, - encryption_algorithms_client_to_server: enc_algos_c2s, - encryption_algorithms_server_to_client: enc_algos_s2c, - mac_algorithms_client_to_server: mac_algos_c2s, - mac_algorithms_server_to_client: mac_algos_s2c, - compression_algorithms_client_to_server: comp_algos_c2s, - compression_algorithms_server_to_client: comp_algos_s2c, - languages_client_to_server: - langs_c2s - .iter() - .filter(|s| !s.is_empty()) - .map(|lang| kex::Language(lang.to_string())) - .collect(), - languages_server_to_client: - langs_c2s - .iter() - .filter(|s| !s.is_empty()) - .map(|lang| kex::Language(lang.to_string())) - .collect(), - first_kex_packet_follows: first_kex_packet_follows - }) -)); - -named!(parse_bool<&[u8], bool>, - map!(take!(1), |i: &[u8]| i[0] != 0) -); - -named!(parse_string<&[u8], &[u8]>, - do_parse!(len: u32!(Endianness::Big) >> - data: take!(len) >> - (data) - ) -); - -named!(parse_name_list<&[u8], Vec<&str>>, - map_res!(parse_string, |s| str::from_utf8(s).map(|s| s.split(",").collect())) -); - -pub fn parse_enum_list(i: &[u8]) -> IResult<&[u8], Vec> { - match parse_name_list(i) { - IResult::Done(i, list) => IResult::Done(i, - list.iter() - .filter_map(|l| T::from_str(&l).ok()) - .collect() - ), - IResult::Error(e) => IResult::Error(e), - IResult::Incomplete(n) => IResult::Incomplete(n) - } -} diff --git a/src/protocol.rs b/src/protocol.rs index 9797f74..d79d872 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,8 +1,5 @@ use std::io::{Read, Write, BufReader, BufRead}; use std::io; -use byteorder::{ReadBytesExt, BigEndian}; - -use packet::Packet; pub fn send_identification(stream: &mut W) -> io::Result { let id = format!("SSH-2.0-RedoxSSH_{}\r\n", env!("CARGO_PKG_VERSION")); @@ -22,24 +19,3 @@ pub fn read_identification(stream: &mut R) -> io::Result { Ok(id.trim_right().to_owned()) } - -pub fn read_packet(stream: &mut R, mac_len: usize) -> io::Result { - let packet_len = stream.read_u32::()? as usize; - let padding_len = stream.read_u8()? as usize; - let payload_len = packet_len - padding_len - 1; - - // TODO: Prevent packets that are too large - - let mut payload = Vec::with_capacity(payload_len); - let mut padding = Vec::with_capacity(padding_len); - let mut mac = Vec::with_capacity(mac_len); - - stream.take(payload_len as u64).read_to_end(&mut payload)?; - stream.take(padding_len as u64).read_to_end(&mut padding)?; - - if mac_len > 0 { - stream.take(mac_len as u64).read_to_end(&mut mac); - } - - Ok(Packet { payload: payload, mac: mac }) -} diff --git a/src/server.rs b/src/server.rs index f1762ca..7e7967c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,14 +1,26 @@ use std::net::TcpListener; -use std::io; +use std::io::{self, Write}; +use session::{Session, SessionType}; +use packet::Packet; use protocol; pub struct ServerConfig { - pub host: String + pub host: String, + pub port: u16, +} + +impl Default for ServerConfig { + fn default() -> ServerConfig { + ServerConfig { + host: "0.0.0.0".to_owned(), + port: 22, + } + } } pub struct Server { - config: ServerConfig + config: ServerConfig, } impl Server { @@ -16,25 +28,56 @@ impl Server { Server { config: config } } - pub fn start(&self) -> io::Result<()> { - let listener = TcpListener::bind(&*self.config.host) - .expect(&*format!("Failed to bind to {}.", self.config.host)); - let (mut stream, addr) = listener.accept() - .expect(&*format!("Failed to establish incomin connection.")); + pub fn run(&self) -> io::Result<()> { + let listener = TcpListener::bind((&*self.config.host, self.config.port)).expect(&*format!( + "sshd: failed to bind to {}:{}", + self.config.host, + self.config.port + )); + let (mut stream, addr) = listener.accept().expect(&*format!( + "sshd: failed to establish incoming connection" + )); - println!("Connection established!"); + println!("Incoming connection from {}", addr); let id = protocol::read_identification(&mut stream)?; - println!("Incoming connection from {}", id); + println!("{} identifies as {}", addr, id); protocol::send_identification(&mut stream)?; + let mut session = Session::new(SessionType::Server, stream.try_clone().unwrap()); + loop { - let packet = protocol::read_packet(&mut stream, 0)?; - let message = packet.parse(); + let packet = Packet::read_from(&mut stream).unwrap(); + println!("packet: {:?}", packet); + session.process(&packet); + use rand::{OsRng, Rng}; + let mut rng = OsRng::new()?; - println!("{:?}", message); + /* + if message.msg_type() == MessageType::KexInit { +xs let cookie: Vec = rng.gen_iter::().take(16).collect(); + let kex = message::kex::KeyExchangeInit { + cookie: cookie, + kex_algorithms: vec![message::kex::KeyExchangeAlgorithm::CURVE25519_SHA256], + server_host_key_algorithms: vec![message::kex::HostKeyAlgorithm::SSH_ED25519], + encryption_algorithms_client_to_server: vec![message::kex::EncryptionAlgorithm::AES256_CTR], + encryption_algorithms_server_to_client: vec![message::kex::EncryptionAlgorithm::AES256_CTR], + mac_algorithms_client_to_server: vec![message::kex::MacAlgorithm::HMAC_SHA2_512], + mac_algorithms_server_to_client: vec![message::kex::MacAlgorithm::HMAC_SHA2_512], + compression_algorithms_client_to_server: vec![message::kex::CompressionAlgorithm::None], + compression_algorithms_server_to_client: vec![message::kex::CompressionAlgorithm::None], + languages_client_to_server: vec![], + languages_server_to_client: vec![], + first_kex_packet_follows: false + }; + protocol::write_message(&mut stream, &kex); + } + else { + println!("Unhandled Message Type"); + } + */ } Ok(()) } diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..fd10d97 --- /dev/null +++ b/src/session.rs @@ -0,0 +1,99 @@ +use key_exchange::KeyExchange; +use message::MessageType; +use packet::{Packet, ReadPacketExt, WritePacketExt}; +use std::io::Write; + +#[derive(PartialEq)] +enum SessionState { + Initial, + KeyExchange, + Established +} + +#[derive(PartialEq)] +pub enum SessionType { + Server, + Client +} + +pub struct Session<'a, W: Write> { + stype: SessionType, + state: SessionState, + key_exchange: Option<&'a KeyExchange>, + stream: W +} + +impl<'a, W: Write> Session<'a, W> { + pub fn new(stype: SessionType, stream: W) -> Session<'a, W> { + Session { + stype: stype, + state: SessionState::Initial, + key_exchange: None, + stream: stream + } + } + + pub fn process(&mut self, packet: &Packet) { + match packet.msg_type() { + MessageType::KexInit => { + println!("Starting Key Exchange!"); + self.kex_init(packet); + } + _ => { + println!("Unhandled packet: {:?}", packet); + } + } + } + + pub fn kex_init(&mut self, packet: &Packet) { + use algorithm::*; + let mut reader = packet.reader(); + + reader.read_msg_type(); + let cookie = reader.read_bytes(16); + let kex_algos = reader.read_enum_list::(); + let srv_host_key_algos = reader.read_enum_list::(); + let enc_algos_c2s = reader.read_enum_list::(); + let enc_algos_s2c = reader.read_enum_list::(); + let mac_algos_c2s = reader.read_enum_list::(); + let mac_algos_s2c = reader.read_enum_list::(); + let comp_algos_c2s = reader.read_enum_list::(); + let comp_algos_s2c = reader.read_enum_list::(); + + let kex_algo = negotiate(KEY_EXCHANGE, kex_algos.unwrap().as_slice()); + let srv_host_key_algo = negotiate(HOST_KEY, srv_host_key_algos.unwrap().as_slice()); + let enc_algo = negotiate(ENCRYPTION, enc_algos_s2c.unwrap().as_slice()); + let mac_algo = negotiate(MAC, mac_algos_s2c.unwrap().as_slice()); + let comp_algo = negotiate(COMPRESSION, comp_algos_s2c.unwrap().as_slice()); + + println!("Negociated Kex Algorithm: {:?}", kex_algo); + println!("Negociated Host Key Algorithm: {:?}", srv_host_key_algo); + println!("Negociated Encryption Algorithm: {:?}", enc_algo); + println!("Negociated Mac Algorithm: {:?}", mac_algo); + println!("Negociated Comp Algorithm: {:?}", comp_algo); + + use rand::{OsRng, Rng}; + let mut rng = OsRng::new().unwrap(); + let cookie: Vec = rng.gen_iter::().take(16).collect(); + + let mut packet = Packet::new(MessageType::KexInit); + packet.with_writer(&|w| { + w.write_bytes(cookie.as_slice()); + w.write_list(KEY_EXCHANGE); + w.write_list(HOST_KEY); + w.write_list(ENCRYPTION); + w.write_list(ENCRYPTION); + w.write_list(MAC); + w.write_list(MAC); + w.write_list(COMPRESSION); + w.write_list(COMPRESSION); + w.write_string(""); + w.write_string(""); + w.write_bool(false); + w.write_bytes(&[0, 0, 0, 0]); + }); + + self.state = SessionState::KeyExchange; + packet.write_to(&mut self.stream); + } +}