diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index 1ce27cd5..7632f6c1 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -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 { + 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 { diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 3c52284b..c4d3e013 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -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 { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported. use decrypt_message to verify signatures instead", + )) + } } impl SspiImpl for Kerberos { diff --git a/src/lib.rs b/src/lib.rs index daed46b6..c78273a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -602,6 +602,224 @@ where sequence_number: u32, ) -> Result; + /// 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; + /// Decrypts a message. Some packages do not encrypt and decrypt messages but rather perform and check an integrity hash. /// /// # Parameters diff --git a/src/negotiate.rs b/src/negotiate.rs index 12fa5c3c..8025a6a6 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -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 { + 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 { diff --git a/src/ntlm/mod.rs b/src/ntlm/mod.rs index 15b27551..dac8fa11 100644 --- a/src/ntlm/mod.rs +++ b/src/ntlm/mod.rs @@ -368,6 +368,46 @@ impl Ntlm { expiry: None, }) } + + fn compute_checksum( + &mut self, + message: &mut [SecurityBuffer], + sequence_number: u32, + digest: &[u8; 16], + ) -> crate::Result<()> { + let checksum = self + .send_sealing_key + .as_mut() + .unwrap() + .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); + + let signature_buffer = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?; + if signature_buffer.buf_len() < SIGNATURE_SIZE { + return Err(Error::new(ErrorKind::BufferTooSmall, "the token buffer is too small")); + } + let signature = compute_signature(&checksum, sequence_number); + signature_buffer.write_data(signature.as_slice())?; + + Ok(()) + } + + fn check_signature(&mut self, sequence_number: u32, digest: &[u8; 16], signature: &[u8]) -> crate::Result<()> { + let checksum = self + .recv_sealing_key + .as_mut() + .unwrap() + .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); + let expected_signature = compute_signature(&checksum, sequence_number); + + if signature != expected_signature.as_ref() { + return Err(Error::new( + ErrorKind::MessageAltered, + "signature verification failed, something nasty is going on", + )); + } + + Ok(()) + } } impl Sspi for Ntlm { @@ -398,18 +438,7 @@ impl Sspi for Ntlm { } data.write_data(&encrypted_data)?; - let checksum = self - .send_sealing_key - .as_mut() - .unwrap() - .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); - - let signature_buffer = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?; - if signature_buffer.buf_len() < SIGNATURE_SIZE { - return Err(Error::new(ErrorKind::BufferTooSmall, "The Token buffer is too small")); - } - let signature = compute_signature(&checksum, sequence_number); - signature_buffer.write_data(signature.as_slice())?; + self.compute_checksum(message, sequence_number, &digest)?; Ok(SecurityStatus::Ok) } @@ -437,19 +466,7 @@ impl Sspi for Ntlm { save_decrypted_data(&decrypted, message)?; let digest = compute_digest(&self.recv_signing_key, sequence_number, &decrypted)?; - let checksum = self - .recv_sealing_key - .as_mut() - .unwrap() - .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); - let expected_signature = compute_signature(&checksum, sequence_number); - - if signature != expected_signature.as_ref() { - return Err(Error::new( - ErrorKind::MessageAltered, - "Signature verification failed, something nasty is going on!", - )); - } + self.check_signature(sequence_number, &digest, signature)?; Ok(DecryptionFlags::empty()) } @@ -503,6 +520,42 @@ impl Sspi for Ntlm { "NTLM does not support change pasword", )) } + + fn make_signature( + &mut self, + _flags: u32, + message: &mut [SecurityBuffer], + sequence_number: u32, + ) -> crate::Result<()> { + if self.recv_sealing_key.is_none() { + self.complete_auth_token(&mut [])?; + } + + SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; // check if exists + + let data = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Data)?; + let digest = compute_digest(&self.send_signing_key, sequence_number, data.data())?; + + self.compute_checksum(message, sequence_number, &digest)?; + + Ok(()) + } + + fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result { + if self.recv_sealing_key.is_none() { + self.complete_auth_token(&mut [])?; + } + + SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; // check if exists + + let data = SecurityBuffer::find_buffer(message, SecurityBufferType::Data)?; + let digest = compute_digest(&self.recv_signing_key, sequence_number, data.data())?; + + let signature = SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; + self.check_signature(sequence_number, &digest, signature.data())?; + + Ok(0) + } } impl SspiEx for Ntlm { diff --git a/src/pku2u/mod.rs b/src/pku2u/mod.rs index 3739c98e..69e2c197 100644 --- a/src/pku2u/mod.rs +++ b/src/pku2u/mod.rs @@ -339,6 +339,25 @@ impl Sspi for Pku2u { "Pku2u does not support change pasword", )) } + + 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", + )) + } + + fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported", + )) + } } impl SspiImpl for Pku2u {