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