Skip to content
Open
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
49 changes: 49 additions & 0 deletions crates/ironrdp-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ use crate::{SoundServerFactory, builder, capabilities};
/// TCP listen backlog size for the RDP server socket.
const LISTENER_BACKLOG: u32 = 1024;

/// Server-side credential validator for TLS-mode connections.
///
/// Called during connection setup when the server receives client credentials
/// via `ClientInfoPdu`. Not used for CredSSP/Hybrid connections (those use
/// pre-loaded credentials for NTLM challenge-response).
Comment thread
glamberson marked this conversation as resolved.
///
/// Implement this trait to validate credentials against external systems
/// (PAM, LDAP, database, etc.). For blocking backends, wrap the call in
/// `tokio::task::spawn_blocking` to avoid stalling the async runtime.
pub trait CredentialValidator: Send + Sync {
/// Validate credentials received from the client.
/// Return `Ok(true)` to accept, `Ok(false)` to reject.
fn validate(&self, credentials: &Credentials) -> Result<bool>;
}

#[derive(Clone)]
pub struct RdpServerOptions {
pub addr: SocketAddr,
Expand Down Expand Up @@ -243,6 +258,7 @@ pub struct RdpServer {
ev_sender: mpsc::UnboundedSender<ServerEvent>,
ev_receiver: Arc<Mutex<mpsc::UnboundedReceiver<ServerEvent>>>,
creds: Option<Credentials>,
credential_validator: Option<Arc<dyn CredentialValidator>>,
local_addr: Option<SocketAddr>,
autodetect: Option<AutoDetectManager>,
}
Expand Down Expand Up @@ -313,6 +329,7 @@ impl RdpServer {
ev_sender,
ev_receiver: Arc::new(Mutex::new(ev_receiver)),
creds: None,
credential_validator: None,
local_addr: None,
autodetect: None,
}
Expand All @@ -322,6 +339,17 @@ impl RdpServer {
builder::RdpServerBuilder::new()
}

/// Set a credential validator for TLS-mode connections.
///
/// When set, credentials received from the client during `SecureSettingsExchange`
/// are validated through this callback before the session is established.
/// If validation fails, the connection is rejected.
///
/// Not used for CredSSP/Hybrid connections (those use pre-loaded credentials).
pub fn set_credential_validator(&mut self, validator: Arc<dyn CredentialValidator>) {
self.credential_validator = Some(validator);
}

pub fn event_sender(&self) -> &mpsc::UnboundedSender<ServerEvent> {
&self.ev_sender
}
Expand Down Expand Up @@ -873,6 +901,27 @@ impl RdpServer {
{
debug!("Client accepted");

// Validate credentials if a validator is configured
if let Some(validator) = &self.credential_validator {
if let Some(creds) = &result.credentials {
match validator.validate(creds) {
Ok(true) => {
debug!("Credential validation succeeded");
}
Ok(false) => {
warn!("Credential validation failed");
bail!("credential validation failed");
}
Err(e) => {
error!(error = %e, "Credential validator error");
bail!("credential validation error");
}
}
} else {
debug!("Skipping credential validation (no credentials in AcceptorResult)");
}
Comment thread
glamberson marked this conversation as resolved.
}

if !result.input_events.is_empty() {
debug!("Handling input event backlog from acceptor sequence");
self.handle_input_backlog(
Expand Down
Loading