From 274b074958fb4d2b2444afab8e9035991719a91a Mon Sep 17 00:00:00 2001 From: Nils Ponsard | Nitrokey Date: Tue, 12 Sep 2023 12:11:21 +0200 Subject: [PATCH] feat: add --skip-tls-verify option to accept self-signed certs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matúš Ferech --- Cargo.lock | 168 +++++++++++++++++++++++++++++++----------------- Cargo.toml | 12 +++- src/fuzzer.rs | 9 ++- src/main.rs | 25 +++++++ src/verifier.rs | 28 ++++++++ 5 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 src/verifier.rs diff --git a/Cargo.lock b/Cargo.lock index ee6f9f4..d52d47f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # 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 = "anyhow" version = "1.0.37" @@ -34,7 +40,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -51,9 +57,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bit-set" @@ -112,12 +118,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chunked_transfer" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" - [[package]] name = "core-foundation" version = "0.9.1" @@ -134,6 +134,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "fastrand" version = "1.7.0" @@ -143,6 +152,16 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -193,7 +212,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.5", + "itoa", ] [[package]] @@ -227,12 +246,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - [[package]] name = "itoa" version = "1.0.5" @@ -287,6 +300,15 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -314,6 +336,7 @@ dependencies = [ "openapi_utils", "openapiv3", "proptest", + "rustls", "serde", "serde_json", "serde_yaml", @@ -365,9 +388,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -407,9 +430,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.8" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -479,9 +502,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.19" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -494,29 +517,57 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ - "base64", "log", "ring", + "rustls-webpki 0.101.5", "sct", - "webpki", ] [[package]] name = "rustls-native-certs" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls", + "rustls-pemfile", "schannel", "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rusty-fork" version = "0.3.0" @@ -547,9 +598,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", @@ -580,31 +631,31 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.32", ] [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ - "itoa 0.4.7", + "itoa", "ryu", "serde", ] @@ -638,6 +689,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -711,20 +773,20 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.1.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbeb1aabb07378cf0e084971a74f24241273304653184f54cdce113c0d7df1b" +checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" dependencies = [ "base64", - "chunked_transfer", + "flate2", "log", "once_cell", "rustls", "rustls-native-certs", + "rustls-webpki 0.100.2", "serde", "serde_json", "url", - "webpki", "webpki-roots", ] @@ -777,7 +839,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -799,7 +861,7 @@ checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -820,23 +882,13 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ - "webpki", + "rustls-webpki 0.100.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d50fae5..9ea63a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,13 @@ default-run = "openapi-fuzzer" [dependencies] argh = "0.1.4" -url = {version = "2.2.0", features = ["serde"]} +url = { version = "2.2.0", features = ["serde"] } anyhow = "1.0.37" openapiv3 = "0.5.0" serde = "1.0" serde_yaml = "0.8" -ureq = {version = "2.1.0", features = ["json", "native-certs"]} +ureq = { version = "2.7.0", features = ["json", "native-certs"] } +rustls = { version = "0.21", features = ["dangerous_configuration"] } openapi_utils = "0.2.2" arbitrary = "1" serde_json = "1.0" @@ -39,4 +40,9 @@ ci = ["github"] # The installers to generate for each app installers = [] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"] +targets = [ + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "aarch64-apple-darwin", +] diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 649412b..d4e147d 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -19,7 +19,7 @@ use proptest::{ test_runner::{Config, FileFailurePersistence, TestCaseError, TestError, TestRunner}, }; use serde::{Deserialize, Serialize}; -use ureq::OrAnyStatus; +use ureq::{Agent, OrAnyStatus}; use url::Url; use crate::{ @@ -49,6 +49,7 @@ pub struct Fuzzer { max_test_case_count: u32, results_dir: PathBuf, stats_dir: Option, + agent: Agent, } impl Fuzzer { @@ -60,6 +61,7 @@ impl Fuzzer { max_test_case_count: u32, results_dir: PathBuf, stats_dir: Option, + agent: Agent, ) -> Fuzzer { Fuzzer { schema, @@ -69,6 +71,7 @@ impl Fuzzer { max_test_case_count, results_dir, stats_dir, + agent, } } @@ -129,6 +132,7 @@ impl Fuzzer { method, &payload, &self.extra_headers, + &self.agent, ) .map_err(|e| { TestCaseError::Fail(format!("unable to send request: {e}").into()) @@ -172,11 +176,12 @@ impl Fuzzer { method: &str, payload: &Payload, extra_headers: &HashMap, + agent: &Agent, ) -> Result { for (name, value) in payload.path_params().iter() { path_with_params = path_with_params.replace(&format!("{{{name}}}"), value); } - let mut request = ureq::request_url(method, &url.join(&path_with_params)?); + let mut request = agent.request_url(method, &url.join(&path_with_params)?); for (param, value) in payload.query_params().iter() { request = request.query(param, value); diff --git a/src/main.rs b/src/main.rs index e16854d..9107618 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod arbitrary; mod fuzzer; mod stats; +mod verifier; use std::path::PathBuf; use std::process::ExitCode; @@ -64,6 +65,9 @@ struct RunArgs { /// will not be saved #[argh(option)] stats_dir: Option, + + #[argh(switch, description = "disable verification of TLS certificates")] + skip_tls_verify: bool, } #[derive(FromArgs, Debug, PartialEq)] @@ -81,6 +85,9 @@ struct ResendArgs { /// url of api #[argh(option, short = 'u')] url: UrlWithTrailingSlash, + + #[argh(switch, description = "disable verification of TLS certificates")] + skip_tls_verify: bool, } #[derive(Debug, PartialEq)] @@ -139,6 +146,8 @@ fn main() -> Result { serde_yaml::from_str(&specfile).context("Failed to parse schema")?; let openapi_schema = openapi_schema.deref_all(); + let agent = create_agent(!args.skip_tls_verify); + let now = Instant::now(); let exit_code = Fuzzer::new( openapi_schema, @@ -148,6 +157,7 @@ fn main() -> Result { args.max_test_case_count, args.results_dir, args.stats_dir, + agent, ) .run()?; println!("Elapsed time: {}s", now.elapsed().as_secs()); @@ -157,12 +167,16 @@ fn main() -> Result { let json = fs::read_to_string(&args.file) .context(format!("Unable to read {:?}", &args.file))?; let result: FuzzResult = serde_json::from_str(&json)?; + + let agent = create_agent(!args.skip_tls_verify); + let response = Fuzzer::send_request( &args.url.into(), result.path.to_owned(), result.method, &result.payload, &args.header.into_iter().map(Into::into).collect(), + &agent, )?; eprintln!("{} ({})", response.status(), response.status_text()); println!("{}", response.into_string()?); @@ -172,3 +186,14 @@ fn main() -> Result { Ok(exit_code) } + +fn create_agent(verify_cert: bool) -> ureq::Agent { + if verify_cert { + ureq::agent() + } else { + let conf = verifier::skip_tls_verification_config(); + ureq::AgentBuilder::new() + .tls_config(std::sync::Arc::new(conf)) + .build() + } +} diff --git a/src/verifier.rs b/src/verifier.rs new file mode 100644 index 0000000..fa774d3 --- /dev/null +++ b/src/verifier.rs @@ -0,0 +1,28 @@ +use std::sync::Arc; + +use rustls::client::ServerCertVerifier; + +// See https://quinn-rs.github.io/quinn/quinn/certificate.html#insecure-connection +struct SkipTlsVerification {} + +impl ServerCertVerifier for SkipTlsVerification { + fn verify_server_cert( + &self, + _end_entity: &rustls::Certificate, + _intermediates: &[rustls::Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result { + // always accept the certificate + Ok(rustls::client::ServerCertVerified::assertion()) + } +} + +pub fn skip_tls_verification_config() -> rustls::ClientConfig { + rustls::ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(SkipTlsVerification {})) + .with_no_client_auth() +}