diff options
Diffstat (limited to 'src/tls.rs')
-rw-r--r-- | src/tls.rs | 352 |
1 files changed, 252 insertions, 100 deletions
@@ -47,6 +47,7 @@ use crate::packet; const TLS1_3_VERSION: u16 = 0x0304; const TLS_ALERT_ERROR: u64 = 0x100; +const INTERNAL_ERROR: u64 = 0x01; #[allow(non_camel_case_types)] #[repr(transparent)] @@ -122,6 +123,47 @@ struct SSL_QUIC_METHOD { extern fn(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int, } +#[cfg(test)] +#[repr(C)] +#[allow(non_camel_case_types)] +#[allow(dead_code)] +enum ssl_private_key_result_t { + ssl_private_key_success, + ssl_private_key_retry, + ssl_private_key_failure, +} + +#[cfg(test)] +#[repr(C)] +#[allow(non_camel_case_types)] +struct SSL_PRIVATE_KEY_METHOD { + sign: extern fn( + ssl: *mut SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + signature_algorithm: u16, + r#in: *const u8, + in_len: usize, + ) -> ssl_private_key_result_t, + + decrypt: extern fn( + ssl: *mut SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + r#in: *const u8, + in_len: usize, + ) -> ssl_private_key_result_t, + + complete: extern fn( + ssl: *mut SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + ) -> ssl_private_key_result_t, +} + lazy_static::lazy_static! { /// BoringSSL Extra Data Index for Quiche Connections pub static ref QUICHE_EX_DATA_INDEX: c_int = unsafe { @@ -167,7 +209,7 @@ impl Context { pub fn new_handshake(&mut self) -> Result<Handshake> { unsafe { let ssl = SSL_new(self.as_mut_ptr()); - Ok(Handshake(ssl)) + Ok(Handshake::new(ssl)) } } @@ -277,11 +319,9 @@ impl Context { } pub fn set_verify(&mut self, verify: bool) { - let mode = if verify { - 0x01 // SSL_VERIFY_PEER - } else { - 0x00 // SSL_VERIFY_NONE - }; + // true -> 0x01 SSL_VERIFY_PEER + // false -> 0x00 SSL_VERIFY_NONE + let mode = i32::from(verify); unsafe { SSL_CTX_set_verify(self.as_mut_ptr(), mode, ptr::null()); @@ -294,12 +334,12 @@ impl Context { } } - pub fn set_alpn(&mut self, v: &[Vec<u8>]) -> Result<()> { + pub fn set_alpn(&mut self, v: &[&[u8]]) -> Result<()> { let mut protos: Vec<u8> = Vec::new(); for proto in v { protos.push(proto.len() as u8); - protos.append(&mut proto.clone()); + protos.extend_from_slice(proto); } // Configure ALPN for servers. @@ -332,7 +372,7 @@ impl Context { } pub fn set_early_data_enabled(&mut self, enabled: bool) { - let enabled = if enabled { 1 } else { 0 }; + let enabled = i32::from(enabled); unsafe { SSL_CTX_set_early_data_enabled(self.as_mut_ptr(), enabled); @@ -358,13 +398,25 @@ impl Drop for Context { } } -pub struct Handshake(*mut SSL); +pub struct Handshake { + /// Raw pointer + ptr: *mut SSL, + /// SSL_process_quic_post_handshake should be called when whenever + /// SSL_provide_quic_data is called to process the provided data. + provided_data_outstanding: bool, +} impl Handshake { #[cfg(feature = "ffi")] pub unsafe fn from_ptr(ssl: *mut c_void) -> Handshake { - let ssl = ssl as *mut SSL; - Handshake(ssl) + Handshake::new(ssl as *mut SSL) + } + + fn new(ptr: *mut SSL) -> Handshake { + Handshake { + ptr, + provided_data_outstanding: false, + } } pub fn get_error(&self, ret_code: c_int) -> c_int { @@ -439,16 +491,14 @@ impl Handshake { } pub fn set_quiet_shutdown(&mut self, mode: bool) { - unsafe { - SSL_set_quiet_shutdown(self.as_mut_ptr(), if mode { 1 } else { 0 }) - } + unsafe { SSL_set_quiet_shutdown(self.as_mut_ptr(), i32::from(mode)) } } pub fn set_host_name(&mut self, name: &str) -> Result<()> { let cstr = ffi::CString::new(name).map_err(|_| Error::TlsFail)?; let rc = unsafe { SSL_set_tlsext_host_name(self.as_mut_ptr(), cstr.as_ptr()) }; - map_result_ssl(self, rc)?; + self.map_result_ssl(rc)?; let param = unsafe { SSL_get0_param(self.as_mut_ptr()) }; @@ -465,14 +515,7 @@ impl Handshake { buf.len(), ) }; - map_result_ssl(self, rc) - } - - #[cfg(test)] - pub fn set_options(&mut self, opts: u32) { - unsafe { - SSL_set_options(self.as_mut_ptr(), opts); - } + self.map_result_ssl(rc) } pub fn quic_transport_params(&self) -> &[u8] { @@ -547,6 +590,7 @@ impl Handshake { pub fn provide_data( &mut self, level: crypto::Level, buf: &[u8], ) -> Result<()> { + self.provided_data_outstanding = true; let rc = unsafe { SSL_provide_quic_data( self.as_mut_ptr(), @@ -555,7 +599,7 @@ impl Handshake { buf.len(), ) }; - map_result_ssl(self, rc) + self.map_result_ssl(rc) } pub fn do_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { @@ -563,15 +607,24 @@ impl Handshake { let rc = unsafe { SSL_do_handshake(self.as_mut_ptr()) }; self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?; - map_result_ssl(self, rc) + self.set_transport_error(ex_data, rc); + self.map_result_ssl(rc) } pub fn process_post_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { + // If SSL_provide_quic_data hasn't been called since we last called + // SSL_process_quic_post_handshake, then there's nothing to do. + if !self.provided_data_outstanding { + return Ok(()); + } + self.provided_data_outstanding = false; + self.set_ex_data(*QUICHE_EX_DATA_INDEX, ex_data)?; let rc = unsafe { SSL_process_quic_post_handshake(self.as_mut_ptr()) }; self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?; - map_result_ssl(self, rc) + self.set_transport_error(ex_data, rc); + self.map_result_ssl(rc) } pub fn reset_early_data_reject(&mut self) { @@ -625,6 +678,39 @@ impl Handshake { Some(sigalg.to_string()) } + pub fn peer_cert_chain(&self) -> Option<Vec<&[u8]>> { + let cert_chain = unsafe { + let chain = + map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?; + + let num = sk_num(chain); + if num <= 0 { + return None; + } + + let mut cert_chain = vec![]; + for i in 0..num { + let buffer = + map_result_ptr(sk_value(chain, i) as *const CRYPTO_BUFFER) + .ok()?; + + let out_len = CRYPTO_BUFFER_len(buffer); + if out_len == 0 { + return None; + } + + let out = CRYPTO_BUFFER_data(buffer); + let slice = slice::from_raw_parts(out, out_len); + + cert_chain.push(slice); + } + + cert_chain + }; + + Some(cert_chain) + } + pub fn peer_cert(&self) -> Option<&[u8]> { let peer_cert = unsafe { let chain = @@ -643,12 +729,57 @@ impl Handshake { } let out = CRYPTO_BUFFER_data(buffer); - slice::from_raw_parts(out, out_len as usize) + slice::from_raw_parts(out, out_len) }; Some(peer_cert) } + #[cfg(test)] + pub fn set_options(&mut self, opts: u32) { + unsafe { + SSL_set_options(self.as_mut_ptr(), opts); + } + } + + // Only used for testing handling of failure during key signing. + #[cfg(test)] + pub fn set_failing_private_key_method(&mut self) { + extern fn failing_sign( + _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize, + _signature_algorithm: u16, _in: *const u8, _in_len: usize, + ) -> ssl_private_key_result_t { + ssl_private_key_result_t::ssl_private_key_failure + } + + extern fn failing_decrypt( + _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize, + _in: *const u8, _in_len: usize, + ) -> ssl_private_key_result_t { + ssl_private_key_result_t::ssl_private_key_failure + } + + extern fn failing_complete( + _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize, + ) -> ssl_private_key_result_t { + ssl_private_key_result_t::ssl_private_key_failure + } + + static QUICHE_PRIVATE_KEY_METHOD: SSL_PRIVATE_KEY_METHOD = + SSL_PRIVATE_KEY_METHOD { + decrypt: failing_decrypt, + sign: failing_sign, + complete: failing_complete, + }; + + unsafe { + SSL_set_private_key_method( + self.as_mut_ptr(), + &QUICHE_PRIVATE_KEY_METHOD, + ); + } + } + pub fn is_completed(&self) -> bool { unsafe { SSL_in_init(self.as_ptr()) == 0 } } @@ -663,15 +794,84 @@ impl Handshake { pub fn clear(&mut self) -> Result<()> { let rc = unsafe { SSL_clear(self.as_mut_ptr()) }; - map_result_ssl(self, rc) + self.map_result_ssl(rc) } fn as_ptr(&self) -> *const SSL { - self.0 + self.ptr } fn as_mut_ptr(&mut self) -> *mut SSL { - self.0 + self.ptr + } + + fn map_result_ssl(&mut self, bssl_result: c_int) -> Result<()> { + match bssl_result { + 1 => Ok(()), + + _ => { + let ssl_err = self.get_error(bssl_result); + match ssl_err { + // SSL_ERROR_SSL + 1 => { + log_ssl_error(); + + Err(Error::TlsFail) + }, + + // SSL_ERROR_WANT_READ + 2 => Err(Error::Done), + + // SSL_ERROR_WANT_WRITE + 3 => Err(Error::Done), + + // SSL_ERROR_WANT_X509_LOOKUP + 4 => Err(Error::Done), + + // SSL_ERROR_SYSCALL + 5 => Err(Error::TlsFail), + + // SSL_ERROR_PENDING_SESSION + 11 => Err(Error::Done), + + // SSL_ERROR_PENDING_CERTIFICATE + 12 => Err(Error::Done), + + // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION + 13 => Err(Error::Done), + + // SSL_ERROR_PENDING_TICKET + 14 => Err(Error::Done), + + // SSL_ERROR_EARLY_DATA_REJECTED + 15 => { + self.reset_early_data_reject(); + Err(Error::Done) + }, + + // SSL_ERROR_WANT_CERTIFICATE_VERIFY + 16 => Err(Error::Done), + + _ => Err(Error::TlsFail), + } + }, + } + } + + fn set_transport_error(&mut self, ex_data: &mut ExData, bssl_result: c_int) { + // SSL_ERROR_SSL + if self.get_error(bssl_result) == 1 { + // SSL_ERROR_SSL can't be recovered so ensure we set a + // local_error so the connection is closed. + // See https://www.openssl.org/docs/man1.1.1/man3/SSL_get_error.html + if ex_data.local_error.is_none() { + *ex_data.local_error = Some(ConnectionError { + is_app: false, + error_code: INTERNAL_ERROR, + reason: Vec::new(), + }) + } + } } } @@ -692,7 +892,7 @@ impl Drop for Handshake { pub struct ExData<'a> { pub application_protos: &'a Vec<Vec<u8>>, - pub pkt_num_spaces: &'a mut [packet::PktNumSpace; packet::EPOCH_COUNT], + pub pkt_num_spaces: &'a mut [packet::PktNumSpace; packet::Epoch::count()], pub session: &'a mut Option<Vec<u8>>, @@ -740,13 +940,13 @@ extern fn set_read_secret( let space = match level { crypto::Level::Initial => - &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL], + &mut ex_data.pkt_num_spaces[packet::Epoch::Initial], crypto::Level::ZeroRTT => - &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION], + &mut ex_data.pkt_num_spaces[packet::Epoch::Application], crypto::Level::Handshake => - &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE], + &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake], crypto::Level::OneRTT => - &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION], + &mut ex_data.pkt_num_spaces[packet::Epoch::Application], }; let aead = match get_cipher_from_ptr(cipher) { @@ -791,13 +991,13 @@ extern fn set_write_secret( let space = match level { crypto::Level::Initial => - &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL], + &mut ex_data.pkt_num_spaces[packet::Epoch::Initial], crypto::Level::ZeroRTT => - &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION], + &mut ex_data.pkt_num_spaces[packet::Epoch::Application], crypto::Level::Handshake => - &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE], + &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake], crypto::Level::OneRTT => - &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION], + &mut ex_data.pkt_num_spaces[packet::Epoch::Application], }; let aead = match get_cipher_from_ptr(cipher) { @@ -843,12 +1043,12 @@ extern fn add_handshake_data( let space = match level { crypto::Level::Initial => - &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL], + &mut ex_data.pkt_num_spaces[packet::Epoch::Initial], crypto::Level::ZeroRTT => unreachable!(), crypto::Level::Handshake => - &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE], + &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake], crypto::Level::OneRTT => - &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION], + &mut ex_data.pkt_num_spaces[packet::Epoch::Application], }; if space.crypto_stream.send.write(buf, false).is_err() { @@ -966,7 +1166,7 @@ extern fn new_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int { None => return 0, }; - let handshake = Handshake(ssl); + let handshake = Handshake::new(ssl); let peer_params = handshake.quic_transport_params(); // Serialize session object into buffer. @@ -1040,59 +1240,6 @@ fn map_result_ptr<'a, T>(bssl_result: *const T) -> Result<&'a T> { } } -fn map_result_ssl(ssl: &mut Handshake, bssl_result: c_int) -> Result<()> { - match bssl_result { - 1 => Ok(()), - - _ => { - let ssl_err = ssl.get_error(bssl_result); - match ssl_err { - // SSL_ERROR_SSL - 1 => { - log_ssl_error(); - - Err(Error::TlsFail) - }, - - // SSL_ERROR_WANT_READ - 2 => Err(Error::Done), - - // SSL_ERROR_WANT_WRITE - 3 => Err(Error::Done), - - // SSL_ERROR_WANT_X509_LOOKUP - 4 => Err(Error::Done), - - // SSL_ERROR_SYSCALL - 5 => Err(Error::TlsFail), - - // SSL_ERROR_PENDING_SESSION - 11 => Err(Error::Done), - - // SSL_ERROR_PENDING_CERTIFICATE - 12 => Err(Error::Done), - - // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION - 13 => Err(Error::Done), - - // SSL_ERROR_PENDING_TICKET - 14 => Err(Error::Done), - - // SSL_ERROR_EARLY_DATA_REJECTED - 15 => { - ssl.reset_early_data_reject(); - Err(Error::Done) - }, - - // SSL_ERROR_WANT_CERTIFICATE_VERIFY - 16 => Err(Error::Done), - - _ => Err(Error::TlsFail), - } - }, - } -} - fn log_ssl_error() { let err = [0; 1024]; @@ -1211,9 +1358,6 @@ extern { ssl: *mut SSL, params: *const u8, params_len: usize, ) -> c_int; - #[cfg(test)] - fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32; - fn SSL_set_quic_method( ssl: *mut SSL, quic_method: *const SSL_QUIC_METHOD, ) -> c_int; @@ -1224,6 +1368,14 @@ extern { ssl: *mut SSL, context: *const u8, context_len: usize, ) -> c_int; + #[cfg(test)] + fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32; + + #[cfg(test)] + fn SSL_set_private_key_method( + ssl: *mut SSL, key_method: *const SSL_PRIVATE_KEY_METHOD, + ); + fn SSL_get_peer_quic_transport_params( ssl: *const SSL, out_params: *mut *const u8, out_params_len: *mut usize, ); |