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