blob: c182169db4f6bdc45f64eb604c87e6e80ee0a736 [file] [log] [blame]
//! Schannel credentials.
use winapi::shared::{sspi, winerror};
use winapi::shared::minwindef as winapi;
use winapi::um::{self, wincrypt};
use std::io;
use std::mem;
use std::ptr;
use std::sync::Arc;
use crate::Inner;
use crate::cert_context::CertContext;
lazy_static! {
static ref UNISP_NAME: Vec<u8> = um::schannel::UNISP_NAME.bytes().chain(Some(0)).collect();
}
/// 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)]
pub enum Algorithm {
/// Advanced Encryption Standard (AES).
Aes = wincrypt::CALG_AES,
/// 128 bit AES.
Aes128 = wincrypt::CALG_AES_128,
/// 192 bit AES.
Aes192 = wincrypt::CALG_AES_192,
/// 256 bit AES.
Aes256 = wincrypt::CALG_AES_256,
/// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
AgreedkeyAny = wincrypt::CALG_AGREEDKEY_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 = wincrypt::CALG_CYLINK_MEK,
/// DES encryption algorithm.
Des = wincrypt::CALG_DES,
/// DESX encryption algorithm.
Desx = wincrypt::CALG_DESX,
/// Diffie-Hellman ephemeral key exchange algorithm.
DhEphem = wincrypt::CALG_DH_EPHEM,
/// Diffie-Hellman store and forward key exchange algorithm.
DhSf = wincrypt::CALG_DH_SF,
/// DSA public key signature algorithm.
DssSign = wincrypt::CALG_DSS_SIGN,
/// Elliptic curve Diffie-Hellman key exchange algorithm.
Ecdh = wincrypt::CALG_ECDH,
/// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
EcdhEphem = wincrypt::CALG_ECDH_EPHEM,
/// Elliptic curve digital signature algorithm.
Ecdsa = wincrypt::CALG_ECDSA,
/// One way function hashing algorithm.
HashReplaceOwf = wincrypt::CALG_HASH_REPLACE_OWF,
/// Hughes MD5 hashing algorithm.
HughesMd5 = wincrypt::CALG_HUGHES_MD5,
/// HMAC keyed hash algorithm.
Hmac = wincrypt::CALG_HMAC,
/// MAC keyed hash algorithm.
Mac = wincrypt::CALG_MAC,
/// MD2 hashing algorithm.
Md2 = wincrypt::CALG_MD2,
/// MD4 hashing algorithm.
Md4 = wincrypt::CALG_MD4,
/// MD5 hashing algorithm.
Md5 = wincrypt::CALG_MD5,
/// No signature algorithm..
NoSign = wincrypt::CALG_NO_SIGN,
/// RC2 block encryption algorithm.
Rc2 = wincrypt::CALG_RC2,
/// RC4 stream encryption algorithm.
Rc4 = wincrypt::CALG_RC4,
/// RC5 block encryption algorithm.
Rc5 = wincrypt::CALG_RC5,
/// RSA public key exchange algorithm.
RsaKeyx = wincrypt::CALG_RSA_KEYX,
/// RSA public key signature algorithm.
RsaSign = wincrypt::CALG_RSA_SIGN,
/// SHA hashing algorithm.
Sha1 = wincrypt::CALG_SHA1,
/// 256 bit SHA hashing algorithm.
Sha256 = wincrypt::CALG_SHA_256,
/// 384 bit SHA hashing algorithm.
Sha384 = wincrypt::CALG_SHA_384,
/// 512 bit SHA hashing algorithm.
Sha512 = wincrypt::CALG_SHA_512,
/// Triple DES encryption algorithm.
TripleDes = wincrypt::CALG_3DES,
/// Two-key triple DES encryption with effective key length equal to 112 bits.
TripleDes112 = wincrypt::CALG_3DES_112,
#[doc(hidden)]
__ForExtensibility,
}
/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
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,
#[doc(hidden)]
__ForExtensibility,
}
impl Protocol {
fn dword(self, direction: Direction) -> winapi::DWORD {
match (self, direction) {
(Protocol::Ssl3, Direction::Inbound) => um::schannel::SP_PROT_SSL3_SERVER,
(Protocol::Tls10, Direction::Inbound) => um::schannel::SP_PROT_TLS1_0_SERVER,
(Protocol::Tls11, Direction::Inbound) => um::schannel::SP_PROT_TLS1_1_SERVER,
(Protocol::Tls12, Direction::Inbound) => um::schannel::SP_PROT_TLS1_2_SERVER,
(Protocol::Ssl3, Direction::Outbound) => um::schannel::SP_PROT_SSL3_CLIENT,
(Protocol::Tls10, Direction::Outbound) => um::schannel::SP_PROT_TLS1_0_CLIENT,
(Protocol::Tls11, Direction::Outbound) => um::schannel::SP_PROT_TLS1_1_CLIENT,
(Protocol::Tls12, Direction::Outbound) => um::schannel::SP_PROT_TLS1_2_CLIENT,
(Protocol::__ForExtensibility, _) => unreachable!(),
}
}
}
/// 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 {
assert!(supported_algorithms.iter()
.all(|a| {
match *a {
Algorithm::__ForExtensibility => false,
_ => true,
}
}));
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 {
assert!(enabled_protocols.iter()
.all(|a| {
match *a {
Protocol::__ForExtensibility => false,
_ => true,
}
}));
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 = mem::zeroed();
let mut cred_data: um::schannel::SCHANNEL_CRED = mem::zeroed();
cred_data.dwVersion = um::schannel::SCHANNEL_CRED_VERSION;
cred_data.dwFlags = um::schannel::SCH_USE_STRONG_CRYPTO | um::schannel::SCH_CRED_NO_DEFAULT_CREDS;
if let Some(ref supported_algorithms) = self.supported_algorithms {
cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD;
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 winapi::DWORD;
cred_data.paCred = certs.as_mut_ptr();
let direction = match direction {
Direction::Inbound => sspi::SECPKG_CRED_INBOUND,
Direction::Outbound => sspi::SECPKG_CRED_OUTBOUND,
};
match sspi::AcquireCredentialsHandleA(ptr::null_mut(),
UNISP_NAME.as_ptr() as *const _ as *mut _,
direction,
ptr::null_mut(),
&mut cred_data as *mut _ as *mut _,
None,
ptr::null_mut(),
&mut handle,
ptr::null_mut()) {
winerror::SEC_E_OK => Ok(SchannelCred::from_inner(handle)),
err => Err(io::Error::from_raw_os_error(err as i32)),
}
}
}
}
/// An SChannel credential.
#[derive(Clone)]
pub struct SchannelCred(Arc<RawCredHandle>);
struct RawCredHandle(sspi::CredHandle);
impl Drop for RawCredHandle {
fn drop(&mut self) {
unsafe {
sspi::FreeCredentialsHandle(&mut self.0);
}
}
}
impl SchannelCred {
/// Returns a builder.
pub fn builder() -> Builder {
Builder::new()
}
unsafe fn from_inner(inner: sspi::CredHandle) -> SchannelCred {
SchannelCred(Arc::new(RawCredHandle(inner)))
}
pub(crate) fn as_inner(&self) -> sspi::CredHandle {
self.0.as_ref().0
}
}