clients::rdp connects over any caller-dialled TCP stream (direct, proxy or SSH tunnel), upgrades to TLS and logs on with NLA (CredSSP/NTLM), optionally falling back to TLS-only logon for servers without NLA such as xrdp (credentials are passed so they log straight in). The server certificate is pinned through a caller check on its SHA256 fingerprint before any credentials are sent. The desktop decodes into the VNC client's RGBA framebuffer; input (IronRDP's scancode/Unicode/mouse operations), Display Control resizes and the deactivation-reactivation sequence run on the session thread. IronRDP's releases need three vendored patches (vendor/README.md): picky and sspi with their RustCrypto release-candidate pins relaxed so they share russh's stable versions (their own suites, 402 tests, pass against them), and ironrdp-session with two unreleased upstream bitmap fixes (#1486 stride, #1524 RDP6 orientation) without which xrdp desktops render sheared. Verified live against xrdp (scottyhardy/docker-remote-desktop): logon, full desktop rendering, keyboard and mouse input.
334 lines
10 KiB
Rust
334 lines
10 KiB
Rust
use std::marker::PhantomData;
|
|
use std::mem;
|
|
|
|
use time::OffsetDateTime;
|
|
|
|
use super::{
|
|
ToAssign, WithContextRequirements, WithCredentialsHandle, WithOutput, WithTargetDataRepresentation,
|
|
WithoutContextRequirements, WithoutCredentialsHandle, WithoutOutput, WithoutTargetDataRepresentation,
|
|
};
|
|
use crate::{ClientRequestFlags, ClientResponseFlags, DataRepresentation, SecurityBuffer, SecurityStatus};
|
|
|
|
pub type EmptyInitializeSecurityContext<'a, 'output, C> = InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
C,
|
|
WithoutCredentialsHandle,
|
|
WithoutContextRequirements,
|
|
WithoutTargetDataRepresentation,
|
|
WithoutOutput,
|
|
>;
|
|
pub type FilledInitializeSecurityContext<'a, 'output, C> = InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
C,
|
|
WithCredentialsHandle,
|
|
WithContextRequirements,
|
|
WithTargetDataRepresentation,
|
|
WithOutput,
|
|
>;
|
|
|
|
/// Contains data returned by calling the `execute` method of
|
|
/// the `InitializeSecurityContextBuilder` structure. The builder is returned by calling
|
|
/// the `initialize_security_context` method.
|
|
#[derive(Debug, Clone)]
|
|
pub struct InitializeSecurityContextResult {
|
|
pub status: SecurityStatus,
|
|
pub flags: ClientResponseFlags,
|
|
pub expiry: Option<OffsetDateTime>,
|
|
}
|
|
|
|
/// A builder to execute one of the SSPI functions. Returned by the `initialize_security_context` method.
|
|
///
|
|
/// # Requirements for execution
|
|
///
|
|
/// These methods are required to be called before calling the `execute` method
|
|
/// * [`with_credentials_handle`](struct.InitializeSecurityContext.html#method.with_credentials_handle)
|
|
/// * [`with_context_requirements`](struct.InitializeSecurityContext.html#method.with_context_requirements)
|
|
/// * [`with_target_data_representation`](struct.InitializeSecurityContext.html#method.with_target_data_representation)
|
|
/// * [`with_output`](struct.InitializeSecurityContext.html#method.with_output)
|
|
#[derive(Debug)]
|
|
pub struct InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
> where
|
|
CredsHandleSet: ToAssign,
|
|
ContextRequirementsSet: ToAssign,
|
|
TargetDataRepresentationSet: ToAssign,
|
|
OutputSet: ToAssign,
|
|
{
|
|
phantom_creds_use_set: PhantomData<CredsHandleSet>,
|
|
phantom_context_req_set: PhantomData<ContextRequirementsSet>,
|
|
phantom_data_repr_set: PhantomData<TargetDataRepresentationSet>,
|
|
phantom_output_set: PhantomData<OutputSet>,
|
|
|
|
pub credentials_handle: Option<&'a mut CredsHandle>,
|
|
pub context_requirements: ClientRequestFlags,
|
|
pub target_data_representation: DataRepresentation,
|
|
pub output: &'output mut [SecurityBuffer],
|
|
|
|
pub target_name: Option<&'a str>,
|
|
pub input: Option<&'a mut [SecurityBuffer]>,
|
|
}
|
|
|
|
// We allow it here because the crate does not compile with single lifetime.
|
|
// The whole purpose of the `'a` lifetime is to allow the user to construct a new builder (via `full_transform` method)
|
|
// with a different lifetime for `credentials_handle` field.
|
|
#[allow(single_use_lifetimes)]
|
|
impl<
|
|
'b,
|
|
'a: 'b,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet: ToAssign,
|
|
ContextRequirementsSet: ToAssign,
|
|
TargetDataRepresentationSet: ToAssign,
|
|
OutputSet: ToAssign,
|
|
>
|
|
InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
>
|
|
{
|
|
/// Creates a new builder with the other `AuthData` and `CredsHandle` types.
|
|
///
|
|
/// References to the input and output buffers will be moved to the created builder leaving the `None` instead.
|
|
pub fn full_transform<CredsHandle2, CredHandleSet2: ToAssign>(
|
|
&mut self,
|
|
credentials_handle: Option<&'b mut CredsHandle2>,
|
|
) -> InitializeSecurityContext<
|
|
'b,
|
|
'output,
|
|
CredsHandle2,
|
|
CredHandleSet2,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
> {
|
|
InitializeSecurityContext {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle,
|
|
context_requirements: self.context_requirements,
|
|
target_data_representation: self.target_data_representation,
|
|
output: mem::take(&mut self.output),
|
|
target_name: self.target_name,
|
|
input: mem::take(&mut self.input),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet: ToAssign,
|
|
ContextRequirementsSet: ToAssign,
|
|
TargetDataRepresentationSet: ToAssign,
|
|
OutputSet: ToAssign,
|
|
>
|
|
InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
>
|
|
{
|
|
pub fn credentials_handle_mut(&mut self) -> &mut Option<&'a mut CredsHandle> {
|
|
&mut self.credentials_handle
|
|
}
|
|
|
|
pub fn credentials_handle(&mut self) -> &Option<&'a mut CredsHandle> {
|
|
&self.credentials_handle
|
|
}
|
|
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle: None,
|
|
context_requirements: ClientRequestFlags::empty(),
|
|
target_data_representation: DataRepresentation::Network,
|
|
output: &mut [],
|
|
|
|
target_name: None,
|
|
input: None,
|
|
}
|
|
}
|
|
|
|
/// Specifies a handle to the credentials returned by `acquire_credentials_handle`. This handle is used
|
|
/// to build the security context. The builder requires at least `CredentialUse::Outbound` credentials.
|
|
pub fn with_credentials_handle(
|
|
self,
|
|
credentials_handle: &'a mut CredsHandle,
|
|
) -> InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
WithCredentialsHandle,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
> {
|
|
InitializeSecurityContext {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle: Some(credentials_handle),
|
|
context_requirements: self.context_requirements,
|
|
target_data_representation: self.target_data_representation,
|
|
output: self.output,
|
|
|
|
target_name: self.target_name,
|
|
input: self.input,
|
|
}
|
|
}
|
|
|
|
/// Specifies bit flags that indicate requests for the context. Not all packages can support all requirements.
|
|
pub fn with_context_requirements(
|
|
self,
|
|
context_requirements: ClientRequestFlags,
|
|
) -> InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
WithContextRequirements,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
> {
|
|
InitializeSecurityContext {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle: self.credentials_handle,
|
|
context_requirements,
|
|
target_data_representation: self.target_data_representation,
|
|
output: self.output,
|
|
|
|
target_name: self.target_name,
|
|
input: self.input,
|
|
}
|
|
}
|
|
|
|
/// Specifies the data representation, such as byte ordering, on the target.
|
|
pub fn with_target_data_representation(
|
|
self,
|
|
target_data_representation: DataRepresentation,
|
|
) -> InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
WithTargetDataRepresentation,
|
|
OutputSet,
|
|
> {
|
|
InitializeSecurityContext {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle: self.credentials_handle,
|
|
context_requirements: self.context_requirements,
|
|
target_data_representation,
|
|
output: self.output,
|
|
|
|
target_name: self.target_name,
|
|
input: self.input,
|
|
}
|
|
}
|
|
|
|
/// Specifies a mutable reference to a buffer with [SecurityBuffer] that receives the output data.
|
|
pub fn with_output(
|
|
self,
|
|
output: &'output mut [SecurityBuffer],
|
|
) -> InitializeSecurityContext<
|
|
'a,
|
|
'output,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
WithOutput,
|
|
> {
|
|
InitializeSecurityContext {
|
|
phantom_creds_use_set: PhantomData,
|
|
phantom_context_req_set: PhantomData,
|
|
phantom_data_repr_set: PhantomData,
|
|
phantom_output_set: PhantomData,
|
|
|
|
credentials_handle: self.credentials_handle,
|
|
context_requirements: self.context_requirements,
|
|
target_data_representation: self.target_data_representation,
|
|
output,
|
|
|
|
target_name: self.target_name,
|
|
input: self.input,
|
|
}
|
|
}
|
|
|
|
pub fn with_target_name(self, target_name: &'a str) -> Self {
|
|
Self {
|
|
target_name: Some(target_name),
|
|
..self
|
|
}
|
|
}
|
|
|
|
/// Specifies a mutable reference to a buffer with [SecurityBuffer] structures. Don't call this method on during
|
|
/// the first execution of the builder. On the second execution, this parameter is a reference to the partially
|
|
/// formed context returned during the first call.
|
|
pub fn with_input(self, input: &'a mut [SecurityBuffer]) -> Self {
|
|
Self {
|
|
input: Some(input),
|
|
..self
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
CredsHandle,
|
|
CredsHandleSet: ToAssign,
|
|
ContextRequirementsSet: ToAssign,
|
|
TargetDataRepresentationSet: ToAssign,
|
|
OutputSet: ToAssign,
|
|
> Default
|
|
for InitializeSecurityContext<
|
|
'_,
|
|
'_,
|
|
CredsHandle,
|
|
CredsHandleSet,
|
|
ContextRequirementsSet,
|
|
TargetDataRepresentationSet,
|
|
OutputSet,
|
|
>
|
|
{
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|