1 //! Schannel credentials. 2 use winapi::shared::{sspi, winerror}; 3 use winapi::shared::minwindef as winapi; 4 use winapi::um::{self, wincrypt}; 5 use std::io; 6 use std::mem; 7 use std::ptr; 8 use std::sync::Arc; 9 10 use crate::Inner; 11 use crate::cert_context::CertContext; 12 13 lazy_static! { 14 static ref UNISP_NAME: Vec<u8> = um::schannel::UNISP_NAME.bytes().chain(Some(0)).collect(); 15 } 16 17 /// The communication direction that an `SchannelCred` will support. 18 #[derive(Copy, Debug, Clone, PartialEq, Eq)] 19 pub enum Direction { 20 /// Server-side, inbound connections. 21 Inbound, 22 /// Client-side, outbound connections. 23 Outbound, 24 } 25 26 /// Algorithms supported by Schannel. 27 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx 28 #[derive(Debug, Copy, Clone)] 29 #[repr(u32)] 30 pub enum Algorithm { 31 /// Advanced Encryption Standard (AES). 32 Aes = wincrypt::CALG_AES, 33 /// 128 bit AES. 34 Aes128 = wincrypt::CALG_AES_128, 35 /// 192 bit AES. 36 Aes192 = wincrypt::CALG_AES_192, 37 /// 256 bit AES. 38 Aes256 = wincrypt::CALG_AES_256, 39 /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys. 40 AgreedkeyAny = wincrypt::CALG_AGREEDKEY_ANY, 41 /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make 42 /// its key length 64 bits. 43 CylinkMek = wincrypt::CALG_CYLINK_MEK, 44 /// DES encryption algorithm. 45 Des = wincrypt::CALG_DES, 46 /// DESX encryption algorithm. 47 Desx = wincrypt::CALG_DESX, 48 /// Diffie-Hellman ephemeral key exchange algorithm. 49 DhEphem = wincrypt::CALG_DH_EPHEM, 50 /// Diffie-Hellman store and forward key exchange algorithm. 51 DhSf = wincrypt::CALG_DH_SF, 52 /// DSA public key signature algorithm. 53 DssSign = wincrypt::CALG_DSS_SIGN, 54 /// Elliptic curve Diffie-Hellman key exchange algorithm. 55 Ecdh = wincrypt::CALG_ECDH, 56 /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm. 57 EcdhEphem = wincrypt::CALG_ECDH_EPHEM, 58 /// Elliptic curve digital signature algorithm. 59 Ecdsa = wincrypt::CALG_ECDSA, 60 /// One way function hashing algorithm. 61 HashReplaceOwf = wincrypt::CALG_HASH_REPLACE_OWF, 62 /// Hughes MD5 hashing algorithm. 63 HughesMd5 = wincrypt::CALG_HUGHES_MD5, 64 /// HMAC keyed hash algorithm. 65 Hmac = wincrypt::CALG_HMAC, 66 /// MAC keyed hash algorithm. 67 Mac = wincrypt::CALG_MAC, 68 /// MD2 hashing algorithm. 69 Md2 = wincrypt::CALG_MD2, 70 /// MD4 hashing algorithm. 71 Md4 = wincrypt::CALG_MD4, 72 /// MD5 hashing algorithm. 73 Md5 = wincrypt::CALG_MD5, 74 /// No signature algorithm.. 75 NoSign = wincrypt::CALG_NO_SIGN, 76 /// RC2 block encryption algorithm. 77 Rc2 = wincrypt::CALG_RC2, 78 /// RC4 stream encryption algorithm. 79 Rc4 = wincrypt::CALG_RC4, 80 /// RC5 block encryption algorithm. 81 Rc5 = wincrypt::CALG_RC5, 82 /// RSA public key exchange algorithm. 83 RsaKeyx = wincrypt::CALG_RSA_KEYX, 84 /// RSA public key signature algorithm. 85 RsaSign = wincrypt::CALG_RSA_SIGN, 86 /// SHA hashing algorithm. 87 Sha1 = wincrypt::CALG_SHA1, 88 /// 256 bit SHA hashing algorithm. 89 Sha256 = wincrypt::CALG_SHA_256, 90 /// 384 bit SHA hashing algorithm. 91 Sha384 = wincrypt::CALG_SHA_384, 92 /// 512 bit SHA hashing algorithm. 93 Sha512 = wincrypt::CALG_SHA_512, 94 /// Triple DES encryption algorithm. 95 TripleDes = wincrypt::CALG_3DES, 96 /// Two-key triple DES encryption with effective key length equal to 112 bits. 97 TripleDes112 = wincrypt::CALG_3DES_112, 98 #[doc(hidden)] 99 __ForExtensibility, 100 } 101 102 /// Protocols supported by Schannel. 103 #[derive(Debug, Copy, Clone)] 104 pub enum Protocol { 105 /// Secure Sockets Layer 3.0 106 Ssl3, 107 /// Transport Layer Security 1.0 108 Tls10, 109 /// Transport Layer Security 1.1 110 Tls11, 111 /// Transport Layer Security 1.2 112 Tls12, 113 #[doc(hidden)] 114 __ForExtensibility, 115 } 116 117 impl Protocol { dword(self, direction: Direction) -> winapi::DWORD118 fn dword(self, direction: Direction) -> winapi::DWORD { 119 match (self, direction) { 120 (Protocol::Ssl3, Direction::Inbound) => um::schannel::SP_PROT_SSL3_SERVER, 121 (Protocol::Tls10, Direction::Inbound) => um::schannel::SP_PROT_TLS1_0_SERVER, 122 (Protocol::Tls11, Direction::Inbound) => um::schannel::SP_PROT_TLS1_1_SERVER, 123 (Protocol::Tls12, Direction::Inbound) => um::schannel::SP_PROT_TLS1_2_SERVER, 124 (Protocol::Ssl3, Direction::Outbound) => um::schannel::SP_PROT_SSL3_CLIENT, 125 (Protocol::Tls10, Direction::Outbound) => um::schannel::SP_PROT_TLS1_0_CLIENT, 126 (Protocol::Tls11, Direction::Outbound) => um::schannel::SP_PROT_TLS1_1_CLIENT, 127 (Protocol::Tls12, Direction::Outbound) => um::schannel::SP_PROT_TLS1_2_CLIENT, 128 (Protocol::__ForExtensibility, _) => unreachable!(), 129 } 130 } 131 } 132 133 /// A builder type for `SchannelCred`s. 134 #[derive(Default, Debug)] 135 pub struct Builder { 136 supported_algorithms: Option<Vec<Algorithm>>, 137 enabled_protocols: Option<Vec<Protocol>>, 138 certs: Vec<CertContext>, 139 } 140 141 impl Builder { 142 /// Returns a new `Builder`. new() -> Builder143 pub fn new() -> Builder { 144 Builder::default() 145 } 146 147 /// Sets the algorithms supported for credentials created from this builder. supported_algorithms(&mut self, supported_algorithms: &[Algorithm]) -> &mut Builder148 pub fn supported_algorithms(&mut self, 149 supported_algorithms: &[Algorithm]) 150 -> &mut Builder { 151 assert!(supported_algorithms.iter() 152 .all(|a| { 153 match *a { 154 Algorithm::__ForExtensibility => false, 155 _ => true, 156 } 157 })); 158 self.supported_algorithms = Some(supported_algorithms.to_owned()); 159 self 160 } 161 162 /// Sets the protocols enabled for credentials created from this builder. enabled_protocols(&mut self, enabled_protocols: &[Protocol]) -> &mut Builder163 pub fn enabled_protocols(&mut self, 164 enabled_protocols: &[Protocol]) 165 -> &mut Builder { 166 assert!(enabled_protocols.iter() 167 .all(|a| { 168 match *a { 169 Protocol::__ForExtensibility => false, 170 _ => true, 171 } 172 })); 173 self.enabled_protocols = Some(enabled_protocols.to_owned()); 174 self 175 } 176 177 /// Add a certificate to get passed down when the credentials are acquired. 178 /// 179 /// Certificates passed here may specify a certificate that contains a 180 /// private key to be used in authenticating the application. Typically, 181 /// this is called once for each key exchange method supported by 182 /// servers. 183 /// 184 /// Clients often do not call this function and either depend on Schannel to 185 /// find an appropriate certificate or create a certificate later if needed. cert(&mut self, cx: CertContext) -> &mut Builder186 pub fn cert(&mut self, cx: CertContext) -> &mut Builder { 187 self.certs.push(cx); 188 self 189 } 190 191 /// Creates a new `SchannelCred`. acquire(&self, direction: Direction) -> io::Result<SchannelCred>192 pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> { 193 unsafe { 194 let mut handle = mem::zeroed(); 195 let mut cred_data: um::schannel::SCHANNEL_CRED = mem::zeroed(); 196 cred_data.dwVersion = um::schannel::SCHANNEL_CRED_VERSION; 197 cred_data.dwFlags = um::schannel::SCH_USE_STRONG_CRYPTO | um::schannel::SCH_CRED_NO_DEFAULT_CREDS; 198 if let Some(ref supported_algorithms) = self.supported_algorithms { 199 cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD; 200 cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _; 201 } 202 if let Some(ref enabled_protocols) = self.enabled_protocols { 203 cred_data.grbitEnabledProtocols = enabled_protocols.iter() 204 .map(|p| p.dword(direction)) 205 .fold(0, |acc, p| acc | p); 206 } 207 let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>(); 208 cred_data.cCreds = certs.len() as winapi::DWORD; 209 cred_data.paCred = certs.as_mut_ptr(); 210 211 let direction = match direction { 212 Direction::Inbound => sspi::SECPKG_CRED_INBOUND, 213 Direction::Outbound => sspi::SECPKG_CRED_OUTBOUND, 214 }; 215 216 match sspi::AcquireCredentialsHandleA(ptr::null_mut(), 217 UNISP_NAME.as_ptr() as *const _ as *mut _, 218 direction, 219 ptr::null_mut(), 220 &mut cred_data as *mut _ as *mut _, 221 None, 222 ptr::null_mut(), 223 &mut handle, 224 ptr::null_mut()) { 225 winerror::SEC_E_OK => Ok(SchannelCred::from_inner(handle)), 226 err => Err(io::Error::from_raw_os_error(err as i32)), 227 } 228 } 229 } 230 } 231 232 /// An SChannel credential. 233 #[derive(Clone)] 234 pub struct SchannelCred(Arc<RawCredHandle>); 235 236 struct RawCredHandle(sspi::CredHandle); 237 238 impl Drop for RawCredHandle { drop(&mut self)239 fn drop(&mut self) { 240 unsafe { 241 sspi::FreeCredentialsHandle(&mut self.0); 242 } 243 } 244 } 245 246 impl SchannelCred { 247 /// Returns a builder. builder() -> Builder248 pub fn builder() -> Builder { 249 Builder::new() 250 } 251 from_inner(inner: sspi::CredHandle) -> SchannelCred252 unsafe fn from_inner(inner: sspi::CredHandle) -> SchannelCred { 253 SchannelCred(Arc::new(RawCredHandle(inner))) 254 } 255 as_inner(&self) -> sspi::CredHandle256 pub(crate) fn as_inner(&self) -> sspi::CredHandle { 257 self.0.as_ref().0 258 } 259 } 260