diff --git a/Cargo.lock b/Cargo.lock index f3bff451e..08ed6fb92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1456,12 +1456,6 @@ dependencies = [ "libc", ] -[[package]] -name = "exitcode" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" - [[package]] name = "fake-simd" version = "0.1.2" @@ -2849,6 +2843,7 @@ dependencies = [ name = "kanidm_client" version = "1.1.0-rc.14-dev" dependencies = [ + "hyper", "kanidm_proto", "reqwest", "serde", @@ -2917,7 +2912,6 @@ dependencies = [ "compact_jwt", "cursive", "dialoguer", - "exitcode", "futures-concurrency", "kanidm_build_profiles", "kanidm_client", diff --git a/libs/client/Cargo.toml b/libs/client/Cargo.toml index 26fcfa4c1..d869c0f1e 100644 --- a/libs/client/Cargo.toml +++ b/libs/client/Cargo.toml @@ -18,9 +18,16 @@ kanidm_proto = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } time = { workspace = true, features = ["serde", "std"] } -tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] } +tokio = { workspace = true, features = [ + "rt", + "net", + "time", + "macros", + "sync", + "signal", +] } toml = { workspace = true } uuid = { workspace = true, features = ["serde", "v4"] } url = { workspace = true, features = ["serde"] } webauthn-rs-proto = { workspace = true, features = ["wasm"] } - +hyper = { workspace = true } diff --git a/libs/client/src/lib.rs b/libs/client/src/lib.rs index 9d0b7b108..ceef6379b 100644 --- a/libs/client/src/lib.rs +++ b/libs/client/src/lib.rs @@ -66,6 +66,7 @@ pub enum ClientError { SystemError, ConfigParseIssue(String), CertParseIssue(String), + UntrustedCertificate(String), } #[derive(Debug, Deserialize, Serialize)] @@ -468,6 +469,23 @@ fn test_make_url() { ); } +/// This is probably pretty jank but it works and was pulled from here: +/// +fn find_reqwest_error_source( + orig: &dyn std::error::Error, +) -> Option<&E> { + let mut cause = orig.source(); + while let Some(err) = cause { + if let Some(typed) = err.downcast_ref::() { + return Some(typed); + } + cause = err.source(); + } + + // else + None +} + impl KanidmClient { pub fn get_origin(&self) -> &Url { &self.origin @@ -541,6 +559,28 @@ impl KanidmClient { *guard = false; } + /// You've got the response from a reqwest and you want to turn it into a `ClientError` + fn handle_response_error(&self, error: reqwest::Error) -> ClientError { + if error.is_connect() { + if find_reqwest_error_source::(&error).is_some() { + // TODO: one day handle IO errors better + trace!("Got an IO error! {:?}", &error); + return ClientError::Transport(error); + } + if let Some(hyper_error) = find_reqwest_error_source::(&error) { + // hyper errors can be *anything* depending on the underlying client libraries + // ref: https://github.com/hyperium/hyper/blob/9feb70e9249d9fb99634ec96f83566e6bb3b3128/src/error.rs#L26C2-L26C2 + if format!("{:?}", hyper_error) + .to_lowercase() + .contains("certificate") + { + return ClientError::UntrustedCertificate(format!("{}", hyper_error)); + } + } + } + ClientError::Transport(error) + } + async fn perform_simple_post_request( &self, dest: &str, @@ -554,7 +594,10 @@ impl KanidmClient { .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -617,7 +660,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -677,7 +723,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -730,7 +779,7 @@ impl KanidmClient { .body(req_string) .send() .await - .map_err(ClientError::Transport)?; + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -781,7 +830,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -825,7 +877,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -870,7 +925,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -920,7 +978,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; @@ -1390,7 +1451,10 @@ impl KanidmClient { } }; - let response = response.send().await.map_err(ClientError::Transport)?; + let response = response + .send() + .await + .map_err(|err| self.handle_response_error(err))?; self.expect_version(&response).await; diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 6b1e09287..31fd71f89 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -4,13 +4,13 @@ default-run = "kanidm" description = "Kanidm Client Tools" documentation = "https://kanidm.github.io/kanidm/stable/" -version = { workspace=true } -authors = { workspace=true } -rust-version = { workspace=true } -edition = { workspace=true } -license = { workspace=true } -homepage = { workspace=true } -repository = { workspace=true } +version = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } [features] default = ["unix"] @@ -34,26 +34,25 @@ path = "src/ssh_authorizedkeys.rs" async-recursion = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } compact_jwt = { workspace = true, features = ["openssl"] } -dialoguer = { workspace=true } -futures-concurrency = { workspace=true } -libc = { workspace=true } -kanidm_client = { workspace=true } -kanidm_proto = { workspace=true } +dialoguer = { workspace = true } +futures-concurrency = { workspace = true } +libc = { workspace = true } +kanidm_client = { workspace = true } +kanidm_proto = { workspace = true } qrcode = { workspace = true } -rpassword = { workspace=true } +rpassword = { workspace = true } serde = { workspace = true, features = ["derive"] } -serde_json = { workspace=true } -shellexpand = { workspace=true } +serde_json = { workspace = true } +shellexpand = { workspace = true } time = { workspace = true, features = ["serde", "std"] } -tracing = { workspace=true } +tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } tokio = { workspace = true, features = ["rt", "macros"] } url = { workspace = true, features = ["serde"] } -uuid = { workspace=true } -zxcvbn = { workspace=true } +uuid = { workspace = true } +zxcvbn = { workspace = true } lazy_static.workspace = true regex.workspace = true -exitcode = "1.1.2" [dependencies.cursive] version = "0.20.0" @@ -63,9 +62,9 @@ features = ["crossterm-backend"] [build-dependencies] clap = { workspace = true, features = ["derive"] } -clap_complete = { workspace=true } +clap_complete = { workspace = true } kanidm_build_profiles = { workspace = true } -uuid = { workspace=true } +uuid = { workspace = true } url = { workspace = true } [target."cfg(target_os = \"windows\")".dependencies.webauthn-authenticator-rs] diff --git a/tools/cli/src/cli/common.rs b/tools/cli/src/cli/common.rs index 53da90eb2..3fd5c359e 100644 --- a/tools/cli/src/cli/common.rs +++ b/tools/cli/src/cli/common.rs @@ -62,20 +62,32 @@ impl CommonOpt { let client_builder = match ca_path { Some(p) => { debug!("Adding trusted CA cert {:?}", p); - client_builder + let client_builder = client_builder .add_root_certificate_filepath(p) .unwrap_or_else(|e| { error!("Failed to add ca certificate -- {:?}", e); std::process::exit(1); - }) + }); + + debug!( + "After attempting to add trusted CA cert, client builder state: {:?}", + client_builder + ); + client_builder } None => client_builder, }; - debug!( - "Post attempting to add trusted CA cert, client builder state: {:?}", - client_builder - ); + let client_builder = match self.skip_hostname_verification { + true => { + warn!( + "Accepting invalid hostnames on the certificate for {:?}", + &self.addr + ); + client_builder.danger_accept_invalid_hostnames(true) + } + false => client_builder, + }; client_builder.build().unwrap_or_else(|e| { error!("Failed to build client instance -- {:?}", e); @@ -176,7 +188,7 @@ impl CommonOpt { match prompt_for_username_get_values() { Ok(tuple) => tuple, Err(msg) => { - error!("{}", msg); + error!("Error: {}", msg); std::process::exit(1); } } diff --git a/tools/cli/src/cli/lib.rs b/tools/cli/src/cli/lib.rs index 40fcddbcd..776400267 100644 --- a/tools/cli/src/cli/lib.rs +++ b/tools/cli/src/cli/lib.rs @@ -56,13 +56,21 @@ pub(crate) fn handle_client_error(response: ClientError, _output_mode: &OutputMo "Internal Server Error in response:{:?} {:?}", error_msg, message ); - std::process::exit(exitcode::SOFTWARE); + std::process::exit(1); } else if status == StatusCode::NOT_FOUND { error!("Item not found:{:?} {:?}", error_msg, message) } else { error!("HTTP Error: {}{} {:?}", status, error_msg, message); } } + ClientError::Transport(e) => { + error!("HTTP-Transport Related Error: {:?}", e); + std::process::exit(1); + } + ClientError::UntrustedCertificate(e) => { + error!("Untrusted Certificate Error: {:?}", e); + std::process::exit(1); + } _ => { eprintln!("{:?}", response); } diff --git a/tools/cli/src/opt/kanidm.rs b/tools/cli/src/opt/kanidm.rs index 00f5f9a66..6419f88af 100644 --- a/tools/cli/src/opt/kanidm.rs +++ b/tools/cli/src/opt/kanidm.rs @@ -49,6 +49,13 @@ pub struct CommonOpt { /// Log format (still in very early development) #[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")] output_mode: OutputMode, + /// Skip hostname verification + #[clap( + long = "skip-hostname-verification", + env = "KANIDM_SKIP_HOSTNAME_VERIFICATION", + default_value_t = false + )] + skip_hostname_verification: bool, } #[derive(Debug, Args)]