From bae70b916b825c63d0e1ef7232c402a695719596 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 16 Jul 2023 16:55:08 -0700 Subject: [PATCH 01/85] feat(sspi): add scard feature; --- Cargo.toml | 5 ++++- rust-toolchain.toml | 4 ++-- src/lib.rs | 12 ++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93f8d435..39f2fe5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,13 @@ exclude = [ ] [features] -default = [] +default = ["scard"] network_client = ["dep:reqwest", "dep:portpicker"] dns_resolver = ["dep:trust-dns-resolver", "dep:tokio"] # TSSSP should be used only on Windows as a native CREDSSP replacement tsssp = ["dep:rustls"] +# Turns on Kerberos smart card login (available only on Windows and users WinSCard API) +scard = ["dep:pcsc"] [dependencies] byteorder = "1.2.7" @@ -60,6 +62,7 @@ tracing = "0.1.37" rustls = { version = "0.20.7", features = ["dangerous_configuration"], optional = true } zeroize = { version = "1.5.7", features = ["zeroize_derive"] } tokio = { version = "1.1", features = ["time", "rt"], optional = true } +pcsc = { version = "2.8.0", optional = true } [target.'cfg(windows)'.dependencies] winreg = "0.10" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 19412a3f..182479d1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.71.1" -components = [ "rustfmt", "clippy" ] \ No newline at end of file +channel = "1.71.0" +components = [ "rustfmt", "clippy" ] diff --git a/src/lib.rs b/src/lib.rs index d26cb835..ca659928 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,9 @@ pub mod negotiate; pub mod network_client; pub mod ntlm; pub mod pku2u; +mod pk_init; +#[cfg(feature = "scard")] +mod smartcard; #[cfg(windows)] pub mod winapi; @@ -96,6 +99,9 @@ mod utils; #[cfg(all(feature = "tsssp", not(target_os = "windows")))] compile_error!("tsssp feature should be used only on Windows"); +#[cfg(all(feature = "scard", not(target_os = "windows")))] +compile_error!("scard feature should be used only on Windows"); + use std::{error, fmt, io, result, str, string}; use bitflags::bitflags; @@ -1928,3 +1934,9 @@ impl From for io::Error { ) } } + +impl From for Error { + fn from(value: pcsc::Error) -> Self { + Self::new(ErrorKind::InternalError, "pcsc error".to_owned()) + } +} From f59c37965027f0ca3559ec33d7df88ce2bd6889e Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 16 Jul 2023 16:55:29 -0700 Subject: [PATCH 02/85] fix(ntlm): warnings --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ca659928..e899eefa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,9 +81,6 @@ pub mod negotiate; pub mod network_client; pub mod ntlm; pub mod pku2u; -mod pk_init; -#[cfg(feature = "scard")] -mod smartcard; #[cfg(windows)] pub mod winapi; From ff9b339feba8de40756b5be31cb3e6d92feaeb44 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 16 Jul 2023 16:56:24 -0700 Subject: [PATCH 03/85] feat(ffi): add scrd feature --- ffi/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index b7e7de17..f7b1fb34 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -13,8 +13,9 @@ name = "sspi" crate-type = ["cdylib"] [features] -default = [] +default = ["scard"] tsssp = ["sspi/tsssp"] +scard = ["sspi/scard"] [dependencies] cfg-if = "0.1" From 2159b19fe1ee6eef8ce6a23d6fd87bd77e157825 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 16 Jul 2023 21:17:35 -0700 Subject: [PATCH 04/85] feat(kerberos): implement certificate extraction by thumbprint --- src/kerberos/cert_utils.rs | 78 ++++++++++++++++++++++++++++++++++++++ src/kerberos/mod.rs | 1 + 2 files changed, 79 insertions(+) create mode 100644 src/kerberos/cert_utils.rs diff --git a/src/kerberos/cert_utils.rs b/src/kerberos/cert_utils.rs new file mode 100644 index 00000000..da7b9c49 --- /dev/null +++ b/src/kerberos/cert_utils.rs @@ -0,0 +1,78 @@ +use std::{slice::from_raw_parts, ptr::null_mut}; + +use picky_asn1_x509::Certificate; +use sha1::{Sha1, Digest}; +use winapi::{um::wincrypt::{ + CertCloseStore, CertEnumCertificatesInStore, CertFreeCertificateContext, CertOpenStore, + CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_CURRENT_USER_ID, + CERT_SYSTEM_STORE_LOCATION_SHIFT, +}, ctypes::c_void}; + +use crate::{Result, Error, ErrorKind}; + +unsafe fn find_cert_by_thumbprint(thumbprint: &[u8], cert_store: *mut c_void) -> Result { + let mut certificate = CertEnumCertificatesInStore(cert_store, null_mut()); + + while !certificate.is_null() { + let cert_der = from_raw_parts((*certificate).pbCertEncoded, (*certificate).cbCertEncoded as usize); + + let mut sha1 = Sha1::new(); + sha1.update(cert_der); + let cert_thumbprint = sha1.finalize().to_vec(); + + if cert_thumbprint == thumbprint { + let cert: Certificate = picky_asn1_der::from_bytes(cert_der)?; + + CertFreeCertificateContext(certificate); + + return Ok(cert); + } + + let next_certificate = CertEnumCertificatesInStore(cert_store, certificate); + + certificate = next_certificate; + } + + Err(Error::new( + ErrorKind::InternalError, + "Cannot find appropriate device certificate", + )) +} + +pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result { + // "My\0" encoded as a wide string. + // More info: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks + let my: [u16; 3] = [77, 121, 0]; + let cert_store = CertOpenStore( + CERT_STORE_PROV_SYSTEM_W, + 0, + 0, + CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT, + my.as_ptr() as *const _, + ); + + if cert_store.is_null() { + return Err(Error::new( + ErrorKind::InternalError, + "Cannot initialize certificate store: permission denied", + )); + } + + let cert = find_cert_by_thumbprint(thumbprint, cert_store)?; + + CertCloseStore(cert_store, 0); + + Ok(cert) +} + +#[cfg(test)] +mod tests { + use super::extract_certificate_by_thumbprint; + + #[test] + fn cert() { + println!("cert here: {:?}", unsafe { + extract_certificate_by_thumbprint(&[60, 51, 235, 194, 72, 148, 15, 37, 176, 168, 245, 241, 146, 185, 12, 11, 235, 139, 141, 82]).unwrap() + }); + } +} diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 46f56aa3..d075ed12 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -1,3 +1,4 @@ +mod cert_utils; pub mod client; pub mod config; mod encryption_params; From 752de6c712f9b0efac9afcc21e28b9858c7a895e Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:29:05 -0700 Subject: [PATCH 05/85] feat(sspi): builders: implement the transform method for accept securoty context builder that allows us to transform builder into another with another authdata type --- src/builders/accept_sec_context.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/builders/accept_sec_context.rs b/src/builders/accept_sec_context.rs index 3c4013f3..e9336e6f 100644 --- a/src/builders/accept_sec_context.rs +++ b/src/builders/accept_sec_context.rs @@ -240,6 +240,29 @@ impl< } } +impl<'a, AuthData, CredsHandle> FilledAcceptSecurityContext<'a, AuthData, CredsHandle> { + pub(crate) fn full_transform( + self, + inner: SspiPackage<'a, CredsHandle2, AuthData2>, + credentials_handle: Option<&'a mut CredsHandle2>, + ) -> FilledAcceptSecurityContext<'a, AuthData2, CredsHandle2> { + AcceptSecurityContext { + inner: Some(inner), + phantom_creds_use_set: PhantomData, + phantom_context_req_set: PhantomData, + phantom_data_repr_set: PhantomData, + phantom_output_set: PhantomData, + + credentials_handle, + context_requirements: self.context_requirements, + target_data_representation: self.target_data_representation, + + output: self.output, + input: self.input, + } + } +} + impl<'a, AuthData, CredsHandle> FilledAcceptSecurityContext<'a, AuthData, CredsHandle> { /// Executes the SSPI function that the builder represents. pub fn execute(mut self) -> crate::Result { From 49cbaa092e1627e25825355fabb10071c0079c1f Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:30:07 -0700 Subject: [PATCH 06/85] feat(sspi): builders: implement the transform method for acquire cred handle builder that allows us to transform builder into another with another authdata type --- src/builders/acq_cred_handle.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/builders/acq_cred_handle.rs b/src/builders/acq_cred_handle.rs index 70270e61..b34cfbb5 100644 --- a/src/builders/acq_cred_handle.rs +++ b/src/builders/acq_cred_handle.rs @@ -117,6 +117,25 @@ where } } +impl<'a, CredsHandle, AuthData> FilledAcquireCredentialsHandle<'a, CredsHandle, AuthData> { + pub(crate) fn full_transform( + self, + inner: SspiPackage<'a, NewCredsHandle, NewAuthData>, + auth_data: Option<&'a NewAuthData>, + ) -> FilledAcquireCredentialsHandle<'a, NewCredsHandle, NewAuthData> { + AcquireCredentialsHandle { + inner: Some(inner), + phantom_cred_handle: PhantomData, + phantom_cred_use_set: PhantomData, + + principal_name: self.principal_name, + credential_use: self.credential_use, + logon_id: self.logon_id, + auth_data, + } + } +} + impl<'a, CredsHandle, AuthData> FilledAcquireCredentialsHandle<'a, CredsHandle, AuthData> { /// Executes the SSPI function that the builder represents. pub fn execute(mut self) -> crate::Result> { From 5819366b39baea148330f7a292d13c044346d9ea Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:30:29 -0700 Subject: [PATCH 07/85] feat(sspi): builders: implement the transform method for init sec context builder that allows us to transform builder into another with another credshandle type --- src/builders/init_sec_context.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/builders/init_sec_context.rs b/src/builders/init_sec_context.rs index b8acbc72..5489b76b 100644 --- a/src/builders/init_sec_context.rs +++ b/src/builders/init_sec_context.rs @@ -72,6 +72,33 @@ pub struct InitializeSecurityContext< pub input: Option<&'a mut [SecurityBuffer]>, } +impl< + 'a, + CredsHandle, + CredsHandleSet: ToAssign, + ContextRequirementsSet: ToAssign, + TargetDataRepresentationSet: ToAssign, + OutputSet: ToAssign, + > InitializeSecurityContext< + 'a, CredsHandle, CredsHandleSet, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet, + > { + pub fn full_transform(&mut self, credentials_handle: Option<&'a mut CredsHandle2>) -> InitializeSecurityContext<'a, CredsHandle2, CredHandleSet2, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet> { + InitializeSecurityContext { + phantom_creds_use_set: PhantomData, + phantom_context_req_set: PhantomData, + phantom_data_repr_set: PhantomData, + phantom_output_set: PhantomData, + + credentials_handle, + context_requirements: self.context_requirements, + target_data_representation: self.target_data_representation, + output: std::mem::take(&mut self.output), + target_name: self.target_name.clone(), + input: std::mem::take(&mut self.input), + } + } +} + impl< 'a, CredsHandle, From 0f64ac41b0b6bd2e83288dafa3d183153d12b2b5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:44:19 -0700 Subject: [PATCH 08/85] feat(auth_identity): add and implement Credetials/CredentialsBuffers --- src/auth_identity.rs | 125 ++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 8 ++- 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index c20f0635..f965df5f 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -1,8 +1,9 @@ use std::fmt; +use picky_asn1_x509::Certificate; use serde::{Deserialize, Serialize}; -use crate::{utils, Secret}; +use crate::{utils, Secret, Error}; /// Allows you to pass a particular user name and password to the run-time library for the purpose of authentication /// @@ -75,3 +76,125 @@ impl From for AuthIdentity { } } } + +/// Represents raw data needed for smart card authentication +#[cfg(feature = "scard")] +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct SmartCardIdentityBuffers { + /// DER-encoded X509 certificate + pub certificate: Vec, + /// UTF-16 encoded smart card reader name + pub reader_name: Vec, + /// Smart card PIN code + pub pin: Secret>, +} + +/// Represents data needed for smart card authentication +#[derive(Debug, Clone, PartialEq)] +pub struct SmartCardIdentity { + /// X509 certificate + pub certificate: Certificate, + /// Smart card reader name + pub reader_name: String, + /// Smart card PIN code + pub pin: Secret>, +} + +impl TryFrom for SmartCardIdentityBuffers { + type Error = Error; + + fn try_from(value: SmartCardIdentity) -> Result { + Ok(Self { + certificate: picky_asn1_der::to_vec(&value.certificate)?, + reader_name: value.reader_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + pin: value.pin.as_ref().clone().into(), + }) + } +} + +impl TryFrom for SmartCardIdentity { + type Error = Error; + + fn try_from(value: SmartCardIdentityBuffers) -> Result { + Ok(Self { + certificate: picky_asn1_der::from_bytes(&value.certificate)?, + reader_name: utils::bytes_to_utf16_string(&value.reader_name), + pin: value.pin.as_ref().clone().into(), + }) + } +} + +/// Generic enum that encapsulates raw credentials for any type of authentication +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum CredentialsBuffers { + /// Raw auth identity buffers for the password based authentication + AuthIdentity(AuthIdentityBuffers), + #[cfg(feature = "scard")] + /// Raw smart card identity buffers for the smart card based authentication + SmartCard(SmartCardIdentityBuffers) +} + +impl CredentialsBuffers { + pub fn auth_identity(self) -> Option { + match self { + CredentialsBuffers::AuthIdentity(identity) => Some(identity), + _ => None, + } + } + + pub fn as_auth_identity(&self) -> Option<&AuthIdentityBuffers> { + match self { + CredentialsBuffers::AuthIdentity(identity) => Some(identity), + _ => None, + } + } + + pub fn as_mut_auth_identity(&mut self) -> Option<&mut AuthIdentityBuffers> { + match self { + CredentialsBuffers::AuthIdentity(identity) => Some(identity), + _ => None, + } + } +} + +/// Generic enum that encapsulates credentials for any type of authentication +#[derive(Clone, PartialEq, Debug)] +pub enum Credentials { + /// Auth identity for the password based authentication + AuthIdentity(AuthIdentity), + #[cfg(feature = "scard")] + /// Smart card identity for the smart card based authentication + SmartCard(SmartCardIdentity) +} + +impl Credentials { + pub fn auth_identity(self) -> Option { + match self { + Credentials::AuthIdentity(identity) => Some(identity), + _ => None, + } + } +} + +impl From for Credentials { + fn from(value: SmartCardIdentity) -> Self { + Self::SmartCard(value) + } +} + +impl From for Credentials { + fn from(value: AuthIdentity) -> Self { + Self::AuthIdentity(value) + } +} + +impl TryFrom for CredentialsBuffers { + type Error = Error; + + fn try_from(value: Credentials) -> Result { + Ok(match value { + Credentials::AuthIdentity(identity) => Self::AuthIdentity(identity.into()), + Credentials::SmartCard(identity) => Self::SmartCard(identity.try_into()?), + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index e899eefa..da91d669 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ use picky_krb::gss_api::GssApiMessageError; use picky_krb::messages::KrbError; use utils::map_keb_error_code_to_sspi_error; -pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers}; +pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, SmartCardIdentity, Credentials}; use self::builders::{ AcceptSecurityContext, AcquireCredentialsHandle, ChangePassword, EmptyAcceptSecurityContext, EmptyAcquireCredentialsHandle, EmptyInitializeSecurityContext, FilledAcceptSecurityContext, @@ -994,8 +994,11 @@ pub struct ConnectionInfo { 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<'a>( @@ -1932,8 +1935,9 @@ impl From for io::Error { } } +#[cfg(feature = "scard")] impl From for Error { - fn from(value: pcsc::Error) -> Self { + fn from(_value: pcsc::Error) -> Self { Self::new(ErrorKind::InternalError, "pcsc error".to_owned()) } } From ad4c6121158bc4b7d12d3fd2510b0c5d96e277d7 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:32:34 -0700 Subject: [PATCH 09/85] feat(sspi): negotiate: improved credentials passing: now we can pass regular auth identity and smart card creds; --- src/negotiate.rs | 85 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/src/negotiate.rs b/src/negotiate.rs index 68e22263..78bc01a9 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -3,7 +3,7 @@ use std::net::IpAddr; use lazy_static::lazy_static; -use crate::kdc::detect_kdc_url; +use crate::{kdc::detect_kdc_url, CredentialsBuffers}; use crate::kerberos::client::generators::get_client_principal_realm; use crate::network_client::NetworkClientFactory; use crate::ntlm::NtlmConfig; @@ -15,7 +15,7 @@ use crate::{ builders, kerberos, ntlm, pku2u, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, AuthIdentityBuffers, CertTrustStatus, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, ErrorKind, InitializeSecurityContextResult, Kerberos, Ntlm, PackageCapabilities, PackageInfo, Pku2u, Result, SecurityBuffer, - SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, PACKAGE_ID_NONE, + SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, PACKAGE_ID_NONE, Credentials, }; pub const PKG_NAME: &str = "Negotiate"; @@ -105,7 +105,7 @@ impl NegotiatedProtocol { pub struct Negotiate { protocol: NegotiatedProtocol, package_list: Option, - auth_identity: Option, + auth_identity: Option, hostname: String, network_client_factory: Box, } @@ -290,12 +290,14 @@ impl Negotiate { impl SspiEx for Negotiate { #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { - self.auth_identity = Some(identity.clone().into()); + self.auth_identity = Some(identity.clone().try_into().unwrap()); match &mut self.protocol { - NegotiatedProtocol::Pku2u(pku2u) => pku2u.custom_set_auth_identity(identity), + NegotiatedProtocol::Pku2u(pku2u) => { + pku2u.custom_set_auth_identity(identity.auth_identity().unwrap()) + }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.custom_set_auth_identity(identity), - NegotiatedProtocol::Ntlm(ntlm) => ntlm.custom_set_auth_identity(identity), + NegotiatedProtocol::Ntlm(ntlm) => ntlm.custom_set_auth_identity(identity.auth_identity().unwrap()), } } } @@ -387,10 +389,10 @@ impl Sspi for Negotiate { } impl SspiImpl for Negotiate { - type CredentialsHandle = Option; - type AuthenticationData = AuthIdentity; + type CredentialsHandle = Option; + type AuthenticationData = Credentials; - #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] + // #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] fn acquire_credentials_handle_impl<'a>( &'a mut self, builder: builders::FilledAcquireCredentialsHandle<'a, Self::CredentialsHandle, Self::AuthenticationData>, @@ -402,16 +404,35 @@ impl SspiImpl for Negotiate { )); } - if let Some(identity) = builder.auth_data { + if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { self.negotiate_protocol(&identity.username, identity.domain.as_deref().unwrap_or_default())?; } - self.auth_identity = builder.auth_data.cloned().map(AuthIdentityBuffers::from); + self.auth_identity = match builder.auth_data.cloned() { + Some(auth_data) => Some(auth_data.try_into()?), + None => None, + }; match &mut self.protocol { - NegotiatedProtocol::Pku2u(pku2u) => pku2u.acquire_credentials_handle_impl(builder)?, + NegotiatedProtocol::Pku2u(pku2u) => { + let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { + identity + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + let new_builder = builder.full_transform(pku2u, Some(auth_identity)); + pku2u.acquire_credentials_handle_impl(new_builder)? + }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.acquire_credentials_handle_impl(builder)?, - NegotiatedProtocol::Ntlm(ntlm) => ntlm.acquire_credentials_handle_impl(builder)?, + NegotiatedProtocol::Ntlm(ntlm) => { + let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { + identity + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + let new_builder = builder.full_transform(ntlm, Some(auth_identity)); + ntlm.acquire_credentials_handle_impl(new_builder)? + }, }; Ok(AcquireCredentialsHandleResult { @@ -429,7 +450,7 @@ impl SspiImpl for Negotiate { self.check_target_name_for_ntlm_downgrade(target_name); } - if let Some(Some(identity)) = builder.credentials_handle { + if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { let auth_identity: AuthIdentity = identity.clone().into(); if let Some(domain) = &auth_identity.domain { @@ -454,16 +475,26 @@ impl SspiImpl for Negotiate { .map(NtlmConfig::new) .unwrap_or_default(); self.protocol = - NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity(self.auth_identity.clone(), ntlm_config)); + NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity(self.auth_identity.clone().map(|c| c.auth_identity()).flatten(), ntlm_config)); } result => return result, }; } match &mut self.protocol { - NegotiatedProtocol::Pku2u(pku2u) => pku2u.initialize_security_context_impl(builder), + NegotiatedProtocol::Pku2u(pku2u) => { + let credentials_handle = self.auth_identity.as_mut().map(|c| c.auth_identity()).flatten(); + let transformed_builder = builder.full_transform(Some(&mut credentials_handle)); + + pku2u.initialize_security_context_impl(&mut transformed_builder) + }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), - NegotiatedProtocol::Ntlm(ntlm) => ntlm.initialize_security_context_impl(builder), + NegotiatedProtocol::Ntlm(ntlm) => { + let credentials_handle = self.auth_identity.as_mut().map(|c| c.auth_identity()).flatten(); + let transformed_builder = builder.full_transform(Some(&mut credentials_handle)); + + ntlm.initialize_security_context_impl(&mut transformed_builder) + }, } } @@ -473,9 +504,25 @@ impl SspiImpl for Negotiate { builder: builders::FilledAcceptSecurityContext<'a, Self::AuthenticationData, Self::CredentialsHandle>, ) -> Result { match &mut self.protocol { - NegotiatedProtocol::Pku2u(pku2u) => pku2u.accept_security_context_impl(builder), + NegotiatedProtocol::Pku2u(pku2u) => { + let creds_handle = if let Some(creds_handle) = builder.credentials_handle { + creds_handle.map(|c| c.auth_identity()).flatten() + } else { + None + }; + let new_builder = builder.full_transform(pku2u, Some(&mut creds_handle)); + pku2u.accept_security_context_impl(new_builder) + }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.accept_security_context_impl(builder), - NegotiatedProtocol::Ntlm(ntlm) => ntlm.accept_security_context_impl(builder), + NegotiatedProtocol::Ntlm(ntlm) => { + let creds_handle = if let Some(creds_handle) = builder.credentials_handle { + creds_handle.map(|c| c.auth_identity()).flatten() + } else { + None + }; + let new_builder = builder.full_transform(ntlm, Some(&mut creds_handle)); + ntlm.accept_security_context_impl(new_builder) + }, } } } From 99b57678641d670cfc7bb454229199ad13257892 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 15:43:54 -0700 Subject: [PATCH 10/85] feat(sspi): kerberos: improved credentials passing: now we can pass regular auth identity and smart card creds --- src/kerberos/mod.rs | 35 ++++++++++++++++++++++------------- src/negotiate.rs | 10 ++++++---- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index d075ed12..22f509c9 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -45,11 +45,11 @@ use crate::kerberos::utils::{generate_initiator_raw, parse_target_name, validate use crate::network_client::NetworkProtocol; use crate::utils::{generate_random_symmetric_key, get_encryption_key, utf16_bytes_to_utf8_string}; use crate::{ - detect_kdc_url, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, AuthIdentityBuffers, + detect_kdc_url, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, ClientRequestFlags, ClientResponseFlags, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, ErrorKind, InitializeSecurityContextResult, PackageCapabilities, PackageInfo, Result, SecurityBuffer, SecurityBufferType, SecurityPackageType, SecurityStatus, ServerResponseFlags, Sspi, SspiEx, SspiImpl, - PACKAGE_ID_NONE, + PACKAGE_ID_NONE, Credentials, CredentialsBuffers, }; pub const PKG_NAME: &str = "Kerberos"; @@ -96,7 +96,7 @@ pub enum KerberosState { pub struct Kerberos { state: KerberosState, config: KerberosConfig, - auth_identity: Option, + auth_identity: Option, encryption_params: EncryptionParams, seq_number: u32, realm: Option, @@ -395,7 +395,7 @@ impl Sspi for Kerberos { #[instrument(level = "debug", ret, fields(state = ?self.state), skip(self))] fn query_context_names(&mut self) -> Result { - if let Some(ref identity_buffers) = self.auth_identity { + if let Some(CredentialsBuffers::AuthIdentity(ref identity_buffers)) = self.auth_identity { let identity: AuthIdentity = identity_buffers.clone().into(); Ok(ContextNames { username: identity.username, @@ -534,9 +534,9 @@ impl Sspi for Kerberos { } impl SspiImpl for Kerberos { - type CredentialsHandle = Option; + type CredentialsHandle = Option; - type AuthenticationData = AuthIdentity; + type AuthenticationData = Credentials; #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] fn acquire_credentials_handle_impl( @@ -550,7 +550,10 @@ impl SspiImpl for Kerberos { )); } - self.auth_identity = builder.auth_data.cloned().map(AuthIdentityBuffers::from); + self.auth_identity = match builder.auth_data.cloned() { + Some(auth_data) => Some(auth_data.try_into()?), + None => None, + }; Ok(AcquireCredentialsHandleResult { credentials_handle: self.auth_identity.clone(), @@ -574,8 +577,11 @@ impl SspiImpl for Kerberos { .as_ref() .ok_or_else(|| Error::new(ErrorKind::NoCredentials, "No credentials provided"))?; - let username = utf16_bytes_to_utf8_string(&credentials.user); - let domain = utf16_bytes_to_utf8_string(&credentials.domain); + // todo: handle smart card creds here + let auth_identity = credentials.as_auth_identity().unwrap(); + + let username = utf16_bytes_to_utf8_string(&auth_identity.user); + let domain = utf16_bytes_to_utf8_string(&auth_identity.domain); let (service_name, _) = parse_target_name(builder.target_name.ok_or_else(|| { Error::new( ErrorKind::NoCredentials, @@ -618,9 +624,12 @@ impl SspiImpl for Kerberos { .as_ref() .ok_or_else(|| Error::new(ErrorKind::WrongCredentialHandle, "No credentials provided"))?; - let username = utf16_bytes_to_utf8_string(&credentials.user); - let domain = utf16_bytes_to_utf8_string(&credentials.domain); - let password = utf16_bytes_to_utf8_string(credentials.password.as_ref()); + // todo: handle smart card creds here + let auth_identity = credentials.as_auth_identity().unwrap(); + + let username = utf16_bytes_to_utf8_string(&auth_identity.user); + let domain = utf16_bytes_to_utf8_string(&auth_identity.domain); + let password = utf16_bytes_to_utf8_string(auth_identity.password.as_ref()); let salt = format!("{}{}", domain, username); self.realm = Some(get_client_principal_realm(&username, &domain)); @@ -848,7 +857,7 @@ impl SspiImpl for Kerberos { impl SspiEx for Kerberos { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { - self.auth_identity = Some(identity.into()); + self.auth_identity = Some(identity.try_into().unwrap()); } } diff --git a/src/negotiate.rs b/src/negotiate.rs index 78bc01a9..2bf834b0 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -13,7 +13,7 @@ use crate::utils::is_azure_ad_domain; use crate::KerberosConfig; use crate::{ builders, kerberos, ntlm, pku2u, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, - AuthIdentityBuffers, CertTrustStatus, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, ErrorKind, + CertTrustStatus, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, ErrorKind, InitializeSecurityContextResult, Kerberos, Ntlm, PackageCapabilities, PackageInfo, Pku2u, Result, SecurityBuffer, SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, PACKAGE_ID_NONE, Credentials, }; @@ -421,9 +421,11 @@ impl SspiImpl for Negotiate { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; let new_builder = builder.full_transform(pku2u, Some(auth_identity)); - pku2u.acquire_credentials_handle_impl(new_builder)? + pku2u.acquire_credentials_handle_impl(new_builder)?; + }, + NegotiatedProtocol::Kerberos(kerberos) => { + kerberos.acquire_credentials_handle_impl(builder)?; }, - NegotiatedProtocol::Kerberos(kerberos) => kerberos.acquire_credentials_handle_impl(builder)?, NegotiatedProtocol::Ntlm(ntlm) => { let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { identity @@ -431,7 +433,7 @@ impl SspiImpl for Negotiate { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; let new_builder = builder.full_transform(ntlm, Some(auth_identity)); - ntlm.acquire_credentials_handle_impl(new_builder)? + ntlm.acquire_credentials_handle_impl(new_builder)?; }, }; From bbe8c675a0238ebcdaee17370a6e839f2856df22 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 20:24:56 -0700 Subject: [PATCH 11/85] fix(sspi): builders: accept_sec_context: change lifetime too in full_transform function for the created builder --- src/builders/accept_sec_context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builders/accept_sec_context.rs b/src/builders/accept_sec_context.rs index e9336e6f..c48195de 100644 --- a/src/builders/accept_sec_context.rs +++ b/src/builders/accept_sec_context.rs @@ -240,12 +240,12 @@ impl< } } -impl<'a, AuthData, CredsHandle> FilledAcceptSecurityContext<'a, AuthData, CredsHandle> { +impl<'b, 'a: 'b, AuthData, CredsHandle> FilledAcceptSecurityContext<'a, AuthData, CredsHandle> { pub(crate) fn full_transform( self, inner: SspiPackage<'a, CredsHandle2, AuthData2>, - credentials_handle: Option<&'a mut CredsHandle2>, - ) -> FilledAcceptSecurityContext<'a, AuthData2, CredsHandle2> { + credentials_handle: Option<&'b mut CredsHandle2>, + ) -> FilledAcceptSecurityContext<'b, AuthData2, CredsHandle2> { AcceptSecurityContext { inner: Some(inner), phantom_creds_use_set: PhantomData, From 3291998bc823dafd8fa67423167d3ba19c25288b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 20:26:12 -0700 Subject: [PATCH 12/85] fix(sspi): builder: acq_cred_handle: change lifetime too in full_transform function for the created builder; feat(sspi): builder: acq_cred_handle: implemnt `.transform` method for the AcquireCredentialsHandleResult; --- src/builders/acq_cred_handle.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/builders/acq_cred_handle.rs b/src/builders/acq_cred_handle.rs index b34cfbb5..99cbae17 100644 --- a/src/builders/acq_cred_handle.rs +++ b/src/builders/acq_cred_handle.rs @@ -18,6 +18,17 @@ pub struct AcquireCredentialsHandleResult { pub expiry: Option, } +// we cannot replace it with the `From` trait implementation due to conflict with blanked impl in the std +impl AcquireCredentialsHandleResult { + pub fn transform(self, transformer: &dyn Fn(T) -> T2) -> AcquireCredentialsHandleResult { + let Self { credentials_handle, expiry } = self; + AcquireCredentialsHandleResult { + credentials_handle: transformer(credentials_handle), + expiry, + } + } +} + /// A builder to execute one of the SSPI functions. Returned by the `acquire_credentials_handle` method. /// /// # Requirements for execution @@ -117,12 +128,12 @@ where } } -impl<'a, CredsHandle, AuthData> FilledAcquireCredentialsHandle<'a, CredsHandle, AuthData> { +impl<'b, 'a: 'b, CredsHandle, AuthData> FilledAcquireCredentialsHandle<'a, CredsHandle, AuthData> { pub(crate) fn full_transform( self, inner: SspiPackage<'a, NewCredsHandle, NewAuthData>, - auth_data: Option<&'a NewAuthData>, - ) -> FilledAcquireCredentialsHandle<'a, NewCredsHandle, NewAuthData> { + auth_data: Option<&'b NewAuthData>, + ) -> FilledAcquireCredentialsHandle<'b, NewCredsHandle, NewAuthData> { AcquireCredentialsHandle { inner: Some(inner), phantom_cred_handle: PhantomData, From 01f59ec2b42f118dd6f0b07ded501579d0609f6c Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 20:28:37 -0700 Subject: [PATCH 13/85] fix(sspi): builder: init_sec_context: change lifetime too in full_transform function for the created builder. Implement credentials_handle_mut function --- src/builders/init_sec_context.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/builders/init_sec_context.rs b/src/builders/init_sec_context.rs index 5489b76b..ae133829 100644 --- a/src/builders/init_sec_context.rs +++ b/src/builders/init_sec_context.rs @@ -73,7 +73,8 @@ pub struct InitializeSecurityContext< } impl< - 'a, + 'b, + 'a: 'b, CredsHandle, CredsHandleSet: ToAssign, ContextRequirementsSet: ToAssign, @@ -82,7 +83,7 @@ impl< > InitializeSecurityContext< 'a, CredsHandle, CredsHandleSet, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet, > { - pub fn full_transform(&mut self, credentials_handle: Option<&'a mut CredsHandle2>) -> InitializeSecurityContext<'a, CredsHandle2, CredHandleSet2, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet> { + pub fn full_transform(&mut self, credentials_handle: Option<&'b mut CredsHandle2>) -> InitializeSecurityContext<'b, CredsHandle2, CredHandleSet2, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet> { InitializeSecurityContext { phantom_creds_use_set: PhantomData, phantom_context_req_set: PhantomData, @@ -116,6 +117,10 @@ impl< OutputSet, > { + pub fn credentials_handle_mut(&mut self) -> &mut Option<&'a mut CredsHandle> { + &mut self.credentials_handle + } + pub fn new() -> Self { Self { phantom_creds_use_set: PhantomData, From 28ceadddf343d4d99b3f72490f519fb19dd7aea3 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 20:30:31 -0700 Subject: [PATCH 14/85] fix(sspi): negotiate: mutation and references errors --- src/negotiate.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/negotiate.rs b/src/negotiate.rs index 2bf834b0..5521df58 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -421,7 +421,7 @@ impl SspiImpl for Negotiate { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; let new_builder = builder.full_transform(pku2u, Some(auth_identity)); - pku2u.acquire_credentials_handle_impl(new_builder)?; + new_builder.execute()?; }, NegotiatedProtocol::Kerberos(kerberos) => { kerberos.acquire_credentials_handle_impl(builder)?; @@ -433,7 +433,7 @@ impl SspiImpl for Negotiate { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; let new_builder = builder.full_transform(ntlm, Some(auth_identity)); - ntlm.acquire_credentials_handle_impl(new_builder)?; + new_builder.execute()?; }, }; @@ -485,45 +485,45 @@ impl SspiImpl for Negotiate { match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { - let credentials_handle = self.auth_identity.as_mut().map(|c| c.auth_identity()).flatten(); - let transformed_builder = builder.full_transform(Some(&mut credentials_handle)); + let mut credentials_handle = self.auth_identity.as_mut().map(|c| c.clone().auth_identity()).flatten(); + let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); pku2u.initialize_security_context_impl(&mut transformed_builder) }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { - let credentials_handle = self.auth_identity.as_mut().map(|c| c.auth_identity()).flatten(); - let transformed_builder = builder.full_transform(Some(&mut credentials_handle)); + let mut credentials_handle = self.auth_identity.as_mut().map(|c| c.clone().auth_identity()).flatten(); + let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); ntlm.initialize_security_context_impl(&mut transformed_builder) }, } } - #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] + // #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] fn accept_security_context_impl<'a>( &'a mut self, builder: builders::FilledAcceptSecurityContext<'a, Self::AuthenticationData, Self::CredentialsHandle>, ) -> Result { match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { - let creds_handle = if let Some(creds_handle) = builder.credentials_handle { - creds_handle.map(|c| c.auth_identity()).flatten() + let mut creds_handle = if let Some(creds_handle) = &builder.credentials_handle { + creds_handle.as_ref().map(|c| c.clone().auth_identity()).flatten() } else { None }; let new_builder = builder.full_transform(pku2u, Some(&mut creds_handle)); - pku2u.accept_security_context_impl(new_builder) + new_builder.execute() }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.accept_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { - let creds_handle = if let Some(creds_handle) = builder.credentials_handle { - creds_handle.map(|c| c.auth_identity()).flatten() + let mut creds_handle = if let Some(creds_handle) = &builder.credentials_handle { + creds_handle.as_ref().map(|c| c.clone().auth_identity()).flatten() } else { None }; let new_builder = builder.full_transform(ntlm, Some(&mut creds_handle)); - ntlm.accept_security_context_impl(new_builder) + new_builder.execute() }, } } From ba8d7fa4d3645f3a0a0e1e88c73eb72ad4f398bf Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 20:31:05 -0700 Subject: [PATCH 15/85] feat(sspi): credssp: improved credentials passing: now we can pass regular auth identity and smart card creds --- src/credssp/mod.rs | 96 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index c9767189..fa33374f 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -32,7 +32,7 @@ use crate::{ DataRepresentation, DecryptionFlags, EncryptionFlags, Error, ErrorKind, FilledAcceptSecurityContext, FilledAcquireCredentialsHandle, FilledInitializeSecurityContext, InitializeSecurityContextResult, Negotiate, NegotiateConfig, PackageInfo, SecurityBuffer, SecurityBufferType, SecurityStatus, ServerRequestFlags, Sspi, SspiEx, - SspiImpl, StreamSizes, + SspiImpl, StreamSizes, Credentials, CredentialsBuffers, }; pub const EARLY_USER_AUTH_RESULT_PDU_SIZE: usize = 4; @@ -255,10 +255,10 @@ impl CredSspClient { .unwrap() .sspi_context .acquire_credentials_handle() - .with_auth_data(&self.credentials) + .with_auth_data(&Credentials::AuthIdentity(self.credentials.clone())) .with_credential_use(CredentialUse::Outbound) .execute()?; - self.credentials_handle = credentials_handle; + self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); } ts_request.version = self.ts_request_version; @@ -271,7 +271,7 @@ impl CredSspClient { )]; let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take(); + let mut credentials_handle = self.credentials_handle.take().map(|c| CredentialsBuffers::AuthIdentity(c)); let cred_ssp_context = self.context.as_mut().unwrap(); let mut builder = EmptyInitializeSecurityContext::<::CredentialsHandle>::new() .with_credentials_handle(&mut credentials_handle) @@ -283,7 +283,7 @@ impl CredSspClient { let result = cred_ssp_context .sspi_context .initialize_security_context_impl(&mut builder)?; - self.credentials_handle = credentials_handle; + self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); ts_request.nego_tokens = Some(output_token.remove(0).buffer); if result.status == SecurityStatus::Ok { @@ -438,7 +438,7 @@ impl> CredSspServer { .execute(), ts_request ); - self.credentials_handle = credentials_handle; + self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); } try_cred_ssp_server!( self.context.as_mut().unwrap().check_peer_version(ts_request.version), @@ -473,7 +473,7 @@ impl> CredSspServer { let input_token = SecurityBuffer::new(input, SecurityBufferType::Token); let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take(); + let mut credentials_handle = self.credentials_handle.take().map(|a| CredentialsBuffers::AuthIdentity(a)); match try_cred_ssp_server!( self.context .as_mut() @@ -506,7 +506,7 @@ impl> CredSspServer { .as_mut() .unwrap() .sspi_context - .custom_set_auth_identity(auth_data); + .custom_set_auth_identity(Credentials::AuthIdentity(auth_data)); try_cred_ssp_server!( self.context.as_mut().unwrap().sspi_context.complete_auth_token(&mut []), @@ -551,7 +551,7 @@ impl> CredSspServer { } _ => unreachable!(), }; - self.credentials_handle = credentials_handle; + self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); Ok(ServerState::ReplyNeeded(ts_request)) } @@ -591,22 +591,36 @@ impl SspiContext { } impl SspiImpl for SspiContext { - type CredentialsHandle = Option; - type AuthenticationData = AuthIdentity; + type CredentialsHandle = Option; + type AuthenticationData = Credentials; #[instrument(ret, fields(security_package = self.package_name()), skip_all)] fn acquire_credentials_handle_impl<'a>( &'a mut self, builder: FilledAcquireCredentialsHandle<'a, Self::CredentialsHandle, Self::AuthenticationData>, ) -> crate::Result> { - match self { - SspiContext::Ntlm(ntlm) => builder.transform(ntlm).execute(), - SspiContext::Kerberos(kerberos) => builder.transform(kerberos).execute(), - SspiContext::Negotiate(negotiate) => builder.transform(negotiate).execute(), - SspiContext::Pku2u(pku2u) => builder.transform(pku2u).execute(), + Ok(match self { + SspiContext::Ntlm(ntlm) => { + let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { + identity + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + builder.full_transform(ntlm, Some(auth_identity)).execute()?.transform(&|a: Option| a.map(|c| CredentialsBuffers::AuthIdentity(c))) + }, + SspiContext::Kerberos(kerberos) => builder.transform(kerberos).execute()?, + SspiContext::Negotiate(negotiate) => builder.transform(negotiate).execute()?, + SspiContext::Pku2u(pku2u) => { + let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { + identity + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + builder.full_transform(pku2u, Some(auth_identity)).execute()?.transform(&|a: Option| a.map(|c| CredentialsBuffers::AuthIdentity(c))) + }, #[cfg(feature = "tsssp")] - SspiContext::CredSsp(credssp) => builder.transform(credssp).execute(), - } + SspiContext::CredSsp(credssp) => builder.transform(credssp).execute()?, + }) } #[instrument(ret, fields(security_package = self.package_name()), skip_all)] @@ -615,10 +629,26 @@ impl SspiImpl for SspiContext { builder: &mut FilledInitializeSecurityContext<'a, Self::CredentialsHandle>, ) -> crate::Result { match self { - SspiContext::Ntlm(ntlm) => ntlm.initialize_security_context_impl(builder), + SspiContext::Ntlm(ntlm) => { + let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = builder.credentials_handle_mut() { + Some(identity.clone()) + } else { + None + }; + let mut new_builder = builder.full_transform(Some(&mut auth_identity)); + ntlm.initialize_security_context_impl(&mut new_builder) + }, SspiContext::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), SspiContext::Negotiate(negotiate) => negotiate.initialize_security_context_impl(builder), - SspiContext::Pku2u(pku2u) => pku2u.initialize_security_context_impl(builder), + SspiContext::Pku2u(pku2u) => { + let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = builder.credentials_handle_mut() { + Some(identity.clone()) + } else { + None + }; + let mut new_builder = builder.full_transform(Some(&mut auth_identity)); + pku2u.initialize_security_context_impl(&mut new_builder) + }, #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => credssp.initialize_security_context_impl(builder), } @@ -630,10 +660,24 @@ impl SspiImpl for SspiContext { builder: FilledAcceptSecurityContext<'a, Self::AuthenticationData, Self::CredentialsHandle>, ) -> crate::Result { match self { - SspiContext::Ntlm(ntlm) => builder.transform(ntlm).execute(), + SspiContext::Ntlm(ntlm) => { + let auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { + identity.clone() + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + builder.full_transform(ntlm, Some(&mut Some(auth_identity))).execute() + }, SspiContext::Kerberos(kerberos) => builder.transform(kerberos).execute(), SspiContext::Negotiate(negotiate) => builder.transform(negotiate).execute(), - SspiContext::Pku2u(pku2u) => builder.transform(pku2u).execute(), + SspiContext::Pku2u(pku2u) => { + let auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { + identity.clone() + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + }; + builder.full_transform(pku2u, Some(&mut Some(auth_identity))).execute() + }, #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => builder.transform(credssp).execute(), } @@ -799,10 +843,14 @@ impl SspiEx for SspiContext { #[instrument(level = "trace", ret, fields(security_package = self.package_name()), skip(self))] fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { match self { - SspiContext::Ntlm(ntlm) => ntlm.custom_set_auth_identity(identity), + SspiContext::Ntlm(ntlm) => { + ntlm.custom_set_auth_identity(identity.auth_identity().unwrap()) + }, SspiContext::Kerberos(kerberos) => kerberos.custom_set_auth_identity(identity), SspiContext::Negotiate(negotiate) => negotiate.custom_set_auth_identity(identity), - SspiContext::Pku2u(pku2u) => pku2u.custom_set_auth_identity(identity), + SspiContext::Pku2u(pku2u) => { + pku2u.custom_set_auth_identity(identity.auth_identity().unwrap()) + }, #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => credssp.custom_set_auth_identity(identity), } From 9c71619a1b6c8a1a5e7e549b1b76ba7357f99f49 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 21:07:34 -0700 Subject: [PATCH 16/85] feat(sspi): sspi_cred_ssp: improved credentials passing: now we can pass regular auth identity and smart card creds --- Cargo.toml | 2 +- src/credssp/sspi_cred_ssp/mod.rs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39f2fe5b..68c11b75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ exclude = [ ] [features] -default = ["scard"] +default = ["scard", "tsssp"] network_client = ["dep:reqwest", "dep:portpicker"] dns_resolver = ["dep:trust-dns-resolver", "dep:tokio"] # TSSSP should be used only on Windows as a native CREDSSP replacement diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index 3c7e9b69..fc94f7b6 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -13,12 +13,12 @@ use super::ts_request::NONCE_SIZE; use super::{CredSspContext, CredSspMode, EndpointType, SspiContext, TsRequest}; use crate::builders::EmptyInitializeSecurityContext; use crate::{ - builders, negotiate, AcquireCredentialsHandleResult, AuthIdentity, AuthIdentityBuffers, CertContext, + builders, negotiate, AcquireCredentialsHandleResult, CertContext, CertEncodingType, CertTrustErrorStatus, CertTrustInfoStatus, CertTrustStatus, ClientRequestFlags, ClientResponseFlags, ConnectionInfo, ContextNames, ContextSizes, CredentialUse, DataRepresentation, DecryptionFlags, EncryptionFlags, Error, ErrorKind, InitializeSecurityContextResult, PackageCapabilities, PackageInfo, Result, SecurityBuffer, SecurityBufferType, SecurityPackageType, SecurityStatus, Sspi, SspiEx, - SspiImpl, StreamSizes, PACKAGE_ID_NONE, + SspiImpl, StreamSizes, PACKAGE_ID_NONE, Credentials, CredentialsBuffers, }; pub const PKG_NAME: &str = "CREDSSP"; @@ -45,7 +45,7 @@ enum CredSspState { pub struct SspiCredSsp { state: CredSspState, cred_ssp_context: Box, - auth_identity: Option, + auth_identity: Option, tls_connection: TlsConnection, nonce: Option<[u8; NONCE_SIZE]>, } @@ -268,8 +268,8 @@ impl Sspi for SspiCredSsp { } impl SspiImpl for SspiCredSsp { - type CredentialsHandle = Option; - type AuthenticationData = AuthIdentity; + type CredentialsHandle = Option; + type AuthenticationData = Credentials; #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] fn acquire_credentials_handle_impl<'a>( @@ -283,7 +283,10 @@ impl SspiImpl for SspiCredSsp { )); } - self.auth_identity = builder.auth_data.cloned().map(AuthIdentityBuffers::from); + self.auth_identity = match builder.auth_data.cloned() { + Some(auth_data) => Some(auth_data.try_into()?), + None => None, + }; Ok(AcquireCredentialsHandleResult { credentials_handle: self.auth_identity.clone(), @@ -429,7 +432,7 @@ impl SspiImpl for SspiCredSsp { ts_request.auth_info = Some( self.cred_ssp_context - .encrypt_ts_credentials(credentials, CredSspMode::WithCredentials)?, + .encrypt_ts_credentials(credentials.as_auth_identity().unwrap(), CredSspMode::WithCredentials)?, ); let mut encoded_ts_request = Vec::new(); @@ -474,6 +477,6 @@ impl SspiImpl for SspiCredSsp { impl SspiEx for SspiCredSsp { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { - self.auth_identity = Some(identity.into()); + self.auth_identity = Some(identity.try_into().unwrap()); } } From c91329306fea6ab96b102d7707193c923bbda7a5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 17 Jul 2023 21:24:44 -0700 Subject: [PATCH 17/85] feat(ffi): add support of the Credentials/CredentialsBuffers instead of AuthIdentityu/AuthIdentityBuffers --- ffi/Cargo.toml | 2 +- ffi/src/sec_handle.rs | 6 +++--- ffi/src/sec_winnt_auth_identity.rs | 34 +++++++++++++++--------------- ffi/src/utils.rs | 4 ++-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index f7b1fb34..9f134866 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -13,7 +13,7 @@ name = "sspi" crate-type = ["cdylib"] [features] -default = ["scard"] +default = ["scard", "tsssp"] tsssp = ["sspi/tsssp"] scard = ["sspi/scard"] diff --git a/ffi/src/sec_handle.rs b/ffi/src/sec_handle.rs index 79e7ea0d..b2c6c7f2 100644 --- a/ffi/src/sec_handle.rs +++ b/ffi/src/sec_handle.rs @@ -13,8 +13,8 @@ use sspi::kerberos::config::KerberosConfig; use sspi::network_client::reqwest_network_client::{RequestClientFactory, ReqwestNetworkClient}; use sspi::ntlm::NtlmConfig; use sspi::{ - kerberos, negotiate, ntlm, pku2u, AuthIdentityBuffers, ClientRequestFlags, DataRepresentation, Error, ErrorKind, - Kerberos, Negotiate, NegotiateConfig, Ntlm, Result, Secret, Sspi, SspiImpl, + kerberos, negotiate, ntlm, pku2u, ClientRequestFlags, DataRepresentation, Error, ErrorKind, + Kerberos, Negotiate, NegotiateConfig, Ntlm, Result, Secret, Sspi, SspiImpl, CredentialsBuffers, }; #[cfg(target_os = "windows")] use winapi::um::wincrypt::{ @@ -82,7 +82,7 @@ pub type PCredHandle = *mut SecHandle; pub type PCtxtHandle = *mut SecHandle; pub struct CredentialsHandle { - pub credentials: AuthIdentityBuffers, + pub credentials: CredentialsBuffers, pub security_package_name: String, pub attributes: CredentialsAttributes, } diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index fe8bf577..916d7792 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, Error, ErrorKind, Result}; +use sspi::{AuthIdentityBuffers, Error, ErrorKind, Result, CredentialsBuffers}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -162,7 +162,7 @@ pub unsafe fn auth_data_to_identity_buffers( security_package_name: &str, p_auth_data: *const c_void, package_list: &mut Option, -) -> Result { +) -> Result { let (_, auth_flags) = get_auth_data_identity_version_and_flags(p_auth_data); if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { @@ -176,7 +176,7 @@ pub unsafe fn auth_data_to_identity_buffers_a( _security_package_name: &str, p_auth_data: *const c_void, package_list: &mut Option, -) -> Result { +) -> Result { #[cfg(feature = "tsssp")] if _security_package_name == sspi::credssp::sspi_cred_ssp::PKG_NAME { let credssp_cred = p_auth_data.cast::().as_ref().unwrap(); @@ -197,18 +197,18 @@ pub unsafe fn auth_data_to_identity_buffers_a( .to_string(), ); } - Ok(AuthIdentityBuffers { + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { user: raw_str_into_bytes((*auth_data).user, (*auth_data).user_length as usize), domain: raw_str_into_bytes((*auth_data).domain, (*auth_data).domain_length as usize), password: raw_str_into_bytes((*auth_data).password, (*auth_data).password_length as usize).into(), - }) + })) } else { let auth_data = p_auth_data.cast::(); - Ok(AuthIdentityBuffers { + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { user: raw_str_into_bytes((*auth_data).user, (*auth_data).user_length as usize), domain: raw_str_into_bytes((*auth_data).domain, (*auth_data).domain_length as usize), password: raw_str_into_bytes((*auth_data).password, (*auth_data).password_length as usize).into(), - }) + })) } } @@ -216,7 +216,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( _security_package_name: &str, p_auth_data: *const c_void, package_list: &mut Option, -) -> Result { +) -> Result { #[cfg(feature = "tsssp")] if _security_package_name == sspi::credssp::sspi_cred_ssp::PKG_NAME { let credssp_cred = p_auth_data.cast::().as_ref().unwrap(); @@ -234,7 +234,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( (*auth_data).package_list_length as usize, ))); } - Ok(AuthIdentityBuffers { + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { user: raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2), domain: raw_str_into_bytes((*auth_data).domain as *const _, (*auth_data).domain_length as usize * 2), password: raw_str_into_bytes( @@ -242,10 +242,10 @@ pub unsafe fn auth_data_to_identity_buffers_w( (*auth_data).password_length as usize * 2, ) .into(), - }) + })) } else { let auth_data = p_auth_data.cast::(); - Ok(AuthIdentityBuffers { + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { user: raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2), domain: raw_str_into_bytes((*auth_data).domain as *const _, (*auth_data).domain_length as usize * 2), password: raw_str_into_bytes( @@ -253,7 +253,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( (*auth_data).password_length as usize * 2, ) .into(), - }) + })) } } @@ -287,7 +287,7 @@ unsafe fn get_sec_winnt_auth_identity_ex2_size(p_auth_data: *const c_void) -> u3 } #[cfg(target_os = "windows")] -pub unsafe fn unpack_sec_winnt_auth_identity_ex2_a(p_auth_data: *const c_void) -> Result { +pub unsafe fn unpack_sec_winnt_auth_identity_ex2_a(p_auth_data: *const c_void) -> Result { use std::ptr::null_mut; use sspi::Secret; @@ -364,11 +364,11 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_a(p_auth_data: *const c_void) - password.as_mut().pop(); auth_identity_buffers.password = password; - Ok(auth_identity_buffers) + Ok(CredentialsBuffers::AuthIdentity(auth_identity_buffers)) } #[cfg(not(target_os = "windows"))] -pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Result { +pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Result { Err(Error::new( ErrorKind::UnsupportedFunction, "SecWinntIdentityEx2 is not supported on non Windows systems", @@ -376,7 +376,7 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu } #[cfg(target_os = "windows")] -pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { +pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { use std::ptr::null_mut; use sspi::Secret; @@ -454,7 +454,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - password.as_mut().truncate(new_len); auth_identity_buffers.password = password; - Ok(auth_identity_buffers) + Ok(CredentialsBuffers::AuthIdentity(auth_identity_buffers)) } #[allow(clippy::missing_safety_doc)] diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 4107f5da..7e70af9b 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::c_char; -use sspi::AuthIdentityBuffers; +use sspi::CredentialsBuffers; use crate::credentials_attributes::CredentialsAttributes; use crate::sec_handle::CredentialsHandle; @@ -38,7 +38,7 @@ pub unsafe fn raw_str_into_bytes(raw_buffer: *const c_char, len: usize) -> Vec( credentials_handle: *mut CredentialsHandle, -) -> Option<(AuthIdentityBuffers, &'a str, &'a CredentialsAttributes)> { +) -> Option<(CredentialsBuffers, &'a str, &'a CredentialsAttributes)> { if credentials_handle.is_null() { None } else { From b78f3901ea3d5caf74a9b260a1f85b3260e9483a Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 20 Jul 2023 18:11:39 -0700 Subject: [PATCH 18/85] feat(sspi): auth_identity: add username field to smart card creds; --- src/auth_identity.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index f965df5f..cfc522a2 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -81,6 +81,8 @@ impl From for AuthIdentity { #[cfg(feature = "scard")] #[derive(Clone, Eq, PartialEq, Debug)] pub struct SmartCardIdentityBuffers { + /// UTF-16 encoded username + pub username: Vec, /// DER-encoded X509 certificate pub certificate: Vec, /// UTF-16 encoded smart card reader name @@ -92,6 +94,8 @@ pub struct SmartCardIdentityBuffers { /// Represents data needed for smart card authentication #[derive(Debug, Clone, PartialEq)] pub struct SmartCardIdentity { + /// Username + pub username: String, /// X509 certificate pub certificate: Certificate, /// Smart card reader name @@ -108,6 +112,7 @@ impl TryFrom for SmartCardIdentityBuffers { certificate: picky_asn1_der::to_vec(&value.certificate)?, reader_name: value.reader_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), pin: value.pin.as_ref().clone().into(), + username: value.username.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), }) } } @@ -119,7 +124,8 @@ impl TryFrom for SmartCardIdentity { Ok(Self { certificate: picky_asn1_der::from_bytes(&value.certificate)?, reader_name: utils::bytes_to_utf16_string(&value.reader_name), - pin: value.pin.as_ref().clone().into(), + pin: utils::bytes_to_utf16_string(value.pin.as_ref()).into_bytes().into(), + username: utils::bytes_to_utf16_string(&value.username), }) } } From 075ed4140570bb52f7b5449ef0748f0e7cbb0ed5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 27 Jul 2023 12:53:37 +0300 Subject: [PATCH 19/85] feat(ffi): handle smart card creds in tsssp; --- ffi/src/sec_winnt_auth_identity.rs | 57 ++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 916d7792..d4080af9 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, Error, ErrorKind, Result, CredentialsBuffers}; +use sspi::{AuthIdentityBuffers, Error, ErrorKind, Result, CredentialsBuffers, SmartCardIdentityBuffers}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -163,13 +163,23 @@ pub unsafe fn auth_data_to_identity_buffers( p_auth_data: *const c_void, package_list: &mut Option, ) -> Result { + warn!("auth_data_to_identity_buffers"); let (_, auth_flags) = get_auth_data_identity_version_and_flags(p_auth_data); if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { - auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) + warn!("auth data to identity buffers w"); } else { - auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) + warn!("auth data to identity buffers a"); + // auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) } + + auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) + + // if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { + // auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) + // } else { + // auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) + // } } pub unsafe fn auth_data_to_identity_buffers_a( @@ -380,6 +390,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - use std::ptr::null_mut; use sspi::Secret; + use winapi::um::wincred::{CredUnmarshalCredentialW, CRED_MARSHAL_TYPE, CertCredential, CERT_CREDENTIAL_INFO}; use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; if p_auth_data.is_null() { @@ -431,6 +442,46 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - )); } + // only marshaled smart card creds starts with '@' char + if username[0] == b'@' { + let mut cred_type = 0; + let mut credential = null_mut(); + + let result = CredUnmarshalCredentialW(username.as_ptr() as *const _, &mut cred_type, &mut credential); + + if result == 1 { + if cred_type == CertCredential { + let cert_credential = credential.cast::(); + + let certificate = sspi::cert_utils::extract_raw_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; + + // test credentials + // todo: use real reader name + let reader_name = "Microsoft Virtual Smart Card 0".encode_utf16().flat_map(|v| v.to_le_bytes()).collect(); + // todo: extract username from the certificate + let username = "pw13@example.com".encode_utf16().flat_map(|v| v.to_le_bytes()).collect(); + + // remove null + let new_len = password.as_ref().len() - 2; + password.as_mut().truncate(new_len); + + let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { + certificate, + reader_name, + pin: password, + username, + }); + warn!(creds = ?creds); + + return Ok(creds); + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential")); + } + } else { + return Err(Error::new(ErrorKind::NoCredentials, "Cannot unmarshal smart card credentials")); + } + } + let mut auth_identity_buffers = AuthIdentityBuffers::default(); // remove null From e9b12c0a4b06f35c004442f337bfd44487362dba Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 27 Jul 2023 14:26:31 +0300 Subject: [PATCH 20/85] feat(ffi): small refactoring --- ffi/Cargo.toml | 1 + ffi/src/sec_winnt_auth_identity.rs | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 9f134866..b1edaec6 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -13,6 +13,7 @@ name = "sspi" crate-type = ["cdylib"] [features] +# for testing default = ["scard", "tsssp"] tsssp = ["sspi/tsssp"] scard = ["sspi/scard"] diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index d4080af9..0dcff4a9 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -158,12 +158,13 @@ pub unsafe fn get_auth_data_identity_version_and_flags(p_auth_data: *const c_voi } } +// todo: refactor: delete this function and use corresponding a/w function in the `sec_handle` module +// motivation: `(auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0` gives `false` for passed smart card credentials in the w function pub unsafe fn auth_data_to_identity_buffers( security_package_name: &str, p_auth_data: *const c_void, package_list: &mut Option, ) -> Result { - warn!("auth_data_to_identity_buffers"); let (_, auth_flags) = get_auth_data_identity_version_and_flags(p_auth_data); if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { @@ -174,12 +175,6 @@ pub unsafe fn auth_data_to_identity_buffers( } auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) - - // if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { - // auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) - // } else { - // auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) - // } } pub unsafe fn auth_data_to_identity_buffers_a( From 5f76d847028017cd75cb585904e0d7871c4f0e3c Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 27 Jul 2023 14:27:27 +0300 Subject: [PATCH 21/85] feat(ffi): format code; --- ffi/src/sec_handle.rs | 4 ++-- ffi/src/sec_winnt_auth_identity.rs | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/ffi/src/sec_handle.rs b/ffi/src/sec_handle.rs index b2c6c7f2..7e5dc143 100644 --- a/ffi/src/sec_handle.rs +++ b/ffi/src/sec_handle.rs @@ -13,8 +13,8 @@ use sspi::kerberos::config::KerberosConfig; use sspi::network_client::reqwest_network_client::{RequestClientFactory, ReqwestNetworkClient}; use sspi::ntlm::NtlmConfig; use sspi::{ - kerberos, negotiate, ntlm, pku2u, ClientRequestFlags, DataRepresentation, Error, ErrorKind, - Kerberos, Negotiate, NegotiateConfig, Ntlm, Result, Secret, Sspi, SspiImpl, CredentialsBuffers, + kerberos, negotiate, ntlm, pku2u, ClientRequestFlags, CredentialsBuffers, DataRepresentation, Error, ErrorKind, + Kerberos, Negotiate, NegotiateConfig, Ntlm, Result, Secret, Sspi, SspiImpl, }; #[cfg(target_os = "windows")] use winapi::um::wincrypt::{ diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 0dcff4a9..719b4d43 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, Error, ErrorKind, Result, CredentialsBuffers, SmartCardIdentityBuffers}; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, SmartCardIdentityBuffers}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -159,7 +159,7 @@ pub unsafe fn get_auth_data_identity_version_and_flags(p_auth_data: *const c_voi } // todo: refactor: delete this function and use corresponding a/w function in the `sec_handle` module -// motivation: `(auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0` gives `false` for passed smart card credentials in the w function +// motivation: `(auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0` gives `false` for passed smart card credentials in the w function pub unsafe fn auth_data_to_identity_buffers( security_package_name: &str, p_auth_data: *const c_void, @@ -385,7 +385,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - use std::ptr::null_mut; use sspi::Secret; - use winapi::um::wincred::{CredUnmarshalCredentialW, CRED_MARSHAL_TYPE, CertCredential, CERT_CREDENTIAL_INFO}; + use winapi::um::wincred::{CertCredential, CredUnmarshalCredentialW, CERT_CREDENTIAL_INFO, CRED_MARSHAL_TYPE}; use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; if p_auth_data.is_null() { @@ -448,13 +448,20 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - if cred_type == CertCredential { let cert_credential = credential.cast::(); - let certificate = sspi::cert_utils::extract_raw_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; + let certificate = + sspi::cert_utils::extract_raw_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; // test credentials // todo: use real reader name - let reader_name = "Microsoft Virtual Smart Card 0".encode_utf16().flat_map(|v| v.to_le_bytes()).collect(); + let reader_name = "Microsoft Virtual Smart Card 0" + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect(); // todo: extract username from the certificate - let username = "pw13@example.com".encode_utf16().flat_map(|v| v.to_le_bytes()).collect(); + let username = "pw13@example.com" + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect(); // remove null let new_len = password.as_ref().len() - 2; @@ -470,10 +477,16 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - return Ok(creds); } else { - return Err(Error::new(ErrorKind::NoCredentials, "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential", + )); } } else { - return Err(Error::new(ErrorKind::NoCredentials, "Cannot unmarshal smart card credentials")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Cannot unmarshal smart card credentials", + )); } } From 53c5e781adef245c8f6dd75281b0d01aed6210dc Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 9 Aug 2023 14:34:01 +0300 Subject: [PATCH 22/85] feat(cargo.toml): temporary replaces picky-* crates with local ones; --- Cargo.toml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68c11b75..b728dd62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,11 +48,13 @@ serde = "1.0" serde_derive = "1.0" url = "2.2.2" reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false } -picky = { version = "7.0.0-rc.4", default-features = false } -picky-krb = "0.6.0" -picky-asn1 = { version = "0.7.1", features = ["chrono_conversion"] } -picky-asn1-der = "0.4.0" -picky-asn1-x509 = { version = "0.9.0", features = ["pkcs7"] } + +picky = { path = "../picky-rs/picky", default-features = false } +picky-asn1 = { path = "../picky-rs/picky-asn1", features = ["chrono_conversion"] } +picky-asn1-der = { path = "../picky-rs/picky-asn1-der" } +picky-asn1-x509 = { path = "../picky-rs/picky-asn1-x509", features = ["pkcs7"] } +picky-krb = { path = "../picky-rs/picky-krb" } + oid = "0.2.1" uuid = { version = "1.1", features = ["v4"] } trust-dns-resolver = { version = "0.21.2", optional = true } From 845832e2c7e44f2720acda9464dc94a07e713908 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 9 Aug 2023 15:13:04 +0300 Subject: [PATCH 23/85] feat(credssp): ts_request: improve credentials encoding/decoding: use general CredentialsBuffers structure. replace custom ber encoding with structures from the picky-krb crate; --- src/credssp/ts_request/mod.rs | 140 +++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 51 deletions(-) diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index 376179ed..ada334fb 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -4,8 +4,16 @@ mod test; use core::fmt; use std::io::{self, Read}; +use picky_asn1::wrapper::{ + ExplicitContextTag0, ExplicitContextTag1, ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, + IntegerAsn1, OctetStringAsn1, Optional, +}; +use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; +use picky_krb::credssp::{TsCredentials, TsCspDataDetail, TsPasswordCreds, TsSmartCardCreds}; + use super::CredSspMode; -use crate::{ber, AuthIdentityBuffers}; +use crate::utils::string_to_utf16; +use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, SmartCardIdentityBuffers}; pub const TS_REQUEST_VERSION: u32 = 6; @@ -244,7 +252,54 @@ impl TsRequest { } } -pub fn write_ts_credentials(credentials: &AuthIdentityBuffers, cred_ssp_mode: CredSspMode) -> io::Result> { +#[instrument(ret)] +fn write_smart_card_credentials(credentials: &SmartCardIdentityBuffers) -> crate::Result> { + let smart_card_creds = TsSmartCardCreds { + pin: ExplicitContextTag0::from(OctetStringAsn1::from(string_to_utf16("214653214653"))), + csp_data: ExplicitContextTag1::from(TsCspDataDetail { + key_spec: ExplicitContextTag0::from(IntegerAsn1::from(vec![1_u8])), + card_name: Optional::from(Some(ExplicitContextTag1::from(OctetStringAsn1::from(string_to_utf16( + "VSCtest", + ))))), + reader_name: Optional::from(Some(ExplicitContextTag2::from(OctetStringAsn1::from(string_to_utf16( + "Microsoft Virtual Smart Card 0", + ))))), + container_name: Optional::from(Some(ExplicitContextTag3::from(OctetStringAsn1::from(string_to_utf16( + "te-RDPsmartcardlogon5-8ff3a38e-c6-50987", + ))))), + csp_name: Optional::from(Some(ExplicitContextTag4::from(OctetStringAsn1::from(string_to_utf16( + "Microsoft Base Smart Card Crypto Provider", + ))))), + }), + user_hint: Optional::from(None), + domain_hint: Optional::from(None), + }; + let ts_creds = TsCredentials { + cred_type: ExplicitContextTag0::from(IntegerAsn1::from(vec![2])), + credentials: ExplicitContextTag1::from(OctetStringAsn1::from(picky_asn1_der::to_vec(&smart_card_creds)?)), + }; + + Ok(picky_asn1_der::to_vec(&ts_creds)?) +} + +pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: CredSspMode) -> crate::Result> { + let (creds_type, encoded_credentials) = match credentials { + CredentialsBuffers::AuthIdentity(creds) => { + (TS_PASSWORD_CREDS, write_password_credentials(creds, cred_ssp_mode)?) + } + CredentialsBuffers::SmartCard(creds) => (TS_SMART_CARD_CREDS, write_smart_card_credentials(creds)?), + }; + + let ts_creds = TsCredentials { + cred_type: ExplicitContextTag0::from(IntegerAsn1::from(vec![creds_type])), + credentials: ExplicitContextTag1::from(OctetStringAsn1::from(encoded_credentials)), + }; + + Ok(picky_asn1_der::to_vec(&ts_creds)?) +} + +#[instrument(ret)] +fn write_password_credentials(credentials: &AuthIdentityBuffers, cred_ssp_mode: CredSspMode) -> io::Result> { let empty_identity = AuthIdentityBuffers::default(); let identity = match cred_ssp_mode { CredSspMode::WithCredentials => credentials, @@ -254,24 +309,9 @@ pub fn write_ts_credentials(credentials: &AuthIdentityBuffers, cred_ssp_mode: Cr let ts_credentials_len = sizeof_ts_credentials(identity); let ts_credentials_sequence_len = ber::sizeof_sequence(ts_credentials_len); let password_credentials_len = sizeof_ts_password_creds(identity); - let password_credentials_sequence_len = ber::sizeof_sequence(password_credentials_len); let mut buffer = Vec::with_capacity(ts_credentials_sequence_len as usize); - // TSCredentials (SEQUENCE) - ber::write_sequence_tag(&mut buffer, ts_credentials_len)?; - // [0] credType (INTEGER) - ber::write_contextual_tag(&mut buffer, 0, ber::sizeof_integer(1), ber::Pc::Construct)?; - ber::write_integer(&mut buffer, 1)?; - /* [1] credentials (OCTET STRING) */ - ber::write_contextual_tag( - &mut buffer, - 1, - ber::sizeof_octet_string(password_credentials_sequence_len), - ber::Pc::Construct, - )?; - ber::write_octet_string_tag(&mut buffer, password_credentials_sequence_len)?; - /* TSPasswordCreds (SEQUENCE) */ ber::write_sequence_tag(&mut buffer, password_credentials_len)?; /* [0] domainName (OCTET STRING) */ @@ -284,44 +324,42 @@ pub fn write_ts_credentials(credentials: &AuthIdentityBuffers, cred_ssp_mode: Cr Ok(buffer) } -pub fn read_ts_credentials(mut buffer: impl io::Read) -> io::Result { - // TSCredentials (SEQUENCE) - ber::read_sequence_tag(&mut buffer)?; - // [0] credType (INTEGER) - ber::read_contextual_tag(&mut buffer, 0, ber::Pc::Construct)?; - ber::read_integer(&mut buffer)?; - // [1] credentials (OCTET STRING) - ber::read_contextual_tag(&mut buffer, 1, ber::Pc::Construct)?; - ber::read_octet_string_tag(&mut buffer)?; +pub fn read_password_credentials(data: impl AsRef<[u8]>) -> crate::Result { + let password_card_creds: TsPasswordCreds = picky_asn1_der::from_bytes(data.as_ref())?; - // Read TS password credentials - let _len = ber::read_sequence_tag(&mut buffer)?; - - /* [0] domainName (OCTET STRING) */ - ber::read_contextual_tag(&mut buffer, 0, ber::Pc::Construct)?; - let length = ber::read_octet_string_tag(&mut buffer)?; - let mut domain = vec![0x00; length as usize]; - if length > 0 { - buffer.read_exact(&mut domain)?; - } + let TsPasswordCreds { + domain_name, + user_name, + password, + } = password_card_creds; - /* [1] userName (OCTET STRING) */ - ber::read_contextual_tag(&mut buffer, 1, ber::Pc::Construct)?; - let length = ber::read_octet_string_tag(&mut buffer)?; - let mut user = vec![0x00; length as usize]; - if length > 0 { - buffer.read_exact(&mut user)?; - } + Ok(AuthIdentityBuffers { + user: user_name.0 .0, + domain: domain_name.0 .0, + password: password.0 .0.into(), + }) +} - /* [2] password (OCTET STRING) */ - ber::read_contextual_tag(&mut buffer, 2, ber::Pc::Construct)?; - let length = ber::read_octet_string_tag(&mut buffer)?; - let mut password = vec![0x00; length as usize]; - if length > 0 { - buffer.read_exact(&mut password)?; +pub fn read_ts_credentials(mut buffer: impl io::Read) -> crate::Result { + let ts_credentials: TsCredentials = picky_asn1_der::from_reader(&mut buffer)?; + + match ts_credentials.cred_type.0 .0.first() { + Some(&TS_PASSWORD_CREDS) => Ok(CredentialsBuffers::AuthIdentity(read_password_credentials( + &ts_credentials.credentials.0 .0, + )?)), + Some(&TS_SMART_CARD_CREDS) => Err(Error::new( + ErrorKind::UnsupportedFunction, + "Reading of the TsSmartCard credentials is not supported yet", + )), + Some(cred_type) => Err(Error::new( + ErrorKind::InvalidToken, + format!("Invalid or unsupported TsCredentials::cred_type value: {}", cred_type), + )), + None => Err(Error::new( + ErrorKind::InvalidToken, + "TsCredentials::cred_type field is empty", + )), } - - Ok(AuthIdentityBuffers::new(user, domain, password)) } fn sizeof_ts_credentials(identity: &AuthIdentityBuffers) -> u16 { From 3591304a8da07afc8daa999076feb93bbe4a5a92 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 9 Aug 2023 15:13:56 +0300 Subject: [PATCH 24/85] feat(credssp): ts_request: tests: improved tests according to the credentials changes; --- src/credssp/ts_request/test.rs | 65 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/credssp/ts_request/test.rs b/src/credssp/ts_request/test.rs index b5c058f4..26987eae 100644 --- a/src/credssp/ts_request/test.rs +++ b/src/credssp/ts_request/test.rs @@ -2,7 +2,7 @@ use lazy_static::lazy_static; use super::*; use crate::credssp::CredSspMode; -use crate::{AuthIdentity, AuthIdentityBuffers}; +use crate::AuthIdentity; const NTLM_CLIENT_NONCE: [u8; 32] = [ 0x22, 0x10, 0x12, 0xad, 0x12, 0x5c, 0x7a, 0x15, 0xfe, 0xb6, 0x4b, 0x1f, 0xcb, 0x94, 0x83, 0x3a, 0xc5, 0x6f, 0x66, @@ -197,33 +197,42 @@ const TS_CREDENTIALS_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: [u8; 25] = [ ]; lazy_static! { - static ref AUTH_IDENTITY_ONE_SYMBOL_USER_AND_PASSWORD: AuthIdentityBuffers = AuthIdentity { - username: String::from("a"), - password: String::from("1").into(), - domain: None - } - .into(); - static ref AUTH_IDENTITY_STRONG_USERNAME_AND_PASSWORD: AuthIdentityBuffers = AuthIdentity { - username: String::from("QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890"), - password: String::from( - "@#$%^&*()_+1234567890-=QWERTYUIOP{}qwertyuiop[]asdfghjkl;ASDFGHJKL:\\\"|zxcvbnm,.ZXCVBNM<>?" - ) - .into(), - domain: None - } - .into(); - static ref AUTH_IDENTITY_SIMPLE_WITH_USERNAME_AND_DOMAIN_AND_PASSWORD: AuthIdentityBuffers = AuthIdentity { - username: String::from("Username"), - password: String::from("Password").into(), - domain: Some(String::from("Domain")) - } - .into(); - static ref AUTH_IDENTITY_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: AuthIdentityBuffers = AuthIdentity { - username: String::from(""), - password: String::from("").into(), - domain: Some(String::from("")) - } - .into(); + static ref AUTH_IDENTITY_ONE_SYMBOL_USER_AND_PASSWORD: CredentialsBuffers = CredentialsBuffers::AuthIdentity( + AuthIdentity { + username: String::from("a"), + password: String::from("1").into(), + domain: None + } + .into() + ); + static ref AUTH_IDENTITY_STRONG_USERNAME_AND_PASSWORD: CredentialsBuffers = CredentialsBuffers::AuthIdentity( + AuthIdentity { + username: String::from("QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890"), + password: String::from( + "@#$%^&*()_+1234567890-=QWERTYUIOP{}qwertyuiop[]asdfghjkl;ASDFGHJKL:\\\"|zxcvbnm,.ZXCVBNM<>?" + ) + .into(), + domain: None + } + .into() + ); + static ref AUTH_IDENTITY_SIMPLE_WITH_USERNAME_AND_DOMAIN_AND_PASSWORD: CredentialsBuffers = + CredentialsBuffers::AuthIdentity( + AuthIdentity { + username: String::from("Username"), + password: String::from("Password").into(), + domain: Some(String::from("Domain")) + } + .into() + ); + static ref AUTH_IDENTITY_WITH_RESTRICTED_ADMIN_MODE_REQUIRED: CredentialsBuffers = CredentialsBuffers::AuthIdentity( + AuthIdentity { + username: String::from(""), + password: String::from("").into(), + domain: Some(String::from("")) + } + .into() + ); } #[test] From 195fdaa208b432df303d695372bd13839efbddbe Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 16 Aug 2023 14:30:34 +0300 Subject: [PATCH 25/85] feat(sspi): refactor cert_utils and smart card credentials; --- src/{kerberos => }/cert_utils.rs | 0 src/kerberos/mod.rs | 1 - src/lib.rs | 3 ++- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{kerberos => }/cert_utils.rs (100%) diff --git a/src/kerberos/cert_utils.rs b/src/cert_utils.rs similarity index 100% rename from src/kerberos/cert_utils.rs rename to src/cert_utils.rs diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 22f509c9..9ef07582 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -1,4 +1,3 @@ -mod cert_utils; pub mod client; pub mod config; mod encryption_params; diff --git a/src/lib.rs b/src/lib.rs index da91d669..0664bd82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ pub mod winapi; mod auth_identity; mod ber; +pub mod cert_utils; mod crypto; mod dns; mod kdc; @@ -112,7 +113,7 @@ use picky_krb::gss_api::GssApiMessageError; use picky_krb::messages::KrbError; use utils::map_keb_error_code_to_sspi_error; -pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, SmartCardIdentity, Credentials}; +pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, SmartCardIdentity, SmartCardIdentityBuffers, Credentials}; use self::builders::{ AcceptSecurityContext, AcquireCredentialsHandle, ChangePassword, EmptyAcceptSecurityContext, EmptyAcquireCredentialsHandle, EmptyInitializeSecurityContext, FilledAcceptSecurityContext, From 351d74673458edfe2d05ec39ee032328d0b3d537 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 9 Aug 2023 15:16:28 +0300 Subject: [PATCH 26/85] feat(credssp): improved credentials encryption/decryption; --- src/credssp/mod.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index fa33374f..ba34bd33 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -335,12 +335,13 @@ impl CredSspClient { peer_version, )?; - ts_request.auth_info = Some( - self.context - .as_mut() - .unwrap() - .encrypt_ts_credentials(&self.credentials.clone().into(), self.cred_ssp_mode)?, - ); + let auth_identity: AuthIdentityBuffers = self.credentials.clone().into(); + ts_request.auth_info = + Some(self.context.as_mut().unwrap().encrypt_ts_credentials( + &CredentialsBuffers::AuthIdentity(auth_identity), + self.cred_ssp_mode, + )?); + info!("tscredentials has been written"); self.state = CredSspState::Final; @@ -466,7 +467,7 @@ impl> CredSspServer { self.state = CredSspState::Final; - Ok(ServerState::Finished(read_credentials.into())) + Ok(ServerState::Finished(read_credentials.auth_identity().unwrap().into())) } CredSspState::NegoToken => { let input = ts_request.nego_tokens.take().unwrap_or_default(); @@ -1025,13 +1026,13 @@ impl CredSspContext { fn encrypt_ts_credentials( &mut self, - credentials: &AuthIdentityBuffers, + credentials: &CredentialsBuffers, cred_ssp_mode: CredSspMode, ) -> crate::Result> { self.encrypt_message(&ts_request::write_ts_credentials(credentials, cred_ssp_mode)?) } - fn decrypt_ts_credentials(&mut self, auth_info: &[u8]) -> crate::Result { + fn decrypt_ts_credentials(&mut self, auth_info: &[u8]) -> crate::Result { let ts_credentials_buffer = self.decrypt_message(auth_info)?; Ok(ts_request::read_ts_credentials(ts_credentials_buffer.as_slice())?) From ab36b1e0c4ffe0a87db25e3fc32a322a55336fb9 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 13:57:58 +0300 Subject: [PATCH 27/85] feat(sspi): credssp: format code. improve credentials encoding: remove hardcoded values; --- src/credssp/mod.rs | 2 +- src/credssp/ts_request/mod.rs | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index ba34bd33..85593d64 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -1035,7 +1035,7 @@ impl CredSspContext { fn decrypt_ts_credentials(&mut self, auth_info: &[u8]) -> crate::Result { let ts_credentials_buffer = self.decrypt_message(auth_info)?; - Ok(ts_request::read_ts_credentials(ts_credentials_buffer.as_slice())?) + ts_request::read_ts_credentials(ts_credentials_buffer.as_slice()) } fn encrypt_message(&mut self, input: &[u8]) -> crate::Result> { diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index ada334fb..1cb8bb06 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -8,11 +8,10 @@ use picky_asn1::wrapper::{ ExplicitContextTag0, ExplicitContextTag1, ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, IntegerAsn1, OctetStringAsn1, Optional, }; -use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; +use picky_krb::constants::cred_ssp::{AT_KEYEXCHANGE, TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; use picky_krb::credssp::{TsCredentials, TsCspDataDetail, TsPasswordCreds, TsSmartCardCreds}; use super::CredSspMode; -use crate::utils::string_to_utf16; use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, SmartCardIdentityBuffers}; pub const TS_REQUEST_VERSION: u32 = 6; @@ -255,21 +254,21 @@ impl TsRequest { #[instrument(ret)] fn write_smart_card_credentials(credentials: &SmartCardIdentityBuffers) -> crate::Result> { let smart_card_creds = TsSmartCardCreds { - pin: ExplicitContextTag0::from(OctetStringAsn1::from(string_to_utf16("214653214653"))), + pin: ExplicitContextTag0::from(OctetStringAsn1::from(credentials.pin.as_ref().to_vec())), csp_data: ExplicitContextTag1::from(TsCspDataDetail { - key_spec: ExplicitContextTag0::from(IntegerAsn1::from(vec![1_u8])), - card_name: Optional::from(Some(ExplicitContextTag1::from(OctetStringAsn1::from(string_to_utf16( - "VSCtest", - ))))), - reader_name: Optional::from(Some(ExplicitContextTag2::from(OctetStringAsn1::from(string_to_utf16( - "Microsoft Virtual Smart Card 0", - ))))), - container_name: Optional::from(Some(ExplicitContextTag3::from(OctetStringAsn1::from(string_to_utf16( - "te-RDPsmartcardlogon5-8ff3a38e-c6-50987", - ))))), - csp_name: Optional::from(Some(ExplicitContextTag4::from(OctetStringAsn1::from(string_to_utf16( - "Microsoft Base Smart Card Crypto Provider", - ))))), + key_spec: ExplicitContextTag0::from(IntegerAsn1::from(vec![AT_KEYEXCHANGE])), + card_name: Optional::from(Some(ExplicitContextTag1::from(OctetStringAsn1::from( + credentials.card_name.clone(), + )))), + reader_name: Optional::from(Some(ExplicitContextTag2::from(OctetStringAsn1::from( + credentials.reader_name.clone(), + )))), + container_name: Optional::from(Some(ExplicitContextTag3::from(OctetStringAsn1::from( + credentials.container_name.clone(), + )))), + csp_name: Optional::from(Some(ExplicitContextTag4::from(OctetStringAsn1::from( + credentials.csp_name.clone(), + )))), }), user_hint: Optional::from(None), domain_hint: Optional::from(None), From 2699233cabf73a8ecdf02fc164c2135cfa8884de Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 13:59:22 +0300 Subject: [PATCH 28/85] feat(sspi): auth_identity: add card_name, container_name, and csp_name fileds to the smart card creds; --- src/auth_identity.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index cfc522a2..5a9bf728 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -85,9 +85,15 @@ pub struct SmartCardIdentityBuffers { pub username: Vec, /// DER-encoded X509 certificate pub certificate: Vec, + /// UTF-16 encoded smart card name + pub card_name: Vec, /// UTF-16 encoded smart card reader name pub reader_name: Vec, - /// Smart card PIN code + /// UTF-16 encoded smart card key container name + pub container_name: Vec, + /// UTF-16 encoded smart card CSP name + pub csp_name: Vec, + /// UTF-16 encoded smart card PIN code pub pin: Secret>, } @@ -100,7 +106,13 @@ pub struct SmartCardIdentity { pub certificate: Certificate, /// Smart card reader name pub reader_name: String, - /// Smart card PIN code + /// Smart card name + pub card_name: String, + /// Smart card key container name + pub container_name: String, + /// Smart card CSP name + pub csp_name: String, + /// ASCII encoded mart card PIN code pub pin: Secret>, } @@ -113,6 +125,13 @@ impl TryFrom for SmartCardIdentityBuffers { reader_name: value.reader_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), pin: value.pin.as_ref().clone().into(), username: value.username.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + card_name: value.card_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + container_name: value + .container_name + .encode_utf16() + .flat_map(|v| v.to_be_bytes()) + .collect(), + csp_name: value.csp_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), }) } } @@ -126,6 +145,9 @@ impl TryFrom for SmartCardIdentity { reader_name: utils::bytes_to_utf16_string(&value.reader_name), pin: utils::bytes_to_utf16_string(value.pin.as_ref()).into_bytes().into(), username: utils::bytes_to_utf16_string(&value.username), + card_name: utils::bytes_to_utf16_string(&value.card_name), + container_name: utils::bytes_to_utf16_string(&value.container_name), + csp_name: utils::bytes_to_utf16_string(&value.csp_name), }) } } @@ -170,7 +192,7 @@ pub enum Credentials { AuthIdentity(AuthIdentity), #[cfg(feature = "scard")] /// Smart card identity for the smart card based authentication - SmartCard(SmartCardIdentity) + SmartCard(Box), } impl Credentials { @@ -184,7 +206,7 @@ impl Credentials { impl From for Credentials { fn from(value: SmartCardIdentity) -> Self { - Self::SmartCard(value) + Self::SmartCard(Box::new(value)) } } @@ -200,7 +222,8 @@ impl TryFrom for CredentialsBuffers { fn try_from(value: Credentials) -> Result { Ok(match value { Credentials::AuthIdentity(identity) => Self::AuthIdentity(identity.into()), - Credentials::SmartCard(identity) => Self::SmartCard(identity.try_into()?), + #[cfg(feature = "scard")] + Credentials::SmartCard(identity) => Self::SmartCard((*identity).try_into()?), }) } } From 999c400051068d00ab7017f80d0be437b1ed0000 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 14:00:39 +0300 Subject: [PATCH 29/85] fix(ffi): auth_identity: compilation error in smart card credentials; --- ffi/src/sec_winnt_auth_identity.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 719b4d43..c0af8e07 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -472,6 +472,9 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - reader_name, pin: password, username, + card_name: todo!(), + container_name: todo!(), + csp_name: todo!(), }); warn!(creds = ?creds); From eaadcb53453d5c87b919616a8a28fd2bda989394 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 14:01:19 +0300 Subject: [PATCH 30/85] feat(sspi): cert_utils: implement smart card info (key container name, reader_name, etc) extraction; --- src/cert_utils.rs | 181 +++++++++++++++++++++++++++++-- src/credssp/sspi_cred_ssp/mod.rs | 2 +- 2 files changed, 175 insertions(+), 8 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index da7b9c49..0f275727 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -1,12 +1,16 @@ -use std::{slice::from_raw_parts, ptr::null_mut}; +use std::ptr::{null, null_mut}; +use std::slice::from_raw_parts; use picky_asn1_x509::Certificate; -use sha1::{Sha1, Digest}; -use winapi::{um::wincrypt::{ - CertCloseStore, CertEnumCertificatesInStore, CertFreeCertificateContext, CertOpenStore, - CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_CURRENT_USER_ID, - CERT_SYSTEM_STORE_LOCATION_SHIFT, -}, ctypes::c_void}; +use sha1::{Digest, Sha1}; +use winapi::ctypes::c_void; +use winapi::um::ncrypt::HCRYPTKEY; +use winapi::um::wincrypt::{ + CertCloseStore, CertEnumCertificatesInStore, CertFreeCertificateContext, CertOpenStore, CryptAcquireContextW, + CryptGetKeyParam, CryptGetProvParam, CryptGetUserKey, AT_KEYEXCHANGE, CERT_STORE_PROV_SYSTEM_W, + CERT_SYSTEM_STORE_CURRENT_USER_ID, CERT_SYSTEM_STORE_LOCATION_SHIFT, CRYPT_FIRST, CRYPT_NEXT, CRYPT_SILENT, + HCRYPTPROV, KP_CERTIFICATE, PP_ENUMCONTAINERS, PP_SMARTCARD_READER, PROV_RSA_FULL, +}; use crate::{Result, Error, ErrorKind}; @@ -65,6 +69,169 @@ pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result Result { + let container_name = key_container_name + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect::>(); + let csp_name = "Microsoft Base Smart Card Crypto Provider" + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect::>(); + let mut crypt_context_handle = HCRYPTPROV::default(); + + if CryptAcquireContextW( + &mut crypt_context_handle, + container_name.as_ptr() as *const _, + csp_name.as_ptr() as *const _, + PROV_RSA_FULL, + CRYPT_SILENT, + ) == 0 + { + return Err(Error::new( + ErrorKind::InternalError, + "Cannot acquire crypt context handle.", + )); + } + + Ok(crypt_context_handle) +} + +unsafe fn get_reader_name(crypt_context_handle: HCRYPTPROV) -> Result { + let mut reader_buff_len = 0; + if CryptGetProvParam( + crypt_context_handle, + PP_SMARTCARD_READER, + null_mut(), + &mut reader_buff_len, + 0, + ) == 0 + { + return Err(Error::new(ErrorKind::InternalError, "Cannot get reader name.")); + } + + let mut reader_buff = vec![0; reader_buff_len as usize]; + if CryptGetProvParam( + crypt_context_handle, + PP_SMARTCARD_READER, + reader_buff.as_mut_ptr(), + &mut reader_buff_len, + 0, + ) == 0 + { + return Err(Error::new(ErrorKind::InternalError, "Cannot get reader name.")); + } + + String::from_utf8(reader_buff) + .map_err(|_| Error::new(ErrorKind::InternalError, "reader name is not valid UTF-8 text")) +} + +pub unsafe fn get_key_container_certificate(crypt_context_handle: HCRYPTPROV) -> Result { + let mut key = HCRYPTKEY::default(); + + if CryptGetUserKey(crypt_context_handle, AT_KEYEXCHANGE, &mut key) == 0 { + return Err(Error::new(ErrorKind::InternalError, "Cannot acquire key handle.")); + } + + let mut cert_data_len = 0; + if CryptGetKeyParam(key, KP_CERTIFICATE, null_mut(), &mut cert_data_len, 0) == 0 { + return Err(Error::new(ErrorKind::InternalError, "Cannot get certificate data len.")); + } + + let mut cert_data = vec![0; cert_data_len as usize]; + if CryptGetKeyParam(key, KP_CERTIFICATE, cert_data.as_mut_ptr(), &mut cert_data_len, 0) == 0 { + return Err(Error::new(ErrorKind::InternalError, "Cannot get certificate data.")); + } + + Ok(picky_asn1_der::from_bytes(&cert_data)?) +} + +pub struct SmartCardInfo { + pub key_container_name: String, + pub reader_name: String, + pub certificate: Certificate, +} + +pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result { + let csp_name = "Microsoft Base Smart Card Crypto Provider" + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect::>(); + + let mut crypt_context_handle = HCRYPTPROV::default(); + if CryptAcquireContextW( + &mut crypt_context_handle, + null(), + csp_name.as_ptr() as *const _, + PROV_RSA_FULL, + CRYPT_SILENT, + ) == 0 + { + return Err(Error::new( + ErrorKind::InternalError, + "Cannot acquire crypt context handle.", + )); + } + + let mut key_container_name_len = 0; + let mut is_first = true; + loop { + if CryptGetProvParam( + crypt_context_handle, + PP_ENUMCONTAINERS, + null_mut(), + &mut key_container_name_len, + if is_first { CRYPT_FIRST } else { CRYPT_NEXT }, + ) == 0 + { + break; + } + + let mut key_container_name = vec![0; key_container_name_len as usize]; + + if CryptGetProvParam( + crypt_context_handle, + PP_ENUMCONTAINERS, + key_container_name.as_mut_ptr(), + &mut key_container_name_len, + if is_first { CRYPT_FIRST } else { CRYPT_NEXT }, + ) == 0 + { + break; + } + println!( + "key container name: {:?} {:?}", + String::from_utf8(key_container_name.clone()), + key_container_name + ); + let key_container_name = String::from_utf8(key_container_name).unwrap(); + + let context = acquire_context(&key_container_name)?; + + if let Ok(certificate) = get_key_container_certificate(context) { + if certificate.tbs_certificate.serial_number.0 == cert_serial_number { + let reader_name = match get_reader_name(crypt_context_handle) { + Ok(reader_name) => reader_name, + Err(err) => { + error!(?err); + continue; + } + }; + + return Ok(SmartCardInfo { + key_container_name, + reader_name, + certificate, + }); + } + } + + is_first = false; + } + + Err(Error::new(ErrorKind::InternalError, "Cannot get smart card info")) +} + #[cfg(test)] mod tests { use super::extract_certificate_by_thumbprint; diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index fc94f7b6..7b5c0620 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -432,7 +432,7 @@ impl SspiImpl for SspiCredSsp { ts_request.auth_info = Some( self.cred_ssp_context - .encrypt_ts_credentials(credentials.as_auth_identity().unwrap(), CredSspMode::WithCredentials)?, + .encrypt_ts_credentials(credentials, CredSspMode::WithCredentials)?, ); let mut encoded_ts_request = Vec::new(); From 94077754d0ec3e3824fffaf00eb270843759743d Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 16:51:47 +0300 Subject: [PATCH 31/85] feat(sspi): cert_utils: implememt user name extraction from the smart card certificate; --- src/cert_utils.rs | 157 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 146 insertions(+), 11 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index 0f275727..571a3c13 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -1,7 +1,8 @@ use std::ptr::{null, null_mut}; use std::slice::from_raw_parts; -use picky_asn1_x509::Certificate; +use picky_asn1::wrapper::{Utf8StringAsn1, IA5StringAsn1}; +use picky_asn1_x509::{Certificate, oids, ExtensionView, GeneralName, AttributeTypeAndValueParameters, DirectoryString}; use sha1::{Digest, Sha1}; use winapi::ctypes::c_void; use winapi::um::ncrypt::HCRYPTKEY; @@ -12,24 +13,25 @@ use winapi::um::wincrypt::{ HCRYPTPROV, KP_CERTIFICATE, PP_ENUMCONTAINERS, PP_SMARTCARD_READER, PROV_RSA_FULL, }; -use crate::{Result, Error, ErrorKind}; +const CSP_NAME: &str = "Microsoft Base Smart Card Crypto Provider\0"; -unsafe fn find_cert_by_thumbprint(thumbprint: &[u8], cert_store: *mut c_void) -> Result { +use crate::{Error, ErrorKind, Result}; + +#[instrument(level = "trace", ret)] +unsafe fn find_raw_cert_by_thumbprint(thumbprint: &[u8], cert_store: *mut c_void) -> Result> { let mut certificate = CertEnumCertificatesInStore(cert_store, null_mut()); while !certificate.is_null() { let cert_der = from_raw_parts((*certificate).pbCertEncoded, (*certificate).cbCertEncoded as usize); - + let mut sha1 = Sha1::new(); sha1.update(cert_der); let cert_thumbprint = sha1.finalize().to_vec(); if cert_thumbprint == thumbprint { - let cert: Certificate = picky_asn1_der::from_bytes(cert_der)?; - CertFreeCertificateContext(certificate); - return Ok(cert); + return Ok(cert_der.to_vec()); } let next_certificate = CertEnumCertificatesInStore(cert_store, certificate); @@ -43,7 +45,7 @@ unsafe fn find_cert_by_thumbprint(thumbprint: &[u8], cert_store: *mut c_void) -> )) } -pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result { +unsafe fn open_user_cert_store() -> Result<*mut c_void> { // "My\0" encoded as a wide string. // More info: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks let my: [u16; 3] = [77, 121, 0]; @@ -62,13 +64,26 @@ pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result Result> { + let cert_store = open_user_cert_store()?; + let cert = find_raw_cert_by_thumbprint(thumbprint, cert_store)?; CertCloseStore(cert_store, 0); Ok(cert) } +#[instrument(level = "trace", ret)] +pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result<(Vec, Certificate)> { + let raw_cert = extract_raw_certificate_by_thumbprint(thumbprint)?; + + Ok((raw_cert.to_vec(), picky_asn1_der::from_bytes(&raw_cert)?)) +} + unsafe fn acquire_context(key_container_name: &str) -> Result { let container_name = key_container_name .encode_utf16() @@ -122,6 +137,9 @@ unsafe fn get_reader_name(crypt_context_handle: HCRYPTPROV) -> Result { return Err(Error::new(ErrorKind::InternalError, "Cannot get reader name.")); } + // remove null byte + reader_buff.pop(); + String::from_utf8(reader_buff) .map_err(|_| Error::new(ErrorKind::InternalError, "reader name is not valid UTF-8 text")) } @@ -146,14 +164,16 @@ pub unsafe fn get_key_container_certificate(crypt_context_handle: HCRYPTPROV) -> Ok(picky_asn1_der::from_bytes(&cert_data)?) } +#[derive(Debug)] pub struct SmartCardInfo { pub key_container_name: String, pub reader_name: String, + pub csp_name: String, pub certificate: Certificate, } pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result { - let csp_name = "Microsoft Base Smart Card Crypto Provider" + let csp_name = CSP_NAME .encode_utf16() .flat_map(|v| v.to_le_bytes()) .collect::>(); @@ -204,6 +224,8 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result Result Result { + let subject_alt_name_ext = &certificate + .tbs_certificate + .extensions + .0 + .0 + .iter() + .find(|extension| { + extension.extn_id().0 == oids::subject_alternative_name() + }) + .ok_or_else(|| Error::new(ErrorKind::IncompleteCredentials, "Subject alternative name certificate extension is not present"))? + .extn_value(); + + let alternate_name = match subject_alt_name_ext { + ExtensionView::SubjectAltName(alternate_name) => { + alternate_name + }, + // safe: checked above + _ => unreachable!(), + }; + let other_name = match alternate_name.0.get(0).unwrap() { + GeneralName::OtherName(other_name) => { + other_name + }, + _ => return Err(Error::new(ErrorKind::IncompleteCredentials, "Subject alternate name has unsupported value type")), + }; + + if other_name.type_id.0 != oids::user_principal_name() { + return Err(Error::new(ErrorKind::IncompleteCredentials, "Subject alternate name must be UPN")); + } + + let data: Utf8StringAsn1 = picky_asn1_der::from_bytes(&other_name.value.0.0)?; + Ok(data.to_string()) +} + +pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result { + let subject = &certificate + .tbs_certificate + .subject + .0 + .0; + let subject_parts = subject + .iter() + .map(|subject_part| { + let set = subject_part.0.get(0).unwrap(); + let t = set.ty.0.clone(); + let v = match &set.value { + AttributeTypeAndValueParameters::CommonName(DirectoryString::PrintableString(name)) => name.to_string(), + AttributeTypeAndValueParameters::CommonName(DirectoryString::Utf8String(name)) => name.clone(), + AttributeTypeAndValueParameters::Custom(custom) => { + let string: IA5StringAsn1 = picky_asn1_der::from_bytes(&custom.0)?; + string.to_string() + }, + _ => return Err(Error::new(ErrorKind::IncompleteCredentials, "Common name has unsupported value type")), + }; + Ok((t, v)) + }) + .collect::>>()?; + + let domain = subject_parts + .iter() + .filter(|subject_part| subject_part.0 == oids::domain_component()) + .map(|subject_part| subject_part.1.as_str()) + .rev() + .fold(String::new(), |mut domain, subject_part| { + if !domain.is_empty() { + domain.push('.'); + } + domain.push_str(subject_part); + domain + }); + + let user_name = subject_parts + .iter() + .filter(|subject_part| subject_part.0 == oids::at_common_name()) + .skip(1) + .map(|subject_part| subject_part.1.as_str()) + .next() + .ok_or_else(|| Error::new(ErrorKind::IncompleteMessage, "User name is not present in certificate common name field"))?; + + Ok(format!("{}@{}", user_name, domain)) +} + +pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result { + match extract_user_name_from_alt_name(certificate) { + Ok(user_name) => Ok(user_name), + Err(_) => extract_user_name_from_subject_name(certificate), + } +} + #[cfg(test)] mod tests { - use super::extract_certificate_by_thumbprint; + use crate::cert_utils::{finalize_smart_card_info, extract_certificate_by_thumbprint, extract_user_name_from_subject_name, extract_user_name_from_alt_name}; + + #[test] + fn smart_card_info() { + let serial_number = [126, 0, 0, 0, 15, 203, 194, 190, 102, 29, 163, 34, 144, 0, 0, 0, 0, 0, 15]; + println!("{:?}", unsafe { + finalize_smart_card_info(&serial_number).unwrap() + }); + } #[test] fn cert() { @@ -242,4 +367,14 @@ mod tests { extract_certificate_by_thumbprint(&[60, 51, 235, 194, 72, 148, 15, 37, 176, 168, 245, 241, 146, 185, 12, 11, 235, 139, 141, 82]).unwrap() }); } + + #[test] + fn username_extraction() { + let cert = unsafe { + extract_certificate_by_thumbprint(&[244, 5, 6, 138, 23, 82, 125, 87, 234, 251, 176, 71, 81, 51, 245, 207, 224, 92, 147, 141]).unwrap() + }; + // dbg!(cert.1.clone()); + println!("username: {}", extract_user_name_from_alt_name(&cert.1).unwrap()); + println!("username: {}", extract_user_name_from_subject_name(&cert.1).unwrap()); + } } From 12f50af9b1923caecaf6dcc26d4b9db3c40833f0 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 16:53:36 +0300 Subject: [PATCH 32/85] feat(ffi): auth_identity: improve credentials gathering; --- ffi/src/sec_winnt_auth_identity.rs | 81 ++++++++++++++---------------- ffi/src/utils.rs | 7 +++ 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index c0af8e07..f1041d15 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -384,10 +384,12 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { use std::ptr::null_mut; - use sspi::Secret; + use sspi::{Secret, cert_utils::{SmartCardInfo, finalize_smart_card_info}}; use winapi::um::wincred::{CertCredential, CredUnmarshalCredentialW, CERT_CREDENTIAL_INFO, CRED_MARSHAL_TYPE}; use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; + use crate::utils::str_to_utf16_bytes; + if p_auth_data.is_null() { return Err(Error::new( ErrorKind::InvalidParameter, @@ -444,53 +446,46 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - let result = CredUnmarshalCredentialW(username.as_ptr() as *const _, &mut cred_type, &mut credential); - if result == 1 { - if cred_type == CertCredential { - let cert_credential = credential.cast::(); - - let certificate = - sspi::cert_utils::extract_raw_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; - - // test credentials - // todo: use real reader name - let reader_name = "Microsoft Virtual Smart Card 0" - .encode_utf16() - .flat_map(|v| v.to_le_bytes()) - .collect(); - // todo: extract username from the certificate - let username = "pw13@example.com" - .encode_utf16() - .flat_map(|v| v.to_le_bytes()) - .collect(); - - // remove null - let new_len = password.as_ref().len() - 2; - password.as_mut().truncate(new_len); - - let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { - certificate, - reader_name, - pin: password, - username, - card_name: todo!(), - container_name: todo!(), - csp_name: todo!(), - }); - warn!(creds = ?creds); - - return Ok(creds); - } else { - return Err(Error::new( - ErrorKind::NoCredentials, - "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential", - )); - } - } else { + if result == 0 { return Err(Error::new( ErrorKind::NoCredentials, "Cannot unmarshal smart card credentials", )); } + + if cred_type != CertCredential { + return Err(Error::new( + ErrorKind::NoCredentials, + "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential", + )); + } + + let cert_credential = credential.cast::(); + + let (raw_certificate, certificate) = + sspi::cert_utils::extract_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; + + let username = str_to_utf16_bytes(sspi::cert_utils::extract_user_name_from_certificate(&certificate)?); + // test credentials + let card_name = str_to_utf16_bytes("VSCtest"); + let SmartCardInfo { key_container_name, reader_name, certificate: _, csp_name } = finalize_smart_card_info(&certificate.tbs_certificate.serial_number.0)?; + + // remove null + let new_len = password.as_ref().len() - 2; + password.as_mut().truncate(new_len); + + let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { + certificate: raw_certificate, + reader_name: str_to_utf16_bytes(reader_name), + pin: password, + username, + card_name, + container_name: str_to_utf16_bytes(key_container_name), + csp_name: str_to_utf16_bytes(csp_name), + }); + warn!(creds = ?creds); + + return Ok(creds); } let mut auth_identity_buffers = AuthIdentityBuffers::default(); diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 7e70af9b..3ceb6711 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -50,3 +50,10 @@ pub unsafe fn transform_credentials_handle<'a>( )) } } + +pub fn str_to_utf16_bytes(s: impl AsRef) -> Vec { + s.as_ref() + .encode_utf16() + .flat_map(|v| v.to_le_bytes()) + .collect() +} From 88c10621d6282230290a757d2855925979e688b3 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 17:09:14 +0300 Subject: [PATCH 33/85] feat(ffi): format code; --- ffi/src/sec_winnt_auth_identity.rs | 10 ++++++++-- ffi/src/utils.rs | 5 +---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index f1041d15..3388a084 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -384,7 +384,8 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { use std::ptr::null_mut; - use sspi::{Secret, cert_utils::{SmartCardInfo, finalize_smart_card_info}}; + use sspi::cert_utils::{finalize_smart_card_info, SmartCardInfo}; + use sspi::Secret; use winapi::um::wincred::{CertCredential, CredUnmarshalCredentialW, CERT_CREDENTIAL_INFO, CRED_MARSHAL_TYPE}; use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; @@ -468,7 +469,12 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - let username = str_to_utf16_bytes(sspi::cert_utils::extract_user_name_from_certificate(&certificate)?); // test credentials let card_name = str_to_utf16_bytes("VSCtest"); - let SmartCardInfo { key_container_name, reader_name, certificate: _, csp_name } = finalize_smart_card_info(&certificate.tbs_certificate.serial_number.0)?; + let SmartCardInfo { + key_container_name, + reader_name, + certificate: _, + csp_name, + } = finalize_smart_card_info(&certificate.tbs_certificate.serial_number.0)?; // remove null let new_len = password.as_ref().len() - 2; diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 3ceb6711..7302e518 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -52,8 +52,5 @@ pub unsafe fn transform_credentials_handle<'a>( } pub fn str_to_utf16_bytes(s: impl AsRef) -> Vec { - s.as_ref() - .encode_utf16() - .flat_map(|v| v.to_le_bytes()) - .collect() + s.as_ref().encode_utf16().flat_map(|v| v.to_le_bytes()).collect() } From 29cf34b772f555ab74f2a012b0b000d2b697a553 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 13 Aug 2023 17:10:22 +0300 Subject: [PATCH 34/85] feat(sspi): cert_utils: format code; --- src/cert_utils.rs | 79 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index 571a3c13..c4e8753c 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -1,8 +1,10 @@ use std::ptr::{null, null_mut}; use std::slice::from_raw_parts; -use picky_asn1::wrapper::{Utf8StringAsn1, IA5StringAsn1}; -use picky_asn1_x509::{Certificate, oids, ExtensionView, GeneralName, AttributeTypeAndValueParameters, DirectoryString}; +use picky_asn1::wrapper::{IA5StringAsn1, Utf8StringAsn1}; +use picky_asn1_x509::{ + oids, AttributeTypeAndValueParameters, Certificate, DirectoryString, ExtensionView, GeneralName, +}; use sha1::{Digest, Sha1}; use winapi::ctypes::c_void; use winapi::um::ncrypt::HCRYPTKEY; @@ -266,40 +268,43 @@ pub fn extract_user_name_from_alt_name(certificate: &Certificate) -> Result { - alternate_name - }, + ExtensionView::SubjectAltName(alternate_name) => alternate_name, // safe: checked above _ => unreachable!(), }; let other_name = match alternate_name.0.get(0).unwrap() { - GeneralName::OtherName(other_name) => { - other_name - }, - _ => return Err(Error::new(ErrorKind::IncompleteCredentials, "Subject alternate name has unsupported value type")), + GeneralName::OtherName(other_name) => other_name, + _ => { + return Err(Error::new( + ErrorKind::IncompleteCredentials, + "Subject alternate name has unsupported value type", + )) + } }; if other_name.type_id.0 != oids::user_principal_name() { - return Err(Error::new(ErrorKind::IncompleteCredentials, "Subject alternate name must be UPN")); + return Err(Error::new( + ErrorKind::IncompleteCredentials, + "Subject alternate name must be UPN", + )); } - let data: Utf8StringAsn1 = picky_asn1_der::from_bytes(&other_name.value.0.0)?; + let data: Utf8StringAsn1 = picky_asn1_der::from_bytes(&other_name.value.0 .0)?; Ok(data.to_string()) } pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result { - let subject = &certificate - .tbs_certificate - .subject - .0 - .0; + let subject = &certificate.tbs_certificate.subject.0 .0; let subject_parts = subject .iter() .map(|subject_part| { @@ -311,8 +316,13 @@ pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result< AttributeTypeAndValueParameters::Custom(custom) => { let string: IA5StringAsn1 = picky_asn1_der::from_bytes(&custom.0)?; string.to_string() - }, - _ => return Err(Error::new(ErrorKind::IncompleteCredentials, "Common name has unsupported value type")), + } + _ => { + return Err(Error::new( + ErrorKind::IncompleteCredentials, + "Common name has unsupported value type", + )) + } }; Ok((t, v)) }) @@ -337,7 +347,12 @@ pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result< .skip(1) .map(|subject_part| subject_part.1.as_str()) .next() - .ok_or_else(|| Error::new(ErrorKind::IncompleteMessage, "User name is not present in certificate common name field"))?; + .ok_or_else(|| { + Error::new( + ErrorKind::IncompleteMessage, + "User name is not present in certificate common name field", + ) + })?; Ok(format!("{}@{}", user_name, domain)) } @@ -351,14 +366,17 @@ pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result Date: Mon, 14 Aug 2023 11:11:01 +0300 Subject: [PATCH 35/85] feat(sspi): cert_utils: improve logging; --- src/cert_utils.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index c4e8753c..60bfce37 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -86,6 +86,7 @@ pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result<(Ve Ok((raw_cert.to_vec(), picky_asn1_der::from_bytes(&raw_cert)?)) } +#[instrument(level = "trace", ret)] unsafe fn acquire_context(key_container_name: &str) -> Result { let container_name = key_container_name .encode_utf16() @@ -114,6 +115,7 @@ unsafe fn acquire_context(key_container_name: &str) -> Result { Ok(crypt_context_handle) } +#[instrument(level = "trace", ret)] unsafe fn get_reader_name(crypt_context_handle: HCRYPTPROV) -> Result { let mut reader_buff_len = 0; if CryptGetProvParam( @@ -146,6 +148,7 @@ unsafe fn get_reader_name(crypt_context_handle: HCRYPTPROV) -> Result { .map_err(|_| Error::new(ErrorKind::InternalError, "reader name is not valid UTF-8 text")) } +#[instrument(level = "trace", ret)] pub unsafe fn get_key_container_certificate(crypt_context_handle: HCRYPTPROV) -> Result { let mut key = HCRYPTKEY::default(); @@ -174,6 +177,7 @@ pub struct SmartCardInfo { pub certificate: Certificate, } +#[instrument(level = "trace", ret)] pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result { let csp_name = CSP_NAME .encode_utf16() @@ -261,6 +265,7 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result { let subject_alt_name_ext = &certificate .tbs_certificate @@ -303,6 +308,7 @@ pub fn extract_user_name_from_alt_name(certificate: &Certificate) -> Result Result { let subject = &certificate.tbs_certificate.subject.0 .0; let subject_parts = subject From ff77833349171631d9d1b2bbbae3451bfaf1d165 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 14 Aug 2023 14:04:23 +0300 Subject: [PATCH 36/85] fix(sspi): cert_utils: smart card info finalizing; --- src/cert_utils.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index 60bfce37..55975de5 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -88,11 +88,13 @@ pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result<(Ve #[instrument(level = "trace", ret)] unsafe fn acquire_context(key_container_name: &str) -> Result { - let container_name = key_container_name + let mut container_name = key_container_name .encode_utf16() .flat_map(|v| v.to_le_bytes()) .collect::>(); - let csp_name = "Microsoft Base Smart Card Crypto Provider" + // add wire null char + container_name.extend_from_slice(&[0, 0]); + let csp_name = CSP_NAME .encode_utf16() .flat_map(|v| v.to_le_bytes()) .collect::>(); @@ -230,11 +232,15 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Date: Mon, 14 Aug 2023 14:04:48 +0300 Subject: [PATCH 37/85] fix(sspi): credssp: ts_request: smart card credentials encoding; --- src/credssp/ts_request/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index 1cb8bb06..cb194c21 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -273,14 +273,11 @@ fn write_smart_card_credentials(credentials: &SmartCardIdentityBuffers) -> crate user_hint: Optional::from(None), domain_hint: Optional::from(None), }; - let ts_creds = TsCredentials { - cred_type: ExplicitContextTag0::from(IntegerAsn1::from(vec![2])), - credentials: ExplicitContextTag1::from(OctetStringAsn1::from(picky_asn1_der::to_vec(&smart_card_creds)?)), - }; - Ok(picky_asn1_der::to_vec(&ts_creds)?) + Ok(picky_asn1_der::to_vec(&smart_card_creds)?) } +#[instrument(level = "trace", ret)] pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: CredSspMode) -> crate::Result> { let (creds_type, encoded_credentials) = match credentials { CredentialsBuffers::AuthIdentity(creds) => { @@ -294,7 +291,9 @@ pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: Cre credentials: ExplicitContextTag1::from(OctetStringAsn1::from(encoded_credentials)), }; - Ok(picky_asn1_der::to_vec(&ts_creds)?) + let encoded_creds = picky_asn1_der::to_vec(&ts_creds)?; + debug!(?encoded_creds, "encodedtscreds"); + Ok(encoded_creds) } #[instrument(ret)] From 9fd02e71c74c1c67af8aabe71e292d6be55c7e5b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 14 Aug 2023 18:15:34 +0300 Subject: [PATCH 38/85] feat(ffi): auth_identity: improve smart card creds handling; --- ffi/src/sec_winnt_auth_identity.rs | 152 +++++++++++++++++------------ 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 3388a084..2b068060 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, SmartCardIdentityBuffers}; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, SmartCardIdentityBuffers, Secret}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -239,25 +239,41 @@ pub unsafe fn auth_data_to_identity_buffers_w( (*auth_data).package_list_length as usize, ))); } + let user = raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2); + let password = raw_str_into_bytes( + (*auth_data).password as *const _, + (*auth_data).password_length as usize * 2, + ).into(); + + // only marshaled smart card creds starts with '@' char + #[cfg(feature = "scard")] + if user[0] == b'@' { + return handle_smart_card_creds(user, password); + } + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { - user: raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2), + user, domain: raw_str_into_bytes((*auth_data).domain as *const _, (*auth_data).domain_length as usize * 2), - password: raw_str_into_bytes( - (*auth_data).password as *const _, - (*auth_data).password_length as usize * 2, - ) - .into(), + password, })) } else { let auth_data = p_auth_data.cast::(); + let user = raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2); + let password = raw_str_into_bytes( + (*auth_data).password as *const _, + (*auth_data).password_length as usize * 2, + ).into(); + + // only marshaled smart card creds starts with '@' char + #[cfg(feature = "scard")] + if user[0] == b'@' { + return handle_smart_card_creds(user, password); + } + Ok(CredentialsBuffers::AuthIdentity(AuthIdentityBuffers { - user: raw_str_into_bytes((*auth_data).user as *const _, (*auth_data).user_length as usize * 2), + user, domain: raw_str_into_bytes((*auth_data).domain as *const _, (*auth_data).domain_length as usize * 2), - password: raw_str_into_bytes( - (*auth_data).password as *const _, - (*auth_data).password_length as usize * 2, - ) - .into(), + password, })) } } @@ -380,17 +396,70 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu )) } -#[cfg(target_os = "windows")] -pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { +#[cfg(feature = "scard")] +#[instrument(level = "trace", ret)] +unsafe fn handle_smart_card_creds(username: Vec, mut password: Secret>) -> Result { use std::ptr::null_mut; use sspi::cert_utils::{finalize_smart_card_info, SmartCardInfo}; - use sspi::Secret; - use winapi::um::wincred::{CertCredential, CredUnmarshalCredentialW, CERT_CREDENTIAL_INFO, CRED_MARSHAL_TYPE}; - use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; + use winapi::um::wincred::{CertCredential, CredUnmarshalCredentialW, CERT_CREDENTIAL_INFO}; use crate::utils::str_to_utf16_bytes; + let mut cred_type = 0; + let mut credential = null_mut(); + + let result = CredUnmarshalCredentialW(username.as_ptr() as *const _, &mut cred_type, &mut credential); + + if result == 0 { + return Err(Error::new( + ErrorKind::NoCredentials, + "Cannot unmarshal smart card credentials", + )); + } + + if cred_type != CertCredential { + return Err(Error::new( + ErrorKind::NoCredentials, + "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential", + )); + } + + let cert_credential = credential.cast::(); + + let (raw_certificate, certificate) = + sspi::cert_utils::extract_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; + + let username = str_to_utf16_bytes(sspi::cert_utils::extract_user_name_from_certificate(&certificate)?); + // test credentials + let card_name = str_to_utf16_bytes("VSCtest"); + let SmartCardInfo { + key_container_name, + reader_name, + certificate: _, + csp_name, + } = finalize_smart_card_info(&certificate.tbs_certificate.serial_number.0)?; + + let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { + certificate: raw_certificate, + reader_name: str_to_utf16_bytes(reader_name), + pin: password.into(), + username, + card_name, + container_name: str_to_utf16_bytes(key_container_name), + csp_name: str_to_utf16_bytes(csp_name), + }); + warn!(creds = ?creds); + + Ok(creds) +} + +#[cfg(target_os = "windows")] +pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) -> Result { + use std::ptr::null_mut; + + use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferW, CRED_PACK_PROTECTED_CREDENTIALS}; + if p_auth_data.is_null() { return Err(Error::new( ErrorKind::InvalidParameter, @@ -442,56 +511,11 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - // only marshaled smart card creds starts with '@' char if username[0] == b'@' { - let mut cred_type = 0; - let mut credential = null_mut(); - - let result = CredUnmarshalCredentialW(username.as_ptr() as *const _, &mut cred_type, &mut credential); - - if result == 0 { - return Err(Error::new( - ErrorKind::NoCredentials, - "Cannot unmarshal smart card credentials", - )); - } - - if cred_type != CertCredential { - return Err(Error::new( - ErrorKind::NoCredentials, - "Unmarshalled credentials is not CRED_MARSHAL_TYPE::CertCredential", - )); - } - - let cert_credential = credential.cast::(); - - let (raw_certificate, certificate) = - sspi::cert_utils::extract_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; - - let username = str_to_utf16_bytes(sspi::cert_utils::extract_user_name_from_certificate(&certificate)?); - // test credentials - let card_name = str_to_utf16_bytes("VSCtest"); - let SmartCardInfo { - key_container_name, - reader_name, - certificate: _, - csp_name, - } = finalize_smart_card_info(&certificate.tbs_certificate.serial_number.0)?; - // remove null let new_len = password.as_ref().len() - 2; password.as_mut().truncate(new_len); - let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { - certificate: raw_certificate, - reader_name: str_to_utf16_bytes(reader_name), - pin: password, - username, - card_name, - container_name: str_to_utf16_bytes(key_container_name), - csp_name: str_to_utf16_bytes(csp_name), - }); - warn!(creds = ?creds); - - return Ok(creds); + return handle_smart_card_creds(username, password); } let mut auth_identity_buffers = AuthIdentityBuffers::default(); From 4f46ddd574f6cf853b27a98bd2a8350374e66185 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 00:55:04 +0300 Subject: [PATCH 39/85] feat(sspi): auth_identity: add private_key_file_index field; --- src/auth_identity.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index 5a9bf728..717df718 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -95,6 +95,8 @@ pub struct SmartCardIdentityBuffers { pub csp_name: Vec, /// UTF-16 encoded smart card PIN code pub pin: Secret>, + /// Private key file index + pub private_key_file_index: u8, } /// Represents data needed for smart card authentication @@ -114,6 +116,8 @@ pub struct SmartCardIdentity { pub csp_name: String, /// ASCII encoded mart card PIN code pub pin: Secret>, + /// Private key file index + pub private_key_file_index: u8, } impl TryFrom for SmartCardIdentityBuffers { @@ -132,6 +136,7 @@ impl TryFrom for SmartCardIdentityBuffers { .flat_map(|v| v.to_be_bytes()) .collect(), csp_name: value.csp_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + private_key_file_index: value.private_key_file_index, }) } } @@ -148,6 +153,7 @@ impl TryFrom for SmartCardIdentity { card_name: utils::bytes_to_utf16_string(&value.card_name), container_name: utils::bytes_to_utf16_string(&value.container_name), csp_name: utils::bytes_to_utf16_string(&value.csp_name), + private_key_file_index: value.private_key_file_index, }) } } From bf5685077d32b3ad6715740996d83938459eda54 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 00:55:52 +0300 Subject: [PATCH 40/85] feat(sspi): cert_utils: implement private_key_file_index calculation; --- src/cert_utils.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index 55975de5..50968160 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -177,6 +177,7 @@ pub struct SmartCardInfo { pub reader_name: String, pub csp_name: String, pub certificate: Certificate, + pub private_key_file_index: u8, } #[instrument(level = "trace", ret)] @@ -203,6 +204,7 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result Date: Tue, 15 Aug 2023 00:57:49 +0300 Subject: [PATCH 41/85] feat(ffi): auth_identity: improve smart card creds handling; --- ffi/src/sec_winnt_auth_identity.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 2b068060..e376b95b 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,7 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, SmartCardIdentityBuffers, Secret}; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, Secret, SmartCardIdentityBuffers}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -243,7 +243,8 @@ pub unsafe fn auth_data_to_identity_buffers_w( let password = raw_str_into_bytes( (*auth_data).password as *const _, (*auth_data).password_length as usize * 2, - ).into(); + ) + .into(); // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] @@ -262,7 +263,8 @@ pub unsafe fn auth_data_to_identity_buffers_w( let password = raw_str_into_bytes( (*auth_data).password as *const _, (*auth_data).password_length as usize * 2, - ).into(); + ) + .into(); // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] @@ -311,7 +313,6 @@ unsafe fn get_sec_winnt_auth_identity_ex2_size(p_auth_data: *const c_void) -> u3 pub unsafe fn unpack_sec_winnt_auth_identity_ex2_a(p_auth_data: *const c_void) -> Result { use std::ptr::null_mut; - use sspi::Secret; use windows_sys::Win32::Security::Credentials::{CredUnPackAuthenticationBufferA, CRED_PACK_PROTECTED_CREDENTIALS}; if p_auth_data.is_null() { @@ -398,7 +399,7 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu #[cfg(feature = "scard")] #[instrument(level = "trace", ret)] -unsafe fn handle_smart_card_creds(username: Vec, mut password: Secret>) -> Result { +unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) -> Result { use std::ptr::null_mut; use sspi::cert_utils::{finalize_smart_card_info, SmartCardInfo}; @@ -438,6 +439,7 @@ unsafe fn handle_smart_card_creds(username: Vec, mut password: Secret, mut password: Secret Date: Tue, 15 Aug 2023 11:33:07 +0300 Subject: [PATCH 42/85] feat(sspi): auth_identity: smart card: make scard name optional; --- src/auth_identity.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index 717df718..fcbc5197 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -86,7 +86,7 @@ pub struct SmartCardIdentityBuffers { /// DER-encoded X509 certificate pub certificate: Vec, /// UTF-16 encoded smart card name - pub card_name: Vec, + pub card_name: Option>, /// UTF-16 encoded smart card reader name pub reader_name: Vec, /// UTF-16 encoded smart card key container name @@ -109,7 +109,7 @@ pub struct SmartCardIdentity { /// Smart card reader name pub reader_name: String, /// Smart card name - pub card_name: String, + pub card_name: Option, /// Smart card key container name pub container_name: String, /// Smart card CSP name @@ -129,7 +129,9 @@ impl TryFrom for SmartCardIdentityBuffers { reader_name: value.reader_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), pin: value.pin.as_ref().clone().into(), username: value.username.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), - card_name: value.card_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + card_name: value + .card_name + .map(|name| name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect()), container_name: value .container_name .encode_utf16() @@ -150,7 +152,7 @@ impl TryFrom for SmartCardIdentity { reader_name: utils::bytes_to_utf16_string(&value.reader_name), pin: utils::bytes_to_utf16_string(value.pin.as_ref()).into_bytes().into(), username: utils::bytes_to_utf16_string(&value.username), - card_name: utils::bytes_to_utf16_string(&value.card_name), + card_name: value.card_name.map(|name| utils::bytes_to_utf16_string(&name)), container_name: utils::bytes_to_utf16_string(&value.container_name), csp_name: utils::bytes_to_utf16_string(&value.csp_name), private_key_file_index: value.private_key_file_index, From bb9f5d05771a357566e62531193168647acb5ab4 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 11:33:37 +0300 Subject: [PATCH 43/85] feat(sspi): credssp: improve smart card creds writing; --- src/credssp/ts_request/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index cb194c21..d5a0a4c7 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -257,9 +257,12 @@ fn write_smart_card_credentials(credentials: &SmartCardIdentityBuffers) -> crate pin: ExplicitContextTag0::from(OctetStringAsn1::from(credentials.pin.as_ref().to_vec())), csp_data: ExplicitContextTag1::from(TsCspDataDetail { key_spec: ExplicitContextTag0::from(IntegerAsn1::from(vec![AT_KEYEXCHANGE])), - card_name: Optional::from(Some(ExplicitContextTag1::from(OctetStringAsn1::from( - credentials.card_name.clone(), - )))), + card_name: Optional::from( + credentials + .card_name + .clone() + .map(|name| ExplicitContextTag1::from(OctetStringAsn1::from(name))), + ), reader_name: Optional::from(Some(ExplicitContextTag2::from(OctetStringAsn1::from( credentials.reader_name.clone(), )))), From 055f2e645ee4e39dcacb74dc0fa2124116134c40 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 11:34:07 +0300 Subject: [PATCH 44/85] feat(ffi): auth_identity: improve smart card credentials handlings; --- ffi/src/sec_winnt_auth_identity.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index e376b95b..56e974c5 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -432,8 +432,6 @@ unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) sspi::cert_utils::extract_certificate_by_thumbprint(&(*cert_credential).rgbHashOfCert)?; let username = str_to_utf16_bytes(sspi::cert_utils::extract_user_name_from_certificate(&certificate)?); - // test credentials - let card_name = str_to_utf16_bytes("VSCtest"); let SmartCardInfo { key_container_name, reader_name, @@ -447,7 +445,7 @@ unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) reader_name: str_to_utf16_bytes(reader_name), pin: password.into(), username, - card_name, + card_name: None, container_name: str_to_utf16_bytes(key_container_name), csp_name: str_to_utf16_bytes(csp_name), private_key_file_index, From df7d1f0354d154d0606d7504cf087a1fe52a20a4 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 11:47:15 +0300 Subject: [PATCH 45/85] feat(ffi): fix clippy warnings; --- ffi/src/sec_winnt_auth_identity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 56e974c5..4a5dd0a8 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -443,7 +443,7 @@ unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) let creds = CredentialsBuffers::SmartCard(SmartCardIdentityBuffers { certificate: raw_certificate, reader_name: str_to_utf16_bytes(reader_name), - pin: password.into(), + pin: password, username, card_name: None, container_name: str_to_utf16_bytes(key_container_name), From fdbc5fcac0897b3011941f95d03fc666835245be Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 11:47:27 +0300 Subject: [PATCH 46/85] feat(sspi): negotiate: fix clippy warnings; --- src/negotiate.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/negotiate.rs b/src/negotiate.rs index 5521df58..fcf42cae 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -508,7 +508,7 @@ impl SspiImpl for Negotiate { match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { let mut creds_handle = if let Some(creds_handle) = &builder.credentials_handle { - creds_handle.as_ref().map(|c| c.clone().auth_identity()).flatten() + creds_handle.as_ref().and_then(|c| c.clone().auth_identity()) } else { None }; @@ -518,7 +518,7 @@ impl SspiImpl for Negotiate { NegotiatedProtocol::Kerberos(kerberos) => kerberos.accept_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { let mut creds_handle = if let Some(creds_handle) = &builder.credentials_handle { - creds_handle.as_ref().map(|c| c.clone().auth_identity()).flatten() + creds_handle.as_ref().and_then(|c| c.clone().auth_identity()) } else { None }; From 0880fc08b3d886a3b43f70337e96a7d9047b4e21 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 11:48:04 +0300 Subject: [PATCH 47/85] feat(sspi): builders: fix clippy warnings; --- src/builders/init_sec_context.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/builders/init_sec_context.rs b/src/builders/init_sec_context.rs index ae133829..3ae19484 100644 --- a/src/builders/init_sec_context.rs +++ b/src/builders/init_sec_context.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::mem; use chrono::NaiveDateTime; @@ -93,9 +94,9 @@ impl< credentials_handle, context_requirements: self.context_requirements, target_data_representation: self.target_data_representation, - output: std::mem::take(&mut self.output), - target_name: self.target_name.clone(), - input: std::mem::take(&mut self.input), + output: mem::take(&mut self.output), + target_name: self.target_name, + input: mem::take(&mut self.input), } } } From 6de545bb386effe409e8cbcc1d2aafaf88db3f4b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 12:47:10 +0300 Subject: [PATCH 48/85] fix(sspi): cert_utils: private_key_index calculation; --- src/cert_utils.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index 50968160..bed584a2 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -92,7 +92,7 @@ unsafe fn acquire_context(key_container_name: &str) -> Result { .encode_utf16() .flat_map(|v| v.to_le_bytes()) .collect::>(); - // add wire null char + // add wide null char container_name.extend_from_slice(&[0, 0]); let csp_name = CSP_NAME .encode_utf16() @@ -241,6 +241,7 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result Date: Tue, 15 Aug 2023 12:48:08 +0300 Subject: [PATCH 49/85] fix(ffi): auth_identity: smart card creds handling: add null byte to the username; --- ffi/src/sec_winnt_auth_identity.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 4a5dd0a8..71883b79 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -399,7 +399,7 @@ pub fn unpack_sec_winnt_auth_identity_ex2_w(_p_auth_data: *const c_void) -> Resu #[cfg(feature = "scard")] #[instrument(level = "trace", ret)] -unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) -> Result { +unsafe fn handle_smart_card_creds(mut username: Vec, password: Secret>) -> Result { use std::ptr::null_mut; use sspi::cert_utils::{finalize_smart_card_info, SmartCardInfo}; @@ -410,6 +410,9 @@ unsafe fn handle_smart_card_creds(username: Vec, password: Secret>) let mut cred_type = 0; let mut credential = null_mut(); + // all wide null char + username.extend_from_slice(&[0, 0]); + let result = CredUnmarshalCredentialW(username.as_ptr() as *const _, &mut cred_type, &mut credential); if result == 0 { From d7d949a081ab892515d8b8608b34adc68f43d790 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:10:06 +0300 Subject: [PATCH 50/85] feat(sspi): return error from SspiEx::custom_set_auth_identity method; --- src/lib.rs | 3 ++- tests/common.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0664bd82..22ee40f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,7 @@ use picky_asn1_x509::Certificate; use picky_krb::gss_api::GssApiMessageError; use picky_krb::messages::KrbError; use utils::map_keb_error_code_to_sspi_error; +pub use utils::string_to_utf16; pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, SmartCardIdentity, SmartCardIdentityBuffers, Credentials}; use self::builders::{ @@ -1022,7 +1023,7 @@ pub trait SspiEx where Self: Sized + SspiImpl, { - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData); + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()>; } pub type SspiPackage<'a, CredsHandle, AuthData> = diff --git a/tests/common.rs b/tests/common.rs index f2e76b9a..b14fbb13 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -160,7 +160,7 @@ where { let ContextNames { username, domain } = server.query_context_names()?; let auth_data = credentials_proxy.auth_data_by_user(username, domain)?; - server.custom_set_auth_identity(auth_data); + server.custom_set_auth_identity(auth_data).unwrap(); let mut token = Vec::new(); server.complete_auth_token(&mut token)?; From 622e6d4f2fe1bfebfa104a6b61f0115a9efba2ae Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:11:02 +0300 Subject: [PATCH 51/85] feat(sspi): utils: improve string_to_utf16 function; --- src/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 2b93c867..fafd6008 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,8 +6,9 @@ use rand::Rng; use crate::kerberos::EncryptionParams; use crate::{Error, ErrorKind, Result}; -pub fn string_to_utf16(value: &str) -> Vec { +pub fn string_to_utf16(value: impl AsRef) -> Vec { value + .as_ref() .encode_utf16() .flat_map(|i| i.to_le_bytes().to_vec()) .collect::>() From 75edea334872ae01db63440c49664738e9bbd55d Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:12:09 +0300 Subject: [PATCH 52/85] feat(sspi): auth_identity: add more doc commets. improve conversion; --- src/auth_identity.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index fcbc5197..5e9c4b8f 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -96,7 +96,8 @@ pub struct SmartCardIdentityBuffers { /// UTF-16 encoded smart card PIN code pub pin: Secret>, /// Private key file index - pub private_key_file_index: u8, + /// This value is used for the APDU message generation + pub private_key_file_index: Option, } /// Represents data needed for smart card authentication @@ -117,7 +118,8 @@ pub struct SmartCardIdentity { /// ASCII encoded mart card PIN code pub pin: Secret>, /// Private key file index - pub private_key_file_index: u8, + /// This value is used for the APDU message generation + pub private_key_file_index: Option, } impl TryFrom for SmartCardIdentityBuffers { @@ -126,18 +128,12 @@ impl TryFrom for SmartCardIdentityBuffers { fn try_from(value: SmartCardIdentity) -> Result { Ok(Self { certificate: picky_asn1_der::to_vec(&value.certificate)?, - reader_name: value.reader_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + reader_name: utils::string_to_utf16(value.reader_name), pin: value.pin.as_ref().clone().into(), - username: value.username.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), - card_name: value - .card_name - .map(|name| name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect()), - container_name: value - .container_name - .encode_utf16() - .flat_map(|v| v.to_be_bytes()) - .collect(), - csp_name: value.csp_name.encode_utf16().flat_map(|v| v.to_be_bytes()).collect(), + username: utils::string_to_utf16(value.username), + card_name: value.card_name.map(utils::string_to_utf16), + container_name: utils::string_to_utf16(value.container_name), + csp_name: utils::string_to_utf16(value.csp_name), private_key_file_index: value.private_key_file_index, }) } From c34550552ec71fc0b218e8b777886145ebb47799 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:13:19 +0300 Subject: [PATCH 53/85] feat(sspi): credssp: small refactoring; --- src/credssp/mod.rs | 53 +++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index 85593d64..45b976fe 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -503,11 +503,14 @@ impl> CredSspServer { .map_err(|e| crate::Error::new(crate::ErrorKind::LogonDenied, e.to_string())), ts_request ); - self.context - .as_mut() - .unwrap() - .sspi_context - .custom_set_auth_identity(Credentials::AuthIdentity(auth_data)); + try_cred_ssp_server!( + self.context + .as_mut() + .unwrap() + .sspi_context + .custom_set_auth_identity(Credentials::AuthIdentity(auth_data)), + ts_request + ); try_cred_ssp_server!( self.context.as_mut().unwrap().sspi_context.complete_auth_token(&mut []), @@ -607,8 +610,13 @@ impl SspiImpl for SspiContext { } else { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; - builder.full_transform(ntlm, Some(auth_identity)).execute()?.transform(&|a: Option| a.map(|c| CredentialsBuffers::AuthIdentity(c))) - }, + builder + .full_transform(ntlm, Some(auth_identity)) + .execute()? + .transform_credentials_handle(&|a: Option| { + a.map(CredentialsBuffers::AuthIdentity) + }) + } SspiContext::Kerberos(kerberos) => builder.transform(kerberos).execute()?, SspiContext::Negotiate(negotiate) => builder.transform(negotiate).execute()?, SspiContext::Pku2u(pku2u) => { @@ -617,8 +625,13 @@ impl SspiImpl for SspiContext { } else { return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); }; - builder.full_transform(pku2u, Some(auth_identity)).execute()?.transform(&|a: Option| a.map(|c| CredentialsBuffers::AuthIdentity(c))) - }, + builder + .full_transform(pku2u, Some(auth_identity)) + .execute()? + .transform_credentials_handle(&|a: Option| { + a.map(CredentialsBuffers::AuthIdentity) + }) + } #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => builder.transform(credssp).execute()?, }) @@ -841,17 +854,23 @@ impl Sspi for SspiContext { } impl SspiEx for SspiContext { - #[instrument(level = "trace", ret, fields(security_package = self.package_name()), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { + // #[instrument(level = "trace", ret, fields(security_package = self.package_name()), skip(self))] + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> crate::Result<()> { match self { - SspiContext::Ntlm(ntlm) => { - ntlm.custom_set_auth_identity(identity.auth_identity().unwrap()) - }, + SspiContext::Ntlm(ntlm) => ntlm.custom_set_auth_identity(identity.auth_identity().ok_or_else(|| { + Error::new( + ErrorKind::IncompleteCredentials, + "Provided credentials are not password-based", + ) + })?), SspiContext::Kerberos(kerberos) => kerberos.custom_set_auth_identity(identity), SspiContext::Negotiate(negotiate) => negotiate.custom_set_auth_identity(identity), - SspiContext::Pku2u(pku2u) => { - pku2u.custom_set_auth_identity(identity.auth_identity().unwrap()) - }, + SspiContext::Pku2u(pku2u) => pku2u.custom_set_auth_identity(identity.auth_identity().ok_or_else(|| { + Error::new( + ErrorKind::IncompleteCredentials, + "Provided credentials are not password-based", + ) + })?), #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => credssp.custom_set_auth_identity(identity), } From 070739400512b6986f7e38b1b2619a684ab5b084 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 16 Aug 2023 14:57:51 +0300 Subject: [PATCH 54/85] fix(sspi): pku2u: return Result from custom_set_auth_identity function; --- src/pku2u/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pku2u/mod.rs b/src/pku2u/mod.rs index f7ebcdb8..b0c3106f 100644 --- a/src/pku2u/mod.rs +++ b/src/pku2u/mod.rs @@ -801,8 +801,10 @@ impl SspiImpl for Pku2u { impl SspiEx for Pku2u { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { self.auth_identity = Some(identity.into()); + + Ok(()) } } From dd3f9c97ab695382bc66001a100bc19afeab64cc Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 16 Aug 2023 14:59:10 +0300 Subject: [PATCH 55/85] fix(sspi): kerberos: return Result from custom_set_auth_identity function; --- src/kerberos/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 9ef07582..84f8bf43 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -855,8 +855,10 @@ impl SspiImpl for Kerberos { impl SspiEx for Kerberos { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { - self.auth_identity = Some(identity.try_into().unwrap()); + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { + self.auth_identity = Some(identity.try_into()?); + + Ok(()) } } From 0614542a48efe447c5ca9b14f8ed4351af62c2d2 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:15:57 +0300 Subject: [PATCH 56/85] feat(sspi): negotiate: small refactoring; --- src/negotiate.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/negotiate.rs b/src/negotiate.rs index fcf42cae..047e577b 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -289,15 +289,27 @@ impl Negotiate { impl SspiEx for Negotiate { #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { self.auth_identity = Some(identity.clone().try_into().unwrap()); match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { - pku2u.custom_set_auth_identity(identity.auth_identity().unwrap()) - }, + pku2u.custom_set_auth_identity(identity.auth_identity().ok_or_else(|| { + Error::new( + ErrorKind::IncompleteCredentials, + "Provided credentials are not password-based", + ) + })?) + } NegotiatedProtocol::Kerberos(kerberos) => kerberos.custom_set_auth_identity(identity), - NegotiatedProtocol::Ntlm(ntlm) => ntlm.custom_set_auth_identity(identity.auth_identity().unwrap()), + NegotiatedProtocol::Ntlm(ntlm) => { + ntlm.custom_set_auth_identity(identity.auth_identity().ok_or_else(|| { + Error::new( + ErrorKind::IncompleteCredentials, + "Provided credentials are not password-based", + ) + })?) + } } } } @@ -500,7 +512,7 @@ impl SspiImpl for Negotiate { } } - // #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] + #[instrument(ret, fields(protocol = self.protocol.protocol_name()), skip_all)] fn accept_security_context_impl<'a>( &'a mut self, builder: builders::FilledAcceptSecurityContext<'a, Self::AuthenticationData, Self::CredentialsHandle>, From 8f02d9c17bc155fdedfa35c07b0adcdceba8a464 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:16:40 +0300 Subject: [PATCH 57/85] feat(sspi): builders: small refactoring. add more comments; --- src/builders/acq_cred_handle.rs | 9 ++++++--- src/builders/init_sec_context.rs | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/builders/acq_cred_handle.rs b/src/builders/acq_cred_handle.rs index 99cbae17..c53adb96 100644 --- a/src/builders/acq_cred_handle.rs +++ b/src/builders/acq_cred_handle.rs @@ -19,9 +19,12 @@ pub struct AcquireCredentialsHandleResult { } // we cannot replace it with the `From` trait implementation due to conflict with blanked impl in the std -impl AcquireCredentialsHandleResult { - pub fn transform(self, transformer: &dyn Fn(T) -> T2) -> AcquireCredentialsHandleResult { - let Self { credentials_handle, expiry } = self; +impl AcquireCredentialsHandleResult { + pub fn transform_credentials_handle(self, transformer: &dyn Fn(T) -> T2) -> AcquireCredentialsHandleResult { + let Self { + credentials_handle, + expiry, + } = self; AcquireCredentialsHandleResult { credentials_handle: transformer(credentials_handle), expiry, diff --git a/src/builders/init_sec_context.rs b/src/builders/init_sec_context.rs index 3ae19484..a6b2c3f8 100644 --- a/src/builders/init_sec_context.rs +++ b/src/builders/init_sec_context.rs @@ -81,9 +81,18 @@ impl< ContextRequirementsSet: ToAssign, TargetDataRepresentationSet: ToAssign, OutputSet: ToAssign, - > InitializeSecurityContext< - 'a, CredsHandle, CredsHandleSet, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet, - > { + > + InitializeSecurityContext< + 'a, + CredsHandle, + CredsHandleSet, + ContextRequirementsSet, + TargetDataRepresentationSet, + OutputSet, + > +{ + /// Creates a new builder with the other `AuthData` and `CredsHandle` types. + /// References to the input and output buffers will be moved to the created builder leaving the `None` instead. pub fn full_transform(&mut self, credentials_handle: Option<&'b mut CredsHandle2>) -> InitializeSecurityContext<'b, CredsHandle2, CredHandleSet2, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet> { InitializeSecurityContext { phantom_creds_use_set: PhantomData, From 1171daa30b679b14c52915fd3ead3c9cf8ea6bd3 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:16:59 +0300 Subject: [PATCH 58/85] feat(sspi): ntlm: small refactoring; --- src/ntlm/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ntlm/mod.rs b/src/ntlm/mod.rs index 52a21274..f57bb1f8 100644 --- a/src/ntlm/mod.rs +++ b/src/ntlm/mod.rs @@ -453,8 +453,10 @@ impl Sspi for Ntlm { impl SspiEx for Ntlm { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> crate::Result<()> { self.identity = Some(identity.into()); + + Ok(()) } } From 30ccb2d442399256d2c3cc34830ae2f93989231d Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:17:38 +0300 Subject: [PATCH 59/85] feat(sspi): credssp: small refactoring. improve conditional compilation; --- src/credssp/sspi_cred_ssp/mod.rs | 11 +++++++++-- src/credssp/ts_request/mod.rs | 26 ++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index 7b5c0620..f6020ee1 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -300,6 +300,11 @@ impl SspiImpl for SspiCredSsp { builder: &mut builders::FilledInitializeSecurityContext<'a, Self::CredentialsHandle>, ) -> Result { trace!(?builder); + // In the CredSSP we always set DELEGATE flag + // + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cssp/e36b36f6-edf4-4df1-9905-9e53b7d7c7b7 + // The CredSSP Protocol enables an application to securely delegate a user's credentials from a client to a target server. + builder.context_requirements |= ClientRequestFlags::DELEGATE; let status = match &self.state { CredSspState::Tls => { @@ -476,7 +481,9 @@ impl SspiImpl for SspiCredSsp { impl SspiEx for SspiCredSsp { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) { - self.auth_identity = Some(identity.try_into().unwrap()); + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { + self.auth_identity = Some(identity.try_into()?); + + Ok(()) } } diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index d5a0a4c7..fb2a40c2 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -5,14 +5,24 @@ use core::fmt; use std::io::{self, Read}; use picky_asn1::wrapper::{ - ExplicitContextTag0, ExplicitContextTag1, ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, - IntegerAsn1, OctetStringAsn1, Optional, + ExplicitContextTag0, ExplicitContextTag1, + IntegerAsn1, OctetStringAsn1, }; -use picky_krb::constants::cred_ssp::{AT_KEYEXCHANGE, TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; -use picky_krb::credssp::{TsCredentials, TsCspDataDetail, TsPasswordCreds, TsSmartCardCreds}; +#[cfg(feature = "scard")] +use picky_asn1::wrapper::{ + ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, Optional, +}; +use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; +#[cfg(feature = "scard")] +use picky_krb::constants::cred_ssp::AT_KEYEXCHANGE; +use picky_krb::credssp::{TsCredentials, TsPasswordCreds}; +#[cfg(feature = "scard")] +use picky_krb::credssp::{TsCspDataDetail, TsSmartCardCreds}; use super::CredSspMode; -use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, SmartCardIdentityBuffers}; +use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind}; +#[cfg(feature = "scard")] +use crate::SmartCardIdentityBuffers; pub const TS_REQUEST_VERSION: u32 = 6; @@ -252,6 +262,7 @@ impl TsRequest { } #[instrument(ret)] +#[cfg(feature = "scard")] fn write_smart_card_credentials(credentials: &SmartCardIdentityBuffers) -> crate::Result> { let smart_card_creds = TsSmartCardCreds { pin: ExplicitContextTag0::from(OctetStringAsn1::from(credentials.pin.as_ref().to_vec())), @@ -286,6 +297,7 @@ pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: Cre CredentialsBuffers::AuthIdentity(creds) => { (TS_PASSWORD_CREDS, write_password_credentials(creds, cred_ssp_mode)?) } + #[cfg(feature = "scard")] CredentialsBuffers::SmartCard(creds) => (TS_SMART_CARD_CREDS, write_smart_card_credentials(creds)?), }; @@ -294,9 +306,7 @@ pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: Cre credentials: ExplicitContextTag1::from(OctetStringAsn1::from(encoded_credentials)), }; - let encoded_creds = picky_asn1_der::to_vec(&ts_creds)?; - debug!(?encoded_creds, "encodedtscreds"); - Ok(encoded_creds) + Ok(picky_asn1_der::to_vec(&ts_creds)?) } #[instrument(ret)] From 3b086f4772aaa759202212dc451ba935b0e49831 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:19:45 +0300 Subject: [PATCH 60/85] feat(sspi): cert_utils: code and tests refactoring. add more comments. remove unneeded logs; --- src/cert_utils.rs | 117 +++++++++++++++++++++---------------------- test_assets/pw11.cer | 34 +++++++++++++ 2 files changed, 91 insertions(+), 60 deletions(-) create mode 100644 test_assets/pw11.cer diff --git a/src/cert_utils.rs b/src/cert_utils.rs index bed584a2..da0211a4 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -10,12 +10,18 @@ use winapi::ctypes::c_void; use winapi::um::ncrypt::HCRYPTKEY; use winapi::um::wincrypt::{ CertCloseStore, CertEnumCertificatesInStore, CertFreeCertificateContext, CertOpenStore, CryptAcquireContextW, - CryptGetKeyParam, CryptGetProvParam, CryptGetUserKey, AT_KEYEXCHANGE, CERT_STORE_PROV_SYSTEM_W, - CERT_SYSTEM_STORE_CURRENT_USER_ID, CERT_SYSTEM_STORE_LOCATION_SHIFT, CRYPT_FIRST, CRYPT_NEXT, CRYPT_SILENT, - HCRYPTPROV, KP_CERTIFICATE, PP_ENUMCONTAINERS, PP_SMARTCARD_READER, PROV_RSA_FULL, + CryptDestroyKey, CryptGetKeyParam, CryptGetProvParam, CryptGetUserKey, CryptReleaseContext, AT_KEYEXCHANGE, + CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_CURRENT_USER_ID, CERT_SYSTEM_STORE_LOCATION_SHIFT, CRYPT_FIRST, + CRYPT_NEXT, CRYPT_SILENT, HCRYPTPROV, KP_CERTIFICATE, PP_ENUMCONTAINERS, PP_SMARTCARD_READER, PROV_RSA_FULL, }; -const CSP_NAME: &str = "Microsoft Base Smart Card Crypto Provider\0"; +// UTF-16 encoded "Microsoft Base Smart Card Crypto Provider\0" +const CSP_NAME_W: &[u8] = &[ + 77, 0, 105, 0, 99, 0, 114, 0, 111, 0, 115, 0, 111, 0, 102, 0, 116, 0, 32, 0, 66, 0, 97, 0, 115, 0, 101, 0, 32, 0, + 83, 0, 109, 0, 97, 0, 114, 0, 116, 0, 32, 0, 67, 0, 97, 0, 114, 0, 100, 0, 32, 0, 67, 0, 114, 0, 121, 0, 112, 0, + 116, 0, 111, 0, 32, 0, 80, 0, 114, 0, 111, 0, 118, 0, 105, 0, 100, 0, 101, 0, 114, 0, 0, 0, +]; +const CSP_NAME: &str = "Microsoft Base Smart Card Crypto Provider"; use crate::{Error, ErrorKind, Result}; @@ -87,15 +93,10 @@ pub unsafe fn extract_certificate_by_thumbprint(thumbprint: &[u8]) -> Result<(Ve } #[instrument(level = "trace", ret)] -unsafe fn acquire_context(key_container_name: &str) -> Result { - let mut container_name = key_container_name - .encode_utf16() - .flat_map(|v| v.to_le_bytes()) - .collect::>(); - // add wide null char - container_name.extend_from_slice(&[0, 0]); - let csp_name = CSP_NAME +unsafe fn acquire_key_container_context(key_container_name: &str) -> Result { + let container_name = key_container_name .encode_utf16() + .chain(std::iter::once(0)) .flat_map(|v| v.to_le_bytes()) .collect::>(); let mut crypt_context_handle = HCRYPTPROV::default(); @@ -103,7 +104,7 @@ unsafe fn acquire_context(key_container_name: &str) -> Result { if CryptAcquireContextW( &mut crypt_context_handle, container_name.as_ptr() as *const _, - csp_name.as_ptr() as *const _, + CSP_NAME_W.as_ptr() as *const _, PROV_RSA_FULL, CRYPT_SILENT, ) == 0 @@ -160,14 +161,18 @@ pub unsafe fn get_key_container_certificate(crypt_context_handle: HCRYPTPROV) -> let mut cert_data_len = 0; if CryptGetKeyParam(key, KP_CERTIFICATE, null_mut(), &mut cert_data_len, 0) == 0 { + CryptDestroyKey(key); return Err(Error::new(ErrorKind::InternalError, "Cannot get certificate data len.")); } let mut cert_data = vec![0; cert_data_len as usize]; if CryptGetKeyParam(key, KP_CERTIFICATE, cert_data.as_mut_ptr(), &mut cert_data_len, 0) == 0 { + CryptDestroyKey(key); return Err(Error::new(ErrorKind::InternalError, "Cannot get certificate data.")); } + CryptDestroyKey(key); + Ok(picky_asn1_der::from_bytes(&cert_data)?) } @@ -180,18 +185,18 @@ pub struct SmartCardInfo { pub private_key_file_index: u8, } +// This function gathers the smart card information like reader name, key container name, +// and so on using the provided certificate serial number. +// It iterates over existing key containers and tries to find a suitable reader name and key container. +// The similar approach is implemented in the FreeRDP for the smart card information gathering: +// https://github.com/FreeRDP/FreeRDP/blob/56324906a2d5b2538675e2f10b9f1ffe4a27de79/libfreerdp/core/smartcardlogon.c#L616 #[instrument(level = "trace", ret)] pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result { - let csp_name = CSP_NAME - .encode_utf16() - .flat_map(|v| v.to_le_bytes()) - .collect::>(); - let mut crypt_context_handle = HCRYPTPROV::default(); if CryptAcquireContextW( &mut crypt_context_handle, null(), - csp_name.as_ptr() as *const _, + CSP_NAME_W.as_ptr() as *const _, PROV_RSA_FULL, CRYPT_SILENT, ) == 0 @@ -229,16 +234,11 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result reader_name, Err(err) => { error!(?err); @@ -255,20 +259,18 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result Result { +pub fn extract_upn_from_alt_name(certificate: &Certificate) -> Result { let subject_alt_name_ext = &certificate .tbs_certificate .extensions @@ -296,7 +299,7 @@ pub fn extract_user_name_from_alt_name(certificate: &Certificate) -> Result alternate_name, // safe: checked above - _ => unreachable!(), + _ => unreachable!("ExtensionView must be SubjectAltName"), }; let other_name = match alternate_name.0.get(0).unwrap() { GeneralName::OtherName(other_name) => other_name, @@ -319,6 +322,15 @@ pub fn extract_user_name_from_alt_name(certificate: &Certificate) -> Result. +// For instance: +// DC = com +// DC = example +// CN = Users +// CN = pw11 +// From the example above, this function will return "pw13@example.com". #[instrument(level = "trace", ret)] pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result { let subject = &certificate.tbs_certificate.subject.0 .0; @@ -361,6 +373,7 @@ pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result< let user_name = subject_parts .iter() .filter(|subject_part| subject_part.0 == oids::at_common_name()) + // Skip "CN = Users" part .skip(1) .map(|subject_part| subject_part.1.as_str()) .next() @@ -374,8 +387,11 @@ pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result< Ok(format!("{}@{}", user_name, domain)) } +// This function extracts the user name from the smart card certificate. pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result { - match extract_user_name_from_alt_name(certificate) { + // Firstly, it looks at the Subject Alternative Name + // because this action doesn't require any data concatenation unlike extracting from the Subject Name. + match extract_upn_from_alt_name(certificate) { Ok(user_name) => Ok(user_name), Err(_) => extract_user_name_from_subject_name(certificate), } @@ -383,36 +399,17 @@ pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result Date: Tue, 15 Aug 2023 19:23:37 +0300 Subject: [PATCH 61/85] feat(ffi): sec_handle: refactor acquire credentials handle; --- ffi/src/sec_handle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ffi/src/sec_handle.rs b/ffi/src/sec_handle.rs index 7e5dc143..6d266abb 100644 --- a/ffi/src/sec_handle.rs +++ b/ffi/src/sec_handle.rs @@ -34,7 +34,7 @@ use crate::credentials_attributes::{ }; use crate::sec_buffer::{copy_to_c_sec_buffer, p_sec_buffers_to_security_buffers, PSecBuffer, PSecBufferDesc}; use crate::sec_pkg_info::{SecNegoInfoA, SecNegoInfoW, SecPkgInfoA, SecPkgInfoW}; -use crate::sec_winnt_auth_identity::auth_data_to_identity_buffers; +use crate::sec_winnt_auth_identity::{auth_data_to_identity_buffers_a, auth_data_to_identity_buffers_w}; use crate::sspi_data_types::{ CertTrustStatus, LpStr, LpcWStr, PSecurityString, PTimeStamp, SecChar, SecGetKeyFn, SecPkgContextConnectionInfo, SecPkgContextFlags, SecPkgContextSizes, SecPkgContextStreamSizes, SecWChar, SecurityStatus, @@ -214,7 +214,7 @@ pub unsafe extern "system" fn AcquireCredentialsHandleA( let mut package_list: Option = None; - let credentials = try_execute!(auth_data_to_identity_buffers(&security_package_name, p_auth_data, &mut package_list)); + let credentials = try_execute!(auth_data_to_identity_buffers_a(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { credentials, @@ -262,7 +262,7 @@ pub unsafe extern "system" fn AcquireCredentialsHandleW( let mut package_list: Option = None; - let credentials = try_execute!(auth_data_to_identity_buffers(&security_package_name, p_auth_data, &mut package_list)); + let credentials = try_execute!(auth_data_to_identity_buffers_w(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { credentials, From 9bd7bd3613aa252259eb192995e0c3d380128100 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:24:08 +0300 Subject: [PATCH 62/85] feat(ffi): utils: remove uneeded function str_to_utf16_bytes; --- ffi/src/utils.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 7302e518..7e70af9b 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -50,7 +50,3 @@ pub unsafe fn transform_credentials_handle<'a>( )) } } - -pub fn str_to_utf16_bytes(s: impl AsRef) -> Vec { - s.as_ref().encode_utf16().flat_map(|v| v.to_le_bytes()).collect() -} From ab38e331f74fa02be26c44773ace1eab6c8da153 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Tue, 15 Aug 2023 19:24:57 +0300 Subject: [PATCH 63/85] feat(ffi): auth_identity: small refactoring; --- ffi/src/sec_winnt_auth_identity.rs | 41 +++++++----------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 71883b79..079c3814 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -158,25 +158,6 @@ pub unsafe fn get_auth_data_identity_version_and_flags(p_auth_data: *const c_voi } } -// todo: refactor: delete this function and use corresponding a/w function in the `sec_handle` module -// motivation: `(auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0` gives `false` for passed smart card credentials in the w function -pub unsafe fn auth_data_to_identity_buffers( - security_package_name: &str, - p_auth_data: *const c_void, - package_list: &mut Option, -) -> Result { - let (_, auth_flags) = get_auth_data_identity_version_and_flags(p_auth_data); - - if (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0 { - warn!("auth data to identity buffers w"); - } else { - warn!("auth data to identity buffers a"); - // auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) - } - - auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) -} - pub unsafe fn auth_data_to_identity_buffers_a( _security_package_name: &str, p_auth_data: *const c_void, @@ -403,19 +384,16 @@ unsafe fn handle_smart_card_creds(mut username: Vec, password: Secret, password: Secret, password: Secret, password: Secret Date: Tue, 15 Aug 2023 19:27:19 +0300 Subject: [PATCH 64/85] feat(sspi): Cargo.toml: remove default features; --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b728dd62..236eb4a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ exclude = [ ] [features] -default = ["scard", "tsssp"] +default = [] network_client = ["dep:reqwest", "dep:portpicker"] dns_resolver = ["dep:trust-dns-resolver", "dep:tokio"] # TSSSP should be used only on Windows as a native CREDSSP replacement @@ -80,3 +80,4 @@ tokio = { version = "1.1", features = ["time", "rt"] } [dev-dependencies] static_assertions = "1.1" whoami = "0.5" +picky = { path = "../picky-rs/picky", features = ["x509"] } From 793af2e8ac137aec435b9e0757822b59a7cb25ef Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 16 Aug 2023 16:11:53 +0300 Subject: [PATCH 65/85] sspi: refactoring; --- src/auth_identity.rs | 157 +++++++++++++++++++++++-------------------- src/credssp/mod.rs | 4 +- src/lib.rs | 4 +- src/negotiate.rs | 6 +- 4 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index 5e9c4b8f..ecb2ed6d 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -1,6 +1,5 @@ use std::fmt; -use picky_asn1_x509::Certificate; use serde::{Deserialize, Serialize}; use crate::{utils, Secret, Error}; @@ -77,85 +76,94 @@ impl From for AuthIdentity { } } -/// Represents raw data needed for smart card authentication #[cfg(feature = "scard")] -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct SmartCardIdentityBuffers { - /// UTF-16 encoded username - pub username: Vec, - /// DER-encoded X509 certificate - pub certificate: Vec, - /// UTF-16 encoded smart card name - pub card_name: Option>, - /// UTF-16 encoded smart card reader name - pub reader_name: Vec, - /// UTF-16 encoded smart card key container name - pub container_name: Vec, - /// UTF-16 encoded smart card CSP name - pub csp_name: Vec, - /// UTF-16 encoded smart card PIN code - pub pin: Secret>, - /// Private key file index - /// This value is used for the APDU message generation - pub private_key_file_index: Option, -} - -/// Represents data needed for smart card authentication -#[derive(Debug, Clone, PartialEq)] -pub struct SmartCardIdentity { - /// Username - pub username: String, - /// X509 certificate - pub certificate: Certificate, - /// Smart card reader name - pub reader_name: String, - /// Smart card name - pub card_name: Option, - /// Smart card key container name - pub container_name: String, - /// Smart card CSP name - pub csp_name: String, - /// ASCII encoded mart card PIN code - pub pin: Secret>, - /// Private key file index - /// This value is used for the APDU message generation - pub private_key_file_index: Option, -} - -impl TryFrom for SmartCardIdentityBuffers { - type Error = Error; +mod scard_credentials { + use picky_asn1_x509::Certificate; + + use crate::{Secret, Error, utils}; + + /// Represents raw data needed for smart card authentication + #[derive(Clone, Eq, PartialEq, Debug)] + pub struct SmartCardIdentityBuffers { + /// UTF-16 encoded username + pub username: Vec, + /// DER-encoded X509 certificate + pub certificate: Vec, + /// UTF-16 encoded smart card name + pub card_name: Option>, + /// UTF-16 encoded smart card reader name + pub reader_name: Vec, + /// UTF-16 encoded smart card key container name + pub container_name: Vec, + /// UTF-16 encoded smart card CSP name + pub csp_name: Vec, + /// UTF-16 encoded smart card PIN code + pub pin: Secret>, + /// Private key file index + /// This value is used for the APDU message generation + pub private_key_file_index: Option, + } - fn try_from(value: SmartCardIdentity) -> Result { - Ok(Self { - certificate: picky_asn1_der::to_vec(&value.certificate)?, - reader_name: utils::string_to_utf16(value.reader_name), - pin: value.pin.as_ref().clone().into(), - username: utils::string_to_utf16(value.username), - card_name: value.card_name.map(utils::string_to_utf16), - container_name: utils::string_to_utf16(value.container_name), - csp_name: utils::string_to_utf16(value.csp_name), - private_key_file_index: value.private_key_file_index, - }) + /// Represents data needed for smart card authentication + #[derive(Debug, Clone, PartialEq)] + pub struct SmartCardIdentity { + /// Username + pub username: String, + /// X509 certificate + pub certificate: Certificate, + /// Smart card reader name + pub reader_name: String, + /// Smart card name + pub card_name: Option, + /// Smart card key container name + pub container_name: String, + /// Smart card CSP name + pub csp_name: String, + /// ASCII encoded mart card PIN code + pub pin: Secret>, + /// Private key file index + /// This value is used for the APDU message generation + pub private_key_file_index: Option, } -} -impl TryFrom for SmartCardIdentity { - type Error = Error; + impl TryFrom for SmartCardIdentityBuffers { + type Error = Error; + + fn try_from(value: SmartCardIdentity) -> Result { + Ok(Self { + certificate: picky_asn1_der::to_vec(&value.certificate)?, + reader_name: utils::string_to_utf16(value.reader_name), + pin: value.pin.as_ref().clone().into(), + username: utils::string_to_utf16(value.username), + card_name: value.card_name.map(utils::string_to_utf16), + container_name: utils::string_to_utf16(value.container_name), + csp_name: utils::string_to_utf16(value.csp_name), + private_key_file_index: value.private_key_file_index, + }) + } + } - fn try_from(value: SmartCardIdentityBuffers) -> Result { - Ok(Self { - certificate: picky_asn1_der::from_bytes(&value.certificate)?, - reader_name: utils::bytes_to_utf16_string(&value.reader_name), - pin: utils::bytes_to_utf16_string(value.pin.as_ref()).into_bytes().into(), - username: utils::bytes_to_utf16_string(&value.username), - card_name: value.card_name.map(|name| utils::bytes_to_utf16_string(&name)), - container_name: utils::bytes_to_utf16_string(&value.container_name), - csp_name: utils::bytes_to_utf16_string(&value.csp_name), - private_key_file_index: value.private_key_file_index, - }) + impl TryFrom for SmartCardIdentity { + type Error = Error; + + fn try_from(value: SmartCardIdentityBuffers) -> Result { + Ok(Self { + certificate: picky_asn1_der::from_bytes(&value.certificate)?, + reader_name: utils::bytes_to_utf16_string(&value.reader_name), + pin: utils::bytes_to_utf16_string(value.pin.as_ref()).into_bytes().into(), + username: utils::bytes_to_utf16_string(&value.username), + card_name: value.card_name.map(|name| utils::bytes_to_utf16_string(&name)), + container_name: utils::bytes_to_utf16_string(&value.container_name), + csp_name: utils::bytes_to_utf16_string(&value.csp_name), + private_key_file_index: value.private_key_file_index, + }) + } } } +#[cfg(feature = "scard")] +pub use self::scard_credentials::{SmartCardIdentity, SmartCardIdentityBuffers}; + /// Generic enum that encapsulates raw credentials for any type of authentication #[derive(Clone, Eq, PartialEq, Debug)] pub enum CredentialsBuffers { @@ -166,6 +174,7 @@ pub enum CredentialsBuffers { SmartCard(SmartCardIdentityBuffers) } +#[allow(unreachable_patterns)] impl CredentialsBuffers { pub fn auth_identity(self) -> Option { match self { @@ -200,6 +209,7 @@ pub enum Credentials { } impl Credentials { + #[allow(unreachable_patterns)] pub fn auth_identity(self) -> Option { match self { Credentials::AuthIdentity(identity) => Some(identity), @@ -208,6 +218,7 @@ impl Credentials { } } +#[cfg(feature = "scard")] impl From for Credentials { fn from(value: SmartCardIdentity) -> Self { Self::SmartCard(Box::new(value)) @@ -230,4 +241,4 @@ impl TryFrom for CredentialsBuffers { Credentials::SmartCard(identity) => Self::SmartCard((*identity).try_into()?), }) } -} +} \ No newline at end of file diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index 45b976fe..e0fedbe4 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -271,7 +271,7 @@ impl CredSspClient { )]; let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take().map(|c| CredentialsBuffers::AuthIdentity(c)); + let mut credentials_handle = self.credentials_handle.take().map(CredentialsBuffers::AuthIdentity); let cred_ssp_context = self.context.as_mut().unwrap(); let mut builder = EmptyInitializeSecurityContext::<::CredentialsHandle>::new() .with_credentials_handle(&mut credentials_handle) @@ -474,7 +474,7 @@ impl> CredSspServer { let input_token = SecurityBuffer::new(input, SecurityBufferType::Token); let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take().map(|a| CredentialsBuffers::AuthIdentity(a)); + let mut credentials_handle = self.credentials_handle.take().map(CredentialsBuffers::AuthIdentity); match try_cred_ssp_server!( self.context .as_mut() diff --git a/src/lib.rs b/src/lib.rs index 22ee40f1..8692bbaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,9 @@ use picky_krb::messages::KrbError; use utils::map_keb_error_code_to_sspi_error; pub use utils::string_to_utf16; -pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, SmartCardIdentity, SmartCardIdentityBuffers, Credentials}; +pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, Credentials}; +#[cfg(feature = "scard")] +pub use self::auth_identity::{SmartCardIdentity, SmartCardIdentityBuffers}; use self::builders::{ AcceptSecurityContext, AcquireCredentialsHandle, ChangePassword, EmptyAcceptSecurityContext, EmptyAcquireCredentialsHandle, EmptyInitializeSecurityContext, FilledAcceptSecurityContext, diff --git a/src/negotiate.rs b/src/negotiate.rs index 047e577b..9eb30bdb 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -489,7 +489,7 @@ impl SspiImpl for Negotiate { .map(NtlmConfig::new) .unwrap_or_default(); self.protocol = - NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity(self.auth_identity.clone().map(|c| c.auth_identity()).flatten(), ntlm_config)); + NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity(self.auth_identity.clone().and_then(|c| c.auth_identity()), ntlm_config)); } result => return result, }; @@ -497,14 +497,14 @@ impl SspiImpl for Negotiate { match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { - let mut credentials_handle = self.auth_identity.as_mut().map(|c| c.clone().auth_identity()).flatten(); + let mut credentials_handle = self.auth_identity.as_mut().and_then(|c| c.clone().auth_identity()); let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); pku2u.initialize_security_context_impl(&mut transformed_builder) }, NegotiatedProtocol::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { - let mut credentials_handle = self.auth_identity.as_mut().map(|c| c.clone().auth_identity()).flatten(); + let mut credentials_handle = self.auth_identity.as_mut().and_then(|c| c.clone().auth_identity()); let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); ntlm.initialize_security_context_impl(&mut transformed_builder) From 2159a7ead5329c230c655e7cd883bdb7d2e0e68d Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 17 Aug 2023 18:52:35 +0300 Subject: [PATCH 66/85] feat(sspi): cert_utils: improved upn extraction from certificate; --- src/cert_utils.rs | 90 +++++------------------------------------------ 1 file changed, 8 insertions(+), 82 deletions(-) diff --git a/src/cert_utils.rs b/src/cert_utils.rs index da0211a4..8108a0cb 100644 --- a/src/cert_utils.rs +++ b/src/cert_utils.rs @@ -1,10 +1,8 @@ use std::ptr::{null, null_mut}; use std::slice::from_raw_parts; -use picky_asn1::wrapper::{IA5StringAsn1, Utf8StringAsn1}; -use picky_asn1_x509::{ - oids, AttributeTypeAndValueParameters, Certificate, DirectoryString, ExtensionView, GeneralName, -}; +use picky_asn1::wrapper::Utf8StringAsn1; +use picky_asn1_x509::{oids, Certificate, ExtensionView, GeneralName}; use sha1::{Digest, Sha1}; use winapi::ctypes::c_void; use winapi::um::ncrypt::HCRYPTKEY; @@ -280,7 +278,7 @@ pub unsafe fn finalize_smart_card_info(cert_serial_number: &[u8]) -> Result Result { +pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result { let subject_alt_name_ext = &certificate .tbs_certificate .extensions @@ -322,87 +320,12 @@ pub fn extract_upn_from_alt_name(certificate: &Certificate) -> Result { Ok(data.to_string()) } -// This function tries to extract the user name from the smart card certificate -// by extracting and concatenating the subject name fields of the provided certificate. -// Usually, the smart card certificate subject name has the following structure: [domain components], "Users", . -// For instance: -// DC = com -// DC = example -// CN = Users -// CN = pw11 -// From the example above, this function will return "pw13@example.com". -#[instrument(level = "trace", ret)] -pub fn extract_user_name_from_subject_name(certificate: &Certificate) -> Result { - let subject = &certificate.tbs_certificate.subject.0 .0; - let subject_parts = subject - .iter() - .map(|subject_part| { - let set = subject_part.0.get(0).unwrap(); - let t = set.ty.0.clone(); - let v = match &set.value { - AttributeTypeAndValueParameters::CommonName(DirectoryString::PrintableString(name)) => name.to_string(), - AttributeTypeAndValueParameters::CommonName(DirectoryString::Utf8String(name)) => name.clone(), - AttributeTypeAndValueParameters::Custom(custom) => { - let string: IA5StringAsn1 = picky_asn1_der::from_bytes(&custom.0)?; - string.to_string() - } - _ => { - return Err(Error::new( - ErrorKind::IncompleteCredentials, - "Common name has unsupported value type", - )) - } - }; - Ok((t, v)) - }) - .collect::>>()?; - - let domain = subject_parts - .iter() - .filter(|subject_part| subject_part.0 == oids::domain_component()) - .map(|subject_part| subject_part.1.as_str()) - .rev() - .fold(String::new(), |mut domain, subject_part| { - if !domain.is_empty() { - domain.push('.'); - } - domain.push_str(subject_part); - domain - }); - - let user_name = subject_parts - .iter() - .filter(|subject_part| subject_part.0 == oids::at_common_name()) - // Skip "CN = Users" part - .skip(1) - .map(|subject_part| subject_part.1.as_str()) - .next() - .ok_or_else(|| { - Error::new( - ErrorKind::IncompleteMessage, - "User name is not present in certificate common name field", - ) - })?; - - Ok(format!("{}@{}", user_name, domain)) -} - -// This function extracts the user name from the smart card certificate. -pub fn extract_user_name_from_certificate(certificate: &Certificate) -> Result { - // Firstly, it looks at the Subject Alternative Name - // because this action doesn't require any data concatenation unlike extracting from the Subject Name. - match extract_upn_from_alt_name(certificate) { - Ok(user_name) => Ok(user_name), - Err(_) => extract_user_name_from_subject_name(certificate), - } -} - #[cfg(test)] mod tests { use picky::x509::Cert; use picky_asn1_x509::Certificate; - use super::extract_upn_from_alt_name; + use super::extract_user_name_from_certificate; #[test] fn username_extraction() { @@ -410,6 +333,9 @@ mod tests { .unwrap() .into(); - assert_eq!("pw11@example.com", extract_upn_from_alt_name(&certificate).unwrap()); + assert_eq!( + "pw11@example.com", + extract_user_name_from_certificate(&certificate).unwrap() + ); } } From 3e5ce346f8d27a5756471e84ecacdf691d7a681c Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 17 Aug 2023 18:52:54 +0300 Subject: [PATCH 67/85] feat(sspi): format code; --- src/auth_identity.rs | 8 ++-- src/builders/init_sec_context.rs | 12 +++++- src/credssp/mod.rs | 66 ++++++++++++++++++++------------ src/credssp/sspi_cred_ssp/mod.rs | 11 +++--- src/credssp/ts_request/mod.rs | 13 ++----- src/dns.rs | 1 + src/kerberos/mod.rs | 10 ++--- src/lib.rs | 2 +- src/negotiate.rs | 38 ++++++++++-------- 9 files changed, 96 insertions(+), 65 deletions(-) diff --git a/src/auth_identity.rs b/src/auth_identity.rs index ecb2ed6d..51508acc 100644 --- a/src/auth_identity.rs +++ b/src/auth_identity.rs @@ -2,7 +2,7 @@ use std::fmt; use serde::{Deserialize, Serialize}; -use crate::{utils, Secret, Error}; +use crate::{utils, Error, Secret}; /// Allows you to pass a particular user name and password to the run-time library for the purpose of authentication /// @@ -80,7 +80,7 @@ impl From for AuthIdentity { mod scard_credentials { use picky_asn1_x509::Certificate; - use crate::{Secret, Error, utils}; + use crate::{utils, Error, Secret}; /// Represents raw data needed for smart card authentication #[derive(Clone, Eq, PartialEq, Debug)] @@ -171,7 +171,7 @@ pub enum CredentialsBuffers { AuthIdentity(AuthIdentityBuffers), #[cfg(feature = "scard")] /// Raw smart card identity buffers for the smart card based authentication - SmartCard(SmartCardIdentityBuffers) + SmartCard(SmartCardIdentityBuffers), } #[allow(unreachable_patterns)] @@ -241,4 +241,4 @@ impl TryFrom for CredentialsBuffers { Credentials::SmartCard(identity) => Self::SmartCard((*identity).try_into()?), }) } -} \ No newline at end of file +} diff --git a/src/builders/init_sec_context.rs b/src/builders/init_sec_context.rs index a6b2c3f8..587e5cb9 100644 --- a/src/builders/init_sec_context.rs +++ b/src/builders/init_sec_context.rs @@ -93,7 +93,17 @@ impl< { /// Creates a new builder with the other `AuthData` and `CredsHandle` types. /// References to the input and output buffers will be moved to the created builder leaving the `None` instead. - pub fn full_transform(&mut self, credentials_handle: Option<&'b mut CredsHandle2>) -> InitializeSecurityContext<'b, CredsHandle2, CredHandleSet2, ContextRequirementsSet, TargetDataRepresentationSet, OutputSet> { + pub fn full_transform( + &mut self, + credentials_handle: Option<&'b mut CredsHandle2>, + ) -> InitializeSecurityContext< + 'b, + CredsHandle2, + CredHandleSet2, + ContextRequirementsSet, + TargetDataRepresentationSet, + OutputSet, + > { InitializeSecurityContext { phantom_creds_use_set: PhantomData, phantom_context_req_set: PhantomData, diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index e0fedbe4..b060aeb4 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -29,10 +29,10 @@ use crate::pku2u::{self, Pku2u, Pku2uConfig}; use crate::{ negotiate, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, AuthIdentityBuffers, CertContext, CertTrustStatus, ClientRequestFlags, ConnectionInfo, ContextNames, ContextSizes, CredentialUse, - DataRepresentation, DecryptionFlags, EncryptionFlags, Error, ErrorKind, FilledAcceptSecurityContext, - FilledAcquireCredentialsHandle, FilledInitializeSecurityContext, InitializeSecurityContextResult, Negotiate, - NegotiateConfig, PackageInfo, SecurityBuffer, SecurityBufferType, SecurityStatus, ServerRequestFlags, Sspi, SspiEx, - SspiImpl, StreamSizes, Credentials, CredentialsBuffers, + Credentials, CredentialsBuffers, DataRepresentation, DecryptionFlags, EncryptionFlags, Error, ErrorKind, + FilledAcceptSecurityContext, FilledAcquireCredentialsHandle, FilledInitializeSecurityContext, + InitializeSecurityContextResult, Negotiate, NegotiateConfig, PackageInfo, SecurityBuffer, SecurityBufferType, + SecurityStatus, ServerRequestFlags, Sspi, SspiEx, SspiImpl, StreamSizes, }; pub const EARLY_USER_AUTH_RESULT_PDU_SIZE: usize = 4; @@ -608,7 +608,10 @@ impl SspiImpl for SspiContext { let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { identity } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); }; builder .full_transform(ntlm, Some(auth_identity)) @@ -623,7 +626,10 @@ impl SspiImpl for SspiContext { let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { identity } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); }; builder .full_transform(pku2u, Some(auth_identity)) @@ -644,25 +650,29 @@ impl SspiImpl for SspiContext { ) -> crate::Result { match self { SspiContext::Ntlm(ntlm) => { - let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = builder.credentials_handle_mut() { + let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = + builder.credentials_handle_mut() + { Some(identity.clone()) - } else { + } else { None }; let mut new_builder = builder.full_transform(Some(&mut auth_identity)); ntlm.initialize_security_context_impl(&mut new_builder) - }, + } SspiContext::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), SspiContext::Negotiate(negotiate) => negotiate.initialize_security_context_impl(builder), SspiContext::Pku2u(pku2u) => { - let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = builder.credentials_handle_mut() { + let mut auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(ref identity))) = + builder.credentials_handle_mut() + { Some(identity.clone()) - } else { + } else { None }; let mut new_builder = builder.full_transform(Some(&mut auth_identity)); pku2u.initialize_security_context_impl(&mut new_builder) - }, + } #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => credssp.initialize_security_context_impl(builder), } @@ -675,23 +685,31 @@ impl SspiImpl for SspiContext { ) -> crate::Result { match self { SspiContext::Ntlm(ntlm) => { - let auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { - identity.clone() - } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); - }; + let auth_identity = + if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { + identity.clone() + } else { + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); + }; builder.full_transform(ntlm, Some(&mut Some(auth_identity))).execute() - }, + } SspiContext::Kerberos(kerberos) => builder.transform(kerberos).execute(), SspiContext::Negotiate(negotiate) => builder.transform(negotiate).execute(), SspiContext::Pku2u(pku2u) => { - let auth_identity = if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { - identity.clone() - } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); - }; + let auth_identity = + if let Some(Some(CredentialsBuffers::AuthIdentity(identity))) = builder.credentials_handle { + identity.clone() + } else { + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); + }; builder.full_transform(pku2u, Some(&mut Some(auth_identity))).execute() - }, + } #[cfg(feature = "tsssp")] SspiContext::CredSsp(credssp) => builder.transform(credssp).execute(), } diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index f6020ee1..5b528b83 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -13,12 +13,11 @@ use super::ts_request::NONCE_SIZE; use super::{CredSspContext, CredSspMode, EndpointType, SspiContext, TsRequest}; use crate::builders::EmptyInitializeSecurityContext; use crate::{ - builders, negotiate, AcquireCredentialsHandleResult, CertContext, - CertEncodingType, CertTrustErrorStatus, CertTrustInfoStatus, CertTrustStatus, ClientRequestFlags, - ClientResponseFlags, ConnectionInfo, ContextNames, ContextSizes, CredentialUse, DataRepresentation, - DecryptionFlags, EncryptionFlags, Error, ErrorKind, InitializeSecurityContextResult, PackageCapabilities, - PackageInfo, Result, SecurityBuffer, SecurityBufferType, SecurityPackageType, SecurityStatus, Sspi, SspiEx, - SspiImpl, StreamSizes, PACKAGE_ID_NONE, Credentials, CredentialsBuffers, + builders, negotiate, AcquireCredentialsHandleResult, CertContext, CertEncodingType, CertTrustErrorStatus, + CertTrustInfoStatus, CertTrustStatus, ClientRequestFlags, ClientResponseFlags, ConnectionInfo, ContextNames, + ContextSizes, CredentialUse, Credentials, CredentialsBuffers, DataRepresentation, DecryptionFlags, EncryptionFlags, + Error, ErrorKind, InitializeSecurityContextResult, PackageCapabilities, PackageInfo, Result, SecurityBuffer, + SecurityBufferType, SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, StreamSizes, PACKAGE_ID_NONE, }; pub const PKG_NAME: &str = "CREDSSP"; diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index fb2a40c2..45eb458d 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -4,25 +4,20 @@ mod test; use core::fmt; use std::io::{self, Read}; -use picky_asn1::wrapper::{ - ExplicitContextTag0, ExplicitContextTag1, - IntegerAsn1, OctetStringAsn1, -}; +use picky_asn1::wrapper::{ExplicitContextTag0, ExplicitContextTag1, IntegerAsn1, OctetStringAsn1}; #[cfg(feature = "scard")] -use picky_asn1::wrapper::{ - ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, Optional, -}; -use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; +use picky_asn1::wrapper::{ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, Optional}; #[cfg(feature = "scard")] use picky_krb::constants::cred_ssp::AT_KEYEXCHANGE; +use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; use picky_krb::credssp::{TsCredentials, TsPasswordCreds}; #[cfg(feature = "scard")] use picky_krb::credssp::{TsCspDataDetail, TsSmartCardCreds}; use super::CredSspMode; -use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind}; #[cfg(feature = "scard")] use crate::SmartCardIdentityBuffers; +use crate::{ber, AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind}; pub const TS_REQUEST_VERSION: u32 = 6; diff --git a/src/dns.rs b/src/dns.rs index e413d13c..e03f43e8 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -344,6 +344,7 @@ where Fut::Output: Send, { use std::thread; + use tokio::runtime::{Builder, Handle}; match Handle::try_current() { diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 84f8bf43..47b6ef09 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -44,11 +44,11 @@ use crate::kerberos::utils::{generate_initiator_raw, parse_target_name, validate use crate::network_client::NetworkProtocol; use crate::utils::{generate_random_symmetric_key, get_encryption_key, utf16_bytes_to_utf8_string}; use crate::{ - detect_kdc_url, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, - ClientRequestFlags, ClientResponseFlags, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, - ErrorKind, InitializeSecurityContextResult, PackageCapabilities, PackageInfo, Result, SecurityBuffer, + detect_kdc_url, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, ClientRequestFlags, + ClientResponseFlags, ContextNames, ContextSizes, CredentialUse, Credentials, CredentialsBuffers, DecryptionFlags, + Error, ErrorKind, InitializeSecurityContextResult, PackageCapabilities, PackageInfo, Result, SecurityBuffer, SecurityBufferType, SecurityPackageType, SecurityStatus, ServerResponseFlags, Sspi, SspiEx, SspiImpl, - PACKAGE_ID_NONE, Credentials, CredentialsBuffers, + PACKAGE_ID_NONE, }; pub const PKG_NAME: &str = "Kerberos"; @@ -855,7 +855,7 @@ impl SspiImpl for Kerberos { impl SspiEx for Kerberos { #[instrument(level = "trace", ret, fields(state = ?self.state), skip(self))] - fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { + fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()> { self.auth_identity = Some(identity.try_into()?); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 8692bbaa..c80c2685 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ use picky_krb::messages::KrbError; use utils::map_keb_error_code_to_sspi_error; pub use utils::string_to_utf16; -pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, CredentialsBuffers, Credentials}; +pub use self::auth_identity::{AuthIdentity, AuthIdentityBuffers, Credentials, CredentialsBuffers}; #[cfg(feature = "scard")] pub use self::auth_identity::{SmartCardIdentity, SmartCardIdentityBuffers}; use self::builders::{ diff --git a/src/negotiate.rs b/src/negotiate.rs index 9eb30bdb..204a8183 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -3,7 +3,7 @@ use std::net::IpAddr; use lazy_static::lazy_static; -use crate::{kdc::detect_kdc_url, CredentialsBuffers}; +use crate::kdc::detect_kdc_url; use crate::kerberos::client::generators::get_client_principal_realm; use crate::network_client::NetworkClientFactory; use crate::ntlm::NtlmConfig; @@ -13,9 +13,9 @@ use crate::utils::is_azure_ad_domain; use crate::KerberosConfig; use crate::{ builders, kerberos, ntlm, pku2u, AcceptSecurityContextResult, AcquireCredentialsHandleResult, AuthIdentity, - CertTrustStatus, ContextNames, ContextSizes, CredentialUse, DecryptionFlags, Error, ErrorKind, - InitializeSecurityContextResult, Kerberos, Ntlm, PackageCapabilities, PackageInfo, Pku2u, Result, SecurityBuffer, - SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, PACKAGE_ID_NONE, Credentials, + CertTrustStatus, ContextNames, ContextSizes, CredentialUse, Credentials, CredentialsBuffers, DecryptionFlags, + Error, ErrorKind, InitializeSecurityContextResult, Kerberos, Ntlm, PackageCapabilities, PackageInfo, Pku2u, Result, + SecurityBuffer, SecurityPackageType, SecurityStatus, Sspi, SspiEx, SspiImpl, PACKAGE_ID_NONE, }; pub const PKG_NAME: &str = "Negotiate"; @@ -430,23 +430,29 @@ impl SspiImpl for Negotiate { let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { identity } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); }; let new_builder = builder.full_transform(pku2u, Some(auth_identity)); new_builder.execute()?; - }, + } NegotiatedProtocol::Kerberos(kerberos) => { kerberos.acquire_credentials_handle_impl(builder)?; - }, + } NegotiatedProtocol::Ntlm(ntlm) => { let auth_identity = if let Some(Credentials::AuthIdentity(identity)) = builder.auth_data { identity } else { - return Err(Error::new(ErrorKind::NoCredentials, "Auth identity is not provided for the Pku2u")); + return Err(Error::new( + ErrorKind::NoCredentials, + "Auth identity is not provided for the Pku2u", + )); }; let new_builder = builder.full_transform(ntlm, Some(auth_identity)); new_builder.execute()?; - }, + } }; Ok(AcquireCredentialsHandleResult { @@ -488,8 +494,10 @@ impl SspiImpl for Negotiate { .clone() .map(NtlmConfig::new) .unwrap_or_default(); - self.protocol = - NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity(self.auth_identity.clone().and_then(|c| c.auth_identity()), ntlm_config)); + self.protocol = NegotiatedProtocol::Ntlm(Ntlm::with_auth_identity( + self.auth_identity.clone().and_then(|c| c.auth_identity()), + ntlm_config, + )); } result => return result, }; @@ -501,14 +509,14 @@ impl SspiImpl for Negotiate { let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); pku2u.initialize_security_context_impl(&mut transformed_builder) - }, + } NegotiatedProtocol::Kerberos(kerberos) => kerberos.initialize_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { let mut credentials_handle = self.auth_identity.as_mut().and_then(|c| c.clone().auth_identity()); let mut transformed_builder = builder.full_transform(Some(&mut credentials_handle)); ntlm.initialize_security_context_impl(&mut transformed_builder) - }, + } } } @@ -526,7 +534,7 @@ impl SspiImpl for Negotiate { }; let new_builder = builder.full_transform(pku2u, Some(&mut creds_handle)); new_builder.execute() - }, + } NegotiatedProtocol::Kerberos(kerberos) => kerberos.accept_security_context_impl(builder), NegotiatedProtocol::Ntlm(ntlm) => { let mut creds_handle = if let Some(creds_handle) = &builder.credentials_handle { @@ -536,7 +544,7 @@ impl SspiImpl for Negotiate { }; let new_builder = builder.full_transform(ntlm, Some(&mut creds_handle)); new_builder.execute() - }, + } } } } From 12e0645a0dc59fcb4706bfa7321aba0a2d6e7e92 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 17 Aug 2023 19:00:58 +0300 Subject: [PATCH 68/85] feat(ffi): remove default features. improve conditional compilation; --- ffi/Cargo.toml | 3 +-- ffi/src/sec_winnt_auth_identity.rs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index b1edaec6..fe9a77cf 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -13,8 +13,7 @@ name = "sspi" crate-type = ["cdylib"] [features] -# for testing -default = ["scard", "tsssp"] +default = [] tsssp = ["sspi/tsssp"] scard = ["sspi/scard"] diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 079c3814..db89ce23 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,9 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, Secret, SmartCardIdentityBuffers}; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, Secret}; +#[cfg(feature = "scard")] +use sspi::SmartCardIdentityBuffers; #[cfg(windows)] use symbol_rename_macro::rename_symbol; @@ -491,6 +493,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - } // only marshaled smart card creds starts with '@' char + #[cfg(feature = "scard")] if username[0] == b'@' { // remove null let new_len = password.as_ref().len() - 2; From 4c90b8869dd1a919b3d6e90e9b53859ae7aee1dd Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 13:09:01 +0300 Subject: [PATCH 69/85] feat(sspi): cert_utils: improve conditional compilation; --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c80c2685..463b1675 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ pub mod winapi; mod auth_identity; mod ber; +#[cfg(scard)] pub mod cert_utils; mod crypto; mod dns; From 4652e80f78fce5f1f4feeeee6456e1b12516d971 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 13:09:29 +0300 Subject: [PATCH 70/85] feat(ffi): auth_identity: improve conditional compilation; --- ffi/src/sec_winnt_auth_identity.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index db89ce23..fdf9f014 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,7 +1,9 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result, Secret}; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; +#[cfg(windows)] +use sspi::Secret; #[cfg(feature = "scard")] use sspi::SmartCardIdentityBuffers; #[cfg(windows)] From 33aab3e6e1876cbfc6eef3ea2de20622b6468e40 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 22:53:20 +0300 Subject: [PATCH 71/85] feat(sspi): update picky-* dependencies: replace local paths with git source; --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 236eb4a1..e320edf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,11 @@ serde_derive = "1.0" url = "2.2.2" reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false } -picky = { path = "../picky-rs/picky", default-features = false } -picky-asn1 = { path = "../picky-rs/picky-asn1", features = ["chrono_conversion"] } -picky-asn1-der = { path = "../picky-rs/picky-asn1-der" } -picky-asn1-x509 = { path = "../picky-rs/picky-asn1-x509", features = ["pkcs7"] } -picky-krb = { path = "../picky-rs/picky-krb" } +picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", default-features = false } +picky-asn1 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["chrono_conversion"] } +picky-asn1-der = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff" } +picky-asn1-x509 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["pkcs7"] } +picky-krb = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff" } oid = "0.2.1" uuid = { version = "1.1", features = ["v4"] } @@ -80,4 +80,4 @@ tokio = { version = "1.1", features = ["time", "rt"] } [dev-dependencies] static_assertions = "1.1" whoami = "0.5" -picky = { path = "../picky-rs/picky", features = ["x509"] } +picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["x509"] } From bcb1c3c1916d03a4b7203f4e772077b3f9eb2881 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 22:56:06 +0300 Subject: [PATCH 72/85] feat(sspi): small refactoring; --- src/credssp/sspi_cred_ssp/mod.rs | 9 +++++---- src/kerberos/mod.rs | 9 +++++---- src/negotiate.rs | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index 5b528b83..7eb077ad 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -282,10 +282,11 @@ impl SspiImpl for SspiCredSsp { )); } - self.auth_identity = match builder.auth_data.cloned() { - Some(auth_data) => Some(auth_data.try_into()?), - None => None, - }; + self.auth_identity = builder + .auth_data + .cloned() + .map(|auth_data| auth_data.try_into()) + .transpose()?; Ok(AcquireCredentialsHandleResult { credentials_handle: self.auth_identity.clone(), diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 47b6ef09..2626acf3 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -549,10 +549,11 @@ impl SspiImpl for Kerberos { )); } - self.auth_identity = match builder.auth_data.cloned() { - Some(auth_data) => Some(auth_data.try_into()?), - None => None, - }; + self.auth_identity = builder + .auth_data + .cloned() + .map(|auth_data| auth_data.try_into()) + .transpose()?; Ok(AcquireCredentialsHandleResult { credentials_handle: self.auth_identity.clone(), diff --git a/src/negotiate.rs b/src/negotiate.rs index 204a8183..454bcc34 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -420,10 +420,11 @@ impl SspiImpl for Negotiate { self.negotiate_protocol(&identity.username, identity.domain.as_deref().unwrap_or_default())?; } - self.auth_identity = match builder.auth_data.cloned() { - Some(auth_data) => Some(auth_data.try_into()?), - None => None, - }; + self.auth_identity = builder + .auth_data + .cloned() + .map(|auth_data| auth_data.try_into()) + .transpose()?; match &mut self.protocol { NegotiatedProtocol::Pku2u(pku2u) => { From d2d25cbc25a448b5523994978e525260bef2ba4f Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 22:56:20 +0300 Subject: [PATCH 73/85] feat(ffi): format code; --- ffi/src/sec_winnt_auth_identity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index fdf9f014..deebdeef 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -1,11 +1,11 @@ use std::slice::from_raw_parts; use libc::{c_char, c_void}; -use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; #[cfg(windows)] use sspi::Secret; #[cfg(feature = "scard")] use sspi::SmartCardIdentityBuffers; +use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; From 671a0006f748852b03bf6d247696277d27d27687 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 18 Aug 2023 23:04:01 +0300 Subject: [PATCH 74/85] revert rust toolchain version: 1.71.0 -> 1.71.1; --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 182479d1..b2eef285 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.71.0" +channel = "1.71.1" components = [ "rustfmt", "clippy" ] From d20660d355764876ed98603b1723d78021183773 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 23 Aug 2023 13:01:48 +0300 Subject: [PATCH 75/85] feat(sspi): add support of smart card credentials in the credssp client/server --- src/credssp/mod.rs | 36 +++++++++++++++++------------------ src/credssp/ts_request/mod.rs | 16 ++++++++++------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index b060aeb4..65b6dda8 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -173,11 +173,11 @@ pub enum ClientMode { pub struct CredSspClient { state: CredSspState, context: Option, - credentials: AuthIdentity, + credentials: Credentials, public_key: Vec, cred_ssp_mode: CredSspMode, client_nonce: [u8; NONCE_SIZE], - credentials_handle: Option, + credentials_handle: Option, ts_request_version: u32, client_mode: Option, service_principal_name: String, @@ -186,7 +186,7 @@ pub struct CredSspClient { impl CredSspClient { pub fn new( public_key: Vec, - credentials: AuthIdentity, + credentials: Credentials, cred_ssp_mode: CredSspMode, client_mode: ClientMode, service_principal_name: String, @@ -207,7 +207,7 @@ impl CredSspClient { pub fn new_with_version( public_key: Vec, - credentials: AuthIdentity, + credentials: Credentials, cred_ssp_mode: CredSspMode, ts_request_version: u32, client_mode: ClientMode, @@ -255,10 +255,10 @@ impl CredSspClient { .unwrap() .sspi_context .acquire_credentials_handle() - .with_auth_data(&Credentials::AuthIdentity(self.credentials.clone())) + .with_auth_data(&self.credentials) .with_credential_use(CredentialUse::Outbound) .execute()?; - self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); + self.credentials_handle = credentials_handle; } ts_request.version = self.ts_request_version; @@ -271,7 +271,7 @@ impl CredSspClient { )]; let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take().map(CredentialsBuffers::AuthIdentity); + let mut credentials_handle = self.credentials_handle.take(); let cred_ssp_context = self.context.as_mut().unwrap(); let mut builder = EmptyInitializeSecurityContext::<::CredentialsHandle>::new() .with_credentials_handle(&mut credentials_handle) @@ -283,7 +283,7 @@ impl CredSspClient { let result = cred_ssp_context .sspi_context .initialize_security_context_impl(&mut builder)?; - self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); + self.credentials_handle = credentials_handle; ts_request.nego_tokens = Some(output_token.remove(0).buffer); if result.status == SecurityStatus::Ok { @@ -335,12 +335,12 @@ impl CredSspClient { peer_version, )?; - let auth_identity: AuthIdentityBuffers = self.credentials.clone().into(); - ts_request.auth_info = - Some(self.context.as_mut().unwrap().encrypt_ts_credentials( - &CredentialsBuffers::AuthIdentity(auth_identity), - self.cred_ssp_mode, - )?); + ts_request.auth_info = Some( + self.context + .as_mut() + .unwrap() + .encrypt_ts_credentials(self.credentials_handle.as_ref().unwrap(), self.cred_ssp_mode)?, + ); info!("tscredentials has been written"); self.state = CredSspState::Final; @@ -367,7 +367,7 @@ pub struct CredSspServer> state: CredSspState, context: Option, public_key: Vec, - credentials_handle: Option, + credentials_handle: Option, ts_request_version: u32, context_config: Option, } @@ -439,7 +439,7 @@ impl> CredSspServer { .execute(), ts_request ); - self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); + self.credentials_handle = credentials_handle; } try_cred_ssp_server!( self.context.as_mut().unwrap().check_peer_version(ts_request.version), @@ -474,7 +474,7 @@ impl> CredSspServer { let input_token = SecurityBuffer::new(input, SecurityBufferType::Token); let mut output_token = vec![SecurityBuffer::new(Vec::with_capacity(1024), SecurityBufferType::Token)]; - let mut credentials_handle = self.credentials_handle.take().map(CredentialsBuffers::AuthIdentity); + let mut credentials_handle = self.credentials_handle.take(); match try_cred_ssp_server!( self.context .as_mut() @@ -555,7 +555,7 @@ impl> CredSspServer { } _ => unreachable!(), }; - self.credentials_handle = credentials_handle.map(|c| c.auth_identity().unwrap()); + self.credentials_handle = credentials_handle; Ok(ServerState::ReplyNeeded(ts_request)) } diff --git a/src/credssp/ts_request/mod.rs b/src/credssp/ts_request/mod.rs index 45eb458d..19de664a 100644 --- a/src/credssp/ts_request/mod.rs +++ b/src/credssp/ts_request/mod.rs @@ -9,7 +9,7 @@ use picky_asn1::wrapper::{ExplicitContextTag0, ExplicitContextTag1, IntegerAsn1, use picky_asn1::wrapper::{ExplicitContextTag2, ExplicitContextTag3, ExplicitContextTag4, Optional}; #[cfg(feature = "scard")] use picky_krb::constants::cred_ssp::AT_KEYEXCHANGE; -use picky_krb::constants::cred_ssp::{TS_PASSWORD_CREDS, TS_SMART_CARD_CREDS}; +use picky_krb::constants::cred_ssp::TS_PASSWORD_CREDS; use picky_krb::credssp::{TsCredentials, TsPasswordCreds}; #[cfg(feature = "scard")] use picky_krb::credssp::{TsCspDataDetail, TsSmartCardCreds}; @@ -293,7 +293,10 @@ pub fn write_ts_credentials(credentials: &CredentialsBuffers, cred_ssp_mode: Cre (TS_PASSWORD_CREDS, write_password_credentials(creds, cred_ssp_mode)?) } #[cfg(feature = "scard")] - CredentialsBuffers::SmartCard(creds) => (TS_SMART_CARD_CREDS, write_smart_card_credentials(creds)?), + CredentialsBuffers::SmartCard(creds) => ( + picky_krb::constants::cred_ssp::TS_SMART_CARD_CREDS, + write_smart_card_credentials(creds)?, + ), }; let ts_creds = TsCredentials { @@ -330,14 +333,14 @@ fn write_password_credentials(credentials: &AuthIdentityBuffers, cred_ssp_mode: Ok(buffer) } -pub fn read_password_credentials(data: impl AsRef<[u8]>) -> crate::Result { - let password_card_creds: TsPasswordCreds = picky_asn1_der::from_bytes(data.as_ref())?; +fn read_password_credentials(data: impl AsRef<[u8]>) -> crate::Result { + let password_creds: TsPasswordCreds = picky_asn1_der::from_bytes(data.as_ref())?; let TsPasswordCreds { domain_name, user_name, password, - } = password_card_creds; + } = password_creds; Ok(AuthIdentityBuffers { user: user_name.0 .0, @@ -353,7 +356,8 @@ pub fn read_ts_credentials(mut buffer: impl io::Read) -> crate::Result Ok(CredentialsBuffers::AuthIdentity(read_password_credentials( &ts_credentials.credentials.0 .0, )?)), - Some(&TS_SMART_CARD_CREDS) => Err(Error::new( + #[cfg(feature = "scard")] + Some(&picky_krb::constants::cred_ssp::TS_SMART_CARD_CREDS) => Err(Error::new( ErrorKind::UnsupportedFunction, "Reading of the TsSmartCard credentials is not supported yet", )), From 75845a43a6ff34ec3c2107375a9f5d3320b1416b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 23 Aug 2023 13:24:54 +0300 Subject: [PATCH 76/85] sspi: update picky-* dependencies --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e320edf4..f6be101e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,11 @@ serde_derive = "1.0" url = "2.2.2" reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false } -picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", default-features = false } -picky-asn1 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["chrono_conversion"] } -picky-asn1-der = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff" } -picky-asn1-x509 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["pkcs7"] } -picky-krb = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff" } +picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", default-features = false } +picky-asn1 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["chrono_conversion"] } +picky-asn1-der = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8" } +picky-asn1-x509 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["pkcs7"] } +picky-krb = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8" } oid = "0.2.1" uuid = { version = "1.1", features = ["v4"] } @@ -80,4 +80,4 @@ tokio = { version = "1.1", features = ["time", "rt"] } [dev-dependencies] static_assertions = "1.1" whoami = "0.5" -picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "fda2fad12a704f2d5008f1ec4df62e5ca8a5e8ff", features = ["x509"] } +picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["x509"] } From b89ff480e7475cb2ee9a51cd2a755d78f6e9bed4 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 23 Aug 2023 13:50:52 +0300 Subject: [PATCH 77/85] fix: wasm-testcompile compilation --- Cargo.toml | 2 +- tools/wasm-testcompile/src/lib.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6be101e..38e1e3e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ picky-asn1-x509 = { git = "https://github.com/Devolutions/picky-rs.git", rev = " picky-krb = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8" } oid = "0.2.1" -uuid = { version = "1.1", features = ["v4"] } +uuid = { version = "1.3", features = ["v4"] } trust-dns-resolver = { version = "0.21.2", optional = true } portpicker = { version = "0.1.1", optional = true } num-bigint-dig = "0.8.1" diff --git a/tools/wasm-testcompile/src/lib.rs b/tools/wasm-testcompile/src/lib.rs index f9e5da5f..690a2c98 100644 --- a/tools/wasm-testcompile/src/lib.rs +++ b/tools/wasm-testcompile/src/lib.rs @@ -1,5 +1,6 @@ use sspi::network_client::{NetworkClient, NetworkClientFactory}; -use sspi::{credssp, AuthIdentity}; +use sspi::ntlm::NtlmConfig; +use sspi::{credssp, Credentials}; use wasm_bindgen::prelude::*; #[derive(Debug, Clone)] @@ -19,10 +20,10 @@ impl NetworkClientFactory for DummyNetworkClientFactory { pub fn credssp_client() { let mut cred_ssp_client = credssp::CredSspClient::new( Vec::new(), - AuthIdentity::default(), + Credentials::AuthIdentity(Default::default()), credssp::CredSspMode::WithCredentials, credssp::ClientMode::Negotiate(sspi::NegotiateConfig { - protocol_config: Box::new(sspi::ntlm::NtlmConfig::default()), + protocol_config: Box::::default(), package_list: None, hostname: "testhostname".into(), network_client_factory: Box::new(DummyNetworkClientFactory), From 9e3eb9dad0ec1245bd634bf411cb7a0cdb3bca5f Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 23 Aug 2023 16:58:47 +0300 Subject: [PATCH 78/85] feat(ffi): add comment about SEC_WINNT_AUTH_IDENTITY_UNICODE flag --- ffi/src/sec_handle.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ffi/src/sec_handle.rs b/ffi/src/sec_handle.rs index 6d266abb..97c3dcb3 100644 --- a/ffi/src/sec_handle.rs +++ b/ffi/src/sec_handle.rs @@ -214,6 +214,10 @@ pub unsafe extern "system" fn AcquireCredentialsHandleA( let mut package_list: Option = None; + // we don't have the general `auth_data_to_identity_buffers` function to automatically handle A/W buffers + // because in Windows 11 the SEC_WINNT_AUTH_IDENTITY_UNICODE flag doesn't work properly + // + // (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) is equal to 0 even on W-function call let credentials = try_execute!(auth_data_to_identity_buffers_a(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { @@ -262,6 +266,10 @@ pub unsafe extern "system" fn AcquireCredentialsHandleW( let mut package_list: Option = None; + // we don't have the general `auth_data_to_identity_buffers` function to automatically handle A/W buffers + // because in Windows 11 the SEC_WINNT_AUTH_IDENTITY_UNICODE flag doesn't work properly + // + // (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) is equal to 0 even on W-function call let credentials = try_execute!(auth_data_to_identity_buffers_w(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { From ba71154a619f2595b67be433d962d19b8649c360 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 19:48:43 +0300 Subject: [PATCH 79/85] feat(ffi): revert and improve auth_data_to_identity_buffers function --- ffi/src/sec_winnt_auth_identity.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index deebdeef..1c437030 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -8,6 +8,8 @@ use sspi::SmartCardIdentityBuffers; use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; +#[cfg(feature = "tsssp")] +use windows_sys::Win32::Security::Authentication::Identity::SspiIsAuthIdentityEncrypted; use crate::sspi_data_types::{SecWChar, SecurityStatus}; use crate::utils::{c_w_str_to_string, into_raw_ptr, raw_str_into_bytes}; @@ -162,6 +164,29 @@ pub unsafe fn get_auth_data_identity_version_and_flags(p_auth_data: *const c_voi } } +pub unsafe fn auth_data_to_identity_buffers( + security_package_name: &str, + p_auth_data: *const c_void, + package_list: &mut Option, +) -> Result { + let (_, auth_flags) = get_auth_data_identity_version_and_flags(p_auth_data); + + let rawcreds = std::slice::from_raw_parts(p_auth_data as *const u8, 128); + debug!(?rawcreds); + + #[cfg(feature = "tsssp")] + if SspiIsAuthIdentityEncrypted(p_auth_data) != 0 { + let credssp_cred = p_auth_data.cast::().as_ref().unwrap(); + return unpack_sec_winnt_auth_identity_ex2_w(credssp_cred.p_spnego_cred); + } + + if (auth_flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0 { + auth_data_to_identity_buffers_a(security_package_name, p_auth_data, package_list) + } else { + auth_data_to_identity_buffers_w(security_package_name, p_auth_data, package_list) + } +} + pub unsafe fn auth_data_to_identity_buffers_a( _security_package_name: &str, p_auth_data: *const c_void, From eafbbda58255e14535652adfc1ad9e42673d43ae Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 19:49:26 +0300 Subject: [PATCH 80/85] fix(ffi): replace auth_data_to_identity_buffers_q/w with auth_data_to_identity_buffers --- ffi/src/sec_handle.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ffi/src/sec_handle.rs b/ffi/src/sec_handle.rs index 97c3dcb3..7e5dc143 100644 --- a/ffi/src/sec_handle.rs +++ b/ffi/src/sec_handle.rs @@ -34,7 +34,7 @@ use crate::credentials_attributes::{ }; use crate::sec_buffer::{copy_to_c_sec_buffer, p_sec_buffers_to_security_buffers, PSecBuffer, PSecBufferDesc}; use crate::sec_pkg_info::{SecNegoInfoA, SecNegoInfoW, SecPkgInfoA, SecPkgInfoW}; -use crate::sec_winnt_auth_identity::{auth_data_to_identity_buffers_a, auth_data_to_identity_buffers_w}; +use crate::sec_winnt_auth_identity::auth_data_to_identity_buffers; use crate::sspi_data_types::{ CertTrustStatus, LpStr, LpcWStr, PSecurityString, PTimeStamp, SecChar, SecGetKeyFn, SecPkgContextConnectionInfo, SecPkgContextFlags, SecPkgContextSizes, SecPkgContextStreamSizes, SecWChar, SecurityStatus, @@ -214,11 +214,7 @@ pub unsafe extern "system" fn AcquireCredentialsHandleA( let mut package_list: Option = None; - // we don't have the general `auth_data_to_identity_buffers` function to automatically handle A/W buffers - // because in Windows 11 the SEC_WINNT_AUTH_IDENTITY_UNICODE flag doesn't work properly - // - // (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) is equal to 0 even on W-function call - let credentials = try_execute!(auth_data_to_identity_buffers_a(&security_package_name, p_auth_data, &mut package_list)); + let credentials = try_execute!(auth_data_to_identity_buffers(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { credentials, @@ -266,11 +262,7 @@ pub unsafe extern "system" fn AcquireCredentialsHandleW( let mut package_list: Option = None; - // we don't have the general `auth_data_to_identity_buffers` function to automatically handle A/W buffers - // because in Windows 11 the SEC_WINNT_AUTH_IDENTITY_UNICODE flag doesn't work properly - // - // (auth_flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) is equal to 0 even on W-function call - let credentials = try_execute!(auth_data_to_identity_buffers_w(&security_package_name, p_auth_data, &mut package_list)); + let credentials = try_execute!(auth_data_to_identity_buffers(&security_package_name, p_auth_data, &mut package_list)); (*ph_credential).dw_lower = into_raw_ptr(CredentialsHandle { credentials, From 904620203baace2c22684fc437d9376e6de074b1 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 20:01:04 +0300 Subject: [PATCH 81/85] feat(ffi): add comment about auth_data_to_identity_buffers function --- ffi/src/sec_winnt_auth_identity.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 1c437030..bafdec3a 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -164,6 +164,11 @@ pub unsafe fn get_auth_data_identity_version_and_flags(p_auth_data: *const c_voi } } +// This function determines what format credentials have: ASCII or UNICODE, +// and then calls an appropriate raw credentials handler function. +// Why do we need such a function: +// Actually, on Linux FreeRDP can pass UNICODE credentials into the AcquireCredentialsHandleA function. +// So, we need to be able to handle any credentials format in the AcquireCredentialsHandleA/W functions. pub unsafe fn auth_data_to_identity_buffers( security_package_name: &str, p_auth_data: *const c_void, From 0455a71d060fc0b9ac84a6eb08b7711ef84e652b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 20:01:26 +0300 Subject: [PATCH 82/85] fix(sspi): cert_utils module conditional compilation --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 463b1675..554a4bbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ pub mod winapi; mod auth_identity; mod ber; -#[cfg(scard)] +#[cfg(feature = "scard")] pub mod cert_utils; mod crypto; mod dns; From 49e70c0dc34d477d43fd7a1dd42614b8317aff59 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 20:07:27 +0300 Subject: [PATCH 83/85] sspi: update picky-* dependencies --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38e1e3e5..81945c1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,11 @@ serde_derive = "1.0" url = "2.2.2" reqwest = { version = "0.11", features = ["blocking", "rustls-tls", "rustls-tls-native-roots"], optional = true, default-features = false } -picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", default-features = false } -picky-asn1 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["chrono_conversion"] } -picky-asn1-der = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8" } -picky-asn1-x509 = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["pkcs7"] } -picky-krb = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8" } +picky = { version = "7.0.0-rc.8", default-features = false } +picky-asn1 = { version = "0.8.0", features = ["chrono_conversion"] } +picky-asn1-der = "0.4.1" +picky-asn1-x509 = { version = "0.12.0", features = ["pkcs7"] } +picky-krb = "0.8.0" oid = "0.2.1" uuid = { version = "1.3", features = ["v4"] } @@ -80,4 +80,4 @@ tokio = { version = "1.1", features = ["time", "rt"] } [dev-dependencies] static_assertions = "1.1" whoami = "0.5" -picky = { git = "https://github.com/Devolutions/picky-rs.git", rev = "19838e703b3e3bddfcac14682eaf88e15cbd55d8", features = ["x509"] } +picky = { version = "7.0.0-rc.8", features = ["x509"] } From 55d2f9cd8fc48290d9c41fad2e464947828d0ebd Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 20:25:07 +0300 Subject: [PATCH 84/85] feat(ffi): improve smart card credentials detection --- ffi/src/sec_winnt_auth_identity.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 1c437030..9d1415da 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -10,6 +10,8 @@ use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; use symbol_rename_macro::rename_symbol; #[cfg(feature = "tsssp")] use windows_sys::Win32::Security::Authentication::Identity::SspiIsAuthIdentityEncrypted; +#[cfg(feature = "scard")] +use winapi::um::wincred::CredIsMarshaledCredentialW; use crate::sspi_data_types::{SecWChar, SecurityStatus}; use crate::utils::{c_w_str_to_string, into_raw_ptr, raw_str_into_bytes}; @@ -258,7 +260,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if user[0] == b'@' { + if user[0] == b'@' && CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { return handle_smart_card_creds(user, password); } @@ -278,7 +280,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if user[0] == b'@' { + if user[0] == b'@' && CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { return handle_smart_card_creds(user, password); } @@ -521,7 +523,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if username[0] == b'@' { + if username[0] == b'@' && CredIsMarshaledCredentialW(username.as_ptr() as *const _) != 0 { // remove null let new_len = password.as_ref().len() - 2; password.as_mut().truncate(new_len); From ade277b75b6d92c094a7e7d3a1b39e5a1dad5f51 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 25 Aug 2023 21:19:16 +0300 Subject: [PATCH 85/85] feat(ffi): improve smart card credentials handling. format code --- ffi/src/sec_winnt_auth_identity.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ffi/src/sec_winnt_auth_identity.rs b/ffi/src/sec_winnt_auth_identity.rs index 79b4a482..3393dd79 100644 --- a/ffi/src/sec_winnt_auth_identity.rs +++ b/ffi/src/sec_winnt_auth_identity.rs @@ -8,10 +8,10 @@ use sspi::SmartCardIdentityBuffers; use sspi::{AuthIdentityBuffers, CredentialsBuffers, Error, ErrorKind, Result}; #[cfg(windows)] use symbol_rename_macro::rename_symbol; -#[cfg(feature = "tsssp")] -use windows_sys::Win32::Security::Authentication::Identity::SspiIsAuthIdentityEncrypted; #[cfg(feature = "scard")] use winapi::um::wincred::CredIsMarshaledCredentialW; +#[cfg(feature = "tsssp")] +use windows_sys::Win32::Security::Authentication::Identity::SspiIsAuthIdentityEncrypted; use crate::sspi_data_types::{SecWChar, SecurityStatus}; use crate::utils::{c_w_str_to_string, into_raw_ptr, raw_str_into_bytes}; @@ -265,7 +265,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if user[0] == b'@' && CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { + if CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { return handle_smart_card_creds(user, password); } @@ -285,7 +285,7 @@ pub unsafe fn auth_data_to_identity_buffers_w( // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if user[0] == b'@' && CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { + if CredIsMarshaledCredentialW(user.as_ptr() as *const _) != 0 { return handle_smart_card_creds(user, password); } @@ -528,7 +528,7 @@ pub unsafe fn unpack_sec_winnt_auth_identity_ex2_w(p_auth_data: *const c_void) - // only marshaled smart card creds starts with '@' char #[cfg(feature = "scard")] - if username[0] == b'@' && CredIsMarshaledCredentialW(username.as_ptr() as *const _) != 0 { + if CredIsMarshaledCredentialW(username.as_ptr() as *const _) != 0 { // remove null let new_len = password.as_ref().len() - 2; password.as_mut().truncate(new_len);