blob: c4b21d18efa1eb4a15db7afe7ce87b7cfcba73ff [file] [log] [blame]
//! Schannel credentials.
use std::ptr;
use std::sync::Arc;
use std::{io, mem};
use windows_sys::Win32::Foundation;
use windows_sys::Win32::Security::Authentication::Identity;
use windows_sys::Win32::Security::{Credentials, Cryptography};
use crate::cert_context::CertContext;
use crate::Inner;
/// The communication direction that an `SchannelCred` will support.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum Direction {
/// Server-side, inbound connections.
Inbound,
/// Client-side, outbound connections.
Outbound,
}
/// Algorithms supported by Schannel.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
#[non_exhaustive]
pub enum Algorithm {
/// Advanced Encryption Standard (AES).
Aes = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_AES,
/// 128 bit AES.
Aes128 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_AES_128,
/// 192 bit AES.
Aes192 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_AES_192,
/// 256 bit AES.
Aes256 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_AES_256,
/// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
AgreedkeyAny = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_DH
| Cryptography::ALG_SID_AGREED_KEY_ANY,
/// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
/// its key length 64 bits.
CylinkMek = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_CYLINK_MEK,
/// DES encryption algorithm.
Des = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_DES,
/// DESX encryption algorithm.
Desx = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_DESX,
/// Diffie-Hellman ephemeral key exchange algorithm.
DhEphem = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_DH
| Cryptography::ALG_SID_DH_EPHEM,
/// Diffie-Hellman store and forward key exchange algorithm.
DhSf = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_DH
| Cryptography::ALG_SID_DH_SANDF,
/// DSA public key signature algorithm.
DssSign = Cryptography::ALG_CLASS_SIGNATURE
| Cryptography::ALG_TYPE_DSS
| Cryptography::ALG_SID_DSS_ANY,
/// Elliptic curve Diffie-Hellman key exchange algorithm.
Ecdh = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_DH
| Cryptography::ALG_SID_ECDH,
/// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
EcdhEphem = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_ECDH
| Cryptography::ALG_SID_ECDH_EPHEM,
/// Elliptic curve digital signature algorithm.
Ecdsa = Cryptography::ALG_CLASS_SIGNATURE
| Cryptography::ALG_TYPE_DSS
| Cryptography::ALG_SID_ECDSA,
/// One way function hashing algorithm.
HashReplaceOwf = Cryptography::ALG_CLASS_HASH
| Cryptography::ALG_TYPE_ANY
| Cryptography::ALG_SID_HASH_REPLACE_OWF,
/// Hughes MD5 hashing algorithm.
HughesMd5 = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_ANY
| Cryptography::ALG_SID_MD5,
/// HMAC keyed hash algorithm.
Hmac = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_HMAC,
/// MAC keyed hash algorithm.
Mac = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MAC,
/// MD2 hashing algorithm.
Md2 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD2,
/// MD4 hashing algorithm.
Md4 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD4,
/// MD5 hashing algorithm.
Md5 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD5,
/// No signature algorithm..
NoSign =
Cryptography::ALG_CLASS_SIGNATURE | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_ANY,
/// RC2 block encryption algorithm.
Rc2 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_RC2,
/// RC4 stream encryption algorithm.
Rc4 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_STREAM
| Cryptography::ALG_SID_RC4,
/// RC5 block encryption algorithm.
Rc5 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_RC5,
/// RSA public key exchange algorithm.
RsaKeyx = Cryptography::ALG_CLASS_KEY_EXCHANGE
| Cryptography::ALG_TYPE_RSA
| Cryptography::ALG_SID_RSA_ANY,
/// RSA public key signature algorithm.
RsaSign = Cryptography::ALG_CLASS_SIGNATURE
| Cryptography::ALG_TYPE_RSA
| Cryptography::ALG_SID_RSA_ANY,
/// SHA hashing algorithm.
Sha1 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA1,
/// 256 bit SHA hashing algorithm.
Sha256 =
Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_256,
/// 384 bit SHA hashing algorithm.
Sha384 =
Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_384,
/// 512 bit SHA hashing algorithm.
Sha512 =
Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_512,
/// Triple DES encryption algorithm.
TripleDes = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_3DES,
/// Two-key triple DES encryption with effective key length equal to 112 bits.
TripleDes112 = Cryptography::ALG_CLASS_DATA_ENCRYPT
| Cryptography::ALG_TYPE_BLOCK
| Cryptography::ALG_SID_3DES_112,
}
/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
pub enum Protocol {
/// Secure Sockets Layer 3.0
Ssl3,
/// Transport Layer Security 1.0
Tls10,
/// Transport Layer Security 1.1
Tls11,
/// Transport Layer Security 1.2
Tls12,
/// Transport Layer Security 1.3
Tls13,
}
impl Protocol {
fn dword(self, direction: Direction) -> u32 {
match (self, direction) {
(Protocol::Ssl3, Direction::Inbound) => Identity::SP_PROT_SSL3_SERVER,
(Protocol::Tls10, Direction::Inbound) => Identity::SP_PROT_TLS1_0_SERVER,
(Protocol::Tls11, Direction::Inbound) => Identity::SP_PROT_TLS1_1_SERVER,
(Protocol::Tls12, Direction::Inbound) => Identity::SP_PROT_TLS1_2_SERVER,
(Protocol::Tls13, Direction::Inbound) => Identity::SP_PROT_TLS1_3_SERVER,
(Protocol::Ssl3, Direction::Outbound) => Identity::SP_PROT_SSL3_CLIENT,
(Protocol::Tls10, Direction::Outbound) => Identity::SP_PROT_TLS1_0_CLIENT,
(Protocol::Tls11, Direction::Outbound) => Identity::SP_PROT_TLS1_1_CLIENT,
(Protocol::Tls12, Direction::Outbound) => Identity::SP_PROT_TLS1_2_CLIENT,
(Protocol::Tls13, Direction::Outbound) => Identity::SP_PROT_TLS1_3_CLIENT,
}
}
}
/// A builder type for `SchannelCred`s.
#[derive(Default, Debug)]
pub struct Builder {
supported_algorithms: Option<Vec<Algorithm>>,
enabled_protocols: Option<Vec<Protocol>>,
certs: Vec<CertContext>,
}
impl Builder {
/// Returns a new `Builder`.
pub fn new() -> Builder {
Builder::default()
}
/// Sets the algorithms supported for credentials created from this builder.
pub fn supported_algorithms(&mut self, supported_algorithms: &[Algorithm]) -> &mut Builder {
self.supported_algorithms = Some(supported_algorithms.to_owned());
self
}
/// Sets the protocols enabled for credentials created from this builder.
pub fn enabled_protocols(&mut self, enabled_protocols: &[Protocol]) -> &mut Builder {
self.enabled_protocols = Some(enabled_protocols.to_owned());
self
}
/// Add a certificate to get passed down when the credentials are acquired.
///
/// Certificates passed here may specify a certificate that contains a
/// private key to be used in authenticating the application. Typically,
/// this is called once for each key exchange method supported by
/// servers.
///
/// Clients often do not call this function and either depend on Schannel to
/// find an appropriate certificate or create a certificate later if needed.
pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
self.certs.push(cx);
self
}
/// Creates a new `SchannelCred`.
pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
unsafe {
let mut handle: Credentials::SecHandle = mem::zeroed();
let mut cred_data: Identity::SCHANNEL_CRED = mem::zeroed();
cred_data.dwVersion = Identity::SCHANNEL_CRED_VERSION;
cred_data.dwFlags =
Identity::SCH_USE_STRONG_CRYPTO | Identity::SCH_CRED_NO_DEFAULT_CREDS;
if let Some(ref supported_algorithms) = self.supported_algorithms {
cred_data.cSupportedAlgs = supported_algorithms.len() as u32;
cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
}
if let Some(ref enabled_protocols) = self.enabled_protocols {
cred_data.grbitEnabledProtocols = enabled_protocols
.iter()
.map(|p| p.dword(direction))
.fold(0, |acc, p| acc | p);
}
let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
cred_data.cCreds = certs.len() as u32;
cred_data.paCred = certs.as_mut_ptr() as _;
let direction = match direction {
Direction::Inbound => Identity::SECPKG_CRED_INBOUND,
Direction::Outbound => Identity::SECPKG_CRED_OUTBOUND,
};
match Identity::AcquireCredentialsHandleA(
ptr::null(),
Identity::UNISP_NAME_A,
direction,
ptr::null_mut(),
&mut cred_data as *const _ as *const _,
None,
ptr::null_mut(),
&mut handle,
ptr::null_mut(),
) {
Foundation::SEC_E_OK => Ok(SchannelCred::from_inner(handle)),
err => Err(io::Error::from_raw_os_error(err)),
}
}
}
}
/// An SChannel credential.
#[derive(Clone)]
pub struct SchannelCred(Arc<RawCredHandle>);
struct RawCredHandle(Credentials::SecHandle);
impl Drop for RawCredHandle {
fn drop(&mut self) {
unsafe {
Identity::FreeCredentialsHandle(&self.0);
}
}
}
impl SchannelCred {
/// Returns a builder.
pub fn builder() -> Builder {
Builder::new()
}
unsafe fn from_inner(inner: Credentials::SecHandle) -> SchannelCred {
SchannelCred(Arc::new(RawCredHandle(inner)))
}
pub(crate) fn as_inner(&self) -> Credentials::SecHandle {
self.0.as_ref().0
}
}