diff --git a/Cargo.lock b/Cargo.lock index 4f8dabe..6e2a4b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,11 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayref" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayvec" version = "0.4.7" @@ -38,15 +43,40 @@ dependencies = [ ] [[package]] -name = "bitflags" -version = "0.4.0" +name = "backtrace" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "bitflags" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "build_const" version = "0.2.1" @@ -67,6 +97,14 @@ name = "cfg-if" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chacha20-poly1305-aead" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.31.2" @@ -81,6 +119,27 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clear_on_drop" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crc" version = "1.8.1" @@ -120,15 +179,31 @@ dependencies = [ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "curve25519-dalek" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "data-encoding" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "encode_unicode" -version = "0.1.3" +name = "digest" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "env_logger" @@ -142,6 +217,25 @@ dependencies = [ "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -161,6 +255,14 @@ name = "gcc" version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "generic-array" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "humantime" version = "1.1.1" @@ -286,15 +388,6 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nix" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "nix" version = "0.11.0" @@ -373,14 +466,6 @@ dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "num-traits" version = "0.2.5" @@ -404,7 +489,7 @@ dependencies = [ [[package]] name = "oxy" -version = "2.0.2" +version = "3.0.0-dev1" dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -412,20 +497,20 @@ dependencies = [ "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_cbor 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "snow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "transportation 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "transportation 2.0.0 (git+https://github.com/oxy-secure/transportation.git?branch=dev)", ] [[package]] @@ -461,6 +546,11 @@ name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.6.3" @@ -469,6 +559,16 @@ dependencies = [ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.4.2" @@ -479,6 +579,23 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rayon" version = "0.8.2" @@ -496,7 +613,7 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -545,23 +662,21 @@ dependencies = [ ] [[package]] -name = "rmp" -version = "0.8.7" +name = "rust-crypto" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (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.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rmp-serde" -version = "0.13.7" +name = "rustc-demangle" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "rustc-serialize" @@ -569,16 +684,11 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "rustyline" -version = "1.0.0" +name = "rustc_version" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "encode_unicode 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -586,6 +696,19 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.66" @@ -625,16 +748,54 @@ name = "smallvec" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "snow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "chacha20-poly1305-aead 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "x25519-dalek 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "stable_deref_trait" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "static_slice" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "subtle" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.2" @@ -645,6 +806,23 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "term_size" version = "0.3.1" @@ -699,6 +877,16 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.4.6" @@ -710,21 +898,23 @@ dependencies = [ [[package]] name = "transportation" version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/oxy-secure/transportation.git?branch=dev#c8539f2673db5931ff18932d2ca63705004dea1e" dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libflate 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_cbor 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ucd-util" version = "0.1.1" @@ -735,6 +925,11 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -814,29 +1009,49 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "x25519-dalek" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "curve25519-dalek 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" -"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" +"checksum backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbdd17cd962b570302f5297aea8648d5923e22e555c2ed2d8b2e34eca646bf6d" +"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum chacha20-poly1305-aead 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77d2058ba29594f69c75e8a9018e0485e3914ca5084e3613cd64529042f5423b" "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum curve25519-dalek 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5808ccadbb61565fd184702be128ac43a6a33561bcd976a0d7a388b06ad696d" "checksum data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67df0571a74bf0d97fb8b2ed22abdd9a48475c96bd327db968b7d9cace99655e" -"checksum encode_unicode 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "28d65f1f5841ef7c6792861294b72beda34c664deb8be27970f36c306b7da1ce" +"checksum digest 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3cae2388d706b52f2f2f9afe280f9d768be36544bd71d1b8120cb34ea6450b55" "checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a" +"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" +"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -853,7 +1068,6 @@ dependencies = [ "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" -"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" @@ -861,7 +1075,6 @@ dependencies = [ "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" @@ -869,8 +1082,12 @@ dependencies = [ "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum rand 0.5.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3795e4701d9628a63a84d0289e66279883b40df165fca7caed7b87122447032a" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" @@ -878,30 +1095,41 @@ dependencies = [ "checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e" "checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54" "checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" -"checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" -"checksum rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustyline 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00b06ac9c8e8e3e83b33d175d39a9f7b6c2c930c82990593719c8e48788ae2d9" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" "checksum serde_cbor 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ad7872ff6e6c2a9221f4c1abe681e7eefc56ca5b3e87196afbfc717d141dc8" "checksum serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "0a90213fa7e0f5eac3f7afe2d5ff6b088af515052cc7303bd68c7e3b91a3fb79" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "312a7df010092e73d6bbaf141957e868d4f30efd2bfd9bb1028ad91abec58514" +"checksum snow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "704bd1cf92bbb3759737e0b3a3f40b3deda7a888d07c6a27288b4ba1bea65f1c" "checksum stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbc596e092fe5f598b12ef46cc03754085ac2f4d8c739ad61c4ae266cc3b3fa" +"checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum subtle 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b3d2ddb6e9a9cc03eb7806a1c994ae1221259669bbe4efe9dd2dff136d3a117d" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c67da57e61ebc7b7b6fff56bb34440ca3a83db037320b0507af4c10368deda7d" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" -"checksum transportation 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4903c1973d641c184b1117e5197eb84b289ecf99ceb3a7a21841e327bcdcd0e" +"checksum transportation 2.0.0 (git+https://github.com/oxy-secure/transportation.git?branch=dev)" = "" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" @@ -915,3 +1143,4 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum x25519-dalek 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92f4783a5e4f8ac0c1f0a4fd1c7123580f3e5d7ea6bcd273e5aafda0fb107927" diff --git a/Cargo.toml b/Cargo.toml index 74492a9..ec17d8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["edition"] [package] name = "oxy" -version = "2.0.2" +version = "3.0.0-dev1" authors = ["Jenna Magius "] license = "BSD-2-Clause" description = "A security focused remote access tool." @@ -16,21 +16,21 @@ clap = "2.31" toml = "0.4" serde = "1.0" serde_derive = "1.0" +serde_cbor = "0.8" +libflate = "0.1" byteorder = "1.2" log = "0.4" env_logger = "0.5" shlex = "0.1" -transportation = { version = "2", features = ["protocol", "encrypt"] } +transportation = { git = "https://github.com/oxy-secure/transportation.git", branch = "dev", features = ["encrypt"] } lazy_static = "1.0" num = "0.1" -rustyline = "1.0" -rmp = "0.8" -rmp-serde = "0.13" nix = "0.11" libc = "0.2.40" data-encoding = "2.1.1" textwrap = { version = "0.10", features = ["term_size"] } parking_lot = { version = "0.6", features = ["nightly"] } +snow = "0.2.1" [target.'cfg(unix)'.dependencies] termion = "1.5.1" diff --git a/protocol.txt b/protocol.txt deleted file mode 100644 index eb9a151..0000000 --- a/protocol.txt +++ /dev/null @@ -1,108 +0,0 @@ -There are three main phases to the lifecycle of an Oxy connection: the knock, the kex, and the main body. - - -THE KNOCK -========= - -The knock phase consists of a client sending a single UDP packet to a server on a specific port between 1025 and 65535. This packet is built using a piece of data known as the "knock key". The knock key is a collection of random bytes that are pre-shared out-of-band between the server and all clients of the server. The port number for the knock is derived from this data, along with the message contents. Message contents are a fixed-width (100 byte) value derived from hashing the knock key with the current number of seconds since the unix epoch modulo 60. - -Upon receiving a UDP packet on the designated port, the server calculates (and caches) knock packet values for the current time-window, the previous time-window (i.e. minus 60 seconds), and the next time-window (i.e. plus sixty seconds). The server performs a byte-for-byte comparison of the packet value with the server-calculated knock values. In the case of an exact match, the server records the source address and monotonic timestamp of the knock packet. Each knock value is retained for fifty seconds after being received. - -When the server has valid knock values recorded, it establishes a TCP bind and begins accepting TCP connection. Upon receiving a TCP connection the server checks the source address to identify if the source address corresponds to a valid outstanding knock. If it does not, the server terminates the connection immediately without transmitting any data. If the connection does correspond to a valid source address, the server forks: the child process re-executes the oxy executable to become a connection process, whereas the server process returns to processing incoming connections. - -Fifty seconds after the most recent valid knock was received, the server closes the TCP bind and will no longer accept incoming TCP connections. - -THE KEX -======= - -The kex (or "Key EXchange") consists of the client sending three size-specified messages to the server, then the server sending three size-specified replied. Messages consist of two bytes (big endian, unsigned), specifying the size of the following message. - -The first message consists of one byte indicating the kex version number (currently must be zero), followed by a Ed25519 public key value. This is the long-term client key. - -The second message consists of eight bytes containing a big-endian representation of the number of whole seconds since the unix epoch, followed by a different X25519 public key. This is the ephemeral client key. - -The third message contains an Ed25519 signature of the value of the second message, signed using the long-term client key from the first message. - -The server performs authentication intially by directly comparing the shared long-term client key value to its database of known client keys. No deserialization or cryptographic processing is done at this time. Only when the server is pre-existingly in possession of a byte-for-byte identical public key value does the server proceed with signature verification. Upon successful verification of the signature contained in the third message (and verification that the eight-byte timestamp is current), the server proceeds to send three symmetrical messages: a long term server public key, an ephemeral server public key, and a signature message authenticating the ephemeral key. - -At this point, both parties derive the connection key by performing Elliptic Curve Diffie-Hellman using the ephemeral keys, and combining the result with the pre-shared static key using the PBKFD2 algorithm. Note: the pre-shared static key used at this step is different than the pre-shared static key used as the "knock key". The static key used at this step may be different for each user of a particular Oxy server, and is selected based on the long-term client key sent in the first kex message. The use of a static key at this point ensures the protocol is robust against adversaries who are able to quickly undermine the ECDSA or ECDH algorithms (i.e. adversaries with effective quantum computing). - -Two distinct static keys are derived from the ECDH Result + PSK: an "Alice" key and a "Bob" key. Both keys are identically derived by both parties, but the use of separate keys prevents initialization vector re-use as both parties transmit data independently. The side of the connection imagined to correspond to a user with a keyboard transmits using the "Alice" key, whereas the non-user side transmits using the "Bob" key. - -THE BODY -======== - -The body of an Oxy connection consists of a series of fixed-size (272 byte) frames of AES-256-GCM data. Each frame consists of 256 bytes of payload data and 16 bytes of Authenticated Encryption tag. Initialization vectors start at zero for each side of the connection, and each side increments its initialization vector counter by one when it transmits a frame. - -Inside the decrypted 256 bytes of payload data is one byte that indicates the number of bytes in each frame that is "relevant data". A "saturated" frame contains 255 bytes of relevant data. An "unsaturated" frame consists of less than 255 bytes of relevant data. A protocol message is delivered across any number of saturated frames, terminated by one unsaturated frame. The use of fixed-size frames limits the ability of attackers to derive information from message length, and in some cases prevents attackers from being able to identify underlying message boundaries. - -A protocol message is a CBOR (RFC 7049) document corresponding to an enum variant of the OxyMessage enum. Enum variants are described using their variant number - as such, re-ordering variants, or inserting a new variant at any location other than the end of the enumeration constitutes a breaking protocol change. As of this writing, there are 45 established variants. - -A full listing of current message variants (with payloads) is included below. This information is copied from the source code file "message.rs": - -pub enum OxyMessage { - ProtocolVersionQuery { }, - ProtocolVersionAnnounce { version: u64 }, - Ping { }, - Pong { }, - Exit { }, - DummyMessage { data: Vec }, - BasicCommand { command: String }, - BasicCommandOutput { stdout: Vec, stderr: Vec }, - PipeCommand { command: String }, - PipeCommandOutput { reference: u64, stdout: Vec, stderr: Vec }, - PipeCommandInput { reference: u64, input: Vec }, - PipeCommandExited { reference: u64 }, - Reject { reference: u64, note: String }, - Success { reference: u64 }, - PtyRequest { command: Option }, - PtySizeAdvertisement { w: u16, h: u16 }, - PtyInput { data: Vec }, - PtyOutput { data: Vec }, - PtyExited { status: i32 }, - DownloadRequest { path: String, offset_start: Option, offset_end: Option }, - UploadRequest { path: String, filepart: String, offset_start: Option }, - FileData { reference: u64, data: Vec }, - RemoteOpen { addr: String }, - RemoteBind { addr: String }, - CloseRemoteBind { reference: u64 }, - RemoteStreamData { reference: u64, data: Vec }, - LocalStreamData { reference: u64, data: Vec }, - RemoteStreamClosed { reference: u64 }, - LocalStreamClosed { reference: u64 }, - BindConnectionAccepted { reference: u64 }, - TunnelRequest { tap: bool, name: String }, - TunnelData { reference: u64, data: Vec }, - StatRequest { path: String }, - StatResult { reference: u64, len: u64, is_dir: bool, is_file: bool, owner: String, group: String, octal_permissions: u16, atime: Option, mtime: Option, ctime: Option }, - ReadDir { path: String }, - ReadDirResult { reference: u64, complete: bool, answers: Vec }, - FileHashRequest { path: String, offset_start: Option, offset_end: Option, hash_algorithm: u64 }, - FileHashData { reference: u64, digest: Vec }, - FileTruncateRequest { path: String, len: u64 }, - KnockForward { destination: String, knock: Vec }, - AdvertiseXAuth { cookie: String }, - UsernameAdvertisement { username: String }, - CompressionRequest { compression_type: u64 }, - CompressionStart { compression_type: u64 }, - EnvironmentAdvertisement { key: String, value: String}, -} - -The oxy protocol relies upon at least half in-order message delivery for message semantics. Full in-order delivery is ensured by the TCP protocol, but this is more than is required as each side maintains a separate "Alice" ticker and "Bob" ticker identifying the protocol message number of each message as it is processed. These tickers are similar to the IV tickers used for encryption frames, but spread apart from the IV tickers anytime a protocol message is spread across more than one frame. - -In many cases, one protocol message will reference a previous protocol message by number (fields name "reference") in the above listing. For example, suppose Alice has previously sent 50 messages, and Bob has previously sent 100 messages. Then, Alice sends a message such as: - -DownloadRequest { - path: "/etc/shadow", - offset_start: None, - offset_end: None, -} - -That message will be recorded by Bob as "inbound message 51". Bob may then attempt to open /etc/shadow but be met with a permission denied eror. Bob would then respond: - -Reject { - reference: 51, - note: "Permission denied" -} - -Oxy is designed to gracefully reject messages with variant numbers larger than all known variants. This permits future extension of the message list without causing a compatibility break. However, it should be noted that this project exists as a break-away from the SSH standard founded on the principal that backwards compatibilty is not a sufficient justification for failings of functionality. As such, this protocol may undergo multiple breaking changes in the future. In particular, key exchange is likely to change from a Ed25519 based mechanism to a supersingular isogeny based method as supersingular isogeny cryptography achieves greater maturity. diff --git a/src/arg.rs b/src/arg.rs index aa003f3..9d5c4ea 100644 --- a/src/arg.rs +++ b/src/arg.rs @@ -24,7 +24,7 @@ crate fn create_app() -> App<'static, 'static> { .takes_value(true) .help("Use [identity] as authentication information for connecting to the remote server.") .env("OXY_IDENTITY"); - let command = Arg::with_name("command").index(2); + let command = Arg::with_name("command").index(2).multiple(true); let l_portfwd = Arg::with_name("local port forward") .multiple(true) .short("L") @@ -53,10 +53,6 @@ crate fn create_app() -> App<'static, 'static> { .help("The port used for TCP") .takes_value(true) .default_value("2600"); - let user = Arg::with_name("user") - .long("user") - .takes_value(true) - .help("The remote username to log in with. Only applicable for servers using --su-mode"); let via = Arg::with_name("via") .long("via") .takes_value(true) @@ -92,6 +88,17 @@ crate fn create_app() -> App<'static, 'static> { .short("C") .long("compress") .help("Enable ZLIB format compression of all transmitted data"); + let no_tmux = Arg::with_name("no tmux") + .long("no-tmux") + .help("Do not use a terminal multiplexer as the default pty command"); + let multiplexer = Arg::with_name("multiplexer") + .long("multiplexer") + .default_value("/usr/bin/tmux new-session -A -s oxy") + .help( + "The command to attach to a terminal multiplexer. Ignored if the first component is not an existent file, or if --no-tmux is supplied.", + ); + let tun = Arg::with_name("tun").long("tun").help("Connect two tunnel devices together. This will work if either: both sides of the connection have root privileges (not recommended), or if the devices have been previously created with appropriate permissions (e.g. 'ip tuntap add tun0 mode tun user [youruser]')").takes_value(true).value_name("local[:remote]"); + let tap = Arg::with_name("tap").long("tap").help("Connect two tap devices together. This will work if either: both sides of the connection have root privileges (not recommended), or if the devices have been previously created with appropriate permissions (e.g. 'ip tuntap add tap0 mode tap user [youruser]')").takes_value(true).value_name("local[:remote]"); let client_args = vec![ metacommand.clone(), identity.clone(), @@ -103,10 +110,11 @@ crate fn create_app() -> App<'static, 'static> { trusted_xforward, server_config.clone(), client_config.clone(), - user, via, compression.clone(), verbose.clone(), + tun, + tap, command, ]; let server_args = vec![ @@ -116,6 +124,8 @@ crate fn create_app() -> App<'static, 'static> { identity.clone(), port.clone(), verbose.clone(), + no_tmux.clone(), + multiplexer.clone(), ]; let subcommands = vec![ @@ -134,8 +144,7 @@ crate fn create_app() -> App<'static, 'static> { .arg(unsafe_reexec), SubCommand::with_name("serve-one") .about("Accept a single TCP connection, then service it in the same process.") - .args(&server_args) - .arg(Arg::with_name("bind-address").index(1).default_value("::0")), + .args(&server_args), SubCommand::with_name("reverse-server") .about("Connect out to a listening client. Then, be a server.") .args(&server_args) @@ -150,8 +159,8 @@ crate fn create_app() -> App<'static, 'static> { .arg(server_config) .arg(compression) .arg(Arg::with_name("location").index(1).multiple(true).number_of_values(1)) - .arg(identity.clone()) - .arg(verbose.clone()), + .arg(&identity) + .arg(&verbose), SubCommand::with_name("guide").about("Print information to help a new user get the most out of Oxy."), SubCommand::with_name("keygen").about("Generate keys"), ]; diff --git a/src/conf.rs b/src/conf.rs index 93dd5b8..81c55a4 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -36,10 +36,6 @@ fn load_conf() -> Conf { result } -crate fn has_server_conf() -> bool { - CONF.server.is_some() -} - fn load_from_home(path: &str) -> Option { let mut path = path.to_string(); if path.starts_with("~") { @@ -93,14 +89,35 @@ impl Conf { return; } let path = path.unwrap(); - self.server = load_from_home(path); - } -} + let mut result = load_from_home(path); -crate fn server_identity() -> Option<&'static str> { - match &CONF.server { - Some(Table(table)) => table.get("identity").map(|x| x.as_str().expect("Identity is not a string?")), - _ => None, + let mut name_ticker: u64 = 0; + + if let Some(table) = result.as_mut() { + if let Some(clients) = table.get_mut("clients") { + if let Some(clients) = clients.as_array_mut() { + for client in clients { + if let Some(client) = client.as_table_mut() { + if !client.contains_key("name") { + client.insert( + "name".to_string(), + ::toml::Value::String(format!("generated-client-name-{}", name_ticker)), + ); + name_ticker += 1; + } else { + if let Some(name) = client.get("name").unwrap().as_str() { + if name.starts_with("generated-client-name-") { + warn!("Warning!!! You are using a statically set client name that starts with 'generated-client-name-'. This is not recommended."); + } + } + } + } + } + } + } + } + + self.server = result; } } @@ -232,38 +249,6 @@ crate fn get_setuser(peer: &str) -> Option { Some(client(peer)?.get("setuser")?.as_str()?.to_string()) } -crate fn client_identity_for_peer(peer: &str) -> Option<&'static str> { - debug!("Trying to load a client identity for {}", peer); - match &CONF.client { - Some(Table(table)) => { - let default_identity = table.get("identity").map(|x| x.as_str().expect("Identity is not a string?")); - debug!("Default identity is {:?}", default_identity); - let servers = table.get("servers"); - debug!("Servers table is {:?}", servers); - match servers { - Some(Array(servers)) => { - for server in servers { - debug!("Examining server {:?}", server.as_table().unwrap().get("name")); - if server.as_table().unwrap().get("name").unwrap().as_str().unwrap() == peer { - debug!("Found matching server entry in client config"); - let identity = server.as_table().unwrap().get("identity").map(|x| x.as_str().unwrap()); - return if identity.is_none() { default_identity } else { identity }; - } - } - default_identity - } - _ => default_identity, - } - } - _ => None, - } -} - -crate fn client_identity() -> Option<&'static str> { - debug!("Trying to load a client identity"); - client_identity_for_peer(&crate::arg::destination()) -} - fn host_part<'a>(dest: &'a str) -> &'a str { if dest.starts_with('[') { return dest.splitn(2, '[').nth(1).unwrap().splitn(2, ']').next().unwrap(); @@ -449,11 +434,42 @@ crate fn locate_destination(dest: &str) -> Vec { return result.unwrap().collect(); } -crate fn identity() -> Option<&'static str> { - match crate::arg::mode().as_str() { - "server" => server_identity(), - "serve-one" => server_identity(), - "client" => client_identity(), - _ => None, +crate fn forced_command(peer: Option<&str>) -> Option { + serverside_setting(peer, "forced command", "forcedcommand") +} + +crate fn multiplexer(peer: Option<&str>) -> Option { + serverside_setting(peer, "multiplexer", "multiplexer") +} + +crate fn serverside_setting(peer: Option<&str>, arg: &str, key: &str) -> Option { + if crate::arg::matches().occurrences_of(arg) > 0 { + let setting = crate::arg::matches().value_of(arg); + if setting.is_some() { + return Some(setting.unwrap().to_string()); + } + } + if peer.is_some() { + let client = client(peer.unwrap()); + if let Some(client) = client { + let setting = client.get(key); + if let Some(setting) = setting { + if let Some(setting) = setting.as_str() { + return Some(setting.to_string()); + } + } + } } + + if let Some(server) = CONF.server.as_ref() { + if let Some(server) = server.as_table() { + if let Some(setting) = server.get(key) { + if let Some(setting) = setting.as_str() { + return Some(setting.to_string()); + } + } + } + } + + crate::arg::matches().value_of(arg).map(|x| x.to_string()) } diff --git a/src/copy.rs b/src/copy.rs index 70816d9..3994bea 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -133,7 +133,10 @@ impl CopyManager { let path = path.to_str().unwrap().to_string(); let path = path.trim_right_matches('/').to_string(); info!("Processing {:?}", path); - let id = connection.send(StatRequest { path: path.clone() }); + let id = connection.send(StatRequest { + path: path.clone(), + follow_links: true, + }); let proxy = self.clone(); connection.clone().watch(Rc::new(move |message, _| match message { StatResult { reference, is_dir, len, .. } if *reference == id => { diff --git a/src/core.rs b/src/core.rs index e49f552..3ff9e70 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,16 +1,17 @@ +mod drop_privs; mod handle_message; mod kex; mod metacommands; mod restrict_message; -use self::kex::{KexData, NakedState}; +use self::kex::NakedState; use byteorder::{self, ByteOrder}; #[cfg(unix)] use crate::pty::Pty; #[cfg(unix)] use crate::tuntap::TunTap; use crate::{ - arg, keys, + arg, message::OxyMessage::{self, *}, ui::Ui, }; @@ -28,9 +29,10 @@ use std::{ use transportation::{ self, mio::net::TcpListener, - set_timeout, BufferedTransport, EncryptedTransport, + ring::rand::SecureRandom, + set_timeout, BufferedTransport, EncryptionPerspective::{Alice, Bob}, - MessageTransport, Notifiable, Notifies, ProtocolTransport, + Notifiable, Notifies, }; #[derive(Clone)] @@ -54,8 +56,8 @@ crate struct PipeChild { #[derive(Default)] crate struct OxyInternal { - naked_transport: RefCell>, - underlying_transport: RefCell>, + naked_transport: RefCell>, + noise_session: RefCell>, peer_name: RefCell>, piped_children: RefCell>, ui: RefCell>, @@ -67,7 +69,6 @@ crate struct OxyInternal { remote_streams: RefCell>, remote_bind_destinations: RefCell>, naked_state: RefCell, - kex_data: RefCell, socks_binds: RefCell>, last_message_seen: RefCell>, launched: RefCell, @@ -85,6 +86,9 @@ crate struct OxyInternal { peer_user: RefCell>, message_claim: RefCell, privs_dropped: RefCell, + outbound_compression: RefCell, + inbound_compression: RefCell, + inbound_cleartext_buffer: RefCell>, #[cfg(unix)] pty: RefCell>, #[cfg(unix)] @@ -112,9 +116,8 @@ impl Oxy { pub fn create>(transport: T) -> Oxy { let bt: BufferedTransport = transport.into(); - let mt = >::from(bt); let internal = OxyInternal::default(); - *internal.naked_transport.borrow_mut() = Some(mt); + *internal.naked_transport.borrow_mut() = Some(bt); *internal.last_message_seen.borrow_mut() = Some(Instant::now()); let x = Oxy { internal: Rc::new(internal) }; let proxy = x.clone(); @@ -131,6 +134,10 @@ impl Oxy { x } + pub fn peer(&self) -> Option { + self.internal.peer_name.borrow().clone() + } + pub fn set_peer_name(&self, name: &str) { trace!("Setting peer name to {:?}", name); *self.internal.peer_name.borrow_mut() = Some(name.to_string()); @@ -180,7 +187,7 @@ impl Oxy { } fn is_encrypted(&self) -> bool { - self.internal.underlying_transport.borrow().is_some() + self.internal.noise_session.borrow().is_some() && self.internal.noise_session.borrow().as_ref().unwrap().is_handshake_finished() } fn launch(&self) { @@ -211,27 +218,99 @@ impl Oxy { self.advertise_client_key(); } if self.perspective() == Bob { - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForClientKey; + *self.internal.naked_state.borrow_mut() = NakedState::WaitingForInitiator; } } - pub fn run>(transport: T) -> ! { + crate fn run>(transport: T) -> ! { Oxy::create(transport); transportation::run(); } - pub fn send(&self, message: OxyMessage) -> u64 { + crate fn send(&self, message: OxyMessage) -> u64 { let message_number = self.tick_outgoing(); debug!("Sending message {}", message_number); trace!("Sending message {}: {:?}", message_number, message); - if self.internal.underlying_transport.borrow().is_none() { + if !self.is_encrypted() { error!("Attempted to send protocol message before key-exchange completed."); crate::exit::exit(1); } - self.internal.underlying_transport.borrow().as_ref().unwrap().send(message); + let serialized: Vec = serialize(message); + let framed: Vec> = frame(serialized); + for frame in framed { + let encrypted_frame: Vec = self.encrypt(frame); + self.internal.naked_transport.borrow().as_ref().unwrap().put(&encrypted_frame); + } message_number } + crate fn encrypt(&self, message: impl AsRef<[u8]>) -> Vec { + let mut buf = [0u8; 65535].to_vec(); + let result = self + .internal + .noise_session + .borrow_mut() + .as_mut() + .unwrap() + .write_message(message.as_ref(), &mut buf); + if result.is_err() { + error!("Failed to encrypt outbound message {:?}", result); + ::std::process::exit(1); + } + buf.resize(result.unwrap(), 0); + buf + } + + crate fn decrypt(&self, message: impl AsRef<[u8]>) -> Vec { + let mut buf = [0u8; 65535].to_vec(); + let result = self + .internal + .noise_session + .borrow_mut() + .as_mut() + .unwrap() + .read_message(message.as_ref(), &mut buf); + if result.is_err() { + error!("Failed to decrypt incoming frame: {:?}", result); + ::std::process::exit(1); + } + buf.resize(result.unwrap(), 0); + buf + } + + crate fn recv(&self) -> Option<(OxyMessage, u64)> { + loop { + let frame = self.internal.naked_transport.borrow().as_ref().unwrap().take_chunk(272); + if frame.is_none() { + return None; + } + let frame = frame.unwrap(); + let plaintext = self.decrypt(frame); + if plaintext.len() != 256 { + error!("Incorrect frame length."); + ::std::process::exit(1); + } + let relevant_bytes = plaintext[0] as usize; + self.internal + .inbound_cleartext_buffer + .borrow_mut() + .extend(&plaintext[1..(1 + relevant_bytes)]); + if relevant_bytes != 255 { + let message_number = self.tick_incoming(); + let message: Result = ::serde_cbor::from_slice(&*self.internal.inbound_cleartext_buffer.borrow_mut()); + self.internal.inbound_cleartext_buffer.borrow_mut().clear(); + if message.is_err() { + self.send(Reject { + reference: message_number, + note: "Invalid message".to_string(), + }); + continue; + } + return Some((message.unwrap(), message_number)); + } + } + } + #[cfg(unix)] pub fn notify_tuntap(&self, reference_number: u64) { let borrow = self.internal.tuntaps.borrow_mut(); @@ -306,7 +385,7 @@ impl Oxy { } pub fn has_write_space(&self) -> bool { - self.internal.underlying_transport.borrow().as_ref().unwrap().has_write_space() + self.internal.naked_transport.borrow().as_ref().unwrap().has_write_space() } fn service_transfers(&self) { @@ -427,23 +506,14 @@ impl Oxy { } fn upgrade_to_encrypted(&self) { - if self.is_encrypted() { - return; - } debug!("Activating encryption."); - let transport = self.internal.naked_transport.borrow_mut().take().unwrap(); - let bt: BufferedTransport = match transport { - MessageTransport::BufferedTransport(bt) => bt, - _ => panic!(), - }; - let mut key = self.internal.kex_data.borrow_mut().keymaterial.as_ref().unwrap().to_vec(); - let peer = self.internal.peer_name.borrow().clone(); - key.extend(keys::static_key(peer.as_ref().map(|x| &**x))); - let et = EncryptedTransport::create(bt, self.perspective(), &key); - let pt = ProtocolTransport::create(et); let proxy = self.clone(); - pt.set_notify(Rc::new(move || proxy.notify_main_transport())); - *self.internal.underlying_transport.borrow_mut() = Some(pt); + self.internal + .naked_transport + .borrow_mut() + .as_mut() + .unwrap() + .set_notify(Rc::new(move || proxy.notify_main_transport())); self.notify_main_transport(); self.do_post_auth(); } @@ -512,20 +582,22 @@ impl Oxy { value: term, }); } - let mut cmd = vec!["pty".to_string()]; - if let Some(command) = crate::arg::matches().value_of("command") { - cmd.push(command.to_string()); + let mut cmd = vec!["pty".to_string(), "--".to_string()]; + if let Some(command) = crate::arg::matches().values_of("command") { + cmd.extend(command.map(|x| x.to_string())); } self.handle_metacommand(cmd); } else { - if let Some(cmd) = crate::arg::matches().value_of("command") { + if let Some(cmd) = crate::arg::matches().values_of("command") { let stdin_bt = BufferedTransport::from(0); let proxy = self.clone(); stdin_bt.set_notify(Rc::new(move || { proxy.notify_pipe_stdin(); })); *self.internal.stdin_bt.borrow_mut() = Some(stdin_bt); - self.handle_metacommand(vec!["pipe".to_string(), cmd.to_string()]); + let mut cmd2 = vec!["pipe".to_string(), "--".to_string()]; + cmd2.extend(cmd.into_iter().map(|x| x.to_string())); + self.handle_metacommand(cmd2); } } } @@ -602,6 +674,27 @@ impl Oxy { if arg::matches().is_present("X Forwarding") { self.initiate_x_forwarding(); } + self.init_tuntap(); + } + + fn init_tuntap(&self) { + for mode in &["tun", "tap"] { + if let Some(tuns) = arg::matches().values_of(mode) { + for tun in tuns { + let local; + let remote; + if tun.contains(':') { + let mut iter = tun.split(':'); + local = iter.next().unwrap().to_string(); + remote = iter.next().unwrap().to_string(); + } else { + local = tun.to_string(); + remote = tun.to_string(); + } + self.handle_metacommand(vec![mode.to_string(), local, remote]); + } + } + } } fn initiate_x_forwarding(&self) { @@ -611,9 +704,13 @@ impl Oxy { } else { "untrusted" }; + let mut nonce = [0u8; 8]; + transportation::RNG.fill(&mut nonce).unwrap(); + let cookiefile = format!("/tmp/oxy-{}.xauth", ::data_encoding::HEXUPPER.encode(&nonce)); + debug!("xauth cookie filename: {}", &cookiefile); let xauth = ::std::process::Command::new("xauth") .arg("-f") - .arg("/tmp/xcookie") + .arg(&cookiefile) .arg("generate") .arg(":0") .arg(".") @@ -625,13 +722,13 @@ impl Oxy { warn!("Failed to generate an xauthority cookie"); return; } - let cookie = ::std::process::Command::new("xauth").arg("-f").arg("/tmp/xcookie").arg("list").output(); + let cookie = ::std::process::Command::new("xauth").arg("-f").arg(&cookiefile).arg("list").output(); if cookie.is_err() { warn!("Failed to retrieve the xauthority cookie"); - ::std::fs::remove_file("/tmp/xcookie").ok(); + ::std::fs::remove_file(&cookiefile).ok(); return; } - ::std::fs::remove_file("/tmp/xcookie").unwrap(); + ::std::fs::remove_file(&cookiefile).unwrap(); let cookie = cookie.unwrap(); let cookie = String::from_utf8(cookie.stdout.clone()); if cookie.is_err() { @@ -736,26 +833,17 @@ impl Oxy { pub fn notify_main_transport(&self) { debug!("Core notified. Has write space: {}", self.has_write_space()); - if self.internal.underlying_transport.borrow().as_ref().unwrap().is_closed() { + if self.internal.naked_transport.borrow().as_ref().unwrap().is_closed() { eprint!("\n\r"); self.log_info("Connection loss detected."); crate::exit::exit(0); } loop { - let message = self.internal.underlying_transport.borrow().as_ref().unwrap().recv_tolerant(); + let message = self.recv(); if message.is_none() { break; } - let message = message.unwrap(); - let message_number = self.tick_incoming(); - if message.is_none() { - self.send(Reject { - reference: message_number, - note: "Invalid message".to_string(), - }); - continue; - } - let message = message.unwrap(); + let (message, message_number) = message.unwrap(); let result = self.handle_message(message, message_number); if result.is_err() { self.send(Reject { @@ -832,3 +920,29 @@ enum SocksState { Initial, Authed, } + +fn serialize(message: OxyMessage) -> Vec { + let result = ::serde_cbor::ser::to_vec_packed(&message); + if result.is_err() { + error!("Failed to serialize outbound message: {:?}, {:?}", result, message); + ::std::process::exit(1); + } + result.unwrap() +} + +fn frame(data: Vec) -> Vec> { + let mut result = Vec::new(); + if data.is_empty() { + return result; + } + for i in data.chunks(255) { + let mut frame = vec![i.len() as u8]; + frame.extend(i); + frame.resize(256, 0); + result.push(frame); + } + if result.iter().last().unwrap()[0] == 255 { + result.push([0u8; 256][..].to_vec()); + } + result +} diff --git a/src/core/drop_privs.rs b/src/core/drop_privs.rs new file mode 100644 index 0000000..d9a49d0 --- /dev/null +++ b/src/core/drop_privs.rs @@ -0,0 +1,73 @@ +use crate::core::Oxy; +#[allow(unused_imports)] +use log::{debug, error, info, log, trace, warn}; +use std::ffi::CString; + +impl Oxy { + crate fn drop_privs(&self) { + for (k, _) in ::std::env::vars() { + if ["LANG", "SHELL", "HOME", "TERM", "USER", "RUST_BACKTRACE", "RUST_LOG", "PATH"].contains(&k.as_str()) { + continue; + } + ::std::env::remove_var(&k); + } + let peer = self.internal.peer_name.borrow().clone(); + if let Some(peer) = peer { + let setuser = crate::conf::get_setuser(&peer); + if let Some(setuser) = setuser { + info!("Setting user: {}", setuser); + let pwent = crate::util::getpwnam(&setuser); + if pwent.is_err() { + error!("Failed to gather user information for {}", setuser); + ::std::process::exit(1); + } + let pwent = pwent.unwrap(); + ::std::env::set_var("HOME", &pwent.home); + ::std::env::set_var("SHELL", &pwent.shell); + ::std::env::set_var("USER", &pwent.name); + let gid = ::nix::unistd::Gid::from_raw(pwent.gid); + let cstr_setuser = CString::new(setuser).unwrap(); + let grouplist = ::nix::unistd::getgrouplist(&cstr_setuser, gid); + if grouplist.is_err() { + error!("Failed to get supplementary group list"); + ::std::process::exit(1); + } + let grouplist = grouplist.unwrap(); + let result = ::nix::unistd::setgroups(&grouplist[..]); + if result.is_err() { + error!("Failed to set supplementary group list"); + ::std::process::exit(1); + } + + let result = ::nix::unistd::setgid(gid); + if result.is_err() { + error!("Failed to setgid"); + ::std::process::exit(1); + } + let result = ::nix::unistd::setuid(::nix::unistd::Uid::from_raw(pwent.uid)); + if result.is_err() { + error!("Failed to setuid"); + ::std::process::exit(1); + } + let result = ::std::env::set_current_dir(pwent.home); + if result.is_err() { + let result = ::std::env::set_current_dir("/"); + if result.is_err() { + error!("Failed to change directory"); + ::std::process::exit(1); + } + } + *self.internal.privs_dropped.borrow_mut() = true; + } else { + if let Some(home) = ::std::env::home_dir() { + ::std::env::set_current_dir(home).ok(); + } + } + } + + if ::nix::unistd::getuid().is_root() && !*self.internal.privs_dropped.borrow() { + error!("Running as root, but did not drop privileges. Exiting."); + ::std::process::exit(1); + } + } +} diff --git a/src/core/handle_message.rs b/src/core/handle_message.rs index 2b6a95e..4225ed6 100644 --- a/src/core/handle_message.rs +++ b/src/core/handle_message.rs @@ -92,15 +92,7 @@ impl Oxy { } BasicCommand { command } => { self.bob_only(); - #[cfg(unix)] - let sh = "/bin/sh"; - #[cfg(unix)] - let flag = "-c"; - #[cfg(windows)] - let sh = "cmd.exe"; - #[cfg(windows)] - let flag = "/c"; - let result = ::std::process::Command::new(sh).arg(flag).arg(command).output(); + let result = ::std::process::Command::new(&command[0]).args(&command[1..]).output(); if let Ok(result) = result { self.send(BasicCommandOutput { stdout: result.stdout, @@ -113,66 +105,30 @@ impl Oxy { if compression_type != 0 { Err("Unsupported compression algorithm")?; } - let outbound_compression: bool = self - .internal - .underlying_transport - .borrow() - .as_ref() - .expect("Shouldn't happen") - .outbound_compression; + let outbound_compression: bool = *self.internal.outbound_compression.borrow(); if !outbound_compression { debug!("Activating compression"); self.send(CompressionStart { compression_type: 0 }); - self.internal - .underlying_transport - .borrow_mut() - .as_mut() - .expect("Shouldn't happen") - .outbound_compression = true; + *self.internal.outbound_compression.borrow_mut() = true; } } CompressionStart { compression_type } => { + warn!("Compression is stubbed out in this build!!! It doesn't actually do anything right now!!"); if compression_type != 0 { panic!("Unknown compression algorithm"); } - self.internal - .underlying_transport - .borrow_mut() - .as_mut() - .expect("Shouldn't happen") - .inbound_compression = true; - if !self - .internal - .underlying_transport - .borrow() - .as_ref() - .expect("Shouldn't happen") - .outbound_compression - { + *self.internal.inbound_compression.borrow_mut() = true; + if !*self.internal.outbound_compression.borrow() { debug!("Activating compression."); self.send(CompressionStart { compression_type: 0 }); - self.internal - .underlying_transport - .borrow_mut() - .as_mut() - .expect("Shouldn't happen") - .outbound_compression = true; + *self.internal.outbound_compression.borrow_mut() = true; } } PipeCommand { command } => { self.bob_only(); use std::process::Stdio; - #[cfg(unix)] - let sh = "/bin/sh"; - #[cfg(unix)] - let flag = "-c"; - #[cfg(windows)] - let sh = "cmd.exe"; - #[cfg(windows)] - let flag = "/c"; - let mut result = ::std::process::Command::new(sh) - .arg(flag) - .arg(command) + let mut result = ::std::process::Command::new(&command[0]) + .args(&command[1..]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .stdin(Stdio::piped()) @@ -263,10 +219,7 @@ impl Oxy { #[cfg(unix)] PtyRequest { command } => { self.bob_only(); - - let command2 = command.as_ref().map(|x| x.as_str()); - - let pty = Pty::forkpty(command2).map_err(|_| "forkpty failed")?; + let pty = Pty::forkpty(command, self.peer().as_ref().map(|x| x.as_str())).map_err(|_| "forkpty failed")?; let proxy = self.clone(); pty.underlying.set_notify(Rc::new(move || proxy.notify_pty())); *self.internal.pty.borrow_mut() = Some(pty); @@ -628,10 +581,14 @@ impl Oxy { let borrow = self.internal.tuntaps.borrow_mut(); borrow.get(&reference).unwrap().send(&data); } - StatRequest { path } => { + StatRequest { path, follow_links } => { self.bob_only(); let path = self.qualify_path(path); - let info = symlink_metadata(path).map_err(|_| "Failed to stat")?; + let info = if follow_links { + ::std::fs::metadata(path).map_err(|_| "Failed to stat")? + } else { + symlink_metadata(path).map_err(|_| "Failed to stat")? + }; let message = StatResult { reference: message_number, len: info.len(), diff --git a/src/core/kex.rs b/src/core/kex.rs index 54c0b34..60c0dbd 100644 --- a/src/core/kex.rs +++ b/src/core/kex.rs @@ -1,40 +1,12 @@ -use byteorder::{self, ByteOrder}; -use crate::{core::Oxy, keys}; -use data_encoding::BASE32_NOPAD; +use crate::core::Oxy; #[allow(unused_imports)] use log::{debug, error, info, log, trace, warn}; -use std::{ - ffi::CString, - time::{SystemTime, UNIX_EPOCH}, -}; -use transportation::{ - ring::{ - agreement::{self, agree_ephemeral, EphemeralPrivateKey, X25519}, - signature, - }, - untrusted::Input, - RNG, -}; - -#[derive(Default)] -pub(super) struct KexData { - crate connection_client_key: Option>, - crate client_key_evidence: Option>, - crate my_ephemeral_key: Option, - crate keymaterial: Option>, - crate server_key: Option>, - crate server_ephemeral: Option>, -} #[derive(Clone, PartialEq, Debug)] pub(super) enum NakedState { Reject, - WaitingForClientKey, - WaitingForClientEphemeral, - WaitingForClientSignature, - WaitingForServerKey, - WaitingForServerEphemeral, - WaitingForServerSignature, + WaitingForInitiator, + WaitingForResponder, } impl Default for NakedState { @@ -46,97 +18,36 @@ impl Default for NakedState { impl Oxy { pub(super) fn advertise_client_key(&self) { let peer_name = self.internal.peer_name.borrow().clone(); - trace!("x Peer name: {:?}", peer_name); - let key = keys::asymmetric_key(peer_name.as_ref().map(|x| &**x)); - let mut pubkey: Vec = key.public_key_bytes().to_vec(); - pubkey.insert(0, 0); - self.send_naked(&pubkey); - let evidence = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); - let mut evidence_buf = [0u8; 8].to_vec(); - byteorder::BE::write_u64(&mut evidence_buf, evidence); - let ephemeral_key = agreement::EphemeralPrivateKey::generate(&X25519, &*RNG).unwrap(); - evidence_buf.resize(8 + ephemeral_key.public_key_len(), 0); - ephemeral_key.compute_public_key(&mut evidence_buf[8..]).unwrap(); - self.send_naked(&evidence_buf); - let msg = key.sign(&evidence_buf); - self.send_naked(msg.as_ref()); - self.internal.kex_data.borrow_mut().my_ephemeral_key = Some(ephemeral_key); - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForServerKey; + let peer_name2 = peer_name.as_ref().map(|x| x.as_str()); + trace!("Peer name: {:?}", peer_name); + let privkey = crate::keys::get_private_key(peer_name2); + let psk = crate::keys::get_static_key(peer_name2); + let peer_public_key = crate::conf::public_key(peer_name2); + if peer_public_key.is_none() { + error!("No peer public key found."); + ::std::process::exit(1); + } + let peer_public_key = peer_public_key.unwrap(); + let mut session = ::snow::NoiseBuilder::new("Noise_IKpsk1_25519_AESGCM_SHA512".parse().unwrap()) + .local_private_key(&privkey[..]) + .psk(1, &psk[..]) + .remote_public_key(&peer_public_key[..]) + .build_initiator() + .unwrap(); + let mut message = Vec::with_capacity(65535); + message.resize(65535, 0); + let size = session.write_message(b"", &mut message).unwrap(); + self.send_naked(&message[..size]); + *self.internal.noise_session.borrow_mut() = Some(session); + *self.internal.naked_state.borrow_mut() = NakedState::WaitingForResponder; } fn send_naked(&self, message: &[u8]) { - self.internal.naked_transport.borrow_mut().as_mut().unwrap().send(message); + self.internal.naked_transport.borrow_mut().as_mut().unwrap().send_message(message); } fn recv_naked(&self) -> Option> { - self.internal.naked_transport.borrow_mut().as_ref().unwrap().recv() - } - - fn drop_privs(&self) { - for (k, _) in ::std::env::vars() { - if ["LANG", "SHELL", "HOME", "TERM", "USER", "RUST_BACKTRACE", "RUST_LOG"].contains(&k.as_str()) { - continue; - } - ::std::env::remove_var(&k); - } - let peer = self.internal.peer_name.borrow().clone(); - if let Some(peer) = peer { - let setuser = crate::conf::get_setuser(&peer); - if let Some(setuser) = setuser { - info!("Setting user: {}", setuser); - let pwent = crate::util::getpwnam(&setuser); - if pwent.is_err() { - error!("Failed to gather user information for {}", setuser); - ::std::process::exit(1); - } - let pwent = pwent.unwrap(); - ::std::env::set_var("HOME", &pwent.home); - ::std::env::set_var("SHELL", &pwent.shell); - ::std::env::set_var("USER", &pwent.name); - let gid = ::nix::unistd::Gid::from_raw(pwent.gid); - let cstr_setuser = CString::new(setuser).unwrap(); - let grouplist = ::nix::unistd::getgrouplist(&cstr_setuser, gid); - if grouplist.is_err() { - error!("Failed to get supplementary group list"); - ::std::process::exit(1); - } - let grouplist = grouplist.unwrap(); - let result = ::nix::unistd::setgroups(&grouplist[..]); - if result.is_err() { - error!("Failed to set supplementary group list"); - ::std::process::exit(1); - } - - let result = ::nix::unistd::setgid(gid); - if result.is_err() { - error!("Failed to setgid"); - ::std::process::exit(1); - } - let result = ::nix::unistd::setuid(::nix::unistd::Uid::from_raw(pwent.uid)); - if result.is_err() { - error!("Failed to setuid"); - ::std::process::exit(1); - } - let result = ::std::env::set_current_dir(pwent.home); - if result.is_err() { - let result = ::std::env::set_current_dir("/"); - if result.is_err() { - error!("Failed to change directory"); - ::std::process::exit(1); - } - } - *self.internal.privs_dropped.borrow_mut() = true; - } else { - if let Some(home) = ::std::env::home_dir() { - ::std::env::set_current_dir(home).ok(); - } - } - } - - if ::nix::unistd::getuid().is_root() && !*self.internal.privs_dropped.borrow() { - error!("Running as root, but did not drop privileges. Exiting."); - ::std::process::exit(1); - } + self.internal.naked_transport.borrow_mut().as_ref().unwrap().recv_message() } pub(super) fn notify_naked(&self) { @@ -150,136 +61,83 @@ impl Oxy { let state = self.internal.naked_state.borrow().clone(); match state { NakedState::Reject => panic!(), - NakedState::WaitingForClientKey => { - self.bob_only(); - if let Some(mut msg) = self.recv_naked() { - let version_indicator = msg.remove(0); - assert!(version_indicator == 0); - let mut peer = self.internal.peer_name.borrow().clone(); + NakedState::WaitingForInitiator => { + if let Some(message) = self.recv_naked() { + let privkey = crate::keys::get_private_key(None); + let mut session = ::snow::NoiseBuilder::new("Noise_IKpsk1_25519_AESGCM_SHA512".parse().unwrap()) + .local_private_key(&privkey[..]) + .build_responder() + .unwrap(); + let mut message_buffer = [0u8; 65535]; + session.read_message(&message, &mut message_buffer).ok(); + + let peer_public_key = session.get_remote_static().map(|x| x.to_vec()); + if peer_public_key.is_none() { + error!("Failed to extract client public key"); + } + let peer_public_key = peer_public_key.unwrap(); + + let peer = crate::keys::get_peer_for_public_key(&peer_public_key[..]); if peer.is_none() { - peer = crate::keys::get_peer_for_public_key(&msg); - *self.internal.peer_name.borrow_mut() = peer.clone(); + error!( + "Rejecting connection for unknown public key: {:?}", + ::data_encoding::BASE32_NOPAD.encode(&peer_public_key) + ); + ::std::process::exit(1); } - if !keys::validate_peer_public_key(&msg, peer.as_ref().map(String::as_ref)) { - panic!("Incorrect client key"); + + let psk = crate::conf::peer_static_key(peer.as_ref().unwrap().as_str()); + if psk.is_none() { + error!("Failed to locate PSK for peer {:?}", peer); + ::std::process::exit(1); } - debug!("Accepted client key {:?}", BASE32_NOPAD.encode(&msg)); - self.internal.kex_data.borrow_mut().connection_client_key = Some(msg.to_vec()); - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForClientEphemeral; - self.notify_naked(); - } - } - NakedState::WaitingForClientEphemeral => { - self.bob_only(); - if let Some(msg) = self.recv_naked() { - assert_timestamp(&msg[..8]); - self.internal.kex_data.borrow_mut().client_key_evidence = Some(msg.to_vec()); - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForClientSignature; - self.notify_naked(); - } - } - NakedState::WaitingForClientSignature => { - self.bob_only(); - if let Some(msg) = self.recv_naked() { - debug!("Evidence message: {:?}", msg); - let kex_data = self.internal.kex_data.borrow_mut(); - let result = signature::verify( - &signature::ED25519, - Input::from(kex_data.connection_client_key.as_ref().unwrap()), - Input::from(kex_data.client_key_evidence.as_ref().unwrap()), - Input::from(&msg), - ); + let psk = psk.unwrap(); + + let mut session = ::snow::NoiseBuilder::new("Noise_IKpsk1_25519_AESGCM_SHA512".parse().unwrap()) + .local_private_key(&privkey) + .psk(1, &psk) + .build_responder() + .unwrap(); + let result = session.read_message(&message, &mut message_buffer); + if result.is_err() { + error!("Handshake failed: {:?}", result); + ::std::process::exit(1); + } + + let result = session.write_message(b"", &mut message_buffer); if result.is_err() { - error!("Client kex signature verification failed."); + error!("Failed to generate handshake response: {:?}", result); ::std::process::exit(1); } + self.send_naked(&message_buffer[..result.unwrap()]); + + let session = session.into_transport_mode().unwrap(); + debug!("Handshake successful!"); + + *self.internal.noise_session.borrow_mut() = Some(session); + self.drop_privs(); - ::std::mem::drop(kex_data); - let ephemeral = agreement::EphemeralPrivateKey::generate(&X25519, &*RNG).unwrap(); - let peer_name = self.internal.peer_name.borrow().clone(); - let server_key = keys::asymmetric_key(peer_name.as_ref().map(|x| &**x)); - let mut public_key_message: Vec = server_key.public_key_bytes().to_vec(); - public_key_message.insert(0, 0); - self.send_naked(&public_key_message); - let mut buf = Vec::new(); - buf.resize(ephemeral.public_key_len() + 8, 0); - let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); - byteorder::BE::write_u64(&mut buf[..8], timestamp); - ephemeral.compute_public_key(&mut buf[8..]).unwrap(); - self.send_naked(&buf); - self.send_naked(server_key.sign(&buf).as_ref()); - let keymaterial = agree_ephemeral( - ephemeral, - &X25519, - Input::from(&self.internal.kex_data.borrow_mut().client_key_evidence.as_ref().unwrap()[8..]), - (), - |x| Ok(x.to_vec()), - ).unwrap(); - debug!("Got keymaterial: {:?}", keymaterial); - self.internal.kex_data.borrow_mut().keymaterial = Some(keymaterial); self.upgrade_to_encrypted(); } } - NakedState::WaitingForServerKey => { - self.alice_only(); - if let Some(mut msg) = self.recv_naked() { - let version_indicator = msg.remove(0); - assert!(version_indicator == 0); - debug!("Host key: {}", BASE32_NOPAD.encode(&msg)); - let peer = self.internal.peer_name.borrow().clone(); - if !keys::validate_peer_public_key(&msg, peer.as_ref().map(String::as_ref)) { - panic!("Invalid host key!"); - } - self.internal.kex_data.borrow_mut().server_key = Some(msg); - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForServerEphemeral; - self.notify_naked(); - } - } - NakedState::WaitingForServerEphemeral => { - self.alice_only(); - if let Some(msg) = self.recv_naked() { - self.internal.kex_data.borrow_mut().server_ephemeral = Some(msg); - *self.internal.naked_state.borrow_mut() = NakedState::WaitingForServerSignature; - self.notify_naked(); - } - } - NakedState::WaitingForServerSignature => { - self.alice_only(); - if let Some(msg) = self.recv_naked() { - let mut kex_data = self.internal.kex_data.borrow_mut(); - signature::verify( - &signature::ED25519, - Input::from(kex_data.server_key.as_ref().unwrap()), - Input::from(kex_data.server_ephemeral.as_ref().unwrap()), - Input::from(&msg), - ).unwrap(); - assert_timestamp(&kex_data.server_ephemeral.as_ref().unwrap()[..8]); - let keymaterial = agree_ephemeral( - kex_data.my_ephemeral_key.take().unwrap(), - &X25519, - Input::from(&kex_data.server_ephemeral.as_ref().unwrap()[8..]), - (), - |x| Ok(x.to_vec()), - ).unwrap(); - debug!("Got keymaterial: {:?}", keymaterial); - kex_data.keymaterial = Some(keymaterial); - ::std::mem::drop(kex_data); - self.upgrade_to_encrypted(); + NakedState::WaitingForResponder => if let Some(message) = self.recv_naked() { + let mut buf = [0u8; 65535].to_vec(); + let result = self + .internal + .noise_session + .borrow_mut() + .as_mut() + .unwrap() + .read_message(&message, &mut buf); + if result.is_err() { + error!("Handshake failed: {:?}", result); + ::std::process::exit(1); } - } - } - } -} - -fn assert_timestamp(timestamp: &[u8]) { - let time = byteorder::BE::read_u64(×tamp); - let expected_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); - if !((time > (expected_time - 60)) && (time < (expected_time + 60))) { - error!("Out-of-date kex signature detected. This either means clock-skew or malice."); - ::std::process::exit(1); - #[allow(unreachable_code)] - { - panic!(); + let session = self.internal.noise_session.borrow_mut().take().unwrap().into_transport_mode().unwrap(); + debug!("Handshake successful!"); + *self.internal.noise_session.borrow_mut() = Some(session); + self.upgrade_to_encrypted(); + }, } } } diff --git a/src/core/metacommands.rs b/src/core/metacommands.rs index 34a322e..15fc4f6 100644 --- a/src/core/metacommands.rs +++ b/src/core/metacommands.rs @@ -66,13 +66,13 @@ fn create_app() -> App<'static, 'static> { "Open a remote PTY. \ Happens by default, usually not necessary", ) - .arg(Arg::with_name("command").index(1)), + .arg(Arg::with_name("command").index(1).multiple(true)), SubCommand::with_name("sh") .about( "Run a remote basic-command. \ Useful for Windows servers.", ) - .arg(Arg::with_name("command").index(1).required(true)), + .arg(Arg::with_name("command").index(1).multiple(true)), SubCommand::with_name("exit").about("Exits the Oxy client."), SubCommand::with_name("f10").about("Send F10 to the remote"), SubCommand::with_name("f12").about("Send F12 to the remote"), @@ -83,7 +83,7 @@ fn create_app() -> App<'static, 'static> { .about("Request the file hash of a file"), SubCommand::with_name("pipe") .about("Run a command without a pty.") - .arg(Arg::with_name("command").index(1).required(true)), + .arg(Arg::with_name("command").index(1).multiple(true)), SubCommand::with_name("KL") .about("Terminate a local portforward") .arg(Arg::with_name("spec").index(1).required(true)), @@ -176,12 +176,21 @@ impl Oxy { let matches = matches2.subcommand_matches(name).unwrap(); match name { "sh" => { - self.send(BasicCommand { - command: matches.value_of("command").unwrap().to_string(), - }); + let command = matches.values_of("command"); + if command.is_none() { + self.log_warn("No command provided!"); + return; + } + let command: Vec = command.unwrap().map(|x| x.to_string()).collect(); + self.send(BasicCommand { command: command }); } "pty" => { - let command = matches.value_of("command").map(|x| x.to_string()); + let command = matches.values_of("command"); + let command = if command.is_none() { + None + } else { + Some(command.unwrap().map(|x| x.to_string()).collect()) + }; let id = self.send(PtyRequest { command: command }); let proxy = self.clone(); #[cfg(unix)] @@ -215,7 +224,10 @@ impl Oxy { let offset_start = matches.value_of("offset start").map(|x| x.parse().unwrap()); let offset_end = matches.value_of("offset end").map(|x| x.parse().unwrap()); - let id = self.send(StatRequest { path: remote_path.clone() }); + let id = self.send(StatRequest { + path: remote_path.clone(), + follow_links: true, + }); let proxy = self.clone(); self.watch(Rc::new(move |message, _| match message { @@ -329,7 +341,12 @@ impl Oxy { } "upload" => { let buf: PathBuf = matches.value_of("local path").unwrap().into(); - let buf = buf.canonicalize().unwrap(); + let buf = buf.canonicalize(); + if buf.is_err() { + self.log_warn("Failed to locate local file."); + return; + } + let buf = buf.unwrap(); let remote_path = matches.value_of("remote path").unwrap_or("").to_string(); let metadata = metadata(&buf); @@ -687,11 +704,12 @@ impl Oxy { }); } "pipe" => { - let command = matches.value_of("command"); + let command = matches.values_of("command"); if command.is_none() { - self.log_warn("No command provided"); + self.log_warn("No command provided!"); + return; } - let command = command.unwrap().to_string(); + let command: Vec = command.unwrap().map(|x| x.to_string()).collect(); let reference = self.send(PipeCommand { command }); *self.internal.pipecmd_reference.borrow_mut() = Some(reference); } diff --git a/src/core/restrict_message.rs b/src/core/restrict_message.rs index df53d77..4130ead 100644 --- a/src/core/restrict_message.rs +++ b/src/core/restrict_message.rs @@ -7,6 +7,9 @@ use log::{debug, error, info, log, trace, warn}; impl Oxy { crate fn restrict_message(&self, message: OxyMessage) -> Result { + if self.perspective() == ::transportation::EncryptionPerspective::Alice { + return Ok(message); + } let message = self.restrict_forcedcommand(message)?; let message = self.restrict_portforwards(message)?; Ok(message) @@ -19,12 +22,18 @@ impl Oxy { } fn restrict_forcedcommand(&self, message: OxyMessage) -> Result { - let forced_command = crate::arg::matches().value_of("forced command"); + let forced_command = crate::conf::forced_command(self.peer().as_ref().map(|x| x.as_str())); if forced_command.is_none() { return Ok(message); } - let forced_command = forced_command.unwrap().to_string(); - debug!("Processing restrictions"); + let forced_command = forced_command.unwrap(); + let forced_command = ::shlex::split(&forced_command); + if forced_command.is_none() { + error!("Failed to parse forced command value."); + ::std::process::exit(1); + } + let forced_command = forced_command.unwrap(); + debug!("Processing restriction: forced_command: {:?}", forced_command); match message.clone() { BasicCommand { .. } => Ok(BasicCommand { command: forced_command }), diff --git a/src/keys.rs b/src/keys.rs index 1329213..9d6ca9f 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,76 +1,24 @@ use byteorder::{self, ByteOrder}; -use crate::arg::{self, perspective}; -use data_encoding; use lazy_static::{__lazy_static_create, __lazy_static_internal, lazy_static}; #[allow(unused_imports)] use log::{debug, error, info, log, trace, warn}; use std::time::UNIX_EPOCH; -use transportation::{ - self, - ring::{self, rand::SecureRandom, signature::Ed25519KeyPair}, - untrusted, - EncryptionPerspective::Alice, -}; +use transportation::ring::{self, rand::SecureRandom}; use parking_lot::Mutex; lazy_static! { - static ref IDENTITY_BYTES: Vec = identity_bytes_initializer(); static ref KNOCK_VALUES: Mutex, Vec)>> = Mutex::new(Vec::new()); } const KNOCK_ROTATION_TIME: u64 = 60; -fn identity_bytes_initializer() -> Vec { - if let Some(identity) = arg::matches().value_of("identity") { - return data_encoding::BASE32_NOPAD.decode(identity.as_bytes()).unwrap(); - } - if let Some(identity) = crate::conf::identity() { - return data_encoding::BASE32_NOPAD.decode(identity.as_bytes()).unwrap(); - } - if arg::mode() == "copy" { - warn!("No identity provided."); - return Vec::new(); - } - if arg::mode() == "guide" || arg::mode() == "keygen" { - return Vec::new(); - } - if perspective() == Alice { - error!("No identity provided. If the server doesn't know who you are it won't talk to you, and how will it know who you are if you don't know who you are?"); - ::std::process::exit(1); - } - if ::nix::unistd::getuid().is_root() { - error!("Quickstart mode is not supported when running as root. Please run as a non-root user, or establish configuration files as described in the user guide."); - ::std::process::exit(1); - } - let mut bytes = [0u8; 36].to_vec(); - transportation::RNG.fill(&mut bytes).unwrap(); - info!( - "Using quickstart mode. Run the client with --identity={}", - data_encoding::BASE32_NOPAD.encode(&bytes) - ); - bytes -} - -crate fn get_peer_id(peer: Option<&str>) -> Vec { - trace!("get_peer_id for peer {:?}", peer); - if peer.is_none() { - return IDENTITY_BYTES.to_vec(); - } - let id = crate::conf::client_identity_for_peer(peer.unwrap()); - if id.is_none() { - return IDENTITY_BYTES.to_vec(); - } - let id = id.unwrap(); - data_encoding::BASE32_NOPAD.decode(id.as_bytes()).unwrap().to_vec() -} - -crate fn static_key(peer: Option<&str>) -> Vec { +crate fn get_static_key(peer: Option<&str>) -> Vec { if let Some(key) = crate::conf::static_key(peer) { return key; } - let id = get_peer_id(peer); - id[12..24].to_vec() + error!("No PSK found"); + ::std::process::exit(1); } crate fn knock_data(peer: Option<&str>) -> Vec { @@ -82,9 +30,8 @@ crate fn knock_data(peer: Option<&str>) -> Vec { if let Some(data) = crate::conf::default_knock() { return data; } - - trace!("Failed to load knock from config"); - get_peer_id(peer)[24..].to_vec() + error!("No knock key found"); + ::std::process::exit(1); } crate fn make_knock(peer: Option<&str>) -> Vec { @@ -158,46 +105,25 @@ crate fn get_peer_for_public_key(key: &[u8]) -> Option { None } -crate fn validate_peer_public_key(key: &[u8], peer: Option<&str>) -> bool { - trace!("Validating pubkey for {:?}", peer); - if let Some(conf_key) = crate::conf::public_key(peer) { - return key[..] == conf_key[..]; - } - let pubkey = asymmetric_key(None); - key == pubkey.public_key_bytes() -} - -fn asymmetric_key_from_seed(seed: &[u8]) -> Ed25519KeyPair { - let mut seed2 = [0u8; 32]; - ring::pbkdf2::derive(&ring::digest::SHA512, 10240, b"oxy", seed, &mut seed2); - let bytes = untrusted::Input::from(&seed2); - ring::signature::Ed25519KeyPair::from_seed_unchecked(bytes).unwrap() -} - -crate fn identity_string() -> String { - data_encoding::BASE32_NOPAD.encode(&*IDENTITY_BYTES) -} - -crate fn asymmetric_key(peer: Option<&str>) -> Ed25519KeyPair { +crate fn get_private_key(peer: Option<&str>) -> Vec { if let Some(key) = crate::conf::asymmetric_key(peer) { debug!("Found key in config"); - if let Some(key) = ring::signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&key[..])).ok() { - return key; - } else { - warn!("Invalid privkey in config?"); - } + return key; } - let id = get_peer_id(peer); - debug!("Using identity data: {:?}", id); - asymmetric_key_from_seed(&id[..12]) + error!("No private key found."); + ::std::process::exit(1); } crate fn keygen() { - let asym = ring::signature::Ed25519KeyPair::generate_pkcs8(&*transportation::RNG).unwrap(); - println!("privkey = {:?}", ::data_encoding::BASE32_NOPAD.encode(&asym[..])); - let asym = ring::signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&asym)).unwrap(); - let pubkey = ::data_encoding::BASE32_NOPAD.encode(asym.public_key_bytes()); - println!("pubkey = {:?}", pubkey); + let mut dh = ::snow::CryptoResolver::resolve_dh(&::snow::DefaultResolver, &::snow::params::DHChoice::Curve25519).unwrap(); + let mut rng = ::snow::CryptoResolver::resolve_rng(&::snow::DefaultResolver).unwrap(); + ::snow::types::Dh::generate(&mut *dh, &mut *rng); + + let privkey = ::snow::types::Dh::privkey(&*dh); + let pubkey = ::snow::types::Dh::pubkey(&*dh); + println!("privkey = {:?}", ::data_encoding::BASE32_NOPAD.encode(privkey)); + println!("pubkey = {:?}", ::data_encoding::BASE32_NOPAD.encode(pubkey)); + let mut knock = [0u8; 32]; ::transportation::RNG.fill(&mut knock).unwrap(); println!("knock = {:?}", ::data_encoding::BASE32_NOPAD.encode(&knock)); diff --git a/src/message.rs b/src/message.rs index 6b38884..b6cbad0 100644 --- a/src/message.rs +++ b/src/message.rs @@ -10,15 +10,15 @@ pub enum OxyMessage { Pong { }, Exit { }, DummyMessage { data: Vec }, - BasicCommand { command: String }, + BasicCommand { command: Vec }, BasicCommandOutput { stdout: Vec, stderr: Vec }, - PipeCommand { command: String }, + PipeCommand { command: Vec }, PipeCommandOutput { reference: u64, stdout: Vec, stderr: Vec }, PipeCommandInput { reference: u64, input: Vec }, PipeCommandExited { reference: u64 }, Reject { reference: u64, note: String }, Success { reference: u64 }, - PtyRequest { command: Option }, + PtyRequest { command: Option> }, PtySizeAdvertisement { w: u16, h: u16 }, PtyInput { data: Vec }, PtyOutput { data: Vec }, @@ -36,7 +36,7 @@ pub enum OxyMessage { BindConnectionAccepted { reference: u64 }, TunnelRequest { tap: bool, name: String }, TunnelData { reference: u64, data: Vec }, - StatRequest { path: String }, + StatRequest { path: String, follow_links: bool }, StatResult { reference: u64, len: u64, is_dir: bool, is_file: bool, owner: String, group: String, octal_permissions: u16, atime: Option, mtime: Option, ctime: Option }, ReadDir { path: String }, ReadDirResult { reference: u64, complete: bool, answers: Vec }, diff --git a/src/pty.rs b/src/pty.rs index c4b25cf..87d990d 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -4,7 +4,7 @@ use log::{debug, error, info, log, trace, warn}; use nix::{ pty::openpty, unistd::{ - close, dup2, execv, fork, setsid, + close, dup2, execvp, fork, setsid, ForkResult::{Child, Parent}, Pid, }, @@ -18,33 +18,62 @@ crate struct Pty { crate child_pid: Pid, } +fn multiplexer_available(peer: Option<&str>) -> bool { + let command = crate::conf::multiplexer(peer); + if command.is_none() { + return false; + } + let command = command.unwrap(); + let command = ::shlex::split(&command); + if command.is_none() { + warn!("Failed to parse multiplexer command"); + return false; + } + let command = command.unwrap(); + if command.is_empty() { + return false; + } + let status = ::std::fs::metadata(&command[0]); + if status.is_err() { + return false; + } + return true; +} + impl Pty { - crate fn forkpty(command: Option<&str>) -> Result { + crate fn forkpty(command: Option>, peer: Option<&str>) -> Result { let result = openpty(None, None).map_err(|_| ())?; let parent_fd = result.master; let child_fd = result.slave; debug!("openpty results: {:?} {:?}", parent_fd, child_fd); let exe; - let argv; + let argv: Vec; if command.is_some() { - let sh = CString::new("/bin/sh").unwrap(); - let sh2 = CString::new("/bin/sh").unwrap(); - let minus_c = CString::new("-c").unwrap(); - let command = CString::new(command.unwrap()).unwrap(); - exe = sh; - argv = vec![sh2, minus_c, command]; + let command = command.unwrap(); + exe = CString::new(command[0].as_str()).map_err(|_| ())?; + let argv2: Vec> = command.into_iter().map(|x| CString::new(x)).collect(); + if argv2.iter().filter(|x| x.is_err()).next().is_some() { + return Err(()); + } + argv = argv2.into_iter().map(|x| x.unwrap()).collect(); } else { - let shell = crate::util::current_user_pw(); - if shell.is_err() { - error!("Failed to get user shell."); - ::std::process::exit(1); + if !crate::arg::matches().is_present("no tmux") && multiplexer_available(peer) { + let command = ::shlex::split(&crate::conf::multiplexer(peer).unwrap()).unwrap(); + argv = command.into_iter().map(|x| CString::new(x).unwrap()).collect(); + exe = argv[0].clone(); + } else { + let shell = crate::util::current_user_pw(); + if shell.is_err() { + error!("Failed to get user shell."); + ::std::process::exit(1); + } + let shell = shell.unwrap().shell; + let shell_fname = PathBuf::from(&shell).file_name().unwrap().to_str().unwrap().to_string(); + exe = CString::new(shell).unwrap(); + argv = vec![CString::new(format!("-{}", shell_fname)).unwrap()]; // A leading - makes it a login shell. Seems like a strange convention to + // me, but OK. } - let shell = shell.unwrap().shell; - let shell_fname = PathBuf::from(&shell).file_name().unwrap().to_str().unwrap().to_string(); - exe = CString::new(shell).unwrap(); - argv = vec![CString::new(format!("-{}", shell_fname)).unwrap()]; // A leading - makes it a login shell. Seems like a strange convention to - // me, but OK. } let mut pids: Vec = Vec::new(); @@ -79,7 +108,7 @@ impl Pty { } Ok(Child) => { setsid().unwrap(); - unsafe { ioctl(child_fd, TIOCSCTTY, 0) }; + unsafe { ioctl(child_fd, TIOCSCTTY.into(), 0) }; dup2(child_fd, 0).unwrap(); dup2(child_fd, 1).unwrap(); dup2(child_fd, 2).unwrap(); @@ -89,7 +118,7 @@ impl Pty { close(*i).ok(); } } - execv(&exe, &argv[..]).expect("execv failed"); + execvp(&exe, &argv[..]).expect("execvp failed"); unreachable!(); } Err(_) => panic!("Fork failed"), diff --git a/src/server.rs b/src/server.rs index a9e9c09..32605bc 100644 --- a/src/server.rs +++ b/src/server.rs @@ -32,10 +32,14 @@ struct Server { #[derive(Default)] struct ServerInternal { - knock_listener: RefCell>, - knock_token: RefCell, - tcp_listener: RefCell>, - tcp_token: RefCell, + knock4_listener: RefCell>, + knock6_listener: RefCell>, + knock4_token: RefCell, + knock6_token: RefCell, + tcp4_listener: RefCell>, + tcp6_listener: RefCell>, + tcp4_token: RefCell, + tcp6_token: RefCell, open_knocks: RefCell>, sweeper_scheduled: RefCell, serve_one: RefCell, @@ -56,24 +60,58 @@ impl Server { crate::reexec::safety_check(); let knock_port = crate::keys::knock_port(None); info!("Listening for knocks on port UDP {}", knock_port); - let bind_addr = format!("[::]:{}", knock_port).parse().unwrap(); - let mut knock_listener = UdpSocket::bind(&bind_addr); - if knock_listener.is_err() { + + { + // Knock6 + let bind_addr = format!("[::]:{}", knock_port).parse().unwrap(); + if let Ok(knock6_listener) = UdpSocket::bind(&bind_addr) { + let proxy = self.clone(); + let knock_token = transportation::insert_listener(Rc::new(move || proxy.notify_knock6())); + let mut registered = false; + transportation::borrow_poll(|poll| { + let result = poll.register(&knock6_listener, Token(knock_token), Ready::readable(), PollOpt::level()); + if result.is_err() { + warn!("Failed to register knock6 socket"); + transportation::remove_listener(knock_token); + return; + } + registered = true; + }); + if registered { + *self.i.knock6_listener.borrow_mut() = Some(knock6_listener); + *self.i.knock6_token.borrow_mut() = knock_token; + }; + } + } + + { + // Knock4 let bind_addr = format!("0.0.0.0:{}", knock_port).parse().unwrap(); - knock_listener = UdpSocket::bind(&bind_addr); - if knock_listener.is_err() { - panic!("Failed to bind knock listener."); + if let Ok(knock4_listener) = UdpSocket::bind(&bind_addr) { + let proxy = self.clone(); + let knock_token = transportation::insert_listener(Rc::new(move || proxy.notify_knock4())); + let mut registered = false; + transportation::borrow_poll(|poll| { + let result = poll.register(&knock4_listener, Token(knock_token), Ready::readable(), PollOpt::level()); + if result.is_err() { + warn!("Failed to register knock4 socket"); + transportation::remove_listener(knock_token); + return; + } + registered = true; + }); + if registered { + *self.i.knock4_listener.borrow_mut() = Some(knock4_listener); + *self.i.knock4_token.borrow_mut() = knock_token; + }; } } - let knock_listener = knock_listener.unwrap(); - let proxy = self.clone(); - let knock_token = transportation::insert_listener(Rc::new(move || proxy.notify_knock())); - transportation::borrow_poll(|poll| { - poll.register(&knock_listener, Token(knock_token), Ready::readable(), PollOpt::level()) - .unwrap(); - }); - *self.i.knock_listener.borrow_mut() = Some(knock_listener); - *self.i.knock_token.borrow_mut() = knock_token; + + if self.i.knock4_listener.borrow().is_none() && self.i.knock6_listener.borrow().is_none() { + error!("Failed to bind knock listener"); + ::std::process::exit(1); + } + let proxy = self.clone(); transportation::set_signal_handler(Rc::new(move || proxy.harvest_children())); } @@ -109,24 +147,60 @@ impl Server { } fn destroy(&self) { - let knock_listener = self.i.knock_listener.borrow_mut().take().unwrap(); - let knock_token = *self.i.knock_token.borrow(); - transportation::borrow_poll(|poll| { - poll.deregister(&knock_listener).unwrap(); - }); - transportation::remove_listener(knock_token); - if self.i.tcp_listener.borrow().is_some() { - let tcp_listener = self.i.tcp_listener.borrow_mut().take().unwrap(); - let tcp_token = *self.i.tcp_token.borrow(); + if let Some(knock_listener) = self.i.knock4_listener.borrow_mut().take() { + let knock_token = *self.i.knock4_token.borrow(); + transportation::borrow_poll(|poll| { + poll.deregister(&knock_listener).unwrap(); + }); + transportation::remove_listener(knock_token); + } + + if let Some(knock_listener) = self.i.knock6_listener.borrow_mut().take() { + let knock_token = *self.i.knock6_token.borrow(); + transportation::borrow_poll(|poll| { + poll.deregister(&knock_listener).unwrap(); + }); + transportation::remove_listener(knock_token); + } + + if let Some(tcp_listener) = self.i.tcp4_listener.borrow_mut().take() { + let tcp_token = *self.i.tcp4_token.borrow(); transportation::borrow_poll(|poll| { poll.deregister(&tcp_listener).ok(); }); transportation::remove_listener(tcp_token); } + + if let Some(tcp_listener) = self.i.tcp6_listener.borrow_mut().take() { + let tcp_token = *self.i.tcp6_token.borrow(); + transportation::borrow_poll(|poll| { + poll.deregister(&tcp_listener).ok(); + }); + transportation::remove_listener(tcp_token); + } + } + + fn notify_tcp4(&self) { + let result = self.i.tcp4_listener.borrow_mut().as_mut().unwrap().accept(); + if let Ok((stream, remote_addr)) = result { + if self.i.open_knocks.borrow().iter().filter(|x| x.1 == remote_addr.ip()).count() > 0 { + info!("Accepting connection for {:?}", remote_addr); + if !*self.i.serve_one.borrow() { + fork_and_handle(stream); + } else { + self.destroy(); + Oxy::run(stream); + } + } else { + warn!("TCP connection from somebody who didn't knock: {:?}", remote_addr); + } + } else { + warn!("Error accepting TCP connection"); + } } - fn notify_tcp(&self) { - let result = self.i.tcp_listener.borrow_mut().as_mut().unwrap().accept(); + fn notify_tcp6(&self) { + let result = self.i.tcp6_listener.borrow_mut().as_mut().unwrap().accept(); if let Ok((stream, remote_addr)) = result { if self.i.open_knocks.borrow().iter().filter(|x| x.1 == remote_addr.ip()).count() > 0 { info!("Accepting connection for {:?}", remote_addr); @@ -149,55 +223,83 @@ impl Server { !self.i.open_knocks.borrow().is_empty() } - fn bind_tcp(&self) -> Option { + fn bind_tcp4(&self) { + let port = crate::arg::matches().value_of("port").unwrap(); + let bind_addr = format!("0.0.0.0:{}", port).parse().unwrap(); + if let Ok(listener) = TcpListener::bind(&bind_addr) { + let proxy = self.clone(); + let tcp4_token = transportation::insert_listener(Rc::new(move || proxy.notify_tcp4())); + let mut registered = false; + transportation::borrow_poll(|poll| { + let result = poll.register(&listener, Token(tcp4_token), Ready::readable(), PollOpt::level()); + if result.is_err() { + transportation::remove_listener(tcp4_token); + warn!("Failed to register TCP4 listener."); + return; + } + registered = true; + }); + if registered { + *self.i.tcp4_listener.borrow_mut() = Some(listener); + *self.i.tcp4_token.borrow_mut() = tcp4_token; + } + } + } + + fn bind_tcp6(&self) { let port = crate::arg::matches().value_of("port").unwrap(); let bind_addr = format!("[::]:{}", port).parse().unwrap(); - let mut listener = TcpListener::bind(&bind_addr); - if listener.is_err() { - let bind_addr = format!("0.0.0.0:{}", port).parse().unwrap(); - listener = TcpListener::bind(&bind_addr); - if listener.is_err() { - return None; + match TcpListener::bind(&bind_addr) { + Ok(listener) => { + let proxy = self.clone(); + let tcp6_token = transportation::insert_listener(Rc::new(move || proxy.notify_tcp6())); + let mut registered = false; + transportation::borrow_poll(|poll| { + let result = poll.register(&listener, Token(tcp6_token), Ready::readable(), PollOpt::level()); + if result.is_err() { + transportation::remove_listener(tcp6_token); + warn!("Failed to register TCP6 listener."); + return; + } + registered = true; + }); + if registered { + *self.i.tcp6_listener.borrow_mut() = Some(listener); + *self.i.tcp6_token.borrow_mut() = tcp6_token; + } + } + Err(err) => { + debug!("Failed to bind TCP6: {:?}", err); } - return listener.ok(); } - return listener.ok(); } fn refresh_tcp(&self) { if self.has_pending_knocks() { - if self.i.tcp_listener.borrow().is_some() { + if self.i.tcp4_listener.borrow().is_some() || self.i.tcp6_listener.borrow().is_some() { return; } - let listener = self.bind_tcp(); - if listener.is_none() { - warn!("Failed to bind TCP listener"); - return; + + { + self.bind_tcp6(); + self.bind_tcp4(); } - let listener = listener.unwrap(); - let proxy = self.clone(); - let listen4_token = transportation::insert_listener(Rc::new(move || proxy.notify_tcp())); - transportation::borrow_poll(|poll| { - poll.register(&listener, Token(listen4_token), Ready::readable(), PollOpt::level()) - .unwrap(); - }); - *self.i.tcp_listener.borrow_mut() = Some(listener); - *self.i.tcp_token.borrow_mut() = listen4_token; } else { - if self.i.tcp_listener.borrow().is_none() { - return; + if let Some(listener) = self.i.tcp4_listener.borrow_mut().take() { + transportation::remove_listener(*self.i.tcp4_token.borrow_mut()); + transportation::borrow_poll(|poll| poll.deregister(&listener).unwrap()); + } + if let Some(listener) = self.i.tcp6_listener.borrow_mut().take() { + transportation::remove_listener(*self.i.tcp6_token.borrow_mut()); + transportation::borrow_poll(|poll| poll.deregister(&listener).unwrap()); } - let token = *self.i.tcp_token.borrow_mut(); - transportation::remove_listener(token); - let listener = self.i.tcp_listener.borrow_mut().take().unwrap(); - transportation::borrow_poll(|poll| poll.deregister(&listener).unwrap()); } } fn sweep(&self) { *self.i.sweeper_scheduled.borrow_mut() = false; self.refresh_tcp(); - if self.i.tcp_listener.borrow().is_some() { + if self.i.tcp4_listener.borrow().is_some() || self.i.tcp6_listener.borrow().is_some() { self.schedule_sweeper(); } } @@ -224,10 +326,24 @@ impl Server { } } - fn notify_knock(&self) { + fn notify_knock4(&self) { + trace!("notify_knock"); + let mut buf = [0u8; 1500]; + let mut borrow = self.i.knock4_listener.borrow_mut(); + let reader = borrow.as_mut().unwrap(); + let result = reader.recv_from(&mut buf); + if result.is_err() { + warn!("Error receiving knock packet {:?}", result); + return; + } + let (size, addr) = result.unwrap(); + self.consider_knock(&buf[..size], addr.ip()); + } + + fn notify_knock6(&self) { trace!("notify_knock"); let mut buf = [0u8; 1500]; - let mut borrow = self.i.knock_listener.borrow_mut(); + let mut borrow = self.i.knock6_listener.borrow_mut(); let reader = borrow.as_mut().unwrap(); let result = reader.recv_from(&mut buf); if result.is_err() { @@ -261,18 +377,16 @@ fn fork_and_handle(stream: TcpStream) { // O_CLOEXEC, but it can't~ let mut args = vec!["reexec".to_string(), format!("--fd={}", fd2)]; - if crate::arg::matches().is_present("identity") { - if let Some(identity) = crate::arg::matches().value_of("identity") { - args.push(format!("--identity={}", identity)); - } - } else if !crate::conf::has_server_conf() { - args.push(format!("--identity={}", crate::keys::identity_string())); - } if let Some(command) = crate::arg::matches().value_of("forced command") { args.push(format!("--forced-command={}", command)); } - if crate::arg::matches().is_present("su mode") { - args.push("--su-mode".to_string()); + if crate::arg::matches().occurrences_of("multiplexer") > 0 { + if let Some(multiplexer) = crate::arg::matches().value_of("multiplexer") { + args.push(format!("--multiplexer={}", multiplexer)); + } + } + if crate::arg::matches().is_present("no tmux") { + args.push("--no-tmux".to_string()); } reexec(&args.iter().map(|x| x.as_str()).collect::>()[..]); close(fd).unwrap(); diff --git a/src/tuntap.rs b/src/tuntap.rs index 3902a20..766cbca 100644 --- a/src/tuntap.rs +++ b/src/tuntap.rs @@ -75,8 +75,10 @@ impl TunTap { } crate fn send(&self, data: &[u8]) { - let result = write(self.fd, data).unwrap(); - assert!(result == data.len()); + let result = write(self.fd, data); + if result != Ok(data.len()) { + warn!("Failed to write tunnel data: {:?}", result); + } } } diff --git a/src/util.rs b/src/util.rs index 74e9a67..c39ddb3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,5 @@ +#[allow(unused_imports)] +use log::{debug, error, info, log, trace, warn}; use std::ffi::{CStr, CString}; crate fn format_throughput(bytes: u64, seconds: u64) -> String { diff --git a/tests/tests.rs b/tests/tests.rs index aa95987..aeb227c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -129,3 +129,26 @@ fn copy_single_file() { assert_eq!(metadata("/etc/hosts").unwrap().len(), metadata("/tmp/hosts").unwrap().len()); remove_file("/tmp/hosts").unwrap(); } + +#[test] +#[cfg(unix)] +fn copy_single_file_fail() { + let _guard = SERIAL_TESTS.lock(); + let identity = mk_identity(); + let bad_identity = mk_identity(); + let mut server = Command::new("./target/debug/oxy").arg("serve-one").arg(&identity).spawn().unwrap(); + hold(); + let mut client = Command::new("./target/debug/oxy") + .arg("copy") + .arg("localhost:/etc/hosts") + .arg("/tmp/") + .arg(&bad_identity) + .spawn() + .unwrap(); + hold(); + hold(); + hold(); + server.kill().ok(); + client.kill().ok(); + assert!(metadata("/tmp/hosts").is_err()); +}