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