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(), ¶ms[..]);
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