#[macro_use]
extern crate tracing;
pub mod builders;
pub mod channel_bindings;
pub mod credssp;
pub mod generator;
pub mod kerberos;
pub mod negotiate;
pub mod network_client;
pub mod ntlm;
mod pk_init;
pub mod pku2u;
pub mod utf16string;
mod auth_identity;
mod ber;
mod crypto;
mod dns;
mod kdc;
mod krb;
mod rustls;
mod secret;
mod security_buffer;
mod smartcard;
mod utils;
#[cfg(all(feature = "tsssp", not(target_os = "windows")))]
compile_error!("tsssp feature should be used only on Windows");
use std::{error, fmt, io, result, str, string};
use bitflags::bitflags;
#[cfg(feature = "tsssp")]
use credssp::sspi_cred_ssp;
pub use generator::NetworkRequest;
use generator::{GeneratorAcceptSecurityContext, GeneratorChangePassword, GeneratorInitSecurityContext};
pub use network_client::NetworkProtocol;
use num_derive::{FromPrimitive, ToPrimitive};
use picky_asn1::restricted_string::CharSetError;
use picky_asn1_der::Asn1DerError;
use picky_asn1_x509::Certificate;
use picky_krb::gss_api::GssApiMessageError;
use picky_krb::messages::KrbError;
#[cfg(feature = "__rustls-used")]
pub use rustls::install_default_crypto_provider_if_necessary;
pub use security_buffer::SecurityBufferRef;
pub use utf16string::{NonEmpty, Utf16Str, Utf16String, Utf16StringExt};
use utils::map_keb_error_code_to_sspi_error;
pub use utils::{modpow, str_to_w_buff, string_to_utf16, utf16_bytes_to_utf8_string};
pub use self::auth_identity::{
AuthIdentity, AuthIdentityBuffers, Credentials, CredentialsBuffers, UserNameFormat, Username,
};
#[cfg(feature = "scard")]
pub use self::auth_identity::{CertificateRaw, SmartCardIdentity, SmartCardIdentityBuffers, SmartCardType};
pub use self::builders::{
AcceptSecurityContextResult, AcquireCredentialsHandleResult, InitializeSecurityContextResult,
};
use self::builders::{
ChangePassword, FilledAcceptSecurityContext, FilledAcquireCredentialsHandle, FilledInitializeSecurityContext,
};
pub use self::kdc::{detect_kdc_host, detect_kdc_url};
pub use self::kerberos::config::{KerberosConfig, KerberosServerConfig};
pub use self::kerberos::{KERBEROS_VERSION, Kerberos, KerberosState};
#[cfg(feature = "__test-data")]
pub use self::negotiate::client::FALLBACK_ERROR_KINDS;
pub use self::negotiate::{Negotiate, NegotiateConfig, NegotiatedProtocol};
pub use self::ntlm::Ntlm;
pub use self::ntlm::hash::{NTLM_HASH_PREFIX, NtlmHash, NtlmHashError};
pub use self::pku2u::{Pku2u, Pku2uConfig, Pku2uState};
pub use self::secret::Secret;
use crate::builders::{
EmptyAcceptSecurityContext, EmptyAcquireCredentialsHandle, EmptyInitializeSecurityContext,
InitializeSecurityContext,
};
pub type Result<T> = result::Result<T, Error>;
pub type Luid = u64;
const PACKAGE_ID_NONE: u16 = 0xFFFF;
pub fn query_security_package_info(package_type: SecurityPackageType) -> Result<PackageInfo> {
match package_type {
SecurityPackageType::Ntlm => Ok(ntlm::PACKAGE_INFO.clone()),
SecurityPackageType::Kerberos => Ok(kerberos::PACKAGE_INFO.clone()),
SecurityPackageType::Negotiate => Ok(negotiate::PACKAGE_INFO.clone()),
SecurityPackageType::Pku2u => Ok(pku2u::PACKAGE_INFO.clone()),
#[cfg(feature = "tsssp")]
SecurityPackageType::CredSsp => Ok(sspi_cred_ssp::PACKAGE_INFO.clone()),
SecurityPackageType::Other(s) => Err(Error::new(
ErrorKind::Unknown,
format!("queried info about unknown package: {s:?}"),
)),
}
}
pub fn enumerate_security_packages() -> Result<Vec<PackageInfo>> {
Ok(vec![
negotiate::PACKAGE_INFO.clone(),
kerberos::PACKAGE_INFO.clone(),
pku2u::PACKAGE_INFO.clone(),
ntlm::PACKAGE_INFO.clone(),
#[cfg(feature = "tsssp")]
sspi_cred_ssp::PACKAGE_INFO.clone(),
])
}
pub trait Sspi
where
Self: Sized + SspiImpl,
{
fn acquire_credentials_handle<'a>(
&mut self,
) -> EmptyAcquireCredentialsHandle<'a, Self::CredentialsHandle, Self::AuthenticationData> {
EmptyAcquireCredentialsHandle::new()
}
fn initialize_security_context<'a, 'output>(
&mut self,
) -> EmptyInitializeSecurityContext<'a, 'output, Self::CredentialsHandle> {
InitializeSecurityContext::new()
}
fn accept_security_context<'a>(&mut self) -> EmptyAcceptSecurityContext<'a, Self::CredentialsHandle> {
EmptyAcceptSecurityContext::new()
}
fn complete_auth_token(&mut self, token: &mut [SecurityBuffer]) -> Result<SecurityStatus>;
fn encrypt_message(
&mut self,
flags: EncryptionFlags,
message: &mut [SecurityBufferRef<'_>],
) -> Result<SecurityStatus>;
fn make_signature(&mut self, flags: u32, message: &mut [SecurityBufferRef<'_>], sequence_number: u32)
-> Result<()>;
fn verify_signature(&mut self, message: &mut [SecurityBufferRef<'_>], sequence_number: u32) -> Result<u32>;
fn decrypt_message(&mut self, message: &mut [SecurityBufferRef<'_>]) -> Result<DecryptionFlags>;
fn query_context_sizes(&mut self) -> Result<ContextSizes>;
fn query_context_names(&mut self) -> Result<ContextNames>;
fn query_context_stream_sizes(&mut self) -> Result<StreamSizes> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"query_context_stream_sizes is not supported",
))
}
fn query_context_package_info(&mut self) -> Result<PackageInfo>;
fn query_context_cert_trust_status(&mut self) -> Result<CertTrustStatus>;
fn query_context_remote_cert(&mut self) -> Result<CertContext> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"query_remote_cert_context is not supported",
))
}
fn query_context_negotiation_package(&mut self) -> Result<PackageInfo> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"query_context_negotiation_package is not supported",
))
}
fn query_context_connection_info(&mut self) -> Result<ConnectionInfo> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"query_context_connection_info is not supported",
))
}
fn query_context_session_key(&self) -> Result<SessionKeys> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"query_context_session_key is not supported",
))
}
fn change_password<'a>(&'a mut self, change_password: ChangePassword<'a>) -> Result<GeneratorChangePassword<'a>>;
}
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ConnectionProtocol {
SpProtTls1Client = 0x80,
SpProtTls1Server = 0x40,
SpProtSsl3Client = 0x20,
SpProtSsl3Server = 0x10,
SpProtTls1_1Client = 0x200,
SpProtTls1_1Server = 0x100,
SpProtTls1_2Client = 0x800,
SpProtTls1_2Server = 0x400,
SpProtTls1_3Client = 0x00002000,
SpProtTls1_3Server = 0x00001000,
SpProtPct1Client = 0x2,
SpProtPct1Server = 0x1,
SpProtSsl2Client = 0x8,
SpProtSsl2Server = 0x4,
}
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ConnectionCipher {
Calg3des = 26115,
CalgAes128 = 26126,
CalgAes256 = 26128,
CalgDes = 26113,
CalgRc2 = 26114,
CalgRc4 = 26625,
NoEncryption = 0,
}
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ConnectionHash {
CalgMd5 = 32771,
CalgSha = 32772,
}
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ConnectionKeyExchange {
CalgRsaKeyx = 41984,
CalgDhEphem = 43522,
}
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum CertEncodingType {
Pkcs7AsnEncoding = 65536,
X509AsnEncoding = 1,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CertContext {
pub encoding_type: CertEncodingType,
pub raw_cert: Vec<u8>,
pub cert: Certificate,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ConnectionInfo {
pub protocol: ConnectionProtocol,
pub cipher: ConnectionCipher,
pub cipher_strength: u32,
pub hash: ConnectionHash,
pub hash_strength: u32,
pub key_exchange: ConnectionKeyExchange,
pub exchange_strength: u32,
}
pub trait SspiImpl {
type CredentialsHandle;
type AuthenticationData;
fn acquire_credentials_handle_impl(
&mut self,
builder: FilledAcquireCredentialsHandle<'_, Self::CredentialsHandle, Self::AuthenticationData>,
) -> Result<AcquireCredentialsHandleResult<Self::CredentialsHandle>>;
fn initialize_security_context_impl<'ctx, 'b, 'g>(
&'ctx mut self,
builder: &'b mut FilledInitializeSecurityContext<'ctx, 'ctx, Self::CredentialsHandle>,
) -> Result<GeneratorInitSecurityContext<'g>>
where
'ctx: 'g,
'b: 'g;
fn accept_security_context_impl<'a>(
&'a mut self,
builder: FilledAcceptSecurityContext<'a, Self::CredentialsHandle>,
) -> Result<GeneratorAcceptSecurityContext<'a>>;
}
mod private {
pub struct Sealed;
}
pub trait SspiEx
where
Self: Sized + SspiImpl,
{
fn custom_set_auth_identity(&mut self, identity: Self::AuthenticationData) -> Result<()>;
fn custom_set_auth_identities(&mut self, identities: Vec<Self::AuthenticationData>) -> Result<()> {
match identities.into_iter().next() {
Some(identity) => self.custom_set_auth_identity(identity),
None => Err(Error::new(ErrorKind::NoCredentials, "no credentials provided")),
}
}
fn verify_mic_token(&mut self, _token: &[u8], _data: &[u8], _: private::Sealed) -> Result<()> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"verify_mic_token is not supported",
))
}
fn generate_mic_token(&mut self, _token: &[u8], _: private::Sealed) -> Result<Vec<u8>> {
Err(Error::new(
ErrorKind::UnsupportedFunction,
"generate_mic_token is not supported",
))
}
}
pub type SspiPackage<'a, CredsHandle, AuthData> =
&'a mut dyn SspiImpl<CredentialsHandle = CredsHandle, AuthenticationData = AuthData>;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EncryptionFlags: u32 {
const WRAP_OOB_DATA = 0x4000_0000;
const WRAP_NO_ENCRYPT = 0x8000_0001;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DecryptionFlags: u32 {
const SIGN_ONLY = 0x8000_0000;
const WRAP_NO_ENCRYPT = 0x8000_0001;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClientRequestFlags: u32 {
const DELEGATE = 0x1;
const MUTUAL_AUTH = 0x2;
const REPLAY_DETECT = 0x4;
const SEQUENCE_DETECT = 0x8;
const CONFIDENTIALITY = 0x10;
const USE_SESSION_KEY = 0x20;
const PROMPT_FOR_CREDS = 0x40;
const USE_SUPPLIED_CREDS = 0x80;
const ALLOCATE_MEMORY = 0x100;
const USE_DCE_STYLE = 0x200;
const DATAGRAM = 0x400;
const CONNECTION = 0x800;
const CALL_LEVEL = 0x1000;
const FRAGMENT_SUPPLIED = 0x2000;
const EXTENDED_ERROR = 0x4000;
const STREAM = 0x8000;
const INTEGRITY = 0x0001_0000;
const IDENTIFY = 0x0002_0000;
const NULL_SESSION = 0x0004_0000;
const MANUAL_CRED_VALIDATION = 0x0008_0000;
const RESERVED1 = 0x0010_0000;
const FRAGMENT_TO_FIT = 0x0020_0000;
const FORWARD_CREDENTIALS = 0x0040_0000;
const NO_INTEGRITY = 0x0080_0000;
const USE_HTTP_STYLE = 0x100_0000;
const UNVERIFIED_TARGET_NAME = 0x2000_0000;
const CONFIDENTIALITY_ONLY = 0x4000_0000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServerRequestFlags: u32 {
const DELEGATE = 0x1;
const MUTUAL_AUTH = 0x2;
const REPLAY_DETECT = 0x4;
const SEQUENCE_DETECT = 0x8;
const CONFIDENTIALITY = 0x10;
const USE_SESSION_KEY = 0x20;
const SESSION_TICKET = 0x40;
const ALLOCATE_MEMORY = 0x100;
const USE_DCE_STYLE = 0x200;
const DATAGRAM = 0x400;
const CONNECTION = 0x800;
const CALL_LEVEL = 0x1000;
const FRAGMENT_SUPPLIED = 0x2000;
const EXTENDED_ERROR = 0x8000;
const STREAM = 0x0001_0000;
const INTEGRITY = 0x0002_0000;
const LICENSING = 0x0004_0000;
const IDENTIFY = 0x0008_0000;
const ALLOW_NULL_SESSION = 0x0010_0000;
const ALLOW_NON_USER_LOGONS = 0x0020_0000;
const ALLOW_CONTEXT_REPLAY = 0x0040_0000;
const FRAGMENT_TO_FIT = 0x80_0000;
const NO_TOKEN = 0x100_0000;
const PROXY_BINDINGS = 0x400_0000;
const ALLOW_MISSING_BINDINGS = 0x1000_0000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClientResponseFlags: u32 {
const DELEGATE = 0x1;
const MUTUAL_AUTH = 0x2;
const REPLAY_DETECT = 0x4;
const SEQUENCE_DETECT = 0x8;
const CONFIDENTIALITY = 0x10;
const USE_SESSION_KEY = 0x20;
const USED_COLLECTED_CREDS = 0x40;
const USED_SUPPLIED_CREDS = 0x80;
const ALLOCATED_MEMORY = 0x100;
const USED_DCE_STYLE = 0x200;
const DATAGRAM = 0x400;
const CONNECTION = 0x800;
const INTERMEDIATE_RETURN = 0x1000;
const CALL_LEVEL = 0x2000;
const EXTENDED_ERROR = 0x4000;
const STREAM = 0x8000;
const INTEGRITY = 0x0001_0000;
const IDENTIFY = 0x0002_0000;
const NULL_SESSION = 0x0004_0000;
const MANUAL_CRED_VALIDATION = 0x0008_0000;
const RESERVED1 = 0x10_0000;
const FRAGMENT_ONLY = 0x0020_0000;
const FORWARD_CREDENTIALS = 0x0040_0000;
const USED_HTTP_STYLE = 0x100_0000;
const NO_ADDITIONAL_TOKEN = 0x200_0000;
const REAUTHENTICATION = 0x800_0000;
const CONFIDENTIALITY_ONLY = 0x4000_0000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServerResponseFlags: u32 {
const DELEGATE = 0x1;
const MUTUAL_AUTH = 0x2;
const REPLAY_DETECT = 0x4;
const SEQUENCE_DETECT = 0x8;
const CONFIDENTIALITY = 0x10;
const USE_SESSION_KEY = 0x20;
const SESSION_TICKET = 0x40;
const ALLOCATED_MEMORY = 0x100;
const USED_DCE_STYLE = 0x200;
const DATAGRAM = 0x400;
const CONNECTION = 0x800;
const CALL_LEVEL = 0x2000;
const THIRD_LEG_FAILED = 0x4000;
const EXTENDED_ERROR = 0x8000;
const STREAM = 0x0001_0000;
const INTEGRITY = 0x0002_0000;
const LICENSING = 0x0004_0000;
const IDENTIFY = 0x0008_0000;
const NULL_SESSION = 0x0010_0000;
const ALLOW_NON_USER_LOGONS = 0x0020_0000;
const ALLOW_CONTEXT_REPLAY = 0x0040_0000;
const FRAGMENT_ONLY = 0x0080_0000;
const NO_TOKEN = 0x100_0000;
const NO_ADDITIONAL_TOKEN = 0x200_0000;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum DataRepresentation {
Network = 0,
Native = 0x10,
}
#[derive(Clone)]
pub struct SecurityBuffer {
pub buffer: Vec<u8>,
pub buffer_type: SecurityBufferType,
}
impl fmt::Debug for SecurityBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SecurityBufferRef {{ buffer_type: {:?}, buffer: 0x",
self.buffer_type
)?;
self.buffer.iter().try_for_each(|byte| write!(f, "{byte:02X}"))?;
write!(f, " }}")?;
Ok(())
}
}
impl SecurityBuffer {
pub fn new(buffer: Vec<u8>, buffer_type: BufferType) -> Self {
Self {
buffer,
buffer_type: SecurityBufferType {
buffer_type,
buffer_flags: SecurityBufferFlags::NONE,
},
}
}
pub fn find_buffer(buffers: &[SecurityBuffer], buffer_type: BufferType) -> Result<&SecurityBuffer> {
buffers
.iter()
.find(|b| b.buffer_type.buffer_type == buffer_type)
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidToken,
format!("no buffer was provided with type {buffer_type:?}"),
)
})
}
pub fn find_buffer_mut(buffers: &mut [SecurityBuffer], buffer_type: BufferType) -> Result<&mut SecurityBuffer> {
buffers
.iter_mut()
.find(|b| b.buffer_type.buffer_type == buffer_type)
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidToken,
format!("no buffer was provided with type {buffer_type:?}"),
)
})
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, FromPrimitive, ToPrimitive)]
pub enum BufferType {
#[default]
Empty = 0,
Data = 1,
Token = 2,
TransportToPackageParameters = 3,
Missing = 4,
Extra = 5,
StreamTrailer = 6,
StreamHeader = 7,
NegotiationInfo = 8,
Padding = 9,
Stream = 10,
ObjectIdsList = 11,
ObjectIdsListSignature = 12,
Target = 13,
ChannelBindings = 14,
ChangePasswordResponse = 15,
TargetHost = 16,
Alert = 17,
ApplicationProtocol = 18,
AttributeMark = 0xF000_0000,
ReadOnly = 0x8000_0000,
ReadOnlyWithChecksum = 0x1000_0000,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SecurityBufferFlags: u32 {
const NONE = 0x0;
const SECBUFFER_READONLY = 0x80000000;
const SECBUFFER_READONLY_WITH_CHECKSUM = 0x10000000;
}
}
#[derive(Clone, Copy, Eq, PartialEq, Default)]
pub struct SecurityBufferType {
pub buffer_type: BufferType,
pub buffer_flags: SecurityBufferFlags,
}
impl SecurityBufferType {
pub const SECBUFFER_ATTRMASK: u32 = 0xf0000000;
}
impl TryFrom<u32> for SecurityBufferType {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
use num_traits::cast::FromPrimitive;
let buffer_type = value & !Self::SECBUFFER_ATTRMASK;
let buffer_type = BufferType::from_u32(buffer_type).ok_or_else(|| {
Error::new(
ErrorKind::InternalError,
format!("u32({buffer_type}) to UnflaggedSecurityBuffer conversion error"),
)
})?;
let buffer_flags = value & Self::SECBUFFER_ATTRMASK;
let buffer_flags = SecurityBufferFlags::from_bits(buffer_flags).ok_or_else(|| {
Error::new(
ErrorKind::InternalError,
format!("invalid SecurityBufferFlags: {buffer_flags}"),
)
})?;
Ok(Self {
buffer_type,
buffer_flags,
})
}
}
impl From<SecurityBufferType> for u32 {
fn from(value: SecurityBufferType) -> u32 {
use num_traits::cast::ToPrimitive;
value.buffer_type.to_u32().unwrap() | value.buffer_flags.bits()
}
}
impl fmt::Debug for SecurityBufferType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("({:?}, {:?})", self.buffer_type, self.buffer_flags))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum CredentialUse {
Inbound = 1,
Outbound = 2,
Both = 3,
Default = 4,
}
#[derive(Debug, Clone)]
pub enum SecurityPackageType {
Ntlm,
Kerberos,
Negotiate,
Pku2u,
#[cfg(feature = "tsssp")]
CredSsp,
Other(String),
}
impl AsRef<str> for SecurityPackageType {
fn as_ref(&self) -> &str {
match self {
SecurityPackageType::Ntlm => ntlm::PKG_NAME,
SecurityPackageType::Kerberos => kerberos::PKG_NAME,
SecurityPackageType::Negotiate => negotiate::PKG_NAME,
SecurityPackageType::Pku2u => pku2u::PKG_NAME,
#[cfg(feature = "tsssp")]
SecurityPackageType::CredSsp => sspi_cred_ssp::PKG_NAME,
SecurityPackageType::Other(name) => name.as_str(),
}
}
}
impl fmt::Display for SecurityPackageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SecurityPackageType::Ntlm => write!(f, "{}", ntlm::PKG_NAME),
SecurityPackageType::Kerberos => write!(f, "{}", kerberos::PKG_NAME),
SecurityPackageType::Negotiate => write!(f, "{}", negotiate::PKG_NAME),
SecurityPackageType::Pku2u => write!(f, "{}", pku2u::PKG_NAME),
#[cfg(feature = "tsssp")]
SecurityPackageType::CredSsp => write!(f, "{}", sspi_cred_ssp::PKG_NAME),
SecurityPackageType::Other(name) => write!(f, "{name}"),
}
}
}
impl str::FromStr for SecurityPackageType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
ntlm::PKG_NAME => Ok(SecurityPackageType::Ntlm),
kerberos::PKG_NAME => Ok(SecurityPackageType::Kerberos),
negotiate::PKG_NAME => Ok(SecurityPackageType::Negotiate),
pku2u::PKG_NAME => Ok(SecurityPackageType::Pku2u),
#[cfg(feature = "tsssp")]
sspi_cred_ssp::PKG_NAME => Ok(SecurityPackageType::CredSsp),
s => Ok(SecurityPackageType::Other(s.to_string())),
}
}
}
#[derive(Debug, Clone)]
pub struct PackageInfo {
pub capabilities: PackageCapabilities,
pub rpc_id: u16,
pub max_token_len: u32,
pub name: SecurityPackageType,
pub comment: String,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PackageCapabilities: u32 {
const INTEGRITY = 0x1;
const PRIVACY = 0x2;
const TOKEN_ONLY = 0x4;
const DATAGRAM = 0x8;
const CONNECTION = 0x10;
const MULTI_REQUIRED = 0x20;
const CLIENT_ONLY = 0x40;
const EXTENDED_ERROR = 0x80;
const IMPERSONATION = 0x100;
const ACCEPT_WIN32_NAME = 0x200;
const STREAM = 0x400;
const NEGOTIABLE = 0x800;
const GSS_COMPATIBLE = 0x1000;
const LOGON = 0x2000;
const ASCII_BUFFERS = 0x4000;
const FRAGMENT = 0x8000;
const MUTUAL_AUTH = 0x1_0000;
const DELEGATION = 0x2_0000;
const READONLY_WITH_CHECKSUM = 0x4_0000;
const RESTRICTED_TOKENS = 0x8_0000;
const NEGO_EXTENDER = 0x10_0000;
const NEGOTIABLE2 = 0x20_0000;
const APP_CONTAINER_PASSTHROUGH = 0x40_0000;
const APP_CONTAINER_CHECKS = 0x80_0000;
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct StreamSizes {
pub header: u32,
pub trailer: u32,
pub max_message: u32,
pub buffers: u32,
pub block_size: u32,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ContextSizes {
pub max_token: u32,
pub max_signature: u32,
pub block: u32,
pub security_trailer: u32,
}
#[derive(Debug, Clone)]
pub struct CertTrustStatus {
pub error_status: CertTrustErrorStatus,
pub info_status: CertTrustInfoStatus,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertTrustErrorStatus: u32 {
const NO_ERROR = 0x0;
const IS_NOT_TIME_VALID = 0x1;
const IS_NOT_TIME_NESTED = 0x2;
const IS_REVOKED = 0x4;
const IS_NOT_SIGNATURE_VALID = 0x8;
const IS_NOT_VALID_FOR_USAGE = 0x10;
const IS_UNTRUSTED_ROOT = 0x20;
const REVOCATION_STATUS_UNKNOWN = 0x40;
const IS_CYCLIC = 0x80;
const INVALID_EXTENSION = 0x100;
const INVALID_POLICY_CONSTRAINTS = 0x200;
const INVALID_BASIC_CONSTRAINTS = 0x400;
const INVALID_NAME_CONSTRAINTS = 0x800;
const HAS_NOT_SUPPORTED_NAME_CONSTRAINT = 0x1000;
const HAS_NOT_DEFINED_NAME_CONSTRAINT = 0x2000;
const HAS_NOT_PERMITTED_NAME_CONSTRAINT = 0x4000;
const HAS_EXCLUDED_NAME_CONSTRAINT = 0x8000;
const IS_PARTIAL_CHAIN = 0x0001_0000;
const CTL_IS_NOT_TIME_VALID = 0x0002_0000;
const CTL_IS_NOT_SIGNATURE_VALID = 0x0004_0000;
const CTL_IS_NOT_VALID_FOR_USAGE = 0x0008_0000;
const IS_OFFLINE_REVOCATION = 0x100_0000;
const NO_ISSUANCE_CHAIN_POLICY = 0x200_0000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertTrustInfoStatus: u32 {
const HAS_EXACT_MATCH_ISSUER = 0x1;
const HAS_KEY_MATCH_ISSUER = 0x2;
const HAS_NAME_MATCH_ISSUER = 0x4;
const IS_SELF_SIGNED = 0x8;
const AUTO_UPDATE_CA_REVOCATION = 0x10;
const AUTO_UPDATE_END_REVOCATION = 0x20;
const NO_OCSP_FAILOVER_TO_CRL = 0x40;
const IS_KEY_ROLLOVER = 0x80;
const HAS_PREFERRED_ISSUER = 0x100;
const HAS_ISSUANCE_CHAIN_POLICY = 0x200;
const HAS_VALID_NAME_CONSTRAINTS = 0x400;
const IS_PEER_TRUSTED = 0x800;
const HAS_CRL_VALIDITY_EXTENDED = 0x1000;
const IS_FROM_EXCLUSIVE_TRUST_STORE = 0x2000;
const IS_CA_TRUSTED = 0x4000;
const HAS_AUTO_UPDATE_WEAK_SIGNATURE = 0x8000;
const SSL_HANDSHAKE_OCSP = 0x0004_0000;
const SSL_TIME_VALID_OCSP = 0x0008_0000;
const SSL_RECONNECT_OCSP = 0x0010_0000;
const IS_COMPLEX_CHAIN = 0x0001_0000;
const HAS_ALLOW_WEAK_SIGNATURE = 0x0002_0000;
const SSL_TIME_VALID = 0x100_0000;
const NO_TIME_CHECK = 0x200_0000;
}
}
#[derive(Debug, Clone)]
pub struct ContextNames {
pub username: Username,
}
#[derive(Debug, Clone)]
pub struct SessionKeys {
pub session_key: Secret<Vec<u8>>,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ErrorKind {
Unknown = 0,
InsufficientMemory = 0x8009_0300,
InvalidHandle = 0x8009_0301,
UnsupportedFunction = 0x8009_0302,
TargetUnknown = 0x8009_0303,
InternalError = 0x8009_0304,
SecurityPackageNotFound = 0x8009_0305,
NotOwned = 0x8009_0306,
CannotInstall = 0x8009_0307,
InvalidToken = 0x8009_0308,
CannotPack = 0x8009_0309,
OperationNotSupported = 0x8009_030A,
NoImpersonation = 0x8009_030B,
LogonDenied = 0x8009_030C,
UnknownCredentials = 0x8009_030D,
NoCredentials = 0x8009_030E,
MessageAltered = 0x8009_030F,
OutOfSequence = 0x8009_0310,
NoAuthenticatingAuthority = 0x8009_0311,
BadPackageId = 0x8009_0316,
ContextExpired = 0x8009_0317,
IncompleteMessage = 0x8009_0318,
IncompleteCredentials = 0x8009_0320,
BufferTooSmall = 0x8009_0321,
WrongPrincipalName = 0x8009_0322,
TimeSkew = 0x8009_0324,
UntrustedRoot = 0x8009_0325,
IllegalMessage = 0x8009_0326,
CertificateUnknown = 0x8009_0327,
CertificateExpired = 0x8009_0328,
EncryptFailure = 0x8009_0329,
DecryptFailure = 0x8009_0330,
AlgorithmMismatch = 0x8009_0331,
SecurityQosFailed = 0x8009_0332,
UnfinishedContextDeleted = 0x8009_0333,
NoTgtReply = 0x8009_0334,
NoIpAddress = 0x8009_0335,
WrongCredentialHandle = 0x8009_0336,
CryptoSystemInvalid = 0x8009_0337,
MaxReferralsExceeded = 0x8009_0338,
MustBeKdc = 0x8009_0339,
StrongCryptoNotSupported = 0x8009_033A,
TooManyPrincipals = 0x8009_033B,
NoPaData = 0x8009_033C,
PkInitNameMismatch = 0x8009_033D,
SmartCardLogonRequired = 0x8009_033E,
ShutdownInProgress = 0x8009_033F,
KdcInvalidRequest = 0x8009_0340,
KdcUnknownEType = 0x8009_0341,
KdcUnknownEType2 = 0x8009_0342,
UnsupportedPreAuth = 0x8009_0343,
DelegationRequired = 0x8009_0345,
BadBindings = 0x8009_0346,
MultipleAccounts = 0x8009_0347,
NoKerbKey = 0x8009_0348,
CertWrongUsage = 0x8009_0349,
DowngradeDetected = 0x8009_0350,
SmartCardCertificateRevoked = 0x8009_0351,
IssuingCAUntrusted = 0x8009_0352,
RevocationOffline = 0x8009_0353,
PkInitClientFailure = 0x8009_0354,
SmartCardCertExpired = 0x8009_0355,
NoS4uProtSupport = 0x8009_0356,
CrossRealmDelegationFailure = 0x8009_0357,
RevocationOfflineKdc = 0x8009_0358,
IssuingCaUntrustedKdc = 0x8009_0359,
KdcCertExpired = 0x8009_035A,
KdcCertRevoked = 0x8009_035B,
InvalidParameter = 0x8009_035D,
DelegationPolicy = 0x8009_035E,
PolicyNtlmOnly = 0x8009_035F,
NoContext = 0x8009_0361,
Pku2uCertFailure = 0x8009_0362,
MutualAuthFailed = 0x8009_0363,
OnlyHttpsAllowed = 0x8009_0365,
ApplicationProtocolMismatch = 0x8009_0367,
}
#[derive(Debug, Clone)]
pub struct Error {
pub error_type: ErrorKind,
pub description: String,
pub nstatus: Option<credssp::NStatusCode>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum SecurityStatus {
Ok = 0,
ContinueNeeded = 0x0009_0312,
CompleteNeeded = 0x0009_0313,
CompleteAndContinue = 0x0009_0314,
LocalLogon = 0x0009_0315,
ContextExpired = 0x0009_0317,
IncompleteCredentials = 0x0009_0320,
Renegotiate = 0x0009_0321,
NoLsaContext = 0x0009_0323,
}
impl Error {
pub fn new(error_type: ErrorKind, description: impl ToString) -> Self {
Self {
error_type,
description: description.to_string(),
nstatus: None,
}
}
pub fn new_with_nstatus(
error_type: ErrorKind,
description: impl Into<String>,
status_code: credssp::NStatusCode,
) -> Self {
Self {
error_type,
description: description.into(),
nstatus: Some(status_code),
}
}
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}: {}", self.error_type, self.description)?;
if let Some(nstatus) = self.nstatus {
write!(f, "; status is {nstatus}")?;
}
Ok(())
}
}
impl From<auth_identity::UsernameError> for Error {
fn from(value: auth_identity::UsernameError) -> Self {
Error::new(ErrorKind::UnknownCredentials, value)
}
}
impl From<rsa::Error> for Error {
fn from(value: rsa::Error) -> Self {
Error::new(
ErrorKind::InternalError,
format!("an unexpected RsaError happened: {value}"),
)
}
}
impl From<Asn1DerError> for Error {
fn from(err: Asn1DerError) -> Self {
Self::new(ErrorKind::InvalidToken, format!("ASN1 DER error: {err:?}"))
}
}
impl From<KrbError> for Error {
fn from(krb_error: KrbError) -> Self {
let (error_kind, mut description) = map_keb_error_code_to_sspi_error(krb_error.0.error_code.0);
if let Some(e_text) = krb_error.0.e_text.0 {
description.push_str(&format!(". Additional error text: {:?}", e_text.0));
}
if let Some(e_data) = krb_error.0.e_data.0 {
description.push_str(&format!(". Additional error data: {:?}", e_data.0));
}
Error::new(error_kind, description)
}
}
impl From<picky_krb::crypto::KerberosCryptoError> for Error {
fn from(err: picky_krb::crypto::KerberosCryptoError) -> Self {
use picky_krb::crypto::KerberosCryptoError;
match err {
KerberosCryptoError::KeyLength(actual, expected) => Self::new(
ErrorKind::InvalidParameter,
format!("invalid key length. actual: {actual}. expected: {expected}"),
),
KerberosCryptoError::CipherLength(actual, expected) => Self::new(
ErrorKind::InvalidParameter,
format!("invalid cipher length. actual: {actual}. expected: {expected}"),
),
KerberosCryptoError::AlgorithmIdentifier(identifier) => Self::new(
ErrorKind::InvalidParameter,
format!("unknown algorithm identifier: {identifier}"),
),
KerberosCryptoError::IntegrityCheck => Self::new(ErrorKind::MessageAltered, err.to_string()),
KerberosCryptoError::CipherError(description) => Self::new(ErrorKind::InvalidParameter, description),
KerberosCryptoError::CipherPad(description) => {
Self::new(ErrorKind::InvalidParameter, description.to_string())
}
KerberosCryptoError::CipherUnpad(description) => {
Self::new(ErrorKind::InvalidParameter, description.to_string())
}
KerberosCryptoError::SeedBitLen(description) => Self::new(ErrorKind::InvalidParameter, description),
KerberosCryptoError::AlgorithmIdentifierData(identifier) => Self::new(
ErrorKind::InvalidParameter,
format!("unknown algorithm identifier: {identifier:?}"),
),
KerberosCryptoError::RandError(rand) => {
Self::new(ErrorKind::InvalidParameter, format!("random error: {rand:?}"))
}
KerberosCryptoError::TooSmallBuffer(inout) => {
Self::new(ErrorKind::InvalidParameter, format!("too small buffer: {inout:?}"))
}
KerberosCryptoError::ArrayTryFromSliceError(array) => Self::new(
ErrorKind::InvalidParameter,
format!("array try from slice error: {array:?}"),
),
}
}
}
impl From<picky_krb::crypto::diffie_hellman::DiffieHellmanError> for Error {
fn from(error: picky_krb::crypto::diffie_hellman::DiffieHellmanError) -> Self {
use picky_krb::crypto::diffie_hellman::DiffieHellmanError;
match error {
DiffieHellmanError::BitLen(description) => Self::new(ErrorKind::InternalError, description),
error => Self::new(ErrorKind::InternalError, error.to_string()),
}
}
}
impl From<CharSetError> for Error {
fn from(err: CharSetError) -> Self {
Self::new(ErrorKind::InternalError, err.to_string())
}
}
impl From<GssApiMessageError> for Error {
fn from(err: GssApiMessageError) -> Self {
match err {
GssApiMessageError::IoError(err) => Self::from(err),
GssApiMessageError::InvalidId(_, _) => Self::new(ErrorKind::InvalidToken, err.to_string()),
GssApiMessageError::InvalidMicFiller(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
GssApiMessageError::InvalidWrapFiller(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
GssApiMessageError::Asn1Error(_) => Self::new(ErrorKind::InvalidToken, err.to_string()),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::new(ErrorKind::InternalError, format!("IO error: {err:?}"))
}
}
impl From<getrandom::Error> for Error {
fn from(err: getrandom::Error) -> Self {
Self::new(ErrorKind::InternalError, format!("rand error: {err:?}"))
}
}
impl From<rand::rngs::SysError> for Error {
fn from(err: rand::rngs::SysError) -> Self {
Self::new(ErrorKind::InternalError, format!("rand error: {:?}", err))
}
}
impl From<str::Utf8Error> for Error {
fn from(err: str::Utf8Error) -> Self {
Self::new(ErrorKind::InternalError, err)
}
}
impl From<string::FromUtf8Error> for Error {
fn from(err: string::FromUtf8Error) -> Self {
Self::new(ErrorKind::InternalError, format!("UTF-8 error: {err:?}"))
}
}
impl From<string::FromUtf16Error> for Error {
fn from(err: string::FromUtf16Error) -> Self {
Self::new(ErrorKind::InternalError, format!("UTF-16 error: {err:?}"))
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::other(format!("{:?}: {}", err.error_type, err.description))
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(_: std::num::TryFromIntError) -> Self {
Self::new(ErrorKind::InternalError, "integer conversion error")
}
}
impl<T> From<std::sync::PoisonError<T>> for Error {
fn from(_: std::sync::PoisonError<T>) -> Self {
Self::new(ErrorKind::InternalError, "can not lock SspiHandle mutex")
}
}
impl From<picky::key::KeyError> for Error {
fn from(err: picky::key::KeyError) -> Self {
Self::new(ErrorKind::InternalError, format!("RSA key error: {err:?}"))
}
}
#[cfg(feature = "scard")]
impl From<winscard::Error> for Error {
fn from(value: winscard::Error) -> Self {
Self::new(
ErrorKind::InternalError,
format!("Error while using a smart card: {value}"),
)
}
}
#[cfg(all(feature = "scard", not(target_arch = "wasm32")))]
impl From<cryptoki::error::Error> for Error {
fn from(value: cryptoki::error::Error) -> Self {
Self::new(
ErrorKind::NoCredentials,
format!("Error while using a smart card: {value}"),
)
}
}
impl From<widestring::error::Utf16Error> for Error {
fn from(value: widestring::error::Utf16Error) -> Self {
Self::new(ErrorKind::InternalError, format!("UTF-16 error: {value:?}"))
}
}