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::ca_certificate;
204
205 #[test]
206 fn server_client() {
207 let listener = p!(TcpListener::bind("localhost:0"));
208 let port = p!(listener.local_addr()).port();
209
210 let handle = thread::spawn(move || {
211 let dir = p!(tempdir());
212
213 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
214 let identity = identity(dir.path());
215 p!(ctx.set_certificate(&identity, &[]));
216
217 let stream = p!(listener.accept()).0;
218 let mut stream = p!(ctx.handshake(stream));
219
220 let mut buf = [0; 12];
221 p!(stream.read(&mut buf));
222 assert_eq!(&buf[..], b"hello world!");
223 });
224
225 let mut ctx = p!(SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM));
226 p!(ctx.set_break_on_server_auth(true));
227 let stream = p!(TcpStream::connect(("localhost", port)));
228
229 let stream = match ctx.handshake(stream) {
230 Ok(_) => panic!("unexpected success"),
231 Err(HandshakeError::Interrupted(stream)) => stream,
232 Err(err) => panic!("unexpected error {err:?}"),
233 };
234
235 assert!(stream.server_auth_completed());
236 let mut peer_trust = p!(stream.context().peer_trust2()).unwrap();
237 p!(peer_trust.set_anchor_certificates(&[ca_certificate()]));
238 p!(peer_trust.evaluate_with_error());
239
240 let mut stream = p!(stream.handshake());
241 p!(stream.write_all(b"hello world!"));
242
243 handle.join().unwrap();
244 }
245
246 #[test]
247 fn server_client_builders() {
248 let listener = p!(TcpListener::bind("localhost:0"));
249 let port = p!(listener.local_addr()).port();
250
251 let handle = thread::spawn(move || {
252 let dir = p!(tempdir());
253
254 let identity = identity(dir.path());
255 let builder = ServerBuilder::new(&identity, &[]);
256
257 let stream = p!(listener.accept()).0;
258 let mut stream = p!(builder.handshake(stream));
259
260 let mut buf = [0; 12];
261 p!(stream.read(&mut buf));
262 assert_eq!(&buf[..], b"hello world!");
263 });
264
265 let stream = p!(TcpStream::connect(("localhost", port)));
266 let mut stream = p!(ClientBuilder::new()
267 .anchor_certificates(&[ca_certificate()])
268 .handshake("foobar.com", stream));
269
270 p!(stream.write_all(b"hello world!"));
271
272 handle.join().unwrap();
273 }
274
275 #[test]
276 fn client_bad_cert() {
277 let _ = env_logger::try_init();
278
279 let listener = p!(TcpListener::bind("localhost:0"));
280 let port = p!(listener.local_addr()).port();
281
282 let handle = thread::spawn(move || {
283 let dir = p!(tempdir());
284
285 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
286 let identity = identity(dir.path());
287 p!(ctx.set_certificate(&identity, &[]));
288
289 let stream = p!(listener.accept()).0;
290 let _ = ctx.handshake(stream);
291 });
292
293 let stream = p!(TcpStream::connect(("localhost", port)));
294 assert!(ClientBuilder::new().handshake("foobar.com", stream).is_err());
295
296 handle.join().unwrap();
297 }
298
299 #[test]
300 fn client() {
301 let listener = p!(TcpListener::bind("localhost:0"));
302 let port = p!(listener.local_addr()).port();
303
304 let handle = thread::spawn(move || {
305 let dir = p!(tempdir());
306
307 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
308 let identity = identity(dir.path());
309 p!(ctx.set_certificate(&identity, &[]));
310
311 let stream = p!(listener.accept()).0;
312 let mut stream = p!(ctx.handshake(stream));
313
314 let mut buf = [0; 12];
315 p!(stream.read(&mut buf));
316 assert_eq!(&buf[..], b"hello world!");
317 });
318
319 let stream = p!(TcpStream::connect(("localhost", port)));
320 let mut stream = p!(ClientBuilder::new()
321 .anchor_certificates(&[ca_certificate()])
322 .handshake("foobar.com", stream));
323 p!(stream.write_all(b"hello world!"));
324
325 handle.join().unwrap();
326 }
327
328 #[test]
329 fn negotiated_cipher() {
330 let listener = p!(TcpListener::bind("localhost:0"));
331 let port = p!(listener.local_addr()).port();
332
333 let handle = thread::spawn(move || {
334 let dir = p!(tempdir());
335
336 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
337 let identity = identity(dir.path());
338 p!(ctx.set_certificate(&identity, &[]));
339 p!(ctx.set_enabled_ciphers(&[
340 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
341 CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
342 ]));
343
344 let stream = p!(listener.accept()).0;
345 let mut stream = p!(ctx.handshake(stream));
346 assert_eq!(
347 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
348 p!(stream.context().negotiated_cipher())
349 );
350 let mut buf = [0; 1];
351 p!(stream.read(&mut buf));
352 });
353
354 let mut ctx = p!(SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM));
355 p!(ctx.set_break_on_server_auth(true));
356 p!(ctx.set_enabled_ciphers(&[
357 CipherSuite::TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
358 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
359 ]));
360 let stream = p!(TcpStream::connect(("localhost", port)));
361
362 let stream = match ctx.handshake(stream) {
363 Ok(_) => panic!("unexpected success"),
364 Err(HandshakeError::Interrupted(stream)) => stream,
365 Err(err) => panic!("unexpected error {err:?}"),
366 };
367
368 let mut stream = p!(stream.handshake());
369 assert_eq!(
370 CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
371 p!(stream.context().negotiated_cipher())
372 );
373 p!(stream.write(&[0]));
374
375 handle.join().unwrap();
376 }
377
378 #[test]
379 fn dh_params() {
380 let params = include_bytes!("../../../test/dhparam.der");
381
382 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
383 assert!(p!(ctx.diffie_hellman_params()).is_none());
384 p!(ctx.set_diffie_hellman_params(params));
385 assert_eq!(p!(ctx.diffie_hellman_params()).unwrap(), ¶ms[..]);
386 }
387
388 #[test]
389 fn try_authenticate_no_cert() {
390 let listener = p!(TcpListener::bind("localhost:0"));
391 let port = p!(listener.local_addr()).port();
392
393 let handle = thread::spawn(move || {
394 let dir = p!(tempdir());
395
396 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
397 let identity = identity(dir.path());
398 p!(ctx.set_certificate(&identity, &[]));
399 p!(ctx.set_client_side_authenticate(SslAuthenticate::TRY));
400 let cert = ca_certificate();
401 p!(ctx.add_certificate_authorities(&[cert]));
402
403 let stream = p!(listener.accept()).0;
404 let mut stream = p!(ctx.handshake(stream));
405 let mut buf = [0; 1];
406 p!(stream.read(&mut buf));
407 });
408
409 let mut ctx = p!(SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM));
410 p!(ctx.set_break_on_server_auth(true));
411 let stream = p!(TcpStream::connect(("localhost", port)));
412
413 let stream = match ctx.handshake(stream) {
414 Ok(_) => panic!("unexpected success"),
415 Err(HandshakeError::Interrupted(stream)) => stream,
416 Err(err) => panic!("unexpected error {err:?}"),
417 };
418
419 let mut stream = p!(stream.handshake());
420 p!(stream.write(&[0]));
421
422 handle.join().unwrap();
423 }
424
425 #[test]
426 fn always_authenticate_no_cert() {
427 let listener = p!(TcpListener::bind("localhost:0"));
428 let port = p!(listener.local_addr()).port();
429
430 let handle = thread::spawn(move || {
431 let dir = p!(tempdir());
432
433 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
434 let identity = identity(dir.path());
435 p!(ctx.set_certificate(&identity, &[]));
436 p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS));
437
438 let stream = p!(listener.accept()).0;
439
440 match ctx.handshake(stream) {
441 Ok(_) => panic!("unexpected success"),
442 Err(HandshakeError::Failure(_)) => {},
443 Err(err) => panic!("unexpected error {err:?}"),
444 }
445 });
446
447 let mut ctx = p!(SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM));
448 p!(ctx.set_break_on_server_auth(true));
449 let stream = p!(TcpStream::connect(("localhost", port)));
450
451 let stream = match ctx.handshake(stream) {
452 Ok(_) => panic!("unexpected success"),
453 Err(HandshakeError::Interrupted(stream)) => stream,
454 Err(err) => panic!("unexpected error {err:?}"),
455 };
456
457 match stream.handshake() {
458 Ok(_) => panic!("unexpected success"),
459 Err(HandshakeError::Failure(_)) => {},
460 Err(err) => panic!("unexpected error {err:?}"),
461 }
462
463 handle.join().unwrap();
464 }
465
466 #[test]
467 fn always_authenticate_with_cert() {
468 let listener = p!(TcpListener::bind("localhost:0"));
469 let port = p!(listener.local_addr()).port();
470
471 let handle = thread::spawn(move || {
472 let dir = p!(tempdir());
473
474 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
475 let identity = identity(dir.path());
476 p!(ctx.set_certificate(&identity, &[]));
477 p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS));
478
479 let stream = p!(listener.accept()).0;
480
481 match ctx.handshake(stream) {
482 Ok(_) => panic!("unexpected success"),
483 Err(HandshakeError::Failure(_)) => {},
484 Err(err) => panic!("unexpected error {err:?}"),
485 }
486 });
487
488 let mut ctx = p!(SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM));
489 p!(ctx.set_break_on_server_auth(true));
490 let dir = p!(tempdir());
491 let identity = identity(dir.path());
492 p!(ctx.set_certificate(&identity, &[]));
493 let stream = p!(TcpStream::connect(("localhost", port)));
494
495 let stream = match ctx.handshake(stream) {
496 Ok(_) => panic!("unexpected success"),
497 Err(HandshakeError::Interrupted(stream)) => stream,
498 Err(err) => panic!("unexpected error {err:?}"),
499 };
500
501 match stream.handshake() {
502 Ok(_) => panic!("unexpected success"),
503 Err(HandshakeError::Failure(_)) => {},
504 Err(err) => panic!("unexpected error {err:?}"),
505 }
506
507 handle.join().unwrap();
508 }
509
510 #[test]
511 fn certificate_authorities() {
512 let mut ctx = p!(SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM));
513 assert!(p!(ctx.certificate_authorities()).is_none());
514 p!(ctx.set_certificate_authorities(&[ca_certificate()]));
515 assert_eq!(p!(ctx.certificate_authorities()).unwrap().len(), 1);
516 }
517
518 #[test]
519 fn close() {
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());
525
526 let identity = identity(dir.path());
527 let builder = ServerBuilder::new(&identity, &[]);
528
529 let stream = p!(listener.accept()).0;
530 let mut stream = p!(builder.handshake(stream));
531 p!(stream.close());
532 });
533
534 let stream = p!(TcpStream::connect(("localhost", port)));
535 let mut stream = p!(ClientBuilder::new()
536 .anchor_certificates(&[ca_certificate()])
537 .handshake("foobar.com", stream));
538
539 let mut buf = [0; 1];
540 assert_eq!(p!(stream.read(&mut buf)), 0);
541 p!(stream.close());
542
543 p!(handle.join());
544 }
545
546 #[test]
547 fn short_read() {
548 let listener = p!(TcpListener::bind("localhost:0"));
549 let port = p!(listener.local_addr()).port();
550
551 let handle = thread::spawn(move || {
552 let dir = p!(tempdir());
553
554 let identity = identity(dir.path());
555 let builder = ServerBuilder::new(&identity, &[]);
556
557 let stream = p!(listener.accept()).0;
558 let mut stream = p!(builder.handshake(stream));
559
560 stream.write_all(b"hello").unwrap();
561 stream
563 });
564
565 let stream = p!(TcpStream::connect(("localhost", port)));
566 let mut stream = p!(ClientBuilder::new()
567 .anchor_certificates(&[ca_certificate()])
568 .handshake("foobar.com", stream));
569
570 let mut b = [0; 1];
571 stream.read_exact(&mut b).unwrap();
572 assert_eq!(stream.context().buffered_read_size().unwrap(), 4);
573 let mut b = [0; 5];
574 let read = stream.read(&mut b).unwrap();
575 assert_eq!(read, 4);
576
577 p!(handle.join());
578 }
579}