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.
2457 lines
100 KiB
Rust
2457 lines
100 KiB
Rust
//! sspi-rs is a Rust implementation of [Security Support Provider Interface (SSPI)](https://docs.microsoft.com/en-us/windows/win32/rpc/security-support-provider-interface-sspi-).
|
|
//! It ships with platform-independent implementations of [Security Support Providers (SSP)](https://docs.microsoft.com/en-us/windows/win32/rpc/security-support-providers-ssps-),
|
|
//! and is able to utilize native Microsoft libraries when ran under Windows.
|
|
//!
|
|
//! The purpose of sspi-rs is to clean the original interface from cluttering and provide users with Rust-friendly SSPs for execution under Linux or any other platform that is
|
|
//! able to compile Rust.
|
|
//!
|
|
//! # Getting started
|
|
//!
|
|
//! Here is a quick example how to start working with the crate. This is the first stage of the client-server authentication performed on the client side.
|
|
//!
|
|
//! ```rust
|
|
//! use sspi::Sspi;
|
|
//! use sspi::Username;
|
|
//! use sspi::Ntlm;
|
|
//! use sspi::builders::EmptyInitializeSecurityContext;
|
|
//! use sspi::SspiImpl;
|
|
//!
|
|
//! let mut ntlm = Ntlm::new();
|
|
//!
|
|
//! let identity = sspi::AuthIdentity {
|
|
//! username: Username::parse("user").unwrap(),
|
|
//! password: "password".to_string().into(),
|
|
//! };
|
|
//!
|
|
//! let mut acq_creds_handle_result = ntlm
|
|
//! .acquire_credentials_handle()
|
|
//! .with_credential_use(sspi::CredentialUse::Outbound)
|
|
//! .with_auth_data(&identity)
|
|
//! .execute(&mut ntlm)
|
|
//! .expect("AcquireCredentialsHandle resulted in error");
|
|
//!
|
|
//! let mut output = vec![sspi::SecurityBuffer::new(
|
|
//! Vec::new(),
|
|
//! sspi::BufferType::Token,
|
|
//! )];
|
|
//!
|
|
//! let mut builder = ntlm.initialize_security_context()
|
|
//! .with_credentials_handle(&mut acq_creds_handle_result.credentials_handle)
|
|
//! .with_context_requirements(
|
|
//! sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY
|
|
//! )
|
|
//! .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
//! .with_output(&mut output);
|
|
//!
|
|
//! let result = ntlm.initialize_security_context_impl(&mut builder)
|
|
//! .expect("InitializeSecurityContext resulted in error")
|
|
//! .resolve_to_result()
|
|
//! .expect("InitializeSecurityContext resulted in error");
|
|
//!
|
|
//! println!("Initialized security context with result status: {:?}", result.status);
|
|
//! ```
|
|
|
|
#[macro_use]
|
|
extern crate tracing;
|
|
|
|
pub mod builders;
|
|
pub mod channel_bindings;
|
|
pub mod credssp;
|
|
pub mod generator;
|
|
pub mod kerberos;
|
|
pub mod negotiate;
|
|
pub mod network_client;
|
|
pub mod ntlm;
|
|
mod pk_init;
|
|
pub mod pku2u;
|
|
pub mod utf16string;
|
|
|
|
mod auth_identity;
|
|
mod ber;
|
|
mod crypto;
|
|
mod dns;
|
|
mod kdc;
|
|
mod krb;
|
|
mod rustls;
|
|
mod secret;
|
|
mod security_buffer;
|
|
mod smartcard;
|
|
mod utils;
|
|
|
|
#[cfg(all(feature = "tsssp", not(target_os = "windows")))]
|
|
compile_error!("tsssp feature should be used only on Windows");
|
|
|
|
use std::{error, fmt, io, result, str, string};
|
|
|
|
use bitflags::bitflags;
|
|
#[cfg(feature = "tsssp")]
|
|
use credssp::sspi_cred_ssp;
|
|
pub use generator::NetworkRequest;
|
|
use generator::{GeneratorAcceptSecurityContext, GeneratorChangePassword, GeneratorInitSecurityContext};
|
|
pub use network_client::NetworkProtocol;
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
|
use picky_asn1::restricted_string::CharSetError;
|
|
use picky_asn1_der::Asn1DerError;
|
|
use picky_asn1_x509::Certificate;
|
|
use picky_krb::gss_api::GssApiMessageError;
|
|
use picky_krb::messages::KrbError;
|
|
#[cfg(feature = "__rustls-used")]
|
|
pub use rustls::install_default_crypto_provider_if_necessary;
|
|
pub use security_buffer::SecurityBufferRef;
|
|
pub use utf16string::{
|
|
NonEmpty, U16CStr, U16CString, U16CStringExt, Utf16Str, Utf16String, Utf16StringExt, ZeroizedUtf16String,
|
|
};
|
|
use utils::map_keb_error_code_to_sspi_error;
|
|
pub use utils::modpow;
|
|
|
|
pub use self::auth_identity::{
|
|
AuthIdentity, AuthIdentityBuffers, Credentials, CredentialsBuffers, DownLevelLogonNameParts, KeytabIdentity,
|
|
UserNameFormat, UserPrincipalNameParts, Username, UsernameParts,
|
|
};
|
|
#[cfg(feature = "scard")]
|
|
pub use self::auth_identity::{CertificateRaw, SmartCardIdentity, SmartCardIdentityBuffers, SmartCardType};
|
|
pub use self::builders::{
|
|
AcceptSecurityContextResult, AcquireCredentialsHandleResult, InitializeSecurityContextResult,
|
|
};
|
|
use self::builders::{
|
|
ChangePassword, FilledAcceptSecurityContext, FilledAcquireCredentialsHandle, FilledInitializeSecurityContext,
|
|
};
|
|
pub use self::kdc::{detect_kdc_host, detect_kdc_url};
|
|
pub use self::kerberos::config::{KerberosConfig, KerberosServerConfig};
|
|
pub use self::kerberos::{KERBEROS_VERSION, Kerberos, KerberosState};
|
|
#[cfg(feature = "__test-data")]
|
|
pub use self::negotiate::client::FALLBACK_ERROR_KINDS;
|
|
pub use self::negotiate::{Negotiate, NegotiateConfig, NegotiatedProtocol};
|
|
pub use self::ntlm::Ntlm;
|
|
pub use self::ntlm::hash::{NTLM_HASH_PREFIX, NtlmHash, NtlmHashError};
|
|
pub use self::pku2u::{Pku2u, Pku2uConfig, Pku2uState};
|
|
pub use self::secret::Secret;
|
|
use crate::builders::{
|
|
EmptyAcceptSecurityContext, EmptyAcquireCredentialsHandle, EmptyInitializeSecurityContext,
|
|
InitializeSecurityContext,
|
|
};
|
|
|
|
/// Representation of SSPI-related result operation. Makes it easier to return a `Result` with SSPI-related `Error`.
|
|
pub type Result<T> = result::Result<T, Error>;
|
|
pub type Luid = u64;
|
|
|
|
const PACKAGE_ID_NONE: u16 = 0xFFFF;
|
|
|
|
/// Retrieves information about a specified security package. This information includes credentials and contexts.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `PackageInfo` containing the information about the security principal upon success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// let package_info = sspi::query_security_package_info(sspi::SecurityPackageType::Ntlm)
|
|
/// .unwrap();
|
|
/// println!("Package info:");
|
|
/// println!("Name: {:?}", package_info.name);
|
|
/// println!("Comment: {}", package_info.comment);
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QuerySecurityPackageInfoW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querysecuritypackageinfow)
|
|
pub fn query_security_package_info(package_type: SecurityPackageType) -> Result<PackageInfo> {
|
|
match package_type {
|
|
SecurityPackageType::Ntlm => Ok(ntlm::PACKAGE_INFO.clone()),
|
|
SecurityPackageType::Kerberos => Ok(kerberos::PACKAGE_INFO.clone()),
|
|
SecurityPackageType::Negotiate => Ok(negotiate::PACKAGE_INFO.clone()),
|
|
SecurityPackageType::Pku2u => Ok(pku2u::PACKAGE_INFO.clone()),
|
|
#[cfg(feature = "tsssp")]
|
|
SecurityPackageType::CredSsp => Ok(sspi_cred_ssp::PACKAGE_INFO.clone()),
|
|
SecurityPackageType::Other(s) => Err(Error::new(
|
|
ErrorKind::Unknown,
|
|
format!("queried info about unknown package: {s:?}"),
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// Returns an array of `PackageInfo` structures that provide information about the security packages available to the client.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Vec` of `PackageInfo` structures upon success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// let packages = sspi::enumerate_security_packages().unwrap();
|
|
///
|
|
/// println!("Available packages:");
|
|
/// for ssp in packages {
|
|
/// println!("{:?}", ssp.name);
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [EnumerateSecurityPackagesW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-enumeratesecuritypackagesw)
|
|
pub fn enumerate_security_packages() -> Result<Vec<PackageInfo>> {
|
|
Ok(vec![
|
|
negotiate::PACKAGE_INFO.clone(),
|
|
kerberos::PACKAGE_INFO.clone(),
|
|
pku2u::PACKAGE_INFO.clone(),
|
|
ntlm::PACKAGE_INFO.clone(),
|
|
#[cfg(feature = "tsssp")]
|
|
sspi_cred_ssp::PACKAGE_INFO.clone(),
|
|
])
|
|
}
|
|
|
|
/// This trait provides interface for all available SSPI functions. The `acquire_credentials_handle`,
|
|
/// `initialize_security_context`, and `accept_security_context` methods return Builders that make it
|
|
/// easier to assemble the list of arguments for the function and then execute it.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SSPI.h](https://docs.microsoft.com/en-us/windows/win32/api/sspi/)
|
|
pub trait Sspi
|
|
where
|
|
Self: Sized + SspiImpl,
|
|
{
|
|
/// Acquires a handle to preexisting credentials of a security principal. The preexisting credentials are
|
|
/// available only for `sspi::winapi` module. This handle is required by the `initialize_security_context`
|
|
/// and `accept_security_context` functions. These can be either preexisting credentials, which are
|
|
/// established through a system logon, or the caller can provide alternative credentials. Alternative
|
|
/// credentials are always required to specify when using platform independent SSPs.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `AcquireCredentialsHandle` builder
|
|
///
|
|
/// # Requirements for execution
|
|
///
|
|
/// These methods are required to be called before calling the `execute` method of the `AcquireCredentialsHandle` builder:
|
|
/// * [`with_credential_use`](builders/struct.AcquireCredentialsHandle.html#method.with_credential_use)
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [AcquireCredentialshandleW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acquirecredentialshandlew)
|
|
fn acquire_credentials_handle<'a>(
|
|
&mut self,
|
|
) -> EmptyAcquireCredentialsHandle<'a, Self::CredentialsHandle, Self::AuthenticationData> {
|
|
EmptyAcquireCredentialsHandle::new()
|
|
}
|
|
|
|
/// Initiates the client side, outbound security context from a credential handle.
|
|
/// The function is used to build a security context between the client application and a remote peer. The function returns a token
|
|
/// that the client must pass to the remote peer, which the peer in turn submits to the local security implementation through the
|
|
/// `accept_security_context` call.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `InitializeSecurityContext` builder
|
|
///
|
|
/// # Requirements for execution
|
|
///
|
|
/// These methods are required to be called before calling the `execute` method
|
|
/// * [`with_credentials_handle`](builders/struct.InitializeSecurityContext.html#method.with_credentials_handle)
|
|
/// * [`with_context_requirements`](builders/struct.InitializeSecurityContext.html#method.with_context_requirements)
|
|
/// * [`with_target_data_representation`](builders/struct.InitializeSecurityContext.html#method.with_target_data_representation)
|
|
/// * [`with_output`](builders/struct.InitializeSecurityContext.html#method.with_output)
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::new(&whoami::username().unwrap(), Some(&whoami::hostname().unwrap())).unwrap(),
|
|
/// password: String::from("password").into(),
|
|
/// };
|
|
///
|
|
/// let mut acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut credentials_handle = acq_cred_result.credentials_handle;
|
|
///
|
|
/// let mut output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let mut builder = ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_output(&mut output_buffer);
|
|
///
|
|
/// let result = ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [InitializeSecurityContextW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw)
|
|
fn initialize_security_context<'a, 'output>(
|
|
&mut self,
|
|
) -> EmptyInitializeSecurityContext<'a, 'output, Self::CredentialsHandle> {
|
|
InitializeSecurityContext::new()
|
|
}
|
|
|
|
/// Lets the server component of a transport application establish a security context between the server and a remote client.
|
|
/// The remote client calls the `initialize_security_context` function to start the process of establishing a security context.
|
|
/// The server can require one or more reply tokens from the remote client to complete establishing the security context.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `AcceptSecurityContext` builder
|
|
///
|
|
/// # Requirements for execution
|
|
///
|
|
/// These methods are required to be called before calling the `execute` method of the `AcceptSecurityContext` builder:
|
|
/// * [`with_credentials_handle`](builders/struct.AcceptSecurityContext.html#method.with_credentials_handle)
|
|
/// * [`with_context_requirements`](builders/struct.AcceptSecurityContext.html#method.with_context_requirements)
|
|
/// * [`with_target_data_representation`](builders/struct.AcceptSecurityContext.html#method.with_target_data_representation)
|
|
/// * [`with_output`](builders/struct.AcceptSecurityContext.html#method.with_output)
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut client_ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = client_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut client_ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let mut builder = client_ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _result = client_ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let mut output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let mut server_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut credentials_handle = server_acq_cred_result.credentials_handle;
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let result = ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut output_buffer)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [AcceptSecurityContext function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acceptsecuritycontext)
|
|
fn accept_security_context<'a>(&mut self) -> EmptyAcceptSecurityContext<'a, Self::CredentialsHandle> {
|
|
EmptyAcceptSecurityContext::new()
|
|
}
|
|
|
|
/// Completes an authentication token. This function is used by protocols, such as DCE,
|
|
/// that need to revise the security information after the transport application has updated some message parameters.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `token`: `SecurityBufferRef` that contains the buffer descriptor for the entire message
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `SspiOk` on success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut client_ntlm = sspi::Ntlm::new();
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
/// let mut output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = client_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut server_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// loop {
|
|
/// client_output_buffer[0].buffer.clear();
|
|
///
|
|
/// let mut builder = client_ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_input(&mut output_buffer)
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _client_result = client_ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let builder = ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut output_buffer);
|
|
/// let server_result = ntlm.accept_security_context_impl(builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// if server_result.status == sspi::SecurityStatus::CompleteAndContinue
|
|
/// || server_result.status == sspi::SecurityStatus::CompleteNeeded
|
|
/// {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let result = ntlm
|
|
/// .complete_auth_token(&mut output_buffer)
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [CompleteAuthToken function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-completeauthtoken)
|
|
fn complete_auth_token(&mut self, token: &mut [SecurityBuffer]) -> Result<SecurityStatus>;
|
|
|
|
/// Encrypts a message to provide privacy. The function allows the application to choose among cryptographic algorithms supported by the chosen mechanism.
|
|
/// Some packages do not have messages to be encrypted or decrypted but rather provide an integrity hash that can be checked.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `flags`: package-specific flags that indicate the quality of protection. A security package can use this parameter to enable the selection of cryptographic algorithms
|
|
/// * `message`: on input, the structure accepts one or more `SecurityBufferRef` structures that can be of type `BufferType::Data`.
|
|
/// That buffer contains the message to be encrypted. The message is encrypted in place, overwriting the original contents of the structure.
|
|
/// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut client_ntlm = sspi::Ntlm::new();
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
/// let mut server_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = client_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut client_ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut server_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// loop {
|
|
/// client_output_buffer[0].buffer.clear();
|
|
///
|
|
/// let mut builder = client_ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_input(&mut server_output_buffer)
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _client_result = client_ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let builder = ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut server_output_buffer);
|
|
/// let server_result = ntlm.accept_security_context_impl(builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// if server_result.status == sspi::SecurityStatus::CompleteAndContinue
|
|
/// || server_result.status == sspi::SecurityStatus::CompleteNeeded
|
|
/// {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let _result = ntlm
|
|
/// .complete_auth_token(&mut server_output_buffer)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut token = [0; 128];
|
|
/// let mut data = "This is a message".as_bytes().to_vec();
|
|
/// let mut msg_buffer = vec![
|
|
/// sspi::SecurityBufferRef::token_buf(token.as_mut_slice()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.as_mut_slice()),
|
|
/// ];
|
|
///
|
|
/// println!("Unencrypted: {:?}", msg_buffer[1].data());
|
|
///
|
|
/// # #[allow(unused_variables)]
|
|
/// let result = ntlm
|
|
/// .encrypt_message(sspi::EncryptionFlags::empty(), &mut msg_buffer).unwrap();
|
|
///
|
|
/// println!("Encrypted: {:?}", msg_buffer[1].data());
|
|
/// ```
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `SspiOk` on success
|
|
/// * `Error` on error
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [EncryptMessage function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-encryptmessage)
|
|
fn encrypt_message(
|
|
&mut self,
|
|
flags: EncryptionFlags,
|
|
message: &mut [SecurityBufferRef<'_>],
|
|
) -> Result<SecurityStatus>;
|
|
|
|
/// Generates a cryptographic checksum of the message, and also includes sequencing information to prevent message loss or insertion.
|
|
/// The function allows the application to choose between several cryptographic algorithms, if supported by the chosen mechanism.
|
|
///
|
|
/// # Parameters
|
|
/// * `flags`: package-specific flags that indicate the quality of protection. A security package can use this parameter to enable the selection of cryptographic algorithms
|
|
/// * `message`: On input, the structure references one or more `SecurityBufferRef` structures of type `BufferType::Data` that contain the message to be signed,
|
|
/// and a `SecurityBufferRef` of type `BufferType::Token` that receives the signature.
|
|
/// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero
|
|
///
|
|
/// # Returns
|
|
/// * `SspiOk` on success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut client_ntlm = sspi::Ntlm::new();
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
/// let mut server_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = client_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut client_ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut server_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// loop {
|
|
/// client_output_buffer[0].buffer.clear();
|
|
///
|
|
/// let mut builder = client_ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_input(&mut server_output_buffer)
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _client_result = client_ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let builder = ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut server_output_buffer);
|
|
/// let server_result = ntlm.accept_security_context_impl(builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// if server_result.status == sspi::SecurityStatus::CompleteAndContinue
|
|
/// || server_result.status == sspi::SecurityStatus::CompleteNeeded
|
|
/// {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let _result = ntlm
|
|
/// .complete_auth_token(&mut server_output_buffer)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut token = [0; 128];
|
|
/// let mut data = "This is a message to be signed".as_bytes().to_vec();
|
|
/// let mut msg_buffer = vec![
|
|
/// sspi::SecurityBufferRef::token_buf(token.as_mut_slice()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.as_mut_slice()),
|
|
/// ];
|
|
///
|
|
/// println!("Input data: {:?}", msg_buffer[1].data());
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let result = ntlm
|
|
/// .make_signature(0, &mut msg_buffer, 0).unwrap();
|
|
///
|
|
/// println!("Data signature: {:?}", msg_buffer[0].data());
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
/// * [MakeSignature function](https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-makesignature)
|
|
fn make_signature(&mut self, flags: u32, message: &mut [SecurityBufferRef<'_>], sequence_number: u32)
|
|
-> Result<()>;
|
|
|
|
/// Verifies that a message signed by using the `make_signature` function was received in the correct sequence and has not been modified.
|
|
///
|
|
/// # Parameters
|
|
/// * `message`: On input, the structure references one or more `SecurityBufferRef` structures of type `BufferType::Data` that contain the message to be verified,
|
|
/// and a `SecurityBufferRef` of type `BufferType::Token` that contains the signature.
|
|
/// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero
|
|
///
|
|
/// # Returns
|
|
/// * `u32` package-specific flags that indicate the quality of protection.
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let mut server_ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
/// let mut server_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut server_acq_cred_result = server_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut server_ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// loop {
|
|
/// client_output_buffer[0].buffer.clear();
|
|
///
|
|
/// let mut builder = ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_input(&mut server_output_buffer)
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _client_result = ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let builder = server_ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut server_output_buffer);
|
|
/// let server_result = server_ntlm.accept_security_context_impl(builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// if server_result.status == sspi::SecurityStatus::CompleteAndContinue
|
|
/// || server_result.status == sspi::SecurityStatus::CompleteNeeded
|
|
/// {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let _result = server_ntlm
|
|
/// .complete_auth_token(&mut server_output_buffer)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut token = [0; 128];
|
|
/// let mut data = "This is a message".as_bytes().to_vec();
|
|
/// let mut msg = [
|
|
/// sspi::SecurityBufferRef::token_buf(token.as_mut_slice()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.as_mut_slice()),
|
|
/// ];
|
|
///
|
|
/// let _result = server_ntlm
|
|
/// .make_signature(0, &mut msg, 0).unwrap();
|
|
///
|
|
/// let [mut token, mut data] = msg;
|
|
///
|
|
/// let mut msg_buffer = vec![
|
|
/// sspi::SecurityBufferRef::token_buf(token.take_data()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.take_data()),
|
|
/// ];
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let signature_flags = ntlm
|
|
/// .verify_signature(&mut msg_buffer, 0)
|
|
/// .unwrap();
|
|
///
|
|
/// println!("Signature calculated and verified.");
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
/// * [VerifySignature function](https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-verifysignature)
|
|
fn verify_signature(&mut self, message: &mut [SecurityBufferRef<'_>], sequence_number: u32) -> Result<u32>;
|
|
|
|
/// Decrypts a message. Some packages do not encrypt and decrypt messages but rather perform and check an integrity hash.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `message`: on input, the structure references one or more `SecurityBufferRef` structures.
|
|
/// At least one of these must be of type `BufferType::Data`.
|
|
/// That buffer contains the encrypted message. The encrypted message is decrypted in place, overwriting the original contents of its buffer
|
|
/// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `DecryptionFlags` upon success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
/// use sspi::builders::EmptyInitializeSecurityContext;
|
|
/// use sspi::SspiImpl;
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let mut server_ntlm = sspi::Ntlm::new();
|
|
///
|
|
/// let mut client_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
/// let mut server_output_buffer = vec![sspi::SecurityBuffer::new(Vec::new(), sspi::BufferType::Token)];
|
|
///
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let mut client_acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Outbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut server_acq_cred_result = server_ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut server_ntlm)
|
|
/// .unwrap();
|
|
///
|
|
/// loop {
|
|
/// client_output_buffer[0].buffer.clear();
|
|
///
|
|
/// let mut builder = ntlm.initialize_security_context()
|
|
/// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(
|
|
/// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY,
|
|
/// )
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_target_name("user")
|
|
/// .with_input(&mut server_output_buffer)
|
|
/// .with_output(&mut client_output_buffer);
|
|
///
|
|
/// let _client_result = ntlm.initialize_security_context_impl(&mut builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// let builder = server_ntlm
|
|
/// .accept_security_context()
|
|
/// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle)
|
|
/// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY)
|
|
/// .with_target_data_representation(sspi::DataRepresentation::Native)
|
|
/// .with_input(&mut client_output_buffer)
|
|
/// .with_output(&mut server_output_buffer);
|
|
/// let server_result = server_ntlm.accept_security_context_impl(builder)
|
|
/// .unwrap()
|
|
/// .resolve_to_result()
|
|
/// .unwrap();
|
|
///
|
|
/// if server_result.status == sspi::SecurityStatus::CompleteAndContinue
|
|
/// || server_result.status == sspi::SecurityStatus::CompleteNeeded
|
|
/// {
|
|
/// break;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let _result = server_ntlm
|
|
/// .complete_auth_token(&mut server_output_buffer)
|
|
/// .unwrap();
|
|
///
|
|
/// let mut token = [0; 128];
|
|
/// let mut data = "This is a message".as_bytes().to_vec();
|
|
/// let mut msg = [
|
|
/// sspi::SecurityBufferRef::token_buf(token.as_mut_slice()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.as_mut_slice()),
|
|
/// ];
|
|
///
|
|
/// let _result = server_ntlm
|
|
/// .encrypt_message(sspi::EncryptionFlags::empty(), &mut msg).unwrap();
|
|
///
|
|
/// let [mut token, mut data] = msg;
|
|
///
|
|
/// let mut msg_buffer = vec![
|
|
/// sspi::SecurityBufferRef::token_buf(token.take_data()),
|
|
/// sspi::SecurityBufferRef::data_buf(data.take_data()),
|
|
/// ];
|
|
///
|
|
/// #[allow(unused_variables)]
|
|
/// let encryption_flags = ntlm
|
|
/// .decrypt_message(&mut msg_buffer)
|
|
/// .unwrap();
|
|
///
|
|
/// println!("Decrypted message: {:?}", msg_buffer[1].data());
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [DecryptMessage function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-decryptmessage)
|
|
fn decrypt_message(&mut self, message: &mut [SecurityBufferRef<'_>]) -> Result<DecryptionFlags>;
|
|
|
|
/// Retrieves information about the bounds of sizes of authentication information of the current security principal.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `ContextSizes` upon success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let sizes = ntlm.query_context_sizes().unwrap();
|
|
/// println!("Max token: {}", sizes.max_token);
|
|
/// println!("Max signature: {}", sizes.max_signature);
|
|
/// println!("Block: {}", sizes.block);
|
|
/// println!("Security trailer: {}", sizes.security_trailer);
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryCredentialsAttributesW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querycredentialsattributesw)
|
|
fn query_context_sizes(&mut self) -> Result<ContextSizes>;
|
|
|
|
/// Retrieves the username of the credential associated to the context.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `ContextNames` upon success
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// use sspi::Username;
|
|
///
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let identity = sspi::AuthIdentity {
|
|
/// username: Username::parse("user").unwrap(),
|
|
/// password: "password".to_string().into(),
|
|
/// };
|
|
///
|
|
/// let _acq_cred_result = ntlm
|
|
/// .acquire_credentials_handle()
|
|
/// .with_credential_use(sspi::CredentialUse::Inbound)
|
|
/// .with_auth_data(&identity)
|
|
/// .execute(&mut ntlm).unwrap();
|
|
///
|
|
/// let names = ntlm.query_context_names().unwrap();
|
|
/// println!("Username: {:?}", names.username.account_name());
|
|
/// println!("Parts: {:?}", names.username.parts());
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QuerySecurityPackageInfoW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querysecuritypackageinfow)
|
|
fn query_context_names(&mut self) -> Result<ContextNames>;
|
|
|
|
/// Queries the sizes of the various parts of a stream used in the per-message functions. This function is implemented only for CredSSP security package.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QuerySecurityPackageInfoW function (`ulAttribute` parameter)](https://learn.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--schannel)
|
|
fn query_context_stream_sizes(&mut self) -> Result<StreamSizes> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"query_context_stream_sizes is not supported",
|
|
))
|
|
}
|
|
|
|
/// Retrieves information about the specified security package. This information includes the bounds of sizes of authentication information, credentials, and contexts.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `PackageInfo` containing the information about the package
|
|
/// * `Error` on error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let info = ntlm.query_context_package_info().unwrap();
|
|
/// println!("Package name: {:?}", info.name);
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QuerySecurityPackageInfoW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querysecuritypackageinfow)
|
|
fn query_context_package_info(&mut self) -> Result<PackageInfo>;
|
|
|
|
/// Retrieves the trust information of the certificate.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `CertTrustStatus` on success
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use sspi::Sspi;
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let cert_info = ntlm.query_context_package_info().unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryContextAttributes (CredSSP) function (`ulAttribute` parameter)](https://docs.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--credssp)
|
|
fn query_context_cert_trust_status(&mut self) -> Result<CertTrustStatus>;
|
|
|
|
/// Retrieves the information about the end certificate supplied by the server. This function is implemented only for CredSSP security package.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `CertContext` on success
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryContextAttributes (CredSSP) function (`ulAttribute` parameter)](https://docs.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--credssp)
|
|
fn query_context_remote_cert(&mut self) -> Result<CertContext> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"query_remote_cert_context is not supported",
|
|
))
|
|
}
|
|
|
|
/// Retrieves the information about the negotiated security package. This function is implemented only for CredSSP security package.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `PackageInfo` on success
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryContextAttributes (CredSSP) function (`ulAttribute` parameter)](https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-querycontextattributesw)
|
|
fn query_context_negotiation_package(&mut self) -> Result<PackageInfo> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"query_context_negotiation_package is not supported",
|
|
))
|
|
}
|
|
|
|
/// Returns detailed information on the established connection. This function is implemented only for CredSSP security package.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `ConnectionInfo` on success
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryContextAttributes (CredSSP) function (`ulAttribute` parameter)](https://docs.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--credssp)
|
|
fn query_context_connection_info(&mut self) -> Result<ConnectionInfo> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"query_context_connection_info is not supported",
|
|
))
|
|
}
|
|
|
|
/// Returns information about the session key used for the security context.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `SessionKeys` on success
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [QueryContextAttributes function (`ulAttribute` parameter)](https://docs.microsoft.com/en-us/windows/win32/secauthn/querycontextattributes--general)
|
|
fn query_context_session_key(&self) -> Result<SessionKeys> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"query_context_session_key is not supported",
|
|
))
|
|
}
|
|
|
|
/// Changes the password for a Windows domain account.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `()` on success
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```ignore
|
|
/// use sspi::{Sspi, ChangePasswordBuilder};
|
|
/// let mut ntlm = sspi::Ntlm::new();
|
|
/// let mut output = [];
|
|
/// let cert_info = ntlm.query_context_package_info().unwrap();
|
|
/// let change_password = ChangePasswordBuilder::new()
|
|
/// .with_domain_name("domain".into())
|
|
/// .with_account_name("username".into())
|
|
/// .with_old_password("old_password".into())
|
|
/// .with_old_password("new_password".into())
|
|
/// .with_output(&mut output)
|
|
/// .build()
|
|
/// .unwrap();
|
|
/// ntlm.change_password(change_password).unwrap();
|
|
/// ```
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [ChangeAccountPasswordW function](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-changeaccountpasswordw)
|
|
fn change_password<'a>(&'a mut self, change_password: ChangePassword<'a>) -> Result<GeneratorChangePassword<'a>>;
|
|
}
|
|
|
|
/// Protocol used to establish connection.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [SecPkgContext_ConnectionInfo (`dwProtocol` field)](https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo)
|
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum ConnectionProtocol {
|
|
SpProtTls1Client = 0x80,
|
|
SpProtTls1Server = 0x40,
|
|
SpProtSsl3Client = 0x20,
|
|
SpProtSsl3Server = 0x10,
|
|
SpProtTls1_1Client = 0x200,
|
|
SpProtTls1_1Server = 0x100,
|
|
SpProtTls1_2Client = 0x800,
|
|
SpProtTls1_2Server = 0x400,
|
|
SpProtTls1_3Client = 0x00002000,
|
|
SpProtTls1_3Server = 0x00001000,
|
|
SpProtPct1Client = 0x2,
|
|
SpProtPct1Server = 0x1,
|
|
SpProtSsl2Client = 0x8,
|
|
SpProtSsl2Server = 0x4,
|
|
}
|
|
|
|
/// Algorithm identifier for the bulk encryption cipher used by the connection.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [SecPkgContext_ConnectionInfo (`aiCipher` field)](https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo)
|
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum ConnectionCipher {
|
|
Calg3des = 26115,
|
|
CalgAes128 = 26126,
|
|
CalgAes256 = 26128,
|
|
CalgDes = 26113,
|
|
CalgRc2 = 26114,
|
|
CalgRc4 = 26625,
|
|
NoEncryption = 0,
|
|
}
|
|
|
|
/// ALG_ID indicating the hash used for generating Message Authentication Codes (MACs).
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [SecPkgContext_ConnectionInfo (`aiHash` field)](https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo)
|
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum ConnectionHash {
|
|
CalgMd5 = 32771,
|
|
CalgSha = 32772,
|
|
}
|
|
|
|
/// ALG_ID indicating the key exchange algorithm used to generate the shared master secret.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [SecPkgContext_ConnectionInfo (`aiExch` field)](https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo)
|
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum ConnectionKeyExchange {
|
|
CalgRsaKeyx = 41984,
|
|
CalgDhEphem = 43522,
|
|
}
|
|
|
|
/// Type of certificate encoding used.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [CERT_CONTEXT (`dwCertEncodingType` field)](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_context)
|
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum CertEncodingType {
|
|
Pkcs7AsnEncoding = 65536,
|
|
X509AsnEncoding = 1,
|
|
}
|
|
|
|
/// The CERT_CONTEXT structure contains both the encoded and decoded representations of a certificate.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [CERT_CONTEXT](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_context)
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct CertContext {
|
|
pub encoding_type: CertEncodingType,
|
|
pub raw_cert: Vec<u8>,
|
|
pub cert: Certificate,
|
|
}
|
|
|
|
/// This structure contains protocol and cipher information.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// [SecPkgContext_ConnectionInfo](https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo)
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct ConnectionInfo {
|
|
pub protocol: ConnectionProtocol,
|
|
pub cipher: ConnectionCipher,
|
|
pub cipher_strength: u32,
|
|
pub hash: ConnectionHash,
|
|
pub hash_strength: u32,
|
|
pub key_exchange: ConnectionKeyExchange,
|
|
pub exchange_strength: u32,
|
|
}
|
|
|
|
/// Trait for performing authentication on the client or server side
|
|
pub trait SspiImpl {
|
|
/// Represents raw data for authentication
|
|
type CredentialsHandle;
|
|
/// Represents authentication data prepared for the authentication process
|
|
type AuthenticationData;
|
|
|
|
fn acquire_credentials_handle_impl(
|
|
&mut self,
|
|
builder: FilledAcquireCredentialsHandle<'_, Self::CredentialsHandle, Self::AuthenticationData>,
|
|
) -> Result<AcquireCredentialsHandleResult<Self::CredentialsHandle>>;
|
|
|
|
fn initialize_security_context_impl<'ctx, 'b, 'g>(
|
|
&'ctx mut self,
|
|
builder: &'b mut FilledInitializeSecurityContext<'ctx, 'ctx, Self::CredentialsHandle>,
|
|
) -> Result<GeneratorInitSecurityContext<'g>>
|
|
where
|
|
'ctx: 'g,
|
|
'b: 'g;
|
|
|
|
fn accept_security_context_impl<'a>(
|
|
&'a mut self,
|
|
builder: FilledAcceptSecurityContext<'a, Self::CredentialsHandle>,
|
|
) -> Result<GeneratorAcceptSecurityContext<'a>>;
|
|
}
|
|
|
|
mod private {
|
|
pub struct Sealed;
|
|
}
|
|
|
|
pub trait SspiEx
|
|
where
|
|
Self: Sized + SspiImpl,
|
|
{
|
|
fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()>;
|
|
|
|
/// Set multiple candidate credentials for server-side verification.
|
|
///
|
|
/// During NTLM authentication, the server will try each candidate to find
|
|
/// one whose password matches the client's challenge-response.
|
|
///
|
|
/// # Security considerations
|
|
///
|
|
/// Candidates should represent a bounded set of currently-valid credentials
|
|
/// (e.g., TTL-bound tokens, or "current + previous" within a defined grace
|
|
/// period), not an unbounded history. Implementations should cap the number
|
|
/// of candidates and ensure existing rate-limiting / lockout behavior remains
|
|
/// effective, so that multi-credential verification does not multiply online
|
|
/// guessing attempts. This mechanism is for selection among multiple valid
|
|
/// credentials, not for weakening a policy that intends immediate
|
|
/// invalidation.
|
|
///
|
|
/// The default implementation uses only the first credential.
|
|
fn custom_set_auth_identities(&mut self, identities: Vec<Self::AuthenticationData>) -> Result<()> {
|
|
match identities.into_iter().next() {
|
|
Some(identity) => self.custom_set_auth_identity(identity),
|
|
None => Err(Error::new(ErrorKind::NoCredentials, "no credentials provided")),
|
|
}
|
|
}
|
|
|
|
/// Verifies a MIC (Message Integrity Code) token for the specified message data.
|
|
///
|
|
/// This method is used only by the Negotiate security package (SPNEGO protocol implementation)
|
|
/// to verify the `mechListMIC` token during the authentication process.
|
|
fn verify_mic_token(&mut self, _token: &[u8], _data: &[u8], _: private::Sealed) -> Result<()> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"verify_mic_token is not supported",
|
|
))
|
|
}
|
|
|
|
/// Generates a MIC (Message Integrity Code) token for the specified message data.
|
|
///
|
|
/// This method is used only by the Negotiate security package (SPNEGO protocol implementation)
|
|
/// to generate `mechListMIC` token during the authentication process.
|
|
fn generate_mic_token(&mut self, _token: &[u8], _: private::Sealed) -> Result<Vec<u8>> {
|
|
Err(Error::new(
|
|
ErrorKind::UnsupportedFunction,
|
|
"generate_mic_token is not supported",
|
|
))
|
|
}
|
|
}
|
|
|
|
pub type SspiPackage<'a, CredsHandle, AuthData> =
|
|
&'a mut dyn SspiImpl<CredentialsHandle = CredsHandle, AuthenticationData = AuthData>;
|
|
|
|
bitflags! {
|
|
/// Indicate the quality of protection. Used in the `encrypt_message` method.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [EncryptMessage function (`fQOP` parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-encryptmessage)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct EncryptionFlags: u32 {
|
|
const WRAP_OOB_DATA = 0x4000_0000;
|
|
const WRAP_NO_ENCRYPT = 0x8000_0001;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Indicate the quality of protection. Returned by the `decrypt_message` method.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [DecryptMessage function (`pfQOP` parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-decryptmessage)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct DecryptionFlags: u32 {
|
|
const SIGN_ONLY = 0x8000_0000;
|
|
const WRAP_NO_ENCRYPT = 0x8000_0001;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Indicate requests for the context. Not all packages can support all requirements. Bit flags can be combined by using bitwise-OR operations.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [Context Requirements](https://docs.microsoft.com/en-us/windows/win32/secauthn/context-requirements)
|
|
/// * [InitializeSecurityContextW function (fContextReq parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct ClientRequestFlags: u32 {
|
|
/// The server can use the context to authenticate to other servers as the client.
|
|
/// The `MUTUAL_AUTH` flag must be set for this flag to work. Valid for Kerberos. Ignore this flag for constrained delegation.
|
|
const DELEGATE = 0x1;
|
|
/// The mutual authentication policy of the service will be satisfied.
|
|
const MUTUAL_AUTH = 0x2;
|
|
/// Detect replayed messages that have been encoded by using the `encrypt_message` or `make_signature` (TBI) functions.
|
|
const REPLAY_DETECT = 0x4;
|
|
/// Detect messages received out of sequence.
|
|
const SEQUENCE_DETECT = 0x8;
|
|
/// Encrypt messages by using the `encrypt_message` function.
|
|
const CONFIDENTIALITY = 0x10;
|
|
/// A new session key must be negotiated. This value is supported only by the Kerberos security package.
|
|
const USE_SESSION_KEY = 0x20;
|
|
const PROMPT_FOR_CREDS = 0x40;
|
|
/// Schannel must not attempt to supply credentials for the client automatically.
|
|
const USE_SUPPLIED_CREDS = 0x80;
|
|
/// The security package allocates output buffers for you.
|
|
const ALLOCATE_MEMORY = 0x100;
|
|
const USE_DCE_STYLE = 0x200;
|
|
const DATAGRAM = 0x400;
|
|
/// The security context will not handle formatting messages. This value is the default for the Kerberos, Negotiate, and NTLM security packages.
|
|
const CONNECTION = 0x800;
|
|
const CALL_LEVEL = 0x1000;
|
|
const FRAGMENT_SUPPLIED = 0x2000;
|
|
/// When errors occur, the remote party will be notified.
|
|
const EXTENDED_ERROR = 0x4000;
|
|
/// Support a stream-oriented connection.
|
|
const STREAM = 0x8000;
|
|
/// Sign messages and verify signatures by using the `encrypt_message` and `make_signature` (TBI) functions.
|
|
const INTEGRITY = 0x0001_0000;
|
|
const IDENTIFY = 0x0002_0000;
|
|
const NULL_SESSION = 0x0004_0000;
|
|
/// Schannel must not authenticate the server automatically.
|
|
const MANUAL_CRED_VALIDATION = 0x0008_0000;
|
|
const RESERVED1 = 0x0010_0000;
|
|
const FRAGMENT_TO_FIT = 0x0020_0000;
|
|
const FORWARD_CREDENTIALS = 0x0040_0000;
|
|
/// If this flag is set, the `Integrity` flag is ignored. This value is supported only by the Negotiate and Kerberos security packages.
|
|
const NO_INTEGRITY = 0x0080_0000;
|
|
const USE_HTTP_STYLE = 0x100_0000;
|
|
const UNVERIFIED_TARGET_NAME = 0x2000_0000;
|
|
const CONFIDENTIALITY_ONLY = 0x4000_0000;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Specify the attributes required by the server to establish the context. Bit flags can be combined by using bitwise-OR operations.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [Context Requirements](https://docs.microsoft.com/en-us/windows/win32/secauthn/context-requirements)
|
|
/// * [AcceptSecurityContext function function (fContextReq parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acceptsecuritycontext?redirectedfrom=MSDN)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct ServerRequestFlags: u32 {
|
|
/// The server is allowed to impersonate the client. Ignore this flag for [constrained delegation](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly).
|
|
const DELEGATE = 0x1;
|
|
const MUTUAL_AUTH = 0x2;
|
|
/// Detect replayed packets.
|
|
const REPLAY_DETECT = 0x4;
|
|
/// Detect messages received out of sequence.
|
|
const SEQUENCE_DETECT = 0x8;
|
|
const CONFIDENTIALITY = 0x10;
|
|
const USE_SESSION_KEY = 0x20;
|
|
const SESSION_TICKET = 0x40;
|
|
/// Credential Security Support Provider (CredSSP) will allocate output buffers.
|
|
const ALLOCATE_MEMORY = 0x100;
|
|
const USE_DCE_STYLE = 0x200;
|
|
const DATAGRAM = 0x400;
|
|
/// The security context will not handle formatting messages.
|
|
const CONNECTION = 0x800;
|
|
const CALL_LEVEL = 0x1000;
|
|
const FRAGMENT_SUPPLIED = 0x2000;
|
|
/// When errors occur, the remote party will be notified.
|
|
const EXTENDED_ERROR = 0x8000;
|
|
/// Support a stream-oriented connection.
|
|
const STREAM = 0x0001_0000;
|
|
const INTEGRITY = 0x0002_0000;
|
|
const LICENSING = 0x0004_0000;
|
|
const IDENTIFY = 0x0008_0000;
|
|
const ALLOW_NULL_SESSION = 0x0010_0000;
|
|
const ALLOW_NON_USER_LOGONS = 0x0020_0000;
|
|
const ALLOW_CONTEXT_REPLAY = 0x0040_0000;
|
|
const FRAGMENT_TO_FIT = 0x80_0000;
|
|
const NO_TOKEN = 0x100_0000;
|
|
const PROXY_BINDINGS = 0x400_0000;
|
|
const ALLOW_MISSING_BINDINGS = 0x1000_0000;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Indicate the attributes of the established context.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [Context Requirements](https://docs.microsoft.com/en-us/windows/win32/secauthn/context-requirements)
|
|
/// * [InitializeSecurityContextW function (pfContextAttr parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct ClientResponseFlags: u32 {
|
|
/// The server can use the context to authenticate to other servers as the client.
|
|
/// The `MUTUAL_AUTH` flag must be set for this flag to work. Valid for Kerberos. Ignore this flag for constrained delegation.
|
|
const DELEGATE = 0x1;
|
|
/// The mutual authentication policy of the service will be satisfied.
|
|
const MUTUAL_AUTH = 0x2;
|
|
/// Detect replayed messages that have been encoded by using the `encrypt_message` or `make_signature` (TBI) functions.
|
|
const REPLAY_DETECT = 0x4;
|
|
/// Detect messages received out of sequence.
|
|
const SEQUENCE_DETECT = 0x8;
|
|
/// Encrypt messages by using the `encrypt_message` function.
|
|
const CONFIDENTIALITY = 0x10;
|
|
/// A new session key must be negotiated. This value is supported only by the Kerberos security package.
|
|
const USE_SESSION_KEY = 0x20;
|
|
const USED_COLLECTED_CREDS = 0x40;
|
|
/// Schannel must not attempt to supply credentials for the client automatically.
|
|
const USED_SUPPLIED_CREDS = 0x80;
|
|
/// The security package allocates output buffers for you.
|
|
const ALLOCATED_MEMORY = 0x100;
|
|
const USED_DCE_STYLE = 0x200;
|
|
const DATAGRAM = 0x400;
|
|
/// The security context will not handle formatting messages. This value is the default for the Kerberos, Negotiate, and NTLM security packages.
|
|
const CONNECTION = 0x800;
|
|
const INTERMEDIATE_RETURN = 0x1000;
|
|
const CALL_LEVEL = 0x2000;
|
|
/// When errors occur, the remote party will be notified.
|
|
const EXTENDED_ERROR = 0x4000;
|
|
/// Support a stream-oriented connection.
|
|
const STREAM = 0x8000;
|
|
/// Sign messages and verify signatures by using the `encrypt_message` and `make_signature` (TBI) functions.
|
|
const INTEGRITY = 0x0001_0000;
|
|
const IDENTIFY = 0x0002_0000;
|
|
const NULL_SESSION = 0x0004_0000;
|
|
/// Schannel must not authenticate the server automatically.
|
|
const MANUAL_CRED_VALIDATION = 0x0008_0000;
|
|
const RESERVED1 = 0x10_0000;
|
|
const FRAGMENT_ONLY = 0x0020_0000;
|
|
const FORWARD_CREDENTIALS = 0x0040_0000;
|
|
const USED_HTTP_STYLE = 0x100_0000;
|
|
const NO_ADDITIONAL_TOKEN = 0x200_0000;
|
|
const REAUTHENTICATION = 0x800_0000;
|
|
const CONFIDENTIALITY_ONLY = 0x4000_0000;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Indicate the attributes of the established context.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [Context Requirements](https://docs.microsoft.com/en-us/windows/win32/secauthn/context-requirements)
|
|
/// * [AcceptSecurityContext function function (pfContextAttr parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acceptsecuritycontext?redirectedfrom=MSDN)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct ServerResponseFlags: u32 {
|
|
/// The server is allowed to impersonate the client. Ignore this flag for [constrained delegation](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly).
|
|
const DELEGATE = 0x1;
|
|
const MUTUAL_AUTH = 0x2;
|
|
/// Detect replayed packets.
|
|
const REPLAY_DETECT = 0x4;
|
|
/// Detect messages received out of sequence.
|
|
const SEQUENCE_DETECT = 0x8;
|
|
const CONFIDENTIALITY = 0x10;
|
|
const USE_SESSION_KEY = 0x20;
|
|
const SESSION_TICKET = 0x40;
|
|
/// Credential Security Support Provider (CredSSP) will allocate output buffers.
|
|
const ALLOCATED_MEMORY = 0x100;
|
|
const USED_DCE_STYLE = 0x200;
|
|
const DATAGRAM = 0x400;
|
|
/// The security context will not handle formatting messages.
|
|
const CONNECTION = 0x800;
|
|
const CALL_LEVEL = 0x2000;
|
|
const THIRD_LEG_FAILED = 0x4000;
|
|
/// When errors occur, the remote party will be notified.
|
|
const EXTENDED_ERROR = 0x8000;
|
|
/// Support a stream-oriented connection.
|
|
const STREAM = 0x0001_0000;
|
|
const INTEGRITY = 0x0002_0000;
|
|
const LICENSING = 0x0004_0000;
|
|
const IDENTIFY = 0x0008_0000;
|
|
const NULL_SESSION = 0x0010_0000;
|
|
const ALLOW_NON_USER_LOGONS = 0x0020_0000;
|
|
const ALLOW_CONTEXT_REPLAY = 0x0040_0000;
|
|
const FRAGMENT_ONLY = 0x0080_0000;
|
|
const NO_TOKEN = 0x100_0000;
|
|
const NO_ADDITIONAL_TOKEN = 0x200_0000;
|
|
}
|
|
}
|
|
|
|
/// The data representation, such as byte ordering, on the target.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [AcceptSecurityContext function (TargetDataRep parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acceptsecuritycontext)
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum DataRepresentation {
|
|
Network = 0,
|
|
Native = 0x10,
|
|
}
|
|
|
|
/// Describes a buffer allocated by a transport application to pass to a security package.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecBuffer structure](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secbuffer)
|
|
#[derive(Clone)]
|
|
pub struct SecurityBuffer {
|
|
pub buffer: Vec<u8>,
|
|
pub buffer_type: SecurityBufferType,
|
|
}
|
|
|
|
impl fmt::Debug for SecurityBuffer {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"SecurityBufferRef {{ buffer_type: {:?}, buffer: 0x",
|
|
self.buffer_type
|
|
)?;
|
|
self.buffer.iter().try_for_each(|byte| write!(f, "{byte:02X}"))?;
|
|
write!(f, " }}")?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl SecurityBuffer {
|
|
pub fn new(buffer: Vec<u8>, buffer_type: BufferType) -> Self {
|
|
Self {
|
|
buffer,
|
|
buffer_type: SecurityBufferType {
|
|
buffer_type,
|
|
buffer_flags: SecurityBufferFlags::NONE,
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn find_buffer(buffers: &[SecurityBuffer], buffer_type: BufferType) -> Result<&SecurityBuffer> {
|
|
buffers
|
|
.iter()
|
|
.find(|b| b.buffer_type.buffer_type == buffer_type)
|
|
.ok_or_else(|| {
|
|
Error::new(
|
|
ErrorKind::InvalidToken,
|
|
format!("no buffer was provided with type {buffer_type:?}"),
|
|
)
|
|
})
|
|
}
|
|
|
|
pub fn find_buffer_mut(buffers: &mut [SecurityBuffer], buffer_type: BufferType) -> Result<&mut SecurityBuffer> {
|
|
buffers
|
|
.iter_mut()
|
|
.find(|b| b.buffer_type.buffer_type == buffer_type)
|
|
.ok_or_else(|| {
|
|
Error::new(
|
|
ErrorKind::InvalidToken,
|
|
format!("no buffer was provided with type {buffer_type:?}"),
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Bit flags that indicate the type of buffer.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecBuffer structure (BufferType parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secbuffer)
|
|
#[repr(u32)]
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, FromPrimitive, ToPrimitive)]
|
|
pub enum BufferType {
|
|
#[default]
|
|
Empty = 0,
|
|
/// The buffer contains common data. The security package can read and write this data, for example, to encrypt some or all of it.
|
|
Data = 1,
|
|
/// The buffer contains the security token portion of the message. This is read-only for input parameters or read/write for output parameters.
|
|
Token = 2,
|
|
TransportToPackageParameters = 3,
|
|
/// The security package uses this value to indicate the number of missing bytes in a particular message.
|
|
Missing = 4,
|
|
/// The security package uses this value to indicate the number of extra or unprocessed bytes in a message.
|
|
Extra = 5,
|
|
/// The buffer contains a protocol-specific trailer for a particular record. It is not usually of interest to callers.
|
|
StreamTrailer = 6,
|
|
/// The buffer contains a protocol-specific header for a particular record. It is not usually of interest to callers.
|
|
StreamHeader = 7,
|
|
NegotiationInfo = 8,
|
|
Padding = 9,
|
|
Stream = 10,
|
|
ObjectIdsList = 11,
|
|
ObjectIdsListSignature = 12,
|
|
/// This flag is reserved. Do not use it.
|
|
Target = 13,
|
|
/// The buffer contains channel binding information.
|
|
ChannelBindings = 14,
|
|
/// The buffer contains a [DOMAIN_PASSWORD_INFORMATION](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-domain_password_information) structure.
|
|
ChangePasswordResponse = 15,
|
|
/// The buffer specifies the [service principal name (SPN)](https://docs.microsoft.com/en-us/windows/win32/secgloss/s-gly) of the target.
|
|
TargetHost = 16,
|
|
/// The buffer contains an alert message.
|
|
Alert = 17,
|
|
/// The buffer contains a list of application protocol IDs, one list per application protocol negotiation extension type to be enabled.
|
|
ApplicationProtocol = 18,
|
|
/// The buffer contains a bitmask for a `ReadOnly` buffer.
|
|
AttributeMark = 0xF000_0000,
|
|
/// The buffer is read-only with no checksum. This flag is intended for sending header information to the security package for computing the checksum.
|
|
/// The package can read this buffer, but cannot modify it.
|
|
ReadOnly = 0x8000_0000,
|
|
/// The buffer is read-only with a checksum.
|
|
ReadOnlyWithChecksum = 0x1000_0000,
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
/// Security buffer flags.
|
|
///
|
|
/// [`SecBuffer` structure (sspi.h)](https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secbuffer).
|
|
pub struct SecurityBufferFlags: u32 {
|
|
/// There is no flags for the buffer.
|
|
const NONE = 0x0;
|
|
/// The buffer is read-only with no checksum. This flag is intended for sending header information to the security package for
|
|
/// computing the checksum. The package can read this buffer, but cannot modify it.
|
|
const SECBUFFER_READONLY = 0x80000000;
|
|
/// The buffer is read-only with a checksum.
|
|
const SECBUFFER_READONLY_WITH_CHECKSUM = 0x10000000;
|
|
}
|
|
}
|
|
|
|
/// Security buffer type.
|
|
///
|
|
/// Contains the actual security buffer type and its flags.
|
|
#[derive(Clone, Copy, Eq, PartialEq, Default)]
|
|
pub struct SecurityBufferType {
|
|
/// Security buffer type.
|
|
pub buffer_type: BufferType,
|
|
/// Security buffer flags.
|
|
pub buffer_flags: SecurityBufferFlags,
|
|
}
|
|
|
|
impl SecurityBufferType {
|
|
/// The buffer contains a bitmask for a `SECBUFFER_READONLY_WITH_CHECKSUM` buffer.
|
|
///
|
|
/// [`SecBuffer` structure (sspi.h)](https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secbuffer)
|
|
pub const SECBUFFER_ATTRMASK: u32 = 0xf0000000;
|
|
}
|
|
|
|
impl TryFrom<u32> for SecurityBufferType {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: u32) -> Result<Self> {
|
|
use num_traits::cast::FromPrimitive;
|
|
|
|
let buffer_type = value & !Self::SECBUFFER_ATTRMASK;
|
|
let buffer_type = BufferType::from_u32(buffer_type).ok_or_else(|| {
|
|
Error::new(
|
|
ErrorKind::InternalError,
|
|
format!("u32({buffer_type}) to UnflaggedSecurityBuffer conversion error"),
|
|
)
|
|
})?;
|
|
|
|
let buffer_flags = value & Self::SECBUFFER_ATTRMASK;
|
|
let buffer_flags = SecurityBufferFlags::from_bits(buffer_flags).ok_or_else(|| {
|
|
Error::new(
|
|
ErrorKind::InternalError,
|
|
format!("invalid SecurityBufferFlags: {buffer_flags}"),
|
|
)
|
|
})?;
|
|
|
|
Ok(Self {
|
|
buffer_type,
|
|
buffer_flags,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<SecurityBufferType> for u32 {
|
|
fn from(value: SecurityBufferType) -> u32 {
|
|
use num_traits::cast::ToPrimitive;
|
|
|
|
value.buffer_type.to_u32().unwrap() | value.buffer_flags.bits()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for SecurityBufferType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_fmt(format_args!("({:?}, {:?})", self.buffer_type, self.buffer_flags))
|
|
}
|
|
}
|
|
|
|
/// A flag that indicates how the credentials are used.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [AcquireCredentialsHandleW function (fCredentialUse parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-acquirecredentialshandlew)
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum CredentialUse {
|
|
Inbound = 1,
|
|
Outbound = 2,
|
|
Both = 3,
|
|
Default = 4,
|
|
}
|
|
|
|
/// Represents the security principal in use.
|
|
#[derive(Debug, Clone)]
|
|
pub enum SecurityPackageType {
|
|
Ntlm,
|
|
Kerberos,
|
|
Negotiate,
|
|
Pku2u,
|
|
#[cfg(feature = "tsssp")]
|
|
CredSsp,
|
|
Other(String),
|
|
}
|
|
|
|
impl AsRef<str> for SecurityPackageType {
|
|
fn as_ref(&self) -> &str {
|
|
match self {
|
|
SecurityPackageType::Ntlm => ntlm::PKG_NAME,
|
|
SecurityPackageType::Kerberos => kerberos::PKG_NAME,
|
|
SecurityPackageType::Negotiate => negotiate::PKG_NAME,
|
|
SecurityPackageType::Pku2u => pku2u::PKG_NAME,
|
|
#[cfg(feature = "tsssp")]
|
|
SecurityPackageType::CredSsp => sspi_cred_ssp::PKG_NAME,
|
|
SecurityPackageType::Other(name) => name.as_str(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for SecurityPackageType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
SecurityPackageType::Ntlm => write!(f, "{}", ntlm::PKG_NAME),
|
|
SecurityPackageType::Kerberos => write!(f, "{}", kerberos::PKG_NAME),
|
|
SecurityPackageType::Negotiate => write!(f, "{}", negotiate::PKG_NAME),
|
|
SecurityPackageType::Pku2u => write!(f, "{}", pku2u::PKG_NAME),
|
|
#[cfg(feature = "tsssp")]
|
|
SecurityPackageType::CredSsp => write!(f, "{}", sspi_cred_ssp::PKG_NAME),
|
|
SecurityPackageType::Other(name) => write!(f, "{name}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl str::FromStr for SecurityPackageType {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
match s {
|
|
ntlm::PKG_NAME => Ok(SecurityPackageType::Ntlm),
|
|
kerberos::PKG_NAME => Ok(SecurityPackageType::Kerberos),
|
|
negotiate::PKG_NAME => Ok(SecurityPackageType::Negotiate),
|
|
pku2u::PKG_NAME => Ok(SecurityPackageType::Pku2u),
|
|
#[cfg(feature = "tsssp")]
|
|
sspi_cred_ssp::PKG_NAME => Ok(SecurityPackageType::CredSsp),
|
|
s => Ok(SecurityPackageType::Other(s.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// General security principal information
|
|
///
|
|
/// Provides general information about a security package, such as its name and capabilities. Returned by `query_security_package_info`.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgInfoW structure](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkginfow)
|
|
#[derive(Debug, Clone)]
|
|
pub struct PackageInfo {
|
|
pub capabilities: PackageCapabilities,
|
|
pub rpc_id: u16,
|
|
pub max_token_len: u32,
|
|
pub name: SecurityPackageType,
|
|
pub comment: String,
|
|
}
|
|
|
|
bitflags! {
|
|
/// Set of bit flags that describes the capabilities of the security package. It is possible to combine them.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgInfoW structure (`fCapabilities` parameter)](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkginfow)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct PackageCapabilities: u32 {
|
|
/// The security package supports the `make_signature` (TBI) and `verify_signature` (TBI) functions.
|
|
const INTEGRITY = 0x1;
|
|
/// The security package supports the `encrypt_message` and `decrypt_message` functions.
|
|
const PRIVACY = 0x2;
|
|
/// The package is interested only in the security-token portion of messages, and will ignore any other buffers. This is a performance-related issue.
|
|
const TOKEN_ONLY = 0x4;
|
|
/// Supports [datagram](https://docs.microsoft.com/en-us/windows/win32/secgloss/d-gly)-style authentication.
|
|
/// For more information, see [SSPI Context Semantics](https://docs.microsoft.com/en-us/windows/win32/secauthn/sspi-context-semantics).
|
|
const DATAGRAM = 0x8;
|
|
/// Supports connection-oriented style authentication. For more information, see [SSPI Context Semantics](https://docs.microsoft.com/en-us/windows/win32/secauthn/sspi-context-semantics).
|
|
const CONNECTION = 0x10;
|
|
/// Multiple legs are required for authentication.
|
|
const MULTI_REQUIRED = 0x20;
|
|
/// Server authentication support is not provided.
|
|
const CLIENT_ONLY = 0x40;
|
|
/// Supports extended error handling. For more information, see [Extended Error Information](https://docs.microsoft.com/en-us/windows/win32/secauthn/extended-error-information).
|
|
const EXTENDED_ERROR = 0x80;
|
|
/// Supports Windows impersonation in server contexts.
|
|
const IMPERSONATION = 0x100;
|
|
/// Understands Windows principal and target names.
|
|
const ACCEPT_WIN32_NAME = 0x200;
|
|
/// Supports stream semantics. For more information, see [SSPI Context Semantics](https://docs.microsoft.com/en-us/windows/win32/secauthn/sspi-context-semantics).
|
|
const STREAM = 0x400;
|
|
/// Can be used by the [Microsoft Negotiate](https://docs.microsoft.com/windows/desktop/SecAuthN/microsoft-negotiate) security package.
|
|
const NEGOTIABLE = 0x800;
|
|
/// Supports GSS compatibility.
|
|
const GSS_COMPATIBLE = 0x1000;
|
|
/// Supports [LsaLogonUser](https://docs.microsoft.com/windows/desktop/api/ntsecapi/nf-ntsecapi-lsalogonuser).
|
|
const LOGON = 0x2000;
|
|
/// Token buffers are in ASCII characters format.
|
|
const ASCII_BUFFERS = 0x4000;
|
|
/// Supports separating large tokens into smaller buffers so that applications can make repeated calls to
|
|
/// `initialize_security_context` and `accept_security_context` with the smaller buffers to complete authentication.
|
|
const FRAGMENT = 0x8000;
|
|
/// Supports mutual authentication.
|
|
const MUTUAL_AUTH = 0x1_0000;
|
|
/// Supports delegation.
|
|
const DELEGATION = 0x2_0000;
|
|
/// The security package supports using a checksum instead of in-place encryption when calling the `encrypt_message` function.
|
|
const READONLY_WITH_CHECKSUM = 0x4_0000;
|
|
/// Supports callers with restricted tokens.
|
|
const RESTRICTED_TOKENS = 0x8_0000;
|
|
/// The security package extends the [Microsoft Negotiate](https://docs.microsoft.com/windows/desktop/SecAuthN/microsoft-negotiate) security package.
|
|
/// There can be at most one package of this type.
|
|
const NEGO_EXTENDER = 0x10_0000;
|
|
/// This package is negotiated by the package of type `NEGO_EXTENDER`.
|
|
const NEGOTIABLE2 = 0x20_0000;
|
|
/// This package receives all calls from app container apps.
|
|
const APP_CONTAINER_PASSTHROUGH = 0x40_0000;
|
|
/// This package receives calls from app container apps if one of the following checks succeeds:
|
|
/// * Caller has default credentials capability
|
|
/// * The target is a proxy server
|
|
/// * The caller has supplied credentials
|
|
const APP_CONTAINER_CHECKS = 0x80_0000;
|
|
}
|
|
}
|
|
|
|
/// Indicates the sizes of the various parts of a stream for use with the message support functions.
|
|
/// `query_context_stream_sizes` function returns this structure.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgContext_StreamSizes](https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_streamsizes)
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct StreamSizes {
|
|
pub header: u32,
|
|
pub trailer: u32,
|
|
pub max_message: u32,
|
|
pub buffers: u32,
|
|
pub block_size: u32,
|
|
}
|
|
|
|
/// Indicates the sizes of important structures used in the message support functions.
|
|
/// `query_context_sizes` function returns this structure.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgContext_Sizes structure](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_sizes)
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct ContextSizes {
|
|
pub max_token: u32,
|
|
pub max_signature: u32,
|
|
pub block: u32,
|
|
pub security_trailer: u32,
|
|
}
|
|
|
|
/// Contains trust information about a certificate in a certificate chain,
|
|
/// summary trust information about a simple chain of certificates, or summary information about an array of simple chains.
|
|
/// `query_context_cert_trust_status` function returns this structure.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [CERT_TRUST_STATUS structure](https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_trust_status)
|
|
#[derive(Debug, Clone)]
|
|
pub struct CertTrustStatus {
|
|
pub error_status: CertTrustErrorStatus,
|
|
pub info_status: CertTrustInfoStatus,
|
|
}
|
|
|
|
bitflags! {
|
|
/// Flags representing the error status codes used in `CertTrustStatus`.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [CERT_TRUST_STATUS structure](https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_trust_status)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct CertTrustErrorStatus: u32 {
|
|
/// No error found for this certificate or chain.
|
|
const NO_ERROR = 0x0;
|
|
/// This certificate or one of the certificates in the certificate chain is not time valid.
|
|
const IS_NOT_TIME_VALID = 0x1;
|
|
const IS_NOT_TIME_NESTED = 0x2;
|
|
/// Trust for this certificate or one of the certificates in the certificate chain has been revoked.
|
|
const IS_REVOKED = 0x4;
|
|
/// The certificate or one of the certificates in the certificate chain does not have a valid signature.
|
|
const IS_NOT_SIGNATURE_VALID = 0x8;
|
|
/// The certificate or certificate chain is not valid for its proposed usage.
|
|
const IS_NOT_VALID_FOR_USAGE = 0x10;
|
|
/// The certificate or certificate chain is based on an untrusted root.
|
|
const IS_UNTRUSTED_ROOT = 0x20;
|
|
/// The revocation status of the certificate or one of the certificates in the certificate chain is unknown.
|
|
const REVOCATION_STATUS_UNKNOWN = 0x40;
|
|
/// One of the certificates in the chain was issued by a
|
|
/// [`certification authority`](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly)
|
|
/// that the original certificate had certified.
|
|
const IS_CYCLIC = 0x80;
|
|
/// One of the certificates has an extension that is not valid.
|
|
const INVALID_EXTENSION = 0x100;
|
|
/// The certificate or one of the certificates in the certificate chain has a policy constraints extension,
|
|
/// and one of the issued certificates has a disallowed policy mapping extension or does not have a
|
|
/// required issuance policies extension.
|
|
const INVALID_POLICY_CONSTRAINTS = 0x200;
|
|
/// The certificate or one of the certificates in the certificate chain has a basic constraints extension,
|
|
/// and either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded.
|
|
const INVALID_BASIC_CONSTRAINTS = 0x400;
|
|
/// The certificate or one of the certificates in the certificate chain has a name constraints extension that is not valid.
|
|
const INVALID_NAME_CONSTRAINTS = 0x800;
|
|
/// The certificate or one of the certificates in the certificate chain has a name constraints extension that contains
|
|
/// unsupported fields. The minimum and maximum fields are not supported.
|
|
/// Thus minimum must always be zero and maximum must always be absent. Only UPN is supported for an Other Name.
|
|
/// The following alternative name choices are not supported:
|
|
/// * X400 Address
|
|
/// * EDI Party Name
|
|
/// * Registered Id
|
|
const HAS_NOT_SUPPORTED_NAME_CONSTRAINT = 0x1000;
|
|
/// The certificate or one of the certificates in the certificate chain has a name constraints extension and a name
|
|
/// constraint is missing for one of the name choices in the end certificate.
|
|
const HAS_NOT_DEFINED_NAME_CONSTRAINT = 0x2000;
|
|
/// The certificate or one of the certificates in the certificate chain has a name constraints extension,
|
|
/// and there is not a permitted name constraint for one of the name choices in the end certificate.
|
|
const HAS_NOT_PERMITTED_NAME_CONSTRAINT = 0x4000;
|
|
/// The certificate or one of the certificates in the certificate chain has a name constraints extension,
|
|
/// and one of the name choices in the end certificate is explicitly excluded.
|
|
const HAS_EXCLUDED_NAME_CONSTRAINT = 0x8000;
|
|
/// The certificate chain is not complete.
|
|
const IS_PARTIAL_CHAIN = 0x0001_0000;
|
|
/// A [certificate trust list](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly)
|
|
/// (CTL) used to create this chain was not time valid.
|
|
const CTL_IS_NOT_TIME_VALID = 0x0002_0000;
|
|
/// A CTL used to create this chain did not have a valid signature.
|
|
const CTL_IS_NOT_SIGNATURE_VALID = 0x0004_0000;
|
|
/// A CTL used to create this chain is not valid for this usage.
|
|
const CTL_IS_NOT_VALID_FOR_USAGE = 0x0008_0000;
|
|
/// The revocation status of the certificate or one of the certificates in the certificate chain is either offline or stale.
|
|
const IS_OFFLINE_REVOCATION = 0x100_0000;
|
|
/// The end certificate does not have any resultant issuance policies, and one of the issuing
|
|
/// [certification authority](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly)
|
|
/// certificates has a policy constraints extension requiring it.
|
|
const NO_ISSUANCE_CHAIN_POLICY = 0x200_0000;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
/// Flags representing the info status codes used in `CertTrustStatus`.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [CERT_TRUST_STATUS structure](https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_trust_status)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct CertTrustInfoStatus: u32 {
|
|
/// An exact match issuer certificate has been found for this certificate. This status code applies to certificates only.
|
|
const HAS_EXACT_MATCH_ISSUER = 0x1;
|
|
/// A key match issuer certificate has been found for this certificate. This status code applies to certificates only.
|
|
const HAS_KEY_MATCH_ISSUER = 0x2;
|
|
/// A name match issuer certificate has been found for this certificate. This status code applies to certificates only.
|
|
const HAS_NAME_MATCH_ISSUER = 0x4;
|
|
/// This certificate is self-signed. This status code applies to certificates only.
|
|
const IS_SELF_SIGNED = 0x8;
|
|
const AUTO_UPDATE_CA_REVOCATION = 0x10;
|
|
const AUTO_UPDATE_END_REVOCATION = 0x20;
|
|
const NO_OCSP_FAILOVER_TO_CRL = 0x40;
|
|
const IS_KEY_ROLLOVER = 0x80;
|
|
/// The certificate or chain has a preferred issuer. This status code applies to certificates and chains.
|
|
const HAS_PREFERRED_ISSUER = 0x100;
|
|
/// An issuance chain policy exists. This status code applies to certificates and chains.
|
|
const HAS_ISSUANCE_CHAIN_POLICY = 0x200;
|
|
/// A valid name constraints for all namespaces, including UPN. This status code applies to certificates and chains.
|
|
const HAS_VALID_NAME_CONSTRAINTS = 0x400;
|
|
/// This certificate is peer trusted. This status code applies to certificates only.
|
|
const IS_PEER_TRUSTED = 0x800;
|
|
/// This certificate's [certificate revocation list](https://docs.microsoft.com/windows/desktop/SecGloss/c-gly)
|
|
/// (CRL) validity has been extended. This status code applies to certificates only.
|
|
const HAS_CRL_VALIDITY_EXTENDED = 0x1000;
|
|
const IS_FROM_EXCLUSIVE_TRUST_STORE = 0x2000;
|
|
const IS_CA_TRUSTED = 0x4000;
|
|
const HAS_AUTO_UPDATE_WEAK_SIGNATURE = 0x8000;
|
|
const SSL_HANDSHAKE_OCSP = 0x0004_0000;
|
|
const SSL_TIME_VALID_OCSP = 0x0008_0000;
|
|
const SSL_RECONNECT_OCSP = 0x0010_0000;
|
|
const IS_COMPLEX_CHAIN = 0x0001_0000;
|
|
const HAS_ALLOW_WEAK_SIGNATURE = 0x0002_0000;
|
|
const SSL_TIME_VALID = 0x100_0000;
|
|
const NO_TIME_CHECK = 0x200_0000;
|
|
}
|
|
}
|
|
|
|
/// Indicates the name of the user associated with a security context.
|
|
/// `query_context_names` function returns this structure.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgContext_NamesW structure](https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_namesw)
|
|
#[derive(Debug, Clone)]
|
|
pub struct ContextNames {
|
|
pub username: Username,
|
|
}
|
|
|
|
/// Contains information about the session key used for the security context.
|
|
/// `query_context_session_key` function returns this structure.
|
|
///
|
|
/// # MSDN
|
|
///
|
|
/// * [SecPkgContext_SessionKey structure](https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-secpkgcontext_sessionkey)
|
|
#[derive(Debug, Clone)]
|
|
pub struct SessionKeys {
|
|
pub session_key: Secret<Vec<u8>>,
|
|
}
|
|
|
|
/// The kind of an SSPI related error. Enables to specify an error based on its type.
|
|
///
|
|
/// [SSPI Status Codes](https://learn.microsoft.com/en-us/windows/win32/secauthn/sspi-status-codes).
|
|
#[repr(u32)]
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum ErrorKind {
|
|
Unknown = 0,
|
|
InsufficientMemory = 0x8009_0300,
|
|
InvalidHandle = 0x8009_0301,
|
|
UnsupportedFunction = 0x8009_0302,
|
|
TargetUnknown = 0x8009_0303,
|
|
/// May correspond to any internal error (I/O error, server error, etc.).
|
|
InternalError = 0x8009_0304,
|
|
SecurityPackageNotFound = 0x8009_0305,
|
|
NotOwned = 0x8009_0306,
|
|
CannotInstall = 0x8009_0307,
|
|
/// Used in cases when supplied data is missing or invalid.
|
|
InvalidToken = 0x8009_0308,
|
|
CannotPack = 0x8009_0309,
|
|
OperationNotSupported = 0x8009_030A,
|
|
NoImpersonation = 0x8009_030B,
|
|
LogonDenied = 0x8009_030C,
|
|
UnknownCredentials = 0x8009_030D,
|
|
NoCredentials = 0x8009_030E,
|
|
/// Used in contexts of supplying invalid credentials.
|
|
MessageAltered = 0x8009_030F,
|
|
/// Used when a required NTLM state does not correspond to the current.
|
|
OutOfSequence = 0x8009_0310,
|
|
NoAuthenticatingAuthority = 0x8009_0311,
|
|
BadPackageId = 0x8009_0316,
|
|
ContextExpired = 0x8009_0317,
|
|
IncompleteMessage = 0x8009_0318,
|
|
IncompleteCredentials = 0x8009_0320,
|
|
BufferTooSmall = 0x8009_0321,
|
|
WrongPrincipalName = 0x8009_0322,
|
|
TimeSkew = 0x8009_0324,
|
|
UntrustedRoot = 0x8009_0325,
|
|
IllegalMessage = 0x8009_0326,
|
|
CertificateUnknown = 0x8009_0327,
|
|
CertificateExpired = 0x8009_0328,
|
|
EncryptFailure = 0x8009_0329,
|
|
DecryptFailure = 0x8009_0330,
|
|
AlgorithmMismatch = 0x8009_0331,
|
|
SecurityQosFailed = 0x8009_0332,
|
|
UnfinishedContextDeleted = 0x8009_0333,
|
|
NoTgtReply = 0x8009_0334,
|
|
NoIpAddress = 0x8009_0335,
|
|
WrongCredentialHandle = 0x8009_0336,
|
|
CryptoSystemInvalid = 0x8009_0337,
|
|
MaxReferralsExceeded = 0x8009_0338,
|
|
MustBeKdc = 0x8009_0339,
|
|
StrongCryptoNotSupported = 0x8009_033A,
|
|
TooManyPrincipals = 0x8009_033B,
|
|
NoPaData = 0x8009_033C,
|
|
PkInitNameMismatch = 0x8009_033D,
|
|
SmartCardLogonRequired = 0x8009_033E,
|
|
ShutdownInProgress = 0x8009_033F,
|
|
KdcInvalidRequest = 0x8009_0340,
|
|
KdcUnknownEType = 0x8009_0341,
|
|
KdcUnknownEType2 = 0x8009_0342,
|
|
UnsupportedPreAuth = 0x8009_0343,
|
|
DelegationRequired = 0x8009_0345,
|
|
BadBindings = 0x8009_0346,
|
|
MultipleAccounts = 0x8009_0347,
|
|
NoKerbKey = 0x8009_0348,
|
|
CertWrongUsage = 0x8009_0349,
|
|
DowngradeDetected = 0x8009_0350,
|
|
SmartCardCertificateRevoked = 0x8009_0351,
|
|
IssuingCAUntrusted = 0x8009_0352,
|
|
RevocationOffline = 0x8009_0353,
|
|
PkInitClientFailure = 0x8009_0354,
|
|
SmartCardCertExpired = 0x8009_0355,
|
|
NoS4uProtSupport = 0x8009_0356,
|
|
CrossRealmDelegationFailure = 0x8009_0357,
|
|
RevocationOfflineKdc = 0x8009_0358,
|
|
IssuingCaUntrustedKdc = 0x8009_0359,
|
|
KdcCertExpired = 0x8009_035A,
|
|
KdcCertRevoked = 0x8009_035B,
|
|
InvalidParameter = 0x8009_035D,
|
|
DelegationPolicy = 0x8009_035E,
|
|
PolicyNtlmOnly = 0x8009_035F,
|
|
NoContext = 0x8009_0361,
|
|
Pku2uCertFailure = 0x8009_0362,
|
|
MutualAuthFailed = 0x8009_0363,
|
|
OnlyHttpsAllowed = 0x8009_0365,
|
|
ApplicationProtocolMismatch = 0x8009_0367,
|
|
}
|
|
|
|
/// Holds the `ErrorKind` and the description of the SSPI-related error.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Error {
|
|
pub error_type: ErrorKind,
|
|
pub description: String,
|
|
pub nstatus: Option<credssp::NStatusCode>,
|
|
}
|
|
|
|
/// The success status of SSPI-related operation.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
|
pub enum SecurityStatus {
|
|
Ok = 0,
|
|
ContinueNeeded = 0x0009_0312,
|
|
CompleteNeeded = 0x0009_0313,
|
|
CompleteAndContinue = 0x0009_0314,
|
|
LocalLogon = 0x0009_0315,
|
|
ContextExpired = 0x0009_0317,
|
|
IncompleteCredentials = 0x0009_0320,
|
|
Renegotiate = 0x0009_0321,
|
|
NoLsaContext = 0x0009_0323,
|
|
}
|
|
|
|
impl Error {
|
|
/// Allows to fill a new error easily, supplying it with a coherent description.
|
|
pub fn new(error_type: ErrorKind, description: impl ToString) -> Self {
|
|
Self {
|
|
error_type,
|
|
description: description.to_string(),
|
|
nstatus: None,
|
|
}
|
|
}
|
|
|
|
pub fn new_with_nstatus(
|
|
error_type: ErrorKind,
|
|
description: impl Into<String>,
|
|
status_code: credssp::NStatusCode,
|
|
) -> Self {
|
|
Self {
|
|
error_type,
|
|
description: description.into(),
|
|
nstatus: Some(status_code),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl error::Error for Error {}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{:?}: {}", self.error_type, self.description)?;
|
|
|
|
if let Some(nstatus) = self.nstatus {
|
|
write!(f, "; status is {nstatus}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl From<auth_identity::UsernameError> for Error {
|
|
fn from(value: auth_identity::UsernameError) -> Self {
|
|
Error::new(ErrorKind::UnknownCredentials, value)
|
|
}
|
|
}
|
|
|
|
impl From<rsa::Error> for Error {
|
|
fn from(value: rsa::Error) -> Self {
|
|
Error::new(
|
|
ErrorKind::InternalError,
|
|
format!("an unexpected RsaError happened: {value}"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<Asn1DerError> for Error {
|
|
fn from(err: Asn1DerError) -> Self {
|
|
Self::new(ErrorKind::InvalidToken, format!("ASN1 DER error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
impl From<KrbError> for Error {
|
|
fn from(krb_error: KrbError) -> Self {
|
|
let (error_kind, mut description) = map_keb_error_code_to_sspi_error(krb_error.0.error_code.0);
|
|
|
|
// https://www.rfc-editor.org/rfc/rfc4120#section-5.9.1
|
|
|
|
// This field contains additional text to help explain the error code
|
|
// associated with the failed request
|
|
if let Some(e_text) = krb_error.0.e_text.0 {
|
|
description.push_str(&format!(". Additional error text: {:?}", e_text.0));
|
|
}
|
|
|
|
// This field contains additional data about the error for use by the
|
|
// application to help it recover from or handle the error.
|
|
if let Some(e_data) = krb_error.0.e_data.0 {
|
|
description.push_str(&format!(". Additional error data: {:?}", e_data.0));
|
|
}
|
|
|
|
Error::new(error_kind, description)
|
|
}
|
|
}
|
|
|
|
impl From<picky_krb::crypto::KerberosCryptoError> for Error {
|
|
fn from(err: picky_krb::crypto::KerberosCryptoError) -> Self {
|
|
use picky_krb::crypto::KerberosCryptoError;
|
|
|
|
match err {
|
|
KerberosCryptoError::KeyLength(actual, expected) => Self::new(
|
|
ErrorKind::InvalidParameter,
|
|
format!("invalid key length. actual: {actual}. expected: {expected}"),
|
|
),
|
|
KerberosCryptoError::CipherLength(actual, expected) => Self::new(
|
|
ErrorKind::InvalidParameter,
|
|
format!("invalid cipher length. actual: {actual}. expected: {expected}"),
|
|
),
|
|
KerberosCryptoError::AlgorithmIdentifier(identifier) => Self::new(
|
|
ErrorKind::InvalidParameter,
|
|
format!("unknown algorithm identifier: {identifier}"),
|
|
),
|
|
KerberosCryptoError::IntegrityCheck => Self::new(ErrorKind::MessageAltered, err.to_string()),
|
|
KerberosCryptoError::CipherError(description) => Self::new(ErrorKind::InvalidParameter, description),
|
|
KerberosCryptoError::CipherPad(description) => {
|
|
Self::new(ErrorKind::InvalidParameter, description.to_string())
|
|
}
|
|
KerberosCryptoError::CipherUnpad(description) => {
|
|
Self::new(ErrorKind::InvalidParameter, description.to_string())
|
|
}
|
|
KerberosCryptoError::SeedBitLen(description) => Self::new(ErrorKind::InvalidParameter, description),
|
|
KerberosCryptoError::AlgorithmIdentifierData(identifier) => Self::new(
|
|
ErrorKind::InvalidParameter,
|
|
format!("unknown algorithm identifier: {identifier:?}"),
|
|
),
|
|
KerberosCryptoError::RandError(rand) => {
|
|
Self::new(ErrorKind::InvalidParameter, format!("random error: {rand:?}"))
|
|
}
|
|
KerberosCryptoError::TooSmallBuffer(inout) => {
|
|
Self::new(ErrorKind::InvalidParameter, format!("too small buffer: {inout:?}"))
|
|
}
|
|
KerberosCryptoError::ArrayTryFromSliceError(array) => Self::new(
|
|
ErrorKind::InvalidParameter,
|
|
format!("array try from slice error: {array:?}"),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<picky_krb::crypto::diffie_hellman::DiffieHellmanError> for Error {
|
|
fn from(error: picky_krb::crypto::diffie_hellman::DiffieHellmanError) -> Self {
|
|
use picky_krb::crypto::diffie_hellman::DiffieHellmanError;
|
|
|
|
match error {
|
|
DiffieHellmanError::BitLen(description) => Self::new(ErrorKind::InternalError, description),
|
|
error => Self::new(ErrorKind::InternalError, error.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CharSetError> for Error {
|
|
fn from(err: CharSetError) -> Self {
|
|
Self::new(ErrorKind::InternalError, err.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<GssApiMessageError> for Error {
|
|
fn from(err: GssApiMessageError) -> Self {
|
|
match err {
|
|
GssApiMessageError::IoError(err) => Self::from(err),
|
|
GssApiMessageError::InvalidId(_, _) => Self::new(ErrorKind::InvalidToken, err.to_string()),
|
|
GssApiMessageError::InvalidMicFiller(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
|
|
GssApiMessageError::InvalidWrapFiller(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
|
|
GssApiMessageError::Asn1Error(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(err: io::Error) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("IO error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
impl From<getrandom::Error> for Error {
|
|
fn from(err: getrandom::Error) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("rand error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
impl From<rand::rngs::SysError> for Error {
|
|
fn from(err: rand::rngs::SysError) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("rand error: {:?}", err))
|
|
}
|
|
}
|
|
|
|
impl From<str::Utf8Error> for Error {
|
|
fn from(err: str::Utf8Error) -> Self {
|
|
Self::new(ErrorKind::InternalError, err)
|
|
}
|
|
}
|
|
|
|
impl From<string::FromUtf8Error> for Error {
|
|
fn from(err: string::FromUtf8Error) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("UTF-8 error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
impl From<string::FromUtf16Error> for Error {
|
|
fn from(err: string::FromUtf16Error) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("UTF-16 error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
impl From<Error> for io::Error {
|
|
fn from(err: Error) -> io::Error {
|
|
io::Error::other(format!("{:?}: {}", err.error_type, err.description))
|
|
}
|
|
}
|
|
|
|
impl From<std::num::TryFromIntError> for Error {
|
|
fn from(_: std::num::TryFromIntError) -> Self {
|
|
Self::new(ErrorKind::InternalError, "integer conversion error")
|
|
}
|
|
}
|
|
|
|
impl<T> From<std::sync::PoisonError<T>> for Error {
|
|
fn from(_: std::sync::PoisonError<T>) -> Self {
|
|
Self::new(ErrorKind::InternalError, "can not lock SspiHandle mutex")
|
|
}
|
|
}
|
|
|
|
impl From<picky::key::KeyError> for Error {
|
|
fn from(err: picky::key::KeyError) -> Self {
|
|
Self::new(ErrorKind::InternalError, format!("RSA key error: {err:?}"))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "scard")]
|
|
impl From<winscard::Error> for Error {
|
|
fn from(value: winscard::Error) -> Self {
|
|
Self::new(
|
|
ErrorKind::InternalError,
|
|
format!("Error while using a smart card: {value}"),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[cfg(all(feature = "scard", not(target_arch = "wasm32")))]
|
|
impl From<cryptoki::error::Error> for Error {
|
|
fn from(value: cryptoki::error::Error) -> Self {
|
|
Self::new(
|
|
ErrorKind::NoCredentials,
|
|
format!("Error while using a smart card: {value}"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<widestring::error::Utf16Error> for Error {
|
|
fn from(value: widestring::error::Utf16Error) -> Self {
|
|
Self::new(ErrorKind::InvalidParameter, format!("UTF-16 error: {value}"))
|
|
}
|
|
}
|
|
|
|
impl From<widestring::error::ContainsNul<u16>> for Error {
|
|
fn from(value: widestring::error::ContainsNul<u16>) -> Self {
|
|
Self::new(ErrorKind::InvalidParameter, format!("UTF-16 error: {value}"))
|
|
}
|
|
}
|