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