mirror of
https://gitlab.redox-os.org/CoffeeCode/redox-ssh.git
synced 2025-12-28 18:42:18 +01:00
Discard the usage of nom
This commit is contained in:
parent
587f810172
commit
6dd79b785d
16 changed files with 771 additions and 472 deletions
171
Cargo.lock
generated
171
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
258
src/algorithm.rs
Normal file
258
src/algorithm.rs
Normal file
|
|
@ -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<A: PartialEq + Copy>(server: &[A], client: &[A]) -> Option<A> {
|
||||
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<KeyExchangeAlgorithm, ()> {
|
||||
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<PublicKeyAlgorithm, ()> {
|
||||
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<EncryptionAlgorithm, ()> {
|
||||
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<MacAlgorithm, ()> {
|
||||
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<CompressionAlgorithm, ()> {
|
||||
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"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
pub enum AuthMethod {
|
||||
PublicKey, Password, HostBased, None
|
||||
}
|
||||
|
||||
pub struct AuthRequest {
|
||||
user: String,
|
||||
service: String,
|
||||
method: AuthMethod
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
3
src/key_exchange/curve25519.rs
Normal file
3
src/key_exchange/curve25519.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub struct Curve25519 {
|
||||
|
||||
}
|
||||
9
src/key_exchange/mod.rs
Normal file
9
src/key_exchange/mod.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
mod curve25519;
|
||||
|
||||
pub use self::curve25519::Curve25519;
|
||||
|
||||
use packet::Packet;
|
||||
|
||||
pub trait KeyExchange {
|
||||
fn process(&self, packet: &Packet);
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
|
|||
108
src/message.rs
Normal file
108
src/message.rs
Normal file
|
|
@ -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<u8> 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<u8> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeyExchangeInit {
|
||||
pub cookie: Vec<u8>,
|
||||
pub kex_algorithms: Vec<KeyExchangeAlgorithm>,
|
||||
pub server_host_key_algorithms: Vec<HostKeyAlgorithm>,
|
||||
pub encryption_algorithms_client_to_server: Vec<EncryptionAlgorithm>,
|
||||
pub encryption_algorithms_server_to_client: Vec<EncryptionAlgorithm>,
|
||||
pub mac_algorithms_client_to_server: Vec<MacAlgorithm>,
|
||||
pub mac_algorithms_server_to_client: Vec<MacAlgorithm>,
|
||||
pub compression_algorithms_client_to_server: Vec<CompressionAlgorithm>,
|
||||
pub compression_algorithms_server_to_client: Vec<CompressionAlgorithm>,
|
||||
pub languages_client_to_server: Vec<Language>,
|
||||
pub languages_server_to_client: Vec<Language>,
|
||||
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<KeyExchangeAlgorithm, ()> {
|
||||
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<HostKeyAlgorithm, ()> {
|
||||
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<EncryptionAlgorithm, ()> {
|
||||
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<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(()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompressionAlgorithm {
|
||||
Zlib,
|
||||
None
|
||||
}
|
||||
|
||||
impl FromStr for CompressionAlgorithm {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<CompressionAlgorithm, ()> {
|
||||
match s {
|
||||
"zlib" => Ok(CompressionAlgorithm::Zlib),
|
||||
"none" => Ok(CompressionAlgorithm::None),
|
||||
_ => { println!("Unknown compression algorithm: {}", s); Err(()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Language(pub String);
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
158
src/packet.rs
158
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<u8>,
|
||||
pub mac: Vec<u8>
|
||||
payload: Vec<u8>
|
||||
}
|
||||
|
||||
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<R: io::Read>(stream: &mut R) -> Result<Packet> {
|
||||
let mac_len = 0;
|
||||
|
||||
let packet_len = stream.read_u32::<BigEndian>()? 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<W: io::Write>(&self, stream: &mut W) -> Result<()> {
|
||||
let padding_len = self.padding_len();
|
||||
let packet_len = self.payload.len() + padding_len + 1;
|
||||
|
||||
stream.write_u32::<BigEndian>(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<MessageType> {
|
||||
Ok(self.read_u8()?.into())
|
||||
}
|
||||
|
||||
fn read_string(&mut self) -> Result<Vec<u8>> {
|
||||
let len = self.read_u32::<BigEndian>()?;
|
||||
self.read_bytes(len as usize)
|
||||
}
|
||||
|
||||
fn read_mpint(&mut self) -> Result<Vec<u8>> {
|
||||
let len = self.read_u32::<BigEndian>()?;
|
||||
self.read_bytes(len as usize)
|
||||
}
|
||||
|
||||
fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
|
||||
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<String> {
|
||||
Ok(str::from_utf8(self.read_string()?.as_slice()).unwrap_or("").to_owned())
|
||||
}
|
||||
|
||||
fn read_bool(&mut self) -> Result<bool> {
|
||||
self.read_u8().map(|i| i != 0)
|
||||
}
|
||||
|
||||
fn read_enum_list<T: FromStr>(&mut self) -> Result<Vec<T>> {
|
||||
let string = self.read_utf8()?;
|
||||
Ok(string.split(",").filter_map(|l| T::from_str(&l).ok()).collect())
|
||||
}
|
||||
|
||||
fn read_name_list(&mut self) -> Result<Vec<String>> {
|
||||
let string = self.read_utf8()?;
|
||||
Ok(string.split(",").map(|l| l.to_owned()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ReadBytesExt> 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::<BigEndian>(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<T: ToString>(&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<R: WriteBytesExt + ?Sized> 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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Message>,
|
||||
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<T: FromStr>(i: &[u8]) -> IResult<&[u8], Vec<T>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<W: Write>(stream: &mut W) -> io::Result<usize> {
|
||||
let id = format!("SSH-2.0-RedoxSSH_{}\r\n", env!("CARGO_PKG_VERSION"));
|
||||
|
|
@ -22,24 +19,3 @@ pub fn read_identification<R: Read>(stream: &mut R) -> io::Result<String> {
|
|||
|
||||
Ok(id.trim_right().to_owned())
|
||||
}
|
||||
|
||||
pub fn read_packet<R: Read>(stream: &mut R, mac_len: usize) -> io::Result<Packet> {
|
||||
let packet_len = stream.read_u32::<BigEndian>()? 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 })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<u8> = rng.gen_iter::<u8>().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(())
|
||||
}
|
||||
|
|
|
|||
99
src/session.rs
Normal file
99
src/session.rs
Normal file
|
|
@ -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::<KeyExchangeAlgorithm>();
|
||||
let srv_host_key_algos = reader.read_enum_list::<PublicKeyAlgorithm>();
|
||||
let enc_algos_c2s = 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_s2c = reader.read_enum_list::<MacAlgorithm>();
|
||||
let comp_algos_c2s = 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 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<u8> = rng.gen_iter::<u8>().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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue