From 0505f886e55827728aa942c1a773d103b2c556ce Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 18 Sep 2019 09:53:15 +0200 Subject: [PATCH] Handle truncated responses when len(query)( } Ok(Some(packet)) } + +pub fn serve_truncated(client_packet: Vec) -> Result, Error> { + ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); + ensure!(qdcount(&client_packet) == 1, "No question"); + ensure!( + !is_response(&client_packet), + "Question expected, but got a response instead" + ); + let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?; + let mut packet = client_packet; + ensure!(packet.len() - offset >= 4, "Short packet"); + packet.truncate(offset + 4); + authoritative_response(&mut packet); + truncate(&mut packet); + Ok(packet) +} diff --git a/src/dnscrypt.rs b/src/dnscrypt.rs index ee0acfb..ae51c0d 100644 --- a/src/dnscrypt.rs +++ b/src/dnscrypt.rs @@ -8,12 +8,37 @@ use rand::prelude::*; use std::ffi::CStr; use std::ptr; -pub const DNSCRYPT_CLIENT_MAGIC_SIZE: usize = 8; -pub const DNSCRYPT_CLIENT_PK_SIZE: usize = 32; -pub const DNSCRYPT_CLIENT_NONCE_SIZE: usize = - crypto_box_curve25519xchacha20poly1305_HALFNONCEBYTES as usize; pub const DNSCRYPT_FULL_NONCE_SIZE: usize = crypto_box_curve25519xchacha20poly1305_NONCEBYTES as usize; +pub const DNSCRYPT_MAC_SIZE: usize = crypto_box_curve25519xchacha20poly1305_MACBYTES as usize; + +pub const DNSCRYPT_QUERY_MAGIC_SIZE: usize = 8; +pub const DNSCRYPT_QUERY_PK_SIZE: usize = 32; +pub const DNSCRYPT_QUERY_NONCE_SIZE: usize = DNSCRYPT_FULL_NONCE_SIZE / 2; +pub const DNSCRYPT_QUERY_HEADER_SIZE: usize = + DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE + DNSCRYPT_QUERY_NONCE_SIZE; +pub const DNSCRYPT_QUERY_MIN_PADDING_SIZE: usize = 1; +pub const DNSCRYPT_QUERY_MIN_OVERHEAD: usize = + DNSCRYPT_QUERY_HEADER_SIZE + DNSCRYPT_MAC_SIZE + DNSCRYPT_QUERY_MIN_PADDING_SIZE; + +pub const DNSCRYPT_RESPONSE_MAGIC_SIZE: usize = 8; +pub const DNSCRYPT_RESPONSE_NONCE_SIZE: usize = DNSCRYPT_FULL_NONCE_SIZE; +pub const DNSCRYPT_RESPONSE_HEADER_SIZE: usize = + DNSCRYPT_RESPONSE_MAGIC_SIZE + DNSCRYPT_RESPONSE_NONCE_SIZE; +pub const DNSCRYPT_RESPONSE_MIN_PADDING_SIZE: usize = 1; +pub const DNSCRYPT_RESPONSE_MIN_OVERHEAD: usize = + DNSCRYPT_RESPONSE_HEADER_SIZE + DNSCRYPT_MAC_SIZE + DNSCRYPT_RESPONSE_MIN_PADDING_SIZE; + +pub const DNSCRYPT_UDP_QUERY_MIN_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_HEADER_SIZE; +pub const DNSCRYPT_UDP_QUERY_MAX_SIZE: usize = DNS_MAX_PACKET_SIZE; +pub const DNSCRYPT_TCP_QUERY_MIN_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_HEADER_SIZE; +pub const DNSCRYPT_TCP_QUERY_MAX_SIZE: usize = DNSCRYPT_QUERY_MIN_OVERHEAD + DNS_MAX_PACKET_SIZE; + +pub const DNSCRYPT_UDP_RESPONSE_MIN_SIZE: usize = DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_HEADER_SIZE; +pub const DNSCRYPT_UDP_RESPONSE_MAX_SIZE: usize = DNS_MAX_PACKET_SIZE; +pub const DNSCRYPT_TCP_RESPONSE_MIN_SIZE: usize = DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_HEADER_SIZE; +pub const DNSCRYPT_TCP_RESPONSE_MAX_SIZE: usize = + DNSCRYPT_RESPONSE_MIN_OVERHEAD + DNS_MAX_PACKET_SIZE; pub fn decrypt( wrapped_packet: &[u8], @@ -21,19 +46,18 @@ pub fn decrypt( ) -> Result<(SharedKey, [u8; DNSCRYPT_FULL_NONCE_SIZE as usize], Vec), Error> { ensure!( wrapped_packet.len() - >= DNSCRYPT_CLIENT_MAGIC_SIZE - + DNSCRYPT_CLIENT_PK_SIZE - + DNSCRYPT_CLIENT_NONCE_SIZE + >= DNSCRYPT_QUERY_MAGIC_SIZE + + DNSCRYPT_QUERY_PK_SIZE + + DNSCRYPT_QUERY_NONCE_SIZE + DNS_HEADER_SIZE, "Short packet" ); - let client_magic = &wrapped_packet[..DNSCRYPT_CLIENT_MAGIC_SIZE]; + let client_magic = &wrapped_packet[..DNSCRYPT_QUERY_MAGIC_SIZE]; let client_pk = &wrapped_packet - [DNSCRYPT_CLIENT_MAGIC_SIZE..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE]; - let client_nonce = &wrapped_packet[DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE - ..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE]; - let encrypted_packet = &wrapped_packet - [DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE..]; + [DNSCRYPT_QUERY_MAGIC_SIZE..DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE]; + let client_nonce = &wrapped_packet[DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE + ..DNSCRYPT_QUERY_MAGIC_SIZE + DNSCRYPT_QUERY_PK_SIZE + DNSCRYPT_QUERY_NONCE_SIZE]; + let encrypted_packet = &wrapped_packet[DNSCRYPT_QUERY_HEADER_SIZE..]; let encrypted_packet_len = encrypted_packet.len(); let dnscrypt_encryption_params = dnscrypt_encryption_params_set @@ -42,11 +66,11 @@ pub fn decrypt( .ok_or_else(|| format_err!("Client magic not found"))?; let mut nonce = [0u8; DNSCRYPT_FULL_NONCE_SIZE as usize]; - &mut nonce[..DNSCRYPT_CLIENT_NONCE_SIZE].copy_from_slice(client_nonce); + &mut nonce[..DNSCRYPT_QUERY_NONCE_SIZE].copy_from_slice(client_nonce); let resolver_kp = dnscrypt_encryption_params.resolver_kp(); let shared_key = resolver_kp.compute_shared_key(client_pk)?; let packet = shared_key.decrypt(&nonce, encrypted_packet)?; - rand::thread_rng().fill_bytes(&mut nonce[DNSCRYPT_CLIENT_NONCE_SIZE..]); + rand::thread_rng().fill_bytes(&mut nonce[DNSCRYPT_QUERY_NONCE_SIZE..]); Ok((shared_key, nonce, packet)) } diff --git a/src/main.rs b/src/main.rs index 2702670..7547cf5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,9 +52,6 @@ use tokio::runtime::Runtime; use tokio::sync::oneshot; use tokio_net::driver::Handle; -const DNSCRYPT_QUERY_MIN_SIZE: usize = 12; -const DNSCRYPT_QUERY_MAX_SIZE: usize = 512; - #[derive(Debug)] struct UdpClientCtx { net_udp_socket: std::net::UdpSocket, @@ -72,30 +69,57 @@ enum ClientCtx { Tcp(TcpClientCtx), } +fn maybe_truncate_response( + client_ctx: &ClientCtx, + packet: Vec, + response: Vec, + original_packet_len: usize, +) -> Result, Error> { + if let ClientCtx::Udp(_) = client_ctx { + let encrypted_response_min_len = + response.len() + DNSCRYPT_RESPONSE_HEADER_SIZE + DNSCRYPT_RESPONSE_MIN_PADDING_SIZE; + if encrypted_response_min_len > original_packet_len + || encrypted_response_min_len > DNSCRYPT_UDP_RESPONSE_MAX_SIZE + { + return Ok(dns::serve_truncated(packet)?); + } + } + Ok(response) +} + async fn respond_to_query( client_ctx: ClientCtx, packet: Vec, + response: Vec, + original_packet_len: usize, shared_key: Option, nonce: Option<[u8; DNSCRYPT_FULL_NONCE_SIZE]>, ) -> Result<(), Error> { - ensure!(dns::is_response(&packet), "Packet is not a response"); - let packet = match &shared_key { - None => packet, - Some(shared_key) => dnscrypt::encrypt(packet, shared_key, nonce.as_ref().unwrap())?, + ensure!(dns::is_response(&response), "Packet is not a response"); + let response = match &shared_key { + None => response, + Some(shared_key) => dnscrypt::encrypt( + maybe_truncate_response(&client_ctx, packet, response, original_packet_len)?, + shared_key, + nonce.as_ref().unwrap(), + )?, }; match client_ctx { ClientCtx::Udp(client_ctx) => { let net_udp_socket = client_ctx.net_udp_socket; - net_udp_socket.send_to(&packet, client_ctx.client_addr)?; + net_udp_socket.send_to(&response, client_ctx.client_addr)?; } ClientCtx::Tcp(client_ctx) => { - let packet_len = packet.len(); - ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Packet too large"); + let response_len = response.len(); + ensure!( + response_len <= DNSCRYPT_TCP_RESPONSE_MAX_SIZE, + "Packet too large" + ); let mut client_connection = client_ctx.client_connection; let mut binlen = [0u8, 0]; - BigEndian::write_u16(&mut binlen[..], packet_len as u16); + BigEndian::write_u16(&mut binlen[..], response_len as u16); client_connection.write_all(&binlen).await?; - client_connection.write_all(&packet).await?; + client_connection.write_all(&response).await?; client_connection.flush(); } } @@ -107,17 +131,26 @@ async fn handle_client_query( client_ctx: ClientCtx, encrypted_packet: Vec, ) -> Result<(), Error> { + let original_packet_len = encrypted_packet.len(); let (shared_key, nonce, mut packet) = match dnscrypt::decrypt(&encrypted_packet, &globals.dnscrypt_encryption_params_set) { Ok(x) => x, - Err(_) => { + Err(e) => { let packet = encrypted_packet; if let Some(synth_packet) = serve_certificates( &packet, &globals.provider_name, &globals.dnscrypt_encryption_params_set, )? { - return respond_to_query(client_ctx, synth_packet, None, None).await; + return respond_to_query( + client_ctx, + packet, + synth_packet, + original_packet_len, + None, + None, + ) + .await; } bail!("Unencrypted query"); } @@ -180,7 +213,15 @@ async fn handle_client_query( ); } dns::set_tid(&mut response, original_tid); - respond_to_query(client_ctx, response, Some(shared_key), Some(nonce)).await + respond_to_query( + client_ctx, + packet, + response, + original_packet_len, + Some(shared_key), + Some(nonce), + ) + .await } async fn tcp_acceptor(globals: Arc, tcp_listener: TcpListener) -> Result<(), Error> { @@ -212,7 +253,7 @@ async fn tcp_acceptor(globals: Arc, tcp_listener: TcpListener) -> Resul client_connection.read_exact(&mut binlen).await?; let packet_len = BigEndian::read_u16(&binlen) as usize; ensure!( - (DNSCRYPT_QUERY_MIN_SIZE..=DNSCRYPT_QUERY_MAX_SIZE).contains(&packet_len), + (DNSCRYPT_TCP_QUERY_MIN_SIZE..=DNSCRYPT_TCP_QUERY_MAX_SIZE).contains(&packet_len), "Unexpected query size" ); let mut packet = vec![0u8; packet_len]; @@ -240,7 +281,7 @@ async fn udp_acceptor( let concurrent_connections = globals.udp_concurrent_connections.clone(); let active_connections = globals.udp_active_connections.clone(); loop { - let mut packet = vec![0u8; DNSCRYPT_QUERY_MAX_SIZE]; + let mut packet = vec![0u8; DNSCRYPT_UDP_QUERY_MAX_SIZE]; let (packet_len, client_addr) = tokio_udp_socket.recv_from(&mut packet).await?; let net_udp_socket = net_udp_socket.try_clone()?; packet.truncate(packet_len);