From 7777bdec778e4874559c1589427542b6158c77f3 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sun, 25 Jun 2023 02:26:43 +0100 Subject: [PATCH] Initial commit --- .github/workflows/audit.yml | 15 +++++++++++++++ .github/workflows/ci.yml | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 2 ++ Cargo.lock | 1563 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 24 ++++++++++++++++++++++++ LICENSE | 13 +++++++++++++ README.md | 13 +++++++++++++ config.toml | 10 ++++++++++ src/audit.rs | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/command.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/server.rs | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/state.rs | 22 ++++++++++++++++++++++ 14 files changed, 2715 insertions(+) create mode 100644 .github/workflows/audit.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config.toml create mode 100644 src/audit.rs create mode 100644 src/command.rs create mode 100644 src/config.rs create mode 100644 src/main.rs create mode 100644 src/server.rs create mode 100644 src/state.rs diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..298f018 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,15 @@ +name: Security audit + +on: + push: + paths: + - '**/Cargo.toml' + +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..dac8797 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +on: [push, pull_request] + +name: CI + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - name: install libsodium + run: sudo apt-get install -y libsodium23 libsodium-dev + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + + test: + name: Test Suite + runs-on: ubuntu-latest + steps: + - name: install libsodium + run: sudo apt-get install -y libsodium23 libsodium-dev + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - name: install libsodium + run: sudo apt-get install -y libsodium23 libsodium-dev + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a5bb84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +audit.jsonl diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bae2b5d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1563 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "ctr", + "opaque-debug", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2 0.12.1", + "sha2 0.10.7", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding", + "cipher 0.3.0", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher 0.4.4", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "once_cell", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "cryptovec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc7fa13a6bbb2322d325292c57f4c8e7291595506f8289968a0eb61c3130bdf" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libsodium-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" +dependencies = [ + "cc", + "libc", + "pkg-config", + "walkdir", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", +] + +[[package]] +name = "password-hash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "base64ct", + "crypto-mac", + "hmac", + "password-hash", + "sha2 0.9.9", +] + +[[package]] +name = "pbkdf2" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pisshoff" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "fastrand", + "futures", + "itertools", + "parking_lot", + "serde", + "serde_json", + "shlex", + "thrussh", + "thrussh-keys", + "tokio", + "toml", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb8d4cebc40aa517dfb69618fa647a346562e67228e2236ae0042ee6ac14775" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "thrussh" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb7f634184fe86d7a9fd587d9350137508cba7b77626a7785db2ca695ebc503" +dependencies = [ + "bitflags", + "byteorder", + "cryptovec", + "digest 0.9.0", + "flate2", + "futures", + "generic-array", + "log", + "rand", + "sha2 0.9.9", + "thiserror", + "thrussh-keys", + "thrussh-libsodium", + "tokio", +] + +[[package]] +name = "thrussh-keys" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43d59b13e4c08db0e379bced99bda596ac5ed33651d919bf3916d34ad4259bb" +dependencies = [ + "aes", + "bcrypt-pbkdf", + "bit-vec", + "block-modes", + "byteorder", + "cryptovec", + "data-encoding", + "dirs", + "futures", + "hmac", + "log", + "md5", + "num-bigint", + "num-integer", + "pbkdf2 0.8.0", + "rand", + "serde", + "serde_derive", + "sha2 0.9.9", + "thiserror", + "thrussh-libsodium", + "tokio", + "tokio-stream", + "yasna", +] + +[[package]] +name = "thrussh-libsodium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9984d637e65935d83281d82716e600ccb91ae49e3c2fb5932abb076030e516e4" +dependencies = [ + "lazy_static", + "libc", + "libsodium-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "bit-vec", + "num-bigint", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..382c463 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pisshoff" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0" +clap = { version = "4.3", features = ["derive", "env", "cargo"] } +futures = "0.3" +parking_lot = "0.12" +fastrand = "1.9" +itertools = "0.10" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +shlex = "1.1" +thrussh = "0.34" +thrussh-keys = "0.22" +tokio = { version = "1.28", features = ["full"] } +toml = "0.7" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +uuid = { version = "1.3", features = ["v4", "serde"] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8b1a9d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c05e95b --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +

+ +

+ +

pisshoff

+ +A very simple SSH server using [thrussh][] that exposes mocked versions of common `bash` commands +to act as a honeypot for would-be crackers. + +All actions undertaken on the connection by the client are recorded in JSON format in an audit log +file. + +[thrussh]: https://crates.io/crates/thrussh \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..b8c834e --- /dev/null +++ b/config.toml @@ -0,0 +1,10 @@ +# Address for the server to listen on. +listen-address = "127.0.0.1:2233" + +# The probability that an authentication attempt will succeed, once a given password +# has been accepted once - it will be accepted for the rest of the lifetime of the +# instance. +access-probability = 0.2 + +# Path of the file to write audit logs to. +audit-output-file = "audit.jsonl" diff --git a/src/audit.rs b/src/audit.rs new file mode 100644 index 0000000..d7c842d --- /dev/null +++ b/src/audit.rs @@ -0,0 +1,191 @@ +use crate::config::Config; +use serde::Serialize; +use std::io::ErrorKind; +use std::sync::Arc; +use std::{ + fmt::{Debug, Formatter}, + net::SocketAddr, + time::{Duration, Instant}, +}; +use tokio::io::{AsyncWriteExt, BufWriter}; +use tokio::{fs::OpenOptions, task::JoinHandle}; +use uuid::Uuid; + +pub fn start_audit_writer( + config: Arc, +) -> ( + tokio::sync::mpsc::UnboundedSender, + JoinHandle>, +) { + let (send, mut recv) = tokio::sync::mpsc::unbounded_channel(); + + let handle = tokio::spawn(async move { + let file = OpenOptions::default() + .create(true) + .append(true) + .open(&config.audit_output_file) + .await?; + let mut writer = BufWriter::new(file); + + while let Some(log) = recv.recv().await { + let log = + serde_json::to_vec(&log).map_err(|e| std::io::Error::new(ErrorKind::Other, e))?; + writer.write_all(&log).await?; + } + + writer.flush().await?; + + Ok(()) + }); + + (send, handle) +} + +#[derive(Serialize)] +pub struct AuditLog { + pub connection_id: Uuid, + pub peer_address: Option, + pub environment_variables: Vec<(Box, Box)>, + pub events: Vec, + #[serde(skip)] + pub start: Instant, +} + +impl Default for AuditLog { + fn default() -> Self { + Self { + connection_id: Uuid::default(), + peer_address: None, + environment_variables: vec![], + events: vec![], + start: Instant::now(), + } + } +} + +impl Debug for AuditLog { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AuditLog") + .field("connection_id", &self.connection_id) + .field("peer_address", &self.peer_address) + .field("environment_variables", &self.environment_variables) + .field("events", &self.events) + .finish() + } +} + +impl AuditLog { + pub fn push_action(&mut self, action: AuditLogAction) { + self.events.push(AuditLogEvent { + start_offset: self.start.elapsed(), + action, + }); + } +} + +#[derive(Debug, Serialize)] +pub struct AuditLogEvent { + pub start_offset: Duration, + pub action: AuditLogAction, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub enum AuditLogAction { + LoginAttempt(LoginAttemptEvent), + PtyRequest(PtyRequestEvent), + X11Request(X11RequestEvent), + OpenX11(OpenX11Event), + OpenDirectTcpIp(OpenDirectTcpIpEvent), + ExecCommand(ExecCommandEvent), + WindowAdjusted(WindowAdjustedEvent), + ShellRequested, + SubsystemRequest(SubsystemRequestEvent), + WindowChangeRequest(WindowChangeRequestEvent), + Signal(SignalEvent), + TcpIpForward(TcpIpForwardEvent), + CancelTcpIpForward(TcpIpForwardEvent), +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub struct ExecCommandEvent { + pub args: Box<[String]>, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub struct WindowAdjustedEvent { + pub new_size: usize, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub struct SubsystemRequestEvent { + pub name: Box, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub struct SignalEvent { + pub name: Box, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +pub enum LoginAttemptEvent { + UsernamePassword { + username: Box, + password: Box, + }, + PublicKey { + kind: &'static str, + fingerprint: Box, + }, +} + +#[derive(Debug, Serialize)] +pub struct PtyRequestEvent { + pub term: Box, + pub col_width: u32, + pub row_height: u32, + pub pix_width: u32, + pub pix_height: u32, + pub modes: Box<[(u8, u32)]>, +} + +#[derive(Debug, Serialize)] +pub struct OpenX11Event { + pub originator_address: Box, + pub originator_port: u32, +} + +#[derive(Debug, Serialize)] +pub struct X11RequestEvent { + pub single_connection: bool, + pub x11_auth_protocol: Box, + pub x11_auth_cookie: Box, + pub x11_screen_number: u32, +} + +#[derive(Debug, Serialize)] +pub struct OpenDirectTcpIpEvent { + pub host_to_connect: Box, + pub port_to_connect: u32, + pub originator_address: Box, + pub originator_port: u32, +} + +#[derive(Debug, Serialize)] +pub struct WindowChangeRequestEvent { + pub col_width: u32, + pub row_height: u32, + pub pix_width: u32, + pub pix_height: u32, +} + +#[derive(Debug, Serialize)] +pub struct TcpIpForwardEvent { + pub address: Box, + pub port: u32, +} diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..7929f7a --- /dev/null +++ b/src/command.rs @@ -0,0 +1,51 @@ +use itertools::Itertools; +use std::{f32, str::FromStr, time::Duration}; +use thrussh::{server::Session, ChannelId}; + +pub async fn run_command(args: &[String], channel: ChannelId, session: &mut Session) { + let Some(command) = args.get(0) else { + return; + }; + + match command.as_str() { + "echo" => { + session.data( + channel, + format!("{}\n", args.iter().skip(1).join(" ")).into(), + ); + } + "whoami" => { + // TODO: grab "logged in" user + session.data(channel, "root\n".to_string().into()); + } + "pwd" => { + // TODO: mock FHS + session.data(channel, "/root\n".to_string().into()); + } + "ls" => { + // pretend /root is empty until we mock the FHS + } + "exit" => { + let exit_status = args + .get(1) + .map(String::as_str) + .map_or(Ok(0), u32::from_str) + .unwrap_or(2); + + session.exit_status_request(channel, exit_status); + session.close(channel); + } + "sleep" => { + if let Some(Ok(secs)) = args.get(1).map(String::as_str).map(f32::from_str) { + tokio::time::sleep(Duration::from_secs_f32(secs)).await; + } + } + other => { + // TODO: fix stderr displaying out of order + session.data( + channel, + format!("bash: {other}: command not found\n").into(), + ); + } + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..0f848a6 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,48 @@ +use clap::Parser; +use serde::{de::DeserializeOwned, Deserialize}; +use std::path::PathBuf; +use std::sync::Arc; +use std::{io::ErrorKind, net::SocketAddr}; + +/// Parser for command line arguments, these arguments can also be passed via capitalised env vars +/// of the same name. +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +pub struct Args { + #[arg(short, long, env, value_parser = load_config::)] + pub config: Arc, + #[arg(short, long, action = clap::ArgAction::Count)] + pub verbose: u8, +} + +impl Args { + pub fn verbosity(&self) -> &'static str { + match self.verbose { + 0 => "info", + 1 => "debug,thrussh=info", + 2 => "debug", + _ => "trace", + } + } +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] +pub struct Config { + /// Address for the server to listen on. + pub listen_address: SocketAddr, + /// The probability that an authentication attempt will succeed, once a given password + /// has been accepted once - it will be accepted for the rest of the lifetime of the + /// instance. + pub access_probability: f64, + /// Path of the file to write audit logs to. + pub audit_output_file: PathBuf, +} + +fn load_config(path: &str) -> Result, std::io::Error> { + let file = std::fs::read_to_string(path)?; + + toml::from_str(&file) + .map(Arc::new) + .map_err(|e| std::io::Error::new(ErrorKind::Other, e)) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6ed62d2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,71 @@ +#![deny(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] + +use crate::{config::Args, server::Server}; +use clap::Parser; +use futures::FutureExt; +use std::sync::Arc; +use thrussh::MethodSet; +use tracing::{error, info}; +use tracing_subscriber::EnvFilter; + +mod audit; +mod command; +mod config; +mod server; +mod state; + +#[tokio::main] +async fn main() { + if let Err(e) = run().await { + error!("Failed to run {}: {}", env!("CARGO_CRATE_NAME"), e); + std::process::exit(1); + } +} + +async fn run() -> anyhow::Result<()> { + let args = Args::parse(); + + std::env::set_var("RUST_LOG", args.verbosity()); + + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); + + info!( + "{} listening on {}", + env!("CARGO_CRATE_NAME"), + args.config.listen_address + ); + + let keys = vec![thrussh_keys::key::KeyPair::generate_ed25519().unwrap()]; + + let thrussh_config = Arc::new(thrussh::server::Config { + methods: MethodSet::PASSWORD | MethodSet::PUBLICKEY | MethodSet::KEYBOARD_INTERACTIVE, + keys, + auth_rejection_time: std::time::Duration::from_secs(1), + ..thrussh::server::Config::default() + }); + + let (audit_send, audit_handle) = audit::start_audit_writer(args.config.clone()); + let mut audit_handle = audit_handle.fuse(); + + let server = Server::new(args.config.clone(), audit_send); + let listen_address = args.config.listen_address.to_string(); + + let fut = thrussh::server::run(thrussh_config, &listen_address, server); + + tokio::select! { + res = fut => res?, + res = &mut audit_handle => res??, + _ = tokio::signal::ctrl_c() => { + info!("Received ctrl-c, initiating shutdown"); + } + } + + info!("Finishing audit log writes"); + audit_handle.await??; + info!("Audit log writes finished"); + + Ok(()) +} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..e311e02 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,624 @@ +use crate::audit::{ + ExecCommandEvent, SignalEvent, SubsystemRequestEvent, TcpIpForwardEvent, WindowAdjustedEvent, + WindowChangeRequestEvent, +}; +use crate::{ + audit::{ + AuditLog, AuditLogAction, LoginAttemptEvent, OpenDirectTcpIpEvent, OpenX11Event, + PtyRequestEvent, X11RequestEvent, + }, + command::run_command, + config::Config, + state::State, +}; +use futures::{ + future::{BoxFuture, InspectErr}, + FutureExt, TryFutureExt, +}; +use std::{ + borrow::Cow, + future::Future, + net::SocketAddr, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; +use thrussh::{ + server::{Auth, Response, Session}, + ChannelId, Pty, Sig, +}; +use thrussh_keys::key::PublicKey; +use tokio::sync::mpsc::UnboundedSender; +use tracing::{error, info, info_span, instrument::Instrumented, Instrument, Span}; + +pub static KEYBOARD_INTERACTIVE_PROMPT: &[(Cow<'static, str>, bool)] = + &[(Cow::Borrowed("Password: "), false)]; +pub const SHELL_PROMPT: &str = "bash-5.1$ "; + +#[derive(Clone)] +pub struct Server { + config: Arc, + state: Arc, + audit_send: UnboundedSender, +} + +impl Server { + pub fn new(config: Arc, audit_send: UnboundedSender) -> Self { + Self { + config, + state: Arc::new(State::default()), + audit_send, + } + } +} + +impl thrussh::server::Server for Server { + type Handler = Connection; + + fn new(&mut self, peer_addr: Option) -> Self::Handler { + let connection_id = uuid::Uuid::new_v4(); + + Connection { + span: info_span!("connection", ?peer_addr, %connection_id), + server: self.clone(), + audit_log: AuditLog { + connection_id, + peer_address: peer_addr, + ..AuditLog::default() + }, + } + } +} + +pub struct Connection { + span: Span, + server: Server, + audit_log: AuditLog, +} + +impl Connection { + fn try_login(&mut self, user: &str, password: &str) -> bool { + let res = if self + .server + .state + .previously_accepted_passwords + .seen(password) + { + info!(user, password, "Accepted login due to it being used before"); + true + } else if fastrand::f64() <= self.server.config.access_probability { + info!(user, password, "Accepted login randomly"); + self.server + .state + .previously_accepted_passwords + .store(password); + true + } else { + info!(?user, ?password, "Rejected login"); + false + }; + + self.audit_log.push_action(AuditLogAction::LoginAttempt( + LoginAttemptEvent::UsernamePassword { + username: Box::from(user), + password: Box::from(password), + }, + )); + + res + } +} + +impl thrussh::server::Handler for Connection { + type Error = anyhow::Error; + type FutureAuth = HandlerFuture; + type FutureUnit = HandlerFuture; + type FutureBool = + ServerFuture>>; + + fn finished_auth(self, auth: Auth) -> Self::FutureAuth { + let span = info_span!(parent: &self.span, "finished_auth"); + futures::future::ok((self, auth)).boxed().wrap(span) + } + + fn finished_bool(self, b: bool, session: Session) -> Self::FutureBool { + let span = info_span!(parent: &self.span, "finished_bool"); + let _entered = span.enter(); + + futures::future::ok((self, session, b)) + .boxed() + .wrap(Span::current()) + } + + fn finished(self, session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "finished"); + let _entered = span.enter(); + + futures::future::ok((self, session)) + .boxed() + .wrap(Span::current()) + } + + fn auth_none(self, _user: &str) -> Self::FutureAuth { + let span = info_span!(parent: &self.span, "auth_none"); + + self.finished_auth(Auth::UnsupportedMethod) + .boxed() + .wrap(span) + } + + fn auth_password(mut self, user: &str, password: &str) -> Self::FutureAuth { + let span = info_span!(parent: &self.span, "auth_password"); + let _entered = span.enter(); + + let res = if self.try_login(user, password) { + Auth::Accept + } else { + Auth::Partial { + name: "".into(), + instructions: "".into(), + prompts: KEYBOARD_INTERACTIVE_PROMPT.into(), + } + }; + + self.finished_auth(res) + } + + fn auth_publickey(mut self, _user: &str, public_key: &PublicKey) -> Self::FutureAuth { + let span = info_span!(parent: &self.span, "auth_publickey"); + let _entered = span.enter(); + + let kind = public_key.name(); + let fingerprint = public_key.fingerprint(); + + self.audit_log + .push_action(AuditLogAction::LoginAttempt(LoginAttemptEvent::PublicKey { + kind, + fingerprint: Box::from(fingerprint), + })); + + self.finished_auth(Auth::Reject) + .boxed() + .wrap(Span::current()) + } + + fn auth_keyboard_interactive( + self, + _user: &str, + _submethods: &str, + _response: Option, + ) -> Self::FutureAuth { + let span = info_span!(parent: &self.span, "auth_publickey"); + let _entered = span.enter(); + + let result = Auth::Reject; + + // TODO: why doesn't this work + // let result = if let Some(password) = response + // .as_mut() + // .and_then(Response::next) + // .map(String::from_utf8_lossy) + // { + // if self.try_login(user, password.as_ref()) { + // Auth::Accept + // } else { + // Auth::Reject + // } + // } else { + // debug!("Client is attempting keyboard-interactive, obliging"); + // + // Auth::Partial { + // name: "".into(), + // instructions: "".into(), + // prompts: KEYBOARD_INTERACTIVE_PROMPT.into(), + // } + // }; + + self.finished_auth(result) + } + + fn channel_close(self, channel: ChannelId, mut session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "channel_close"); + let _entered = span.enter(); + + info!("In here"); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn channel_eof(self, channel: ChannelId, mut session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "channel_eof"); + let _entered = span.enter(); + + info!("In here"); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn channel_open_session(self, channel: ChannelId, mut session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "channel_open_session"); + let _entered = span.enter(); + + info!("In here"); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn channel_open_x11( + mut self, + channel: ChannelId, + originator_address: &str, + originator_port: u32, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "channel_open_x11"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::OpenX11(OpenX11Event { + originator_address: Box::from(originator_address), + originator_port, + })); + + session.channel_failure(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn channel_open_direct_tcpip( + mut self, + channel: ChannelId, + host_to_connect: &str, + port_to_connect: u32, + originator_address: &str, + originator_port: u32, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "channel_open_direct_tcpip"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::OpenDirectTcpIp(OpenDirectTcpIpEvent { + host_to_connect: Box::from(host_to_connect), + port_to_connect, + originator_address: Box::from(originator_address), + originator_port, + })); + + session.channel_failure(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn data(mut self, channel: ChannelId, data: &[u8], mut session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "data"); + let _entered = span.enter(); + + let data = shlex::split(String::from_utf8_lossy(data).as_ref()); + + async move { + if let Some(args) = data { + run_command(&args, channel, &mut session).await; + self.audit_log + .push_action(AuditLogAction::ExecCommand(ExecCommandEvent { + args: Box::from(args), + })); + } + + session.data(channel, SHELL_PROMPT.to_string().into()); + self.finished(session).await + } + .boxed() + .wrap(Span::current()) + } + + fn extended_data( + self, + _channel: ChannelId, + _code: u32, + _data: &[u8], + session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "extended_data"); + let _entered = span.enter(); + + self.finished(session).boxed().wrap(Span::current()) + } + + fn window_adjusted( + mut self, + _channel: ChannelId, + new_window_size: usize, + session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "window_adjusted"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::WindowAdjusted(WindowAdjustedEvent { + new_size: new_window_size, + })); + + self.finished(session).boxed().wrap(Span::current()) + } + + fn adjust_window(&mut self, _channel: ChannelId, current: u32) -> u32 { + let span = info_span!(parent: &self.span, "adjust_window"); + let _entered = span.enter(); + + current + } + + fn pty_request( + mut self, + channel: ChannelId, + term: &str, + col_width: u32, + row_height: u32, + pix_width: u32, + pix_height: u32, + modes: &[(Pty, u32)], + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "pty_request"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::PtyRequest(PtyRequestEvent { + term: Box::from(term), + col_width, + row_height, + pix_width, + pix_height, + modes: Box::from( + modes + .iter() + .copied() + .map(|(pty, val)| (pty as u8, val)) + .collect::>(), + ), + })); + + session.channel_failure(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn x11_request( + mut self, + channel: ChannelId, + single_connection: bool, + x11_auth_protocol: &str, + x11_auth_cookie: &str, + x11_screen_number: u32, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "x11_request"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::X11Request(X11RequestEvent { + single_connection, + x11_auth_protocol: Box::from(x11_auth_protocol), + x11_auth_cookie: Box::from(x11_auth_cookie), + x11_screen_number, + })); + + session.channel_failure(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn env_request( + mut self, + channel: ChannelId, + variable_name: &str, + variable_value: &str, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "env_request"); + let _entered = span.enter(); + + self.audit_log + .environment_variables + .push((Box::from(variable_name), Box::from(variable_value))); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn shell_request(mut self, channel: ChannelId, mut session: Session) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "shell_request"); + let _entered = span.enter(); + + self.audit_log.push_action(AuditLogAction::ShellRequested); + + session.data(channel, SHELL_PROMPT.to_string().into()); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn exec_request( + mut self, + channel: ChannelId, + data: &[u8], + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "exec_request"); + let _entered = span.enter(); + + let data = shlex::split(String::from_utf8_lossy(data).as_ref()); + + async move { + if let Some(args) = data { + run_command(&args, channel, &mut session).await; + self.audit_log + .push_action(AuditLogAction::ExecCommand(ExecCommandEvent { + args: Box::from(args), + })); + + session.channel_success(channel); + } else { + session.channel_failure(channel); + } + + self.finished(session).await + } + .boxed() + .wrap(Span::current()) + } + + fn subsystem_request( + mut self, + channel: ChannelId, + name: &str, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "subsystem_request"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::SubsystemRequest(SubsystemRequestEvent { + name: Box::from(name), + })); + + session.channel_failure(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn window_change_request( + mut self, + channel: ChannelId, + col_width: u32, + row_height: u32, + pix_width: u32, + pix_height: u32, + mut session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "window_change_request"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::WindowChangeRequest( + WindowChangeRequestEvent { + col_width, + row_height, + pix_width, + pix_height, + }, + )); + + session.channel_success(channel); + self.finished(session).boxed().wrap(Span::current()) + } + + fn signal( + mut self, + _channel: ChannelId, + signal_name: Sig, + session: Session, + ) -> Self::FutureUnit { + let span = info_span!(parent: &self.span, "signal"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::Signal(SignalEvent { + name: format!("{signal_name:?}").into(), + })); + + self.finished(session).boxed().wrap(Span::current()) + } + + fn tcpip_forward(mut self, address: &str, port: u32, session: Session) -> Self::FutureBool { + let span = info_span!(parent: &self.span, "tcpip_forward"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::TcpIpForward(TcpIpForwardEvent { + address: Box::from(address), + port, + })); + + self.finished_bool(false, session) + .boxed() + .wrap(Span::current()) + } + + fn cancel_tcpip_forward( + mut self, + address: &str, + port: u32, + session: Session, + ) -> Self::FutureBool { + let span = info_span!(parent: &self.span, "cancel_tcpip_forward"); + let _entered = span.enter(); + + self.audit_log + .push_action(AuditLogAction::CancelTcpIpForward(TcpIpForwardEvent { + address: Box::from(address), + port, + })); + + self.finished_bool(false, session) + .boxed() + .wrap(Span::current()) + } +} + +impl Drop for Connection { + fn drop(&mut self) { + let span = info_span!(parent: &self.span, "drop"); + let _entered = span.enter(); + + info!("Connection closed"); + + let _res = self + .server + .audit_send + .send(std::mem::take(&mut self.audit_log)); + } +} + +type HandlerResult = Result::Error>; +type HandlerFuture = ServerFuture< + ::Error, + BoxFuture<'static, HandlerResult<(Connection, T)>>, +>; + +/// Wraps a future, providing logging and instrumentation. This provides a newtype over the future +/// (`ServerFuture`) in order to enforce usage within the `thrussh::server::Handler` impl. +pub trait WrapFuture: Sized { + type Ok; + type Err; + + fn wrap(self, span: Span) -> ServerFuture; +} + +impl>> WrapFuture for F { + type Ok = T; + type Err = anyhow::Error; + + fn wrap(self, span: Span) -> ServerFuture { + ServerFuture( + self.inspect_err(log_err as fn(&anyhow::Error)) + .instrument(span), + ) + } +} + +/// Logs an error from a future result. +fn log_err(e: &anyhow::Error) { + error!("Connection closed due to: {}", e); +} + +/// A wrapped future, providing logging ad instrumentation. +#[allow(clippy::type_complexity)] +pub struct ServerFuture(Instrumented>); + +impl> + Unpin> Future for ServerFuture { + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) + } +} diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..0ade91a --- /dev/null +++ b/src/state.rs @@ -0,0 +1,22 @@ +use parking_lot::RwLock; +use std::collections::HashSet; + +#[derive(Default)] +pub struct State { + /// A list of passwords that have previously been accepted, and will forever be accepted + /// to further attract the bear. + pub previously_accepted_passwords: StoredPasswords, +} + +#[derive(Default)] +pub struct StoredPasswords(RwLock>>); + +impl StoredPasswords { + pub fn seen(&self, password: &str) -> bool { + self.0.read().contains(password) + } + + pub fn store(&self, password: &str) -> bool { + self.0.write().insert(Box::from(password.to_string())) + } +} -- libgit2 1.7.2