Skip to content

Commit

Permalink
feat: add make_signature and verify_signature to Sspi trait (#343)
Browse files Browse the repository at this point in the history
  • Loading branch information
AvivNaaman authored Jan 20, 2025
1 parent 1486869 commit 040188a
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 27 deletions.
19 changes: 19 additions & 0 deletions ffi/src/sspi/sec_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,25 @@ impl Sspi for SspiHandle {
let mut context = self.sspi_context.lock()?;
Ok(context.change_password_sync(change_password).into())
}

fn make_signature(
&mut self,
_flags: u32,
_message: &mut [sspi::SecurityBuffer],
_sequence_number: u32,
) -> Result<()> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"make_signature is not supported",
))
}

fn verify_signature(&mut self, _message: &mut [sspi::SecurityBuffer], _sequence_number: u32) -> Result<u32> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"verify_signature is not supported",
))
}
}

pub struct CredentialsHandle {
Expand Down
27 changes: 27 additions & 0 deletions src/credssp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,33 @@ impl Sspi for SspiContext {
self.change_password_impl(&mut yield_point, change_password).await
}))
}

fn make_signature(
&mut self,
flags: u32,
message: &mut [SecurityBuffer],
sequence_number: u32,
) -> crate::Result<()> {
match self {
SspiContext::Ntlm(ntlm) => ntlm.make_signature(flags, message, sequence_number),
SspiContext::Kerberos(kerberos) => kerberos.make_signature(flags, message, sequence_number),
SspiContext::Negotiate(negotiate) => negotiate.make_signature(flags, message, sequence_number),
SspiContext::Pku2u(pku2u) => pku2u.make_signature(flags, message, sequence_number),
#[cfg(feature = "tsssp")]
SspiContext::CredSsp(credssp) => credssp.make_signature(flags, message, sequence_number),
}
}

fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result<u32> {
match self {
SspiContext::Ntlm(ntlm) => ntlm.verify_signature(message, sequence_number),
SspiContext::Kerberos(kerberos) => kerberos.verify_signature(message, sequence_number),
SspiContext::Negotiate(negotiate) => negotiate.verify_signature(message, sequence_number),
SspiContext::Pku2u(pku2u) => pku2u.verify_signature(message, sequence_number),
#[cfg(feature = "tsssp")]
SspiContext::CredSsp(credssp) => credssp.verify_signature(message, sequence_number),
}
}
}

impl SspiEx for SspiContext {
Expand Down
21 changes: 21 additions & 0 deletions src/credssp/sspi_cred_ssp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,27 @@ impl Sspi for SspiCredSsp {
"ChangePassword is not supported in SspiCredSsp context",
))
}

#[instrument(level = "debug", ret, fields(state = ?self.state), skip_all)]
fn make_signature(
&mut self,
_flags: u32,
_message: &mut [SecurityBuffer],
_sequence_number: u32,
) -> crate::Result<()> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"make_signature is not supported",
))
}

#[instrument(level = "debug", ret, fields(state = ?self.state), skip_all)]
fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result<u32> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"verify_signature is not supported",
))
}
}

impl SspiImpl for SspiCredSsp {
Expand Down
19 changes: 19 additions & 0 deletions src/kerberos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,25 @@ impl Sspi for Kerberos {
self.change_password(&mut yield_point, change_password).await
}))
}

fn make_signature(
&mut self,
_flags: u32,
_message: &mut [SecurityBuffer],
_sequence_number: u32,
) -> crate::Result<()> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"make_signature is not supported. use encrypt_message to sign messages instead",
))
}

fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result<u32> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"verify_signature is not supported. use decrypt_message to verify signatures instead",
))
}
}

impl SspiImpl for Kerberos {
Expand Down
221 changes: 220 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ 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;
Expand All @@ -92,7 +94,6 @@ use picky_asn1_x509::Certificate;
use picky_krb::gss_api::GssApiMessageError;
use picky_krb::messages::KrbError;
pub use security_buffer::SecurityBuffer;
use std::{error, fmt, io, result, str, string};
use utils::map_keb_error_code_to_sspi_error;
pub use utils::string_to_utf16;

Expand Down Expand Up @@ -602,6 +603,224 @@ where
sequence_number: u32,
) -> 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 `SecurityBuffer` structures of `type SecurityBufferType::Data` that contain the message to be signed,
/// and a `SecurityBuffer` of type `SecurityBufferType::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::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)];
/// let mut server_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::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 server_result = 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)
/// .execute(&mut ntlm)
/// .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::SecurityBuffer::Token(token.as_mut_slice()),
/// sspi::SecurityBuffer::Data(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 [SecurityBuffer], sequence_number: u32)
-> crate::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 `SecurityBuffer` structures of `type SecurityBufferType::Data` that contain the message to be verified,
/// and a `SecurityBuffer` of type `SecurityBufferType::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::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)];
/// let mut server_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::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 server_result = 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)
/// .execute(&mut server_ntlm)
/// .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::SecurityBuffer::Token(token.as_mut_slice()),
/// sspi::SecurityBuffer::Data(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::SecurityBuffer::Token(token.take_data()),
/// sspi::SecurityBuffer::Data(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 [SecurityBuffer], sequence_number: u32) -> crate::Result<u32>;

/// Decrypts a message. Some packages do not encrypt and decrypt messages but rather perform and check an integrity hash.
///
/// # Parameters
Expand Down
21 changes: 21 additions & 0 deletions src/negotiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,27 @@ impl Sspi for Negotiate {
self.change_password(&mut yield_point, change_password).await
}))
}

fn make_signature(
&mut self,
flags: u32,
message: &mut [SecurityBuffer],
sequence_number: u32,
) -> crate::Result<()> {
match &mut self.protocol {
NegotiatedProtocol::Pku2u(pku2u) => pku2u.make_signature(flags, message, sequence_number),
NegotiatedProtocol::Kerberos(kerberos) => kerberos.make_signature(flags, message, sequence_number),
NegotiatedProtocol::Ntlm(ntlm) => ntlm.make_signature(flags, message, sequence_number),
}
}

fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result<u32> {
match &mut self.protocol {
NegotiatedProtocol::Pku2u(pku2u) => pku2u.verify_signature(message, sequence_number),
NegotiatedProtocol::Kerberos(kerberos) => kerberos.verify_signature(message, sequence_number),
NegotiatedProtocol::Ntlm(ntlm) => ntlm.verify_signature(message, sequence_number),
}
}
}

impl SspiImpl for Negotiate {
Expand Down
3 changes: 2 additions & 1 deletion src/ntlm/messages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ pub mod test;
mod av_pair;
mod computations;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

use crate::ntlm::{NegotiateFlags, NTLM_VERSION_SIZE};

const NTLM_SIGNATURE: &[u8; NTLM_SIGNATURE_SIZE] = b"NTLMSSP\0";
Expand Down
Loading

0 comments on commit 040188a

Please sign in to comment.