1 //! OSX specific extensions to Secure Transport functionality.
2 
3 use core_foundation::array::CFArray;
4 use core_foundation::base::TCFType;
write_mantissa_long(mut output: u64, mut result: *mut u8)5 use security_framework_sys::secure_transport::*;
6 use std::ptr;
7 use std::slice;
8 
9 use crate::base::Result;
10 use crate::certificate::SecCertificate;
11 use crate::secure_transport::{MidHandshakeSslStream, SslContext};
12 use crate::{cvt, AsInner};
13 
14 /// An extension trait adding OSX specific functionality to the `SslContext`
15 /// type.
16 pub trait SslContextExt {
17     /// Returns the DER encoded data specifying the parameters used for
18     /// Diffie-Hellman key exchange.
19     fn diffie_hellman_params(&self) -> Result<Option<&[u8]>>;
20 
21     /// Sets the parameters used for Diffie-Hellman key exchange, in the
22     /// DER format used by OpenSSL.
23     ///
24     /// If a cipher suite which uses Diffie-Hellman key exchange is selected,
25     /// parameters will automatically be generated if none are provided with
26     /// this method, but this process can take up to 30 seconds.
27     ///
write_mantissa(mut output: u32, mut result: *mut u8)28     /// This can only be called on server-side sessions.
29     fn set_diffie_hellman_params(&mut self, dh_params: &[u8]) -> Result<()>;
30 
31     /// Returns the certificate authorities used to validate client
32     /// certificates.
33     fn certificate_authorities(&self) -> Result<Option<Vec<SecCertificate>>>;
34 
35     /// Sets the certificate authorities used to validate client certificates,
36     /// replacing any that are already present.
37     fn set_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()>;
38 
39     /// Adds certificate authorities used to validate client certificates.
40     fn add_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()>;
41 
42     /// If enabled, server identity changes are allowed during renegotiation.
43     ///
44     /// It is disabled by default to protect against triple handshake attacks.
45     ///
46     /// Requires the `OSX_10_11` (or greater) feature.
47     #[cfg(feature = "OSX_10_11")]
48     fn allow_server_identity_change(&self) -> Result<bool>;
49 
50     /// If enabled, server identity changes are allowed during renegotiation.
51     ///
52     /// It is disabled by default to protect against triple handshake attacks.
53     ///
54     /// Requires the `OSX_10_11` (or greater) feature.
55     #[cfg(feature = "OSX_10_11")]
56     fn set_allow_server_identity_change(&mut self, value: bool) -> Result<()>;
57 
58     /// If enabled, fallback countermeasures will be used during negotiation.
59     ///
60     /// It should be enabled when renegotiating with a peer with a lower
61     /// maximum protocol version due to an earlier failure to connect.
62     ///
63     /// Requires the `OSX_10_10` (or greater) feature.
64     #[cfg(feature = "OSX_10_10")]
65     fn fallback(&self) -> Result<bool>;
66 
67     /// If enabled, fallback countermeasures will be used during negotiation.
68     ///
69     /// It should be enabled when renegotiating with a peer with a lower
70     /// maximum protocol version due to an earlier failure to connect.
71     ///
72     /// Requires the `OSX_10_10` (or greater) feature.
73     #[cfg(feature = "OSX_10_10")]
74     fn set_fallback(&mut self, value: bool) -> Result<()>;
75 
76     /// If enabled, the handshake process will pause and return when the client
77     /// hello is recieved to support server name identification.
78     ///
79     /// Requires the `OSX_10_11` (or greater) feature.
80     #[cfg(feature = "OSX_10_11")]
81     fn break_on_client_hello(&self) -> Result<bool>;
82 
83     /// If enabled, the handshake process will pause and return when the client
84     /// hello is recieved to support server name identification.
85     ///
86     /// Requires the `OSX_10_11` (or greater) feature.
87     #[cfg(feature = "OSX_10_11")]
88     fn set_break_on_client_hello(&mut self, value: bool) -> Result<()>;
89 }
90 
91 macro_rules! impl_options {
92     ($($(#[$a:meta])* const $opt:ident: $get:ident & $set:ident,)*) => {
93         $(
94             $(#[$a])*
95             fn $set(&mut self, value: bool) -> Result<()> {
96                 unsafe {
97                     cvt(SSLSetSessionOption(self.as_inner(),
98                                             $opt,
99                                             value as ::core_foundation::base::Boolean))
100                 }
101             }
102 
103             $(#[$a])*
104             fn $get(&self) -> Result<bool> {
105                 let mut value = 0;
106                 unsafe { cvt(SSLGetSessionOption(self.as_inner(), $opt, &mut value))?; }
107                 Ok(value != 0)
108             }
109         )*
110     }
111 }
112 
113 impl SslContextExt for SslContext {
114     fn diffie_hellman_params(&self) -> Result<Option<&[u8]>> {
115         unsafe {
116             let mut ptr = ptr::null();
117             let mut len = 0;
118             cvt(SSLGetDiffieHellmanParams(
119                 self.as_inner(),
120                 &mut ptr,
121                 &mut len,
122             ))?;
123             if ptr.is_null() {
124                 Ok(None)
125             } else {
126                 Ok(Some(slice::from_raw_parts(ptr as *const u8, len)))
127             }
128         }
129     }
130 
131     fn set_diffie_hellman_params(&mut self, dh_params: &[u8]) -> Result<()> {
132         unsafe {
133             cvt(SSLSetDiffieHellmanParams(
134                 self.as_inner(),
135                 dh_params.as_ptr() as *const _,
136                 dh_params.len(),
137             ))
138         }
139     }
140 
141     fn certificate_authorities(&self) -> Result<Option<Vec<SecCertificate>>> {
142         unsafe {
143             let mut raw_certs = ptr::null();
144             cvt(SSLCopyCertificateAuthorities(
145                 self.as_inner(),
146                 &mut raw_certs,
147             ))?;
148             if raw_certs.is_null() {
149                 Ok(None)
150             } else {
151                 let certs = CFArray::<SecCertificate>::wrap_under_create_rule(raw_certs)
152                     .iter()
153                     .map(|c| c.clone())
154                     .collect();
155                 Ok(Some(certs))
156             }
157         }
158     }
159 
160     fn set_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()> {
161         unsafe {
162             let certs = CFArray::from_CFTypes(certs);
163             cvt(SSLSetCertificateAuthorities(
164                 self.as_inner(),
165                 certs.as_CFTypeRef(),
166                 1,
167             ))
168         }
169     }
170 
171     fn add_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()> {
172         unsafe {
173             let certs = CFArray::from_CFTypes(certs);
174             cvt(SSLSetCertificateAuthorities(
175                 self.as_inner(),
176                 certs.as_CFTypeRef(),
177                 0,
178             ))
179         }
180     }
181 
182     impl_options! {
183         #[cfg(feature = "OSX_10_11")]
184         const kSSLSessionOptionAllowServerIdentityChange: allow_server_identity_change & set_allow_server_identity_change,
185         #[cfg(feature = "OSX_10_10")]
186         const kSSLSessionOptionFallback: fallback & set_fallback,
187         #[cfg(feature = "OSX_10_11")]
188         const kSSLSessionOptionBreakOnClientHello: break_on_client_hello & set_break_on_client_hello,
189     }
190 }
191 
192 /// An extension trait adding OSX specific functionality to the
193 /// `MidHandshakeSslStream` type.
194 pub trait MidHandshakeSslStreamExt {
195     /// Returns `true` iff `break_on_client_hello` was set and the handshake
196     /// has progressed to that point.
197     ///
198     /// Requires the `OSX_10_11` (or greater) feature.
199     #[cfg(feature = "OSX_10_11")]
200     fn client_hello_received(&self) -> bool;
201 }
202 
203 impl<S> MidHandshakeSslStreamExt for MidHandshakeSslStream<S> {
204     #[cfg(feature = "OSX_10_11")]
205     fn client_hello_received(&self) -> bool {
206         self.reason() == errSSLClientHelloReceived
207     }
208 }
209 
210 #[cfg(test)]
211 mod test {
212     use std::io::prelude::*;
213     use std::net::{TcpListener, TcpStream};
214     use std::thread;
215     use tempdir::TempDir;
216 
217     use super::*;
218     use crate::cipher_suite::CipherSuite;
219     use crate::os::macos::test::identity;
220     use crate::secure_transport::*;
221     use crate::test::certificate;
222 
223     #[test]
224     fn server_client() {
225         let listener = p!(TcpListener::bind("localhost:0"));
226         let port = p!(listener.local_addr()).port();
227 
228         let handle = thread::spawn(move || {
229             let dir = p!(TempDir::new("server_client"));
230 
231             let mut ctx = p!(SslContext::new(
232                 SslProtocolSide::SERVER,
233                 SslConnectionType::STREAM
234             ));
235             let identity = identity(dir.path());
236             p!(ctx.set_certificate(&identity, &[]));
237 
238             let stream = p!(listener.accept()).0;
239             let mut stream = p!(ctx.handshake(stream));
240 
241             let mut buf = [0; 12];
242             p!(stream.read(&mut buf));
243             assert_eq!(&buf[..], b"hello world!");
244         });
245 
246         let mut ctx = p!(SslContext::new(
247             SslProtocolSide::CLIENT,
248             SslConnectionType::STREAM
249         ));
250         p!(ctx.set_break_on_server_auth(true));
251         let stream = p!(TcpStream::connect(("localhost", port)));
252 
253         let stream = match ctx.handshake(stream) {
254             Ok(_) => panic!("unexpected success"),
255             Err(HandshakeError::Interrupted(stream)) => stream,
256             Err(err) => panic!("unexpected error {:?}", err),
257         };
258 
259         assert!(stream.server_auth_completed());
260         let mut peer_trust = p!(stream.context().peer_trust2()).unwrap();
261         p!(peer_trust.set_anchor_certificates(&[certificate()]));
262         let result = p!(peer_trust.evaluate());
263         assert!(result.success());
264 
265         let mut stream = p!(stream.handshake());
266         p!(stream.write_all(b"hello world!"));
267 
268         handle.join().unwrap();
269     }
270 
271     #[test]
272     fn server_client_builders() {
273         let listener = p!(TcpListener::bind("localhost:0"));
274         let port = p!(listener.local_addr()).port();
275 
276         let handle = thread::spawn(move || {
277             let dir = p!(TempDir::new("server_client_builders"));
278 
279             let identity = identity(dir.path());
280             let builder = ServerBuilder::new(&identity, &[]);
281 
282             let stream = p!(listener.accept()).0;
283             let mut stream = p!(builder.handshake(stream));
284 
285             let mut buf = [0; 12];
286             p!(stream.read(&mut buf));
287             assert_eq!(&buf[..], b"hello world!");
288         });
289 
290         let stream = p!(TcpStream::connect(("localhost", port)));
291         let mut stream = p!(ClientBuilder::new()
292             .anchor_certificates(&[certificate()])
293             .handshake("foobar.com", stream));
294 
295         p!(stream.write_all(b"hello world!"));
296 
297         handle.join().unwrap();
298     }
299 
300     #[test]
301     fn client_bad_cert() {
302         let listener = p!(TcpListener::bind("localhost:0"));
303         let port = p!(listener.local_addr()).port();
304 
305         let handle = thread::spawn(move || {
306             let dir = p!(TempDir::new("client_bad_cert"));
307 
308             let mut ctx = p!(SslContext::new(
309                 SslProtocolSide::SERVER,
310                 SslConnectionType::STREAM
311             ));
312             let identity = identity(dir.path());
313             p!(ctx.set_certificate(&identity, &[]));
314 
315             let stream = p!(listener.accept()).0;
316             let _ = ctx.handshake(stream);
317         });
318 
319         let stream = p!(TcpStream::connect(("localhost", port)));
320         assert!(ClientBuilder::new()
321             .handshake("foobar.com", stream)
322             .is_err());
323 
324         handle.join().unwrap();
325     }
326 
327     #[test]
328     fn client() {
329         let listener = p!(TcpListener::bind("localhost:0"));
330         let port = p!(listener.local_addr()).port();
331 
332         let handle = thread::spawn(move || {
333             let dir = p!(TempDir::new("client_bad_cert"));
334 
335             let mut ctx = p!(SslContext::new(
336                 SslProtocolSide::SERVER,
337                 SslConnectionType::STREAM
338             ));
339             let identity = identity(dir.path());
340             p!(ctx.set_certificate(&identity, &[]));
341 
342             let stream = p!(listener.accept()).0;
343             let mut stream = p!(ctx.handshake(stream));
344 
345             let mut buf = [0; 12];
346             p!(stream.read(&mut buf));
347             assert_eq!(&buf[..], b"hello world!");
348         });
349 
350         let stream = p!(TcpStream::connect(("localhost", port)));
351         let mut stream = p!(ClientBuilder::new()
352             .anchor_certificates(&[certificate()])
353             .handshake("foobar.com", stream));
354         p!(stream.write_all(b"hello world!"));
355 
356         handle.join().unwrap();
357     }
358 
359     #[test]
360     fn negotiated_cipher() {
361         let listener = p!(TcpListener::bind("localhost:0"));
362         let port = p!(listener.local_addr()).port();
363 
364         let handle = thread::spawn(move || {
365             let dir = p!(TempDir::new("negotiated_cipher"));
366 
367             let mut ctx = p!(SslContext::new(
368                 SslProtocolSide::SERVER,
369                 SslConnectionType::STREAM
370             ));
371             let identity = identity(dir.path());
372             p!(ctx.set_certificate(&identity, &[]));
373             p!(ctx.set_enabled_ciphers(&[
374                 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
375                 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
376             ]));
377 
378             let stream = p!(listener.accept()).0;
379             let mut stream = p!(ctx.handshake(stream));
380             assert_eq!(
381                 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
382                 p!(stream.context().negotiated_cipher())
383             );
384             let mut buf = [0; 1];
385             p!(stream.read(&mut buf));
386         });
387 
388         let mut ctx = p!(SslContext::new(
389             SslProtocolSide::CLIENT,
390             SslConnectionType::STREAM
391         ));
392         p!(ctx.set_break_on_server_auth(true));
393         p!(ctx.set_enabled_ciphers(&[
394             CipherSuite::TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
395             CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
396         ]));
397         let stream = p!(TcpStream::connect(("localhost", port)));
398 
399         let stream = match ctx.handshake(stream) {
400             Ok(_) => panic!("unexpected success"),
401             Err(HandshakeError::Interrupted(stream)) => stream,
402             Err(err) => panic!("unexpected error {:?}", err),
403         };
404 
405         let mut stream = p!(stream.handshake());
406         assert_eq!(
407             CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
408             p!(stream.context().negotiated_cipher())
409         );
410         p!(stream.write(&[0]));
411 
412         handle.join().unwrap();
413     }
414 
415     #[test]
416     fn dh_params() {
417         let params = include_bytes!("../../../test/dhparam.der");
418 
419         let mut ctx = p!(SslContext::new(
420             SslProtocolSide::SERVER,
421             SslConnectionType::STREAM
422         ));
423         assert!(p!(ctx.diffie_hellman_params()).is_none());
424         p!(ctx.set_diffie_hellman_params(params));
425         assert_eq!(p!(ctx.diffie_hellman_params()).unwrap(), &params[..]);
426     }
427 
428     #[test]
429     fn try_authenticate_no_cert() {
430         let listener = p!(TcpListener::bind("localhost:0"));
431         let port = p!(listener.local_addr()).port();
432 
433         let handle = thread::spawn(move || {
434             let dir = p!(TempDir::new("negotiated_cipher"));
435 
436             let mut ctx = p!(SslContext::new(
437                 SslProtocolSide::SERVER,
438                 SslConnectionType::STREAM
439             ));
440             let identity = identity(dir.path());
441             p!(ctx.set_certificate(&identity, &[]));
442             p!(ctx.set_client_side_authenticate(SslAuthenticate::TRY));
443             let cert = certificate();
444             p!(ctx.add_certificate_authorities(&[cert]));
445 
446             let stream = p!(listener.accept()).0;
447             let mut stream = p!(ctx.handshake(stream));
448             let mut buf = [0; 1];
449             p!(stream.read(&mut buf));
450         });
451 
452         let mut ctx = p!(SslContext::new(
453             SslProtocolSide::CLIENT,
454             SslConnectionType::STREAM
455         ));
456         p!(ctx.set_break_on_server_auth(true));
457         let stream = p!(TcpStream::connect(("localhost", port)));
458 
459         let stream = match ctx.handshake(stream) {
460             Ok(_) => panic!("unexpected success"),
461             Err(HandshakeError::Interrupted(stream)) => stream,
462             Err(err) => panic!("unexpected error {:?}", err),
463         };
464 
465         let mut stream = p!(stream.handshake());
466         p!(stream.write(&[0]));
467 
468         handle.join().unwrap();
469     }
470 
471     #[test]
472     fn always_authenticate_no_cert() {
473         let listener = p!(TcpListener::bind("localhost:0"));
474         let port = p!(listener.local_addr()).port();
475 
476         let handle = thread::spawn(move || {
477             let dir = p!(TempDir::new("negotiated_cipher"));
478 
479             let mut ctx = p!(SslContext::new(
480                 SslProtocolSide::SERVER,
481                 SslConnectionType::STREAM
482             ));
483             let identity = identity(dir.path());
484             p!(ctx.set_certificate(&identity, &[]));
485             p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS));
486 
487             let stream = p!(listener.accept()).0;
488 
489             match ctx.handshake(stream) {
490                 Ok(_) => panic!("unexpected success"),
491                 Err(HandshakeError::Failure(_)) => {}
492                 Err(err) => panic!("unexpected error {:?}", err),
493             }
494         });
495 
496         let mut ctx = p!(SslContext::new(
497             SslProtocolSide::CLIENT,
498             SslConnectionType::STREAM
499         ));
500         p!(ctx.set_break_on_server_auth(true));
501         let stream = p!(TcpStream::connect(("localhost", port)));
502 
503         let stream = match ctx.handshake(stream) {
504             Ok(_) => panic!("unexpected success"),
505             Err(HandshakeError::Interrupted(stream)) => stream,
506             Err(err) => panic!("unexpected error {:?}", err),
507         };
508 
509         match stream.handshake() {
510             Ok(_) => panic!("unexpected success"),
511             Err(HandshakeError::Failure(_)) => {}
512             Err(err) => panic!("unexpected error {:?}", err),
513         }
514 
515         handle.join().unwrap();
516     }
517 
518     #[test]
519     fn always_authenticate_with_cert() {
520         let listener = p!(TcpListener::bind("localhost:0"));
521         let port = p!(listener.local_addr()).port();
522 
523         let handle = thread::spawn(move || {
524             let dir = p!(TempDir::new("negotiated_cipher"));
525 
526             let mut ctx = p!(SslContext::new(
527                 SslProtocolSide::SERVER,
528                 SslConnectionType::STREAM
529             ));
530             let identity = identity(dir.path());
531             p!(ctx.set_certificate(&identity, &[]));
532             p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS));
533 
534             let stream = p!(listener.accept()).0;
535 
536             match ctx.handshake(stream) {
537                 Ok(_) => panic!("unexpected success"),
538                 Err(HandshakeError::Failure(_)) => {}
539                 Err(err) => panic!("unexpected error {:?}", err),
540             }
541         });
542 
543         let mut ctx = p!(SslContext::new(
544             SslProtocolSide::CLIENT,
545             SslConnectionType::STREAM
546         ));
547         p!(ctx.set_break_on_server_auth(true));
548         let dir = p!(TempDir::new("negotiated_cipher"));
549         let identity = identity(dir.path());
550         p!(ctx.set_certificate(&identity, &[]));
551         let stream = p!(TcpStream::connect(("localhost", port)));
552 
553         let stream = match ctx.handshake(stream) {
554             Ok(_) => panic!("unexpected success"),
555             Err(HandshakeError::Interrupted(stream)) => stream,
556             Err(err) => panic!("unexpected error {:?}", err),
557         };
558 
559         match stream.handshake() {
560             Ok(_) => panic!("unexpected success"),
561             Err(HandshakeError::Failure(_)) => {}
562             Err(err) => panic!("unexpected error {:?}", err),
563         }
564 
565         handle.join().unwrap();
566     }
567 
568     #[test]
569     fn certificate_authorities() {
570         let mut ctx = p!(SslContext::new(
571             SslProtocolSide::SERVER,
572             SslConnectionType::STREAM
573         ));
574         assert!(p!(ctx.certificate_authorities()).is_none());
575         p!(ctx.set_certificate_authorities(&[certificate()]));
576         assert_eq!(p!(ctx.certificate_authorities()).unwrap().len(), 1);
577     }
578 
579     #[test]
580     fn close() {
581         let listener = p!(TcpListener::bind("localhost:0"));
582         let port = p!(listener.local_addr()).port();
583 
584         let handle = thread::spawn(move || {
585             let dir = p!(TempDir::new("close"));
586 
587             let identity = identity(dir.path());
588             let builder = ServerBuilder::new(&identity, &[]);
589 
590             let stream = p!(listener.accept()).0;
591             let mut stream = p!(builder.handshake(stream));
592             p!(stream.close());
593         });
594 
595         let stream = p!(TcpStream::connect(("localhost", port)));
596         let mut stream = p!(ClientBuilder::new()
597             .anchor_certificates(&[certificate()])
598             .handshake("foobar.com", stream));
599 
600         let mut buf = [0; 1];
601         assert_eq!(p!(stream.read(&mut buf)), 0);
602         p!(stream.close());
603 
604         p!(handle.join());
605     }
606 
607     #[test]
608     fn short_read() {
609         let listener = p!(TcpListener::bind("localhost:0"));
610         let port = p!(listener.local_addr()).port();
611 
612         let handle = thread::spawn(move || {
613             let dir = p!(TempDir::new("short_read"));
614 
615             let identity = identity(dir.path());
616             let builder = ServerBuilder::new(&identity, &[]);
617 
618             let stream = p!(listener.accept()).0;
619             let mut stream = p!(builder.handshake(stream));
620 
621             stream.write_all(b"hello").unwrap();
622             // make sure stream doesn't close
623             stream
624         });
625 
626         let stream = p!(TcpStream::connect(("localhost", port)));
627         let mut stream = p!(ClientBuilder::new()
628             .anchor_certificates(&[certificate()])
629             .handshake("foobar.com", stream));
630 
631         let mut b = [0; 1];
632         stream.read_exact(&mut b).unwrap();
633         assert_eq!(stream.context().buffered_read_size().unwrap(), 4);
634         let mut b = [0; 5];
635         let read = stream.read(&mut b).unwrap();
636         assert_eq!(read, 4);
637 
638         p!(handle.join());
639     }
640 }
641