recv: recognize alert

master
occheung 2020-12-03 16:51:58 +08:00
parent fe6418df36
commit 5ca7c6b3ff
2 changed files with 140 additions and 66 deletions

View File

@ -73,7 +73,7 @@ pub(crate) fn parse_tls_repr(bytes: &[u8]) -> IResult<&[u8], (&[u8], TlsRepr)> {
)(bytes)?; )(bytes)?;
repr.handshake = Some(handshake); repr.handshake = Some(handshake);
}, },
ChangeCipherSpec | ApplicationData => { ChangeCipherSpec | ApplicationData | Alert => {
let mut vec: Vec<u8> = Vec::new(); let mut vec: Vec<u8> = Vec::new();
vec.extend_from_slice(bytes); vec.extend_from_slice(bytes);
repr.payload = Some(vec); repr.payload = Some(vec);

View File

@ -156,7 +156,7 @@ impl<'a, 'b, 'c> TlsSocket<'a, 'b, 'c> {
{ {
let tcp_state = self.sockets.get::<TcpSocket>(self.tcp_handle).state(); let tcp_state = self.sockets.get::<TcpSocket>(self.tcp_handle).state();
//Close TCP socket if necessary // Close TCP socket if necessary
if tcp_state == TcpState::Established && tls_state == TlsState::DEFAULT { if tcp_state == TcpState::Established && tls_state == TlsState::DEFAULT {
self.sockets.get::<TcpSocket>(self.tcp_handle).close(); self.sockets.get::<TcpSocket>(self.tcp_handle).close();
return Ok(false); return Ok(false);
@ -164,19 +164,42 @@ impl<'a, 'b, 'c> TlsSocket<'a, 'b, 'c> {
// Skip handshake processing if it is already completed // Skip handshake processing if it is already completed
// However, redo TCP handshake if TLS socket is trying to connect and // However, redo TCP handshake if TLS socket is trying to connect and
// TCP socket is not connected // TCP socket is not connected <= seems like a bad piece of idea to me
// Reset TLS state to DEFAULT if TCP session is interrupted
// This is to close off hanged TLS sockets, when its dependent TCP session
// has already ended.
if tcp_state != TcpState::Established { if tcp_state != TcpState::Established {
if tls_state == TlsState::CLIENT_START { use TlsState::*;
// Restart TCP handshake is it is closed for some reason match tls_state {
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle); // Do nothing on the starting states
let session = self.session.borrow(); // Namely those immediate precedes TCP handshake,
if !tcp_socket.is_open() { // as handshake can legitimately be incomplete
tcp_socket.connect( DEFAULT |
session.get_remote_endpoint(), SERVER_START => {},
session.get_local_endpoint()
)?; // Attempt to reconnect if the socket went down before TLS socket sent anything
CLIENT_START => {
let mut tcp_socket = self.sockets.get::<TcpSocket>(self.tcp_handle);
let session = self.session.borrow();
if !tcp_socket.is_open() {
log::info!("Socket closed initially");
tcp_socket.connect(
session.get_remote_endpoint(),
session.get_local_endpoint()
)?;
}
}
// For any other functioning state, the TCP connection being not
// established should imply that the TLS connection had been derailed
// Reset TLS state to DEFAULT to allow terminate a dead link
_ => {
let mut session = self.session.borrow_mut();
session.reset_state();
log::info!("TLS socket resets after TCP socket closed")
} }
} }
// Terminate the procedure, as no processing is necessary // Terminate the procedure, as no processing is necessary
return Ok(false); return Ok(false);
} }
@ -813,64 +836,115 @@ impl<'a, 'b, 'c> TlsSocket<'a, 'b, 'c> {
// Process record base on content type // Process record base on content type
log::info!("Record type: {:?}", repr.content_type); log::info!("Record type: {:?}", repr.content_type);
if repr.content_type == TlsContentType::ApplicationData { // Handle TLS represenatation according to the content type:
log::info!("Found application data"); // Handshake & ChangeCipherSpec: Directly process the handshake
// Take the payload out of TLS Record and decrypt // Alert: Reset session immediately
let mut app_data = repr.payload.take().unwrap(); // ApplicationData: Decrypt and then handle, with similar criteria
let mut associated_data = [0; 5]; // Reject invalid contents by invalidating the TLS session
associated_data[0] = repr.content_type.into(); match repr.content_type {
NetworkEndian::write_u16( TlsContentType::ApplicationData => {
&mut associated_data[1..3], log::info!("Found application data");
repr.version.into() // Take the payload out of TLS Record and decrypt
); let mut app_data = repr.payload.take().unwrap();
NetworkEndian::write_u16( let mut associated_data = [0; 5];
&mut associated_data[3..5], associated_data[0] = repr.content_type.into();
repr.length NetworkEndian::write_u16(
); &mut associated_data[1..3],
{ repr.version.into()
let mut session = self.session.borrow_mut(); );
session.decrypt_in_place_detached( NetworkEndian::write_u16(
&associated_data, &mut associated_data[3..5],
&mut app_data repr.length
).unwrap(); );
session.increment_remote_sequence_number(); {
} let mut session = self.session.borrow_mut();
session.decrypt_in_place_detached(
// Discard last 16 bytes (auth tag) &associated_data,
let inner_plaintext = &app_data[..app_data.len()-16]; &mut app_data
let (inner_content_type, _) = get_content_type_inner_plaintext( ).unwrap();
inner_plaintext session.increment_remote_sequence_number();
); }
if inner_content_type != TlsContentType::Handshake {
// Silently ignore non-handshakes // Discard last 16 bytes (auth tag)
return Ok(self.session.borrow().has_completed_handshake()) let inner_plaintext = &app_data[..app_data.len()-16];
} let (inner_content_type, begin_zero) = get_content_type_inner_plaintext(
let (_, mut inner_handshakes) = complete( inner_plaintext
parse_inner_plaintext_for_handshake );
)(inner_plaintext).unwrap(); // Find the index of the content type byte
let content_type_index = match begin_zero {
// Sequentially process all handshakes Some(index) => index - 1,
let num_of_handshakes = inner_handshakes.len(); None => app_data.len() - 16 - 1
for _ in 0..num_of_handshakes { };
let (handshake_slice, handshake_repr) = inner_handshakes.remove(0);
if self.process( // Process contents that are not handshakes differently
handshake_slice, // Invalid: Raise an alert to remote side
TlsRepr { // ChangeCipherSpec: It should not be encrypted, raise alert
content_type: TlsContentType::Handshake, // Alert: Reset TLS session and terminate TCP session directly
version: repr.version, // Handshake: Normal procedure as below
length: u16::try_from(handshake_repr.length).unwrap() + 4, // ApplicationData: Early data is silently ignored and wont be processed
payload: None, match inner_content_type {
handshake: Some(handshake_repr) TlsContentType::Invalid | TlsContentType::ChangeCipherSpec => {
self.session.borrow_mut().invalidate_session(
AlertType::UnexpectedMessage,
&inner_plaintext[..content_type_index]
);
return Ok(false);
},
TlsContentType::Alert => {
self.session.borrow_mut().reset_state();
return Ok(false);
},
TlsContentType::ApplicationData => {
return Ok(
self.session.borrow().has_completed_handshake()
);
},
_ => ()
}
let (_, mut inner_handshakes) = complete(
parse_inner_plaintext_for_handshake
)(inner_plaintext).unwrap();
// Sequentially process all handshakes
let num_of_handshakes = inner_handshakes.len();
for _ in 0..num_of_handshakes {
let (handshake_slice, handshake_repr) = inner_handshakes.remove(0);
if self.process(
handshake_slice,
TlsRepr {
content_type: TlsContentType::Handshake,
version: repr.version,
length: u16::try_from(handshake_repr.length).unwrap() + 4,
payload: None,
handshake: Some(handshake_repr)
}
).is_err() {
return Ok(self.session.borrow().has_completed_handshake())
} }
).is_err() { }
},
TlsContentType::ChangeCipherSpec |
TlsContentType::Handshake => {
if self.process(repr_slice, repr).is_err() {
return Ok(self.session.borrow().has_completed_handshake()) return Ok(self.session.borrow().has_completed_handshake())
} }
log::info!("Processed record");
},
TlsContentType::Alert => {
self.session.borrow_mut().reset_state();
log::info!("Received alert, closing TCP socket..");
},
TlsContentType::Invalid => {
self.session.borrow_mut().invalidate_session(
AlertType::UnexpectedMessage,
&repr.payload.unwrap_or(Vec::new())
);
log::info!("Received invalid TLS records, terminate immediately..");
} }
} else {
if self.process(repr_slice, repr).is_err() {
return Ok(self.session.borrow().has_completed_handshake())
}
log::info!("Processed record");
} }
} }