diff --git a/Cargo.lock b/Cargo.lock index 2bdb166342..66caf8b224 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1314,9 +1314,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d28fb6aa7dd680b3ffd1a3e64e115c177bdba63c0ae7f9cd48129ff05c4fa0" +checksum = "fe3e9d21d39e6f39ff46e03d5b621424f88ee23d8a1672c6bfc9ccc7f2da4f6a" dependencies = [ "cmov", ] diff --git a/crates/defguard_core/src/grpc/proxy/client_mfa.rs b/crates/defguard_core/src/grpc/proxy/client_mfa.rs index 8f6201c06e..d7b6f6e7ea 100644 --- a/crates/defguard_core/src/grpc/proxy/client_mfa.rs +++ b/crates/defguard_core/src/grpc/proxy/client_mfa.rs @@ -27,12 +27,13 @@ use defguard_proto::{ ClientMfaFinishRequest, ClientMfaFinishResponse, ClientMfaStartRequest, ClientMfaStartResponse, MfaAdvanced, MfaAwaitingExternal, MfaBiometricSignature, MfaCodeCredential, MfaCompleted, MfaFido2Assertion, MfaFido2Challenge, - MfaFlowApproveRequest, MfaFlowRemoteRequest, MfaFlowRemoteResponse, MfaFlowStartAccepted, - MfaFlowStartRejected, MfaFlowStartRequest, MfaFlowStartResponse, MfaFlowStepFinishRequest, - MfaFlowStepFinishResponse, MfaFlowStepStartRequest, MfaFlowStepStartResponse, MfaMethod, - MfaSignatureChallenge, MfaStartRejectionReason, MfaStepRejection, MfaStepResult, - MfaStepStarted, mfa_flow_start_response, mfa_flow_step_finish_request, mfa_step_result, - mfa_step_started, + MfaFlowApproveRequest, MfaFlowRemoteRejected, MfaFlowRemoteRequest, MfaFlowRemoteResponse, + MfaFlowStartAccepted, MfaFlowStartRejected, MfaFlowStartRequest, MfaFlowStartResponse, + MfaFlowStepFinishRequest, MfaFlowStepFinishResponse, MfaFlowStepStartRequest, + MfaFlowStepStartResponse, MfaMethod, MfaRemoteRejectionReason, MfaSignatureChallenge, + MfaStartRejectionReason, MfaStepRejection, MfaStepResult, MfaStepStarted, + mfa_flow_remote_response, mfa_flow_start_response, mfa_flow_step_finish_request, + mfa_step_result, mfa_step_started, }, enterprise::posture::{DevicePostureCheckRequest, DevicePostureData}, proxy::{ @@ -1065,14 +1066,18 @@ impl ClientMfaServer { .await } Ok(Ok(RemoteAuthSignal::MobileClientOutdated)) => { - remove_waiter_and_send_error( - &waiters, - &hash, - &waiter_identity, - &response_tx, - request_id, - Status::failed_precondition(LEGACY_MOBILE_CLIENT_MESSAGE), - ); + remove_remote_mfa_waiter(&waiters, &hash, &waiter_identity); + let _ = response_tx.send(CoreResponse { + id: request_id, + payload: Some(Payload::MfaFlowRemote(MfaFlowRemoteResponse { + outcome: Some(mfa_flow_remote_response::Outcome::Rejected( + MfaFlowRemoteRejected { + reason: MfaRemoteRejectionReason::MfaRemoteRejectionMobileClientOutdated + .into(), + }, + )), + })), + }); return; } Ok(Ok(RemoteAuthSignal::Superseded)) => { @@ -1122,7 +1127,7 @@ impl ClientMfaServer { Payload::CoreError(Status::internal("mobile approval did not complete").into()) } Ok(outcome) => Payload::MfaFlowRemote(MfaFlowRemoteResponse { - result: Some(outcome.into()), + outcome: Some(mfa_flow_remote_response::Outcome::Result(outcome.into())), }), Err(error) => Payload::CoreError(Status::from(error).into()), }; diff --git a/crates/defguard_core/src/grpc/proxy/client_mfa/tests.rs b/crates/defguard_core/src/grpc/proxy/client_mfa/tests.rs index 70554b8788..bc2e155b6b 100644 --- a/crates/defguard_core/src/grpc/proxy/client_mfa/tests.rs +++ b/crates/defguard_core/src/grpc/proxy/client_mfa/tests.rs @@ -36,7 +36,8 @@ use defguard_proto::{ ClientMfaFinishRequest, ClientMfaStartRequest, MfaCodeCredential, MfaFlowApproveRequest, MfaFlowRemoteRequest, MfaFlowStartRequest, MfaFlowStepFinishRequest, MfaFlowStepStartRequest, MfaMethod, MfaMobileApprovalProof, MfaStartRejectionReason, - mfa_flow_start_response, mfa_flow_step_finish_request, mfa_step_result, mfa_step_started, + mfa_flow_remote_response, mfa_flow_start_response, mfa_flow_step_finish_request, + mfa_step_result, mfa_step_started, }, enterprise::posture::{BoolCheck, DevicePostureCheckRequest, DevicePostureData, bool_check}, proxy::{ClientMfaOidcAuthenticateRequest, ClientMfaTokenValidationRequest, DeviceInfo}, @@ -2077,9 +2078,9 @@ async fn test_mfa_flow_remote_wakes_matching_attempt_and_delivers_psk( let Some(super::Payload::MfaFlowRemote(response)) = response.payload else { panic!("expected a typed remote flow response"); }; - let result = response - .result - .expect("remote response must include a result"); + let Some(mfa_flow_remote_response::Outcome::Result(result)) = response.outcome else { + panic!("remote response must include a result"); + }; let Some(mfa_step_result::Outcome::Completed(completed)) = result.outcome else { panic!("expected a completed remote result"); }; @@ -2208,11 +2209,10 @@ async fn test_mfa_flow_remote_observes_approval_before_registration( let Some(super::Payload::MfaFlowRemote(response)) = response.payload else { panic!("expected a typed remote flow response"); }; - let Some(mfa_step_result::Outcome::Completed(completed)) = response - .result - .expect("remote response must include a result") - .outcome - else { + let Some(mfa_flow_remote_response::Outcome::Result(result)) = response.outcome else { + panic!("remote response must include a result"); + }; + let Some(mfa_step_result::Outcome::Completed(completed)) = result.outcome else { panic!("expected a completed remote result"); }; assert_ne!(completed.preshared_key, ""); diff --git a/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/mfa.rs b/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/mfa.rs index f7508d7683..aeaa283bea 100644 --- a/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/mfa.rs +++ b/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/mfa.rs @@ -19,8 +19,9 @@ use defguard_proto::{ client_types::{ ClientMfaFinishRequest, ClientMfaStartRequest, MfaBiometricSignature, MfaCodeCredential, MfaFlowApproveRequest, MfaFlowRemoteRequest, MfaFlowStartRequest, MfaFlowStepFinishRequest, - MfaFlowStepStartRequest, MfaMethod, MfaMobileApprovalProof, MfaStepResult, - mfa_flow_start_response, mfa_flow_step_finish_request, mfa_step_result, mfa_step_started, + MfaFlowStepStartRequest, MfaMethod, MfaMobileApprovalProof, MfaRemoteRejectionReason, + MfaStepResult, mfa_flow_remote_response, mfa_flow_start_response, + mfa_flow_step_finish_request, mfa_step_result, mfa_step_started, }, proxy::{CoreRequest, CoreResponse, core_request, core_response}, }; @@ -257,9 +258,10 @@ fn flow_step_result(response: CoreResponse) -> MfaStepResult { fn flow_remote_result(response: CoreResponse) -> MfaStepResult { match response.payload { - Some(core_response::Payload::MfaFlowRemote(response)) => response - .result - .expect("remote finish must include a result"), + Some(core_response::Payload::MfaFlowRemote(response)) => match response.outcome { + Some(mfa_flow_remote_response::Outcome::Result(result)) => result, + _ => panic!("remote finish must include a result"), + }, Some(core_response::Payload::CoreError(error)) => panic!( "remote finish failed with status={} msg={}", error.status_code, error.message @@ -686,10 +688,17 @@ async fn test_legacy_mfa_finish_rejects_valid_multi_step_mobile_proof( assert_eq!(remote.id, 2); assert_eq!(phone.id, 3); - let (remote_code, remote_message) = assert_error_response_details(&remote); + let Some(core_response::Payload::MfaFlowRemote(response)) = remote.payload else { + panic!("expected MfaFlowRemote rejection"); + }; + let Some(mfa_flow_remote_response::Outcome::Rejected(rejected)) = response.outcome else { + panic!("expected outdated mobile client rejection"); + }; + assert_eq!( + rejected.reason(), + MfaRemoteRejectionReason::MfaRemoteRejectionMobileClientOutdated + ); let (phone_code, phone_message) = assert_error_response_details(&phone); - assert_eq!(remote_code, Code::FailedPrecondition); - assert_eq!(remote_message, LEGACY_MOBILE_CLIENT_MESSAGE); assert_eq!(phone_code, Code::FailedPrecondition); assert_eq!(phone_message, LEGACY_MOBILE_CLIENT_MESSAGE); diff --git a/deny.toml b/deny.toml index 73920a59a2..e8ed37dd42 100644 --- a/deny.toml +++ b/deny.toml @@ -71,6 +71,7 @@ feature-depth = 1 # output a note when they are encountered. ignore = [ { id = "RUSTSEC-2023-0071", reason = "https://github.com/RustCrypto/RSA/issues/19" }, + { id = "RUSTSEC-2026-0387", reason = "Transitive dependency of jsonwebtoken" }, ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. diff --git a/proto b/proto index 5a66c4e4c4..2a39d0a09e 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 5a66c4e4c40f223e4702eca87b811a12967a93e3 +Subproject commit 2a39d0a09ebed4fe3ea08f1b54be5259ec63bed6