Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,6 @@
"type": "string",
"description": "Path to lib XMF files."
},
"kerberos_credential_injection": {
"type": "boolean",
"default": false,
"description": "Whether to enable proxy-based RDP credential injection against Kerberos-enforced targets."
},
"enable_unstable": {
"type": "boolean",
"default": false,
Expand Down
25 changes: 0 additions & 25 deletions devolutions-gateway/src/api/kdc_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use axum::Router;
use axum::extract::State;
use axum::routing::post;
use picky_krb::messages::KdcProxyMessage;
use uuid::Uuid;

use crate::DgwState;
use crate::credential_injection::{
Expand Down Expand Up @@ -44,8 +43,6 @@ async fn kdc_proxy(

match destination {
KdcDestination::Inject { jti } => {
enforce_credential_injection_enabled(jti, conf.debug.enable_unstable)?;

let kdc = synthetic_kdc_registry
.get(jti)
.ok_or_else(|| HttpError::bad_request().msg("no live synthetic KDC published for this session"))?;
Expand Down Expand Up @@ -132,18 +129,6 @@ async fn forward_to_real_kdc(
reply.to_vec().map_err(HttpError::internal().err())
}

fn enforce_credential_injection_enabled(jet_cred_id: Uuid, enable_unstable: bool) -> Result<(), HttpError> {
if enable_unstable {
return Ok(());
}

warn!(
%jet_cred_id,
"Credential-injection KDC token rejected because unstable Kerberos injection is disabled"
);
Err(HttpError::bad_request().msg("credential-injection KDC proxy is not enabled"))
}

/// Refuses to forward a KDC request whose realm disagrees with the realm the token was issued for.
///
/// `bypass=true` (only when `__debug__.disable_token_validation` is on) downgrades the mismatch
Expand Down Expand Up @@ -188,14 +173,4 @@ mod tests {
// explicitly to catch an inverted gate.
assert!(enforce_realm_token_match("ad.example", "evil.example", true).is_ok());
}

#[test]
fn credential_injection_gate_allows_jet_cred_id_when_enabled() {
assert!(enforce_credential_injection_enabled(Uuid::new_v4(), true).is_ok());
}

#[test]
fn credential_injection_gate_rejects_jet_cred_id_when_disabled() {
assert!(enforce_credential_injection_enabled(Uuid::new_v4(), false).is_err());
}
}
11 changes: 0 additions & 11 deletions devolutions-gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1418,15 +1418,6 @@ pub mod dto {
#[serde(default = "ws_keep_alive_interval_default_value")]
pub ws_keep_alive_interval: u64,

/// Enable proxy-based RDP credential injection against Kerberos-enforced targets
///
/// Turns on the in-process KDC acceptor the Gateway presents to the client when injecting
/// credentials for accounts that can't fall back to NTLM (e.g. AD Protected Users).
/// Target-side KDC routing is not configured here. Off by default; still requires
/// `enable_unstable`.
#[serde(default)]
pub kerberos_credential_injection: bool,

/// Enable unstable features which may break at any point
#[serde(default)]
pub enable_unstable: bool,
Expand All @@ -1444,7 +1435,6 @@ pub mod dto {
capture_path: None,
lib_xmf_path: None,
enable_unstable: false,
kerberos_credential_injection: false,
ws_keep_alive_interval: ws_keep_alive_interval_default_value(),
}
}
Expand All @@ -1459,7 +1449,6 @@ pub mod dto {
&& self.capture_path.is_none()
&& self.lib_xmf_path.is_none()
&& !self.enable_unstable
&& !self.kerberos_credential_injection
&& self.ws_keep_alive_interval == ws_keep_alive_interval_default_value()
}
}
Expand Down
15 changes: 1 addition & 14 deletions devolutions-gateway/src/credential_injection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,7 @@ impl CredentialInjection {
}
}

/// Unstable debug opt-in for Kerberos credential injection (both legs).
pub(crate) fn kerberos_injection_opt_in(enable_unstable: bool, kerberos_credential_injection: bool) -> bool {
enable_unstable && kerberos_credential_injection
}

/// Whether target username + opt-in select Kerberos injection (otherwise NTLM).
/// Whether the target username should use Kerberos injection (otherwise NTLM).
pub(crate) fn select_kerberos_for_target(kerberos_enabled: bool, target_username: &str) -> bool {
if !kerberos_enabled {
return false;
Expand Down Expand Up @@ -926,14 +921,6 @@ mod tests {
}
}

#[test]
fn kerberos_injection_opt_in_requires_both_flags() {
assert!(!kerberos_injection_opt_in(false, false));
assert!(!kerberos_injection_opt_in(false, true));
assert!(!kerberos_injection_opt_in(true, false));
assert!(kerberos_injection_opt_in(true, true));
}

#[test]
fn select_kerberos_for_target_matrix() {
assert!(!select_kerberos_for_target(false, "user@CORP.EXAMPLE"));
Expand Down
24 changes: 7 additions & 17 deletions devolutions-gateway/src/generic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,14 @@ where

// Checkout before dialing so missing Kerberos material cannot open an upstream socket.
let credential_injection = if inject {
let kerberos_enabled = crate::credential_injection::kerberos_injection_opt_in(
conf.debug.enable_unstable,
conf.debug.kerberos_credential_injection,
);
Some(
CredentialInjection::checkout(
&provisioning,
&synthetic_kdc_registry,
claims.jti,
token,
kerberos_enabled,
)
.with_context(|| {
format!(
"credential-injection material for {} is missing or expired; re-provision to retry",
claims.jti
)
})?,
CredentialInjection::checkout(&provisioning, &synthetic_kdc_registry, claims.jti, token, true)
.with_context(|| {
format!(
"credential-injection material for {} is missing or expired; re-provision to retry",
claims.jti
)
})?,
)
} else {
None
Expand Down
15 changes: 3 additions & 12 deletions devolutions-gateway/src/rd_clean_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,18 +471,9 @@ async fn handle_with_credential_injection(
.clone()
.context("missing token in RDCleanPath PDU")?;

let kerberos_enabled = crate::credential_injection::kerberos_injection_opt_in(
conf.debug.enable_unstable,
conf.debug.kerberos_credential_injection,
);
let credential_injection = CredentialInjection::checkout(
provisioning,
synthetic_kdc_registry,
claims.jti,
&token,
kerberos_enabled,
)
.context("checkout credential-injection material before connecting upstream")?;
let credential_injection =
CredentialInjection::checkout(provisioning, synthetic_kdc_registry, claims.jti, &token, true)
.context("checkout credential-injection material before connecting upstream")?;

let ConnectedRdpServer {
tls_stream: server_stream,
Expand Down
Loading