sspi-rs is a Rust implementation of Security Support Provider Interface (SSPI). It ships with platform-independent implementations of Security Support Providers (SSP), 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 *nix or any other platform that is able to compile Rust.
The sspi-rs works in accordance with the MSDN documentation. At the moment, NT LAN Manager (NTLM) is implemented and available for platform independent execution. It is also possible to create your own SSPs by implementing the SspiImpl trait.
Some SSPI functions tend to be cumbersome, that's why sspi-rs allows to use SSPI in a convenient way by utilizing builders.
The usage of the SSPs is as simple as creating an instance of the security provider and calling its functions.
Here is an example of acquiring a credentials handle and a timestamp of their validity:
use sspi::{CredentialUse, Ntlm, Sspi, Username, builders::EmptyInitializeSecurityContext, SecurityBuffer, ClientRequestFlags, DataRepresentation, BufferType, SspiImpl};
fn main() {
let account_name = "example_user";
let computer_name = "example_computer";
let mut ntlm = Ntlm::new();
let username = Username::new(&account_name, Some(&computer_name)).unwrap();
let identity = sspi::AuthIdentity {
username,
password: String::from("example_password").into(),
};
let mut acq_cred_result = ntlm
.acquire_credentials_handle()
.with_credential_use(CredentialUse::Outbound)
.with_auth_data(&identity)
.execute()
.unwrap();
let mut output_buffer = vec![SecurityBuffer::new(Vec::new(), BufferType::Token)];
// first time calling initialize_security_context, the input buffer should be empty
let mut input_buffer = vec![SecurityBuffer::new(Vec::new(), BufferType::Token)];
// create a builder for the first call to initialize_security_context
// the target should start with the protocol name, e.g. "HTTP/example.com" or "LDAP/example.com"
let mut builder = EmptyInitializeSecurityContext::<<Ntlm as SspiImpl>::CredentialsHandle>::new()
.with_credentials_handle(&mut acq_cred_result.credentials_handle)
.with_context_requirements(ClientRequestFlags::CONFIDENTIALITY | ClientRequestFlags::ALLOCATE_MEMORY)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name("LDAP/example.com")
.with_input(&mut input_buffer)
.with_output(&mut output_buffer);
// call initialize_security_context
// Note: the initialize_security_context_impl returns a generator, for NTLM,
// this generator will never yield as NTLM requires no network communication to a third party
// but negotiate and kerberos do require network communication, so the generator is used to
// allow the caller to provide the network information through the generator.resume() method
// take a look at the examples/kerberos.rs for more information
let _result = ntlm
.initialize_security_context_impl(&mut builder)
.resolve_to_result()
.unwrap();
// ... exchange your token in output buffer with the server and repeat the process until either server is satisfied or an error is thrown
}Example of acquiring an SSP provided by Windows:
let mut negotiate = SecurityPackage::from_package_type(
SecurityPackageType::Other(String::from("Negotiate"))
);Both CredSspClient::new and, with the tsssp feature, SspiCredSsp::new_client_with_mode accept an explicit CredSspMode.
SspiCredSsp::new_client preserves ordinary delegation for compatibility.
| Mode | Authentication | Final credentials |
|---|---|---|
WithCredentials |
Ordinary configured negotiation | Supplied password or smart-card credentials |
CredentialLess (Restricted Admin) |
Ordinary configured negotiation, including Kerberos or NTLM | Empty type-1 TSPasswordCreds, regardless of the authentication credential source |
RemoteGuard |
Fresh Kerberos-enabled Negotiate; no NTLM or password-delegation fallback | Type-6 TSRemoteGuardCreds; continuing RDPEAR session available after completion |
Restricted Admin is non-delegating, not anonymous: the client still authenticates.
It does not delegate a password or smart-card PIN and does not create an RDPEAR session.
Restricted Admin and Remote Credential Guard reject conflicting DELEGATE requirements.
RDP connectors must separately request the selected mode and require the matching server capability; selecting a CredSSP mode does not negotiate RDP flags.
These are Rust client APIs, not support for native extended credential structures or server-side type-6 credential import.
Remote Credential Guard keeps reusable TGT keys and the CredSSP protection key client-owned.
Ordinary service-ticket session keys are intentionally returned inside protected RDPEAR replies, and retained only for session-authorized operations.
TGS authenticators with peer-supplied subkeys fail explicitly because such keys would let the peer decrypt TGT replies outside the client.
There is no generic raw-key export API.
Remote Credential Guard requires KdcResolution::KdcUrl for direct KDC or KDC-proxy communication; IAKerb proxy negotiation is not supported in this mode.
Bounded Windows integration has verified initial logon and optional redirected NTLMv2 authentication on a fresh target logon; initial authentication still requires Kerberos.
Successful outbound Kerberos authentication remains unverified.
The service-key and authenticator-skew corrections have local synthetic validation only.
Reconnecting to an existing target logon retained an old credential binding in the tested setup; a fresh target logon is required and no credential state is transferred between connections.
See sspi::rcg for the specific results and limitations.
(Feel free to open a PR if you know about other projects!)
Licensed under either of:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.