From 5ab094de1e19a3c3c981da4a2e02e0560524ee88 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 5 Apr 2024 11:14:05 +0200 Subject: [PATCH] Make payload of connection checker configurable --- test/connection-checker/src/cli.rs | 4 ++++ test/connection-checker/src/net.rs | 18 +++++++-------- test/test-manager/src/tests/helpers.rs | 32 +++++++++++++++++++------- test/test-manager/src/tests/tunnel.rs | 13 ++++++----- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/test/connection-checker/src/cli.rs b/test/connection-checker/src/cli.rs index dddb348b255c..ecc33dd7e9db 100644 --- a/test/connection-checker/src/cli.rs +++ b/test/connection-checker/src/cli.rs @@ -33,4 +33,8 @@ pub struct Opt { /// Timeout for leak check network connections (in millis). #[clap(long, default_value = "1000")] pub leak_timeout: u64, + + /// Junk data for each UDP and TCP packet + #[clap(long, requires = "leak", default_value = "Hello there!")] + pub payload: String, } diff --git a/test/connection-checker/src/net.rs b/test/connection-checker/src/net.rs index 0cedbb7382f2..1bf01cf64b29 100644 --- a/test/connection-checker/src/net.rs +++ b/test/connection-checker/src/net.rs @@ -27,17 +27,17 @@ pub fn send_tcp(opt: &Opt, destination: SocketAddr) -> eyre::Result<()> { sock.set_read_timeout(Some(timeout))?; sock.connect_timeout(&socket2::SockAddr::from(destination), timeout) - .wrap_err(eyre!("Failed to connect to {destination}"))?; - - let mut stream = std::net::TcpStream::from(sock); + .map(String::as_bytes) + .unwrap_or(PAYLOAD); + .write_all(opt.payload.as_bytes()) stream - .write_all(PAYLOAD) + .write_all(payload) .wrap_err(eyre!("Failed to send message to {destination}"))?; Ok(()) } -pub fn send_udp(_opt: &Opt, destination: SocketAddr) -> Result<(), eyre::Error> { +pub fn send_udp(opt: &Opt, destination: SocketAddr) -> Result<(), eyre::Error> { let bind_addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 0); eprintln!("Leaking UDP packets to {destination}"); @@ -50,13 +50,11 @@ pub fn send_udp(_opt: &Opt, destination: SocketAddr) -> Result<(), eyre::Error> .wrap_err("Failed to create UDP socket")?; sock.bind(&socket2::SockAddr::from(bind_addr)) - .wrap_err(eyre!("Failed to bind UDP socket to {bind_addr}"))?; - - //log::debug!("Send message from {bind_addr} to {destination}/UDP"); + .unwrap_or(PAYLOAD); - let std_socket = std::net::UdpSocket::from(sock); + .send_to(opt.payload.as_bytes(), destination) std_socket - .send_to(PAYLOAD, destination) + .send_to(payload, destination) .wrap_err(eyre!("Failed to send message to {destination}"))?; Ok(()) diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index 2dff6eccdd62..86cbecb50cee 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -715,6 +715,9 @@ pub struct ConnChecker { /// Whether the process should be split when spawned. Needed on Linux. split: bool, + + /// Some arbitrary payload + payload: Option, } pub struct ConnCheckerHandle<'a> { @@ -756,9 +759,15 @@ impl ConnChecker { leak_destination, split: false, executable_path, + payload: None, } } + /// Set a custom magic payload that the connection checker binary should use when leak-testing. + pub fn payload(&mut self, payload: impl Into) { + self.payload = Some(payload.into()) + } + /// Spawn the connecton checker process and return a handle to it. /// /// Dropping the handle will stop the process. @@ -766,18 +775,14 @@ impl ConnChecker { pub async fn spawn(&mut self) -> anyhow::Result> { log::debug!("spawning connection checker"); - let opts = SpawnOpts { - attach_stdin: true, - attach_stdout: true, - args: [ + let opts = { + let mut args = [ "--interactive", "--timeout", &AM_I_MULLVAD_TIMEOUT_MS.to_string(), // try to leak traffic to LEAK_DESTINATION "--leak", &self.leak_destination.to_string(), - //TODO(markus): Remove - //&LEAK_DESTINATION.to_string(), "--leak-timeout", &LEAK_TIMEOUT_MS.to_string(), "--leak-tcp", @@ -785,8 +790,19 @@ impl ConnChecker { "--leak-icmp", ] .map(String::from) - .to_vec(), - ..SpawnOpts::new(&self.executable_path) + .to_vec(); + + if let Some(payload) = &self.payload { + args.push("--payload".to_string()); + args.push(payload.clone()); + }; + + SpawnOpts { + attach_stdin: true, + attach_stdout: true, + args, + ..SpawnOpts::new(&self.executable_path) + } }; let pid = self.rpc.spawn(opts).await?; diff --git a/test/test-manager/src/tests/tunnel.rs b/test/test-manager/src/tests/tunnel.rs index 42a90603cada..f8eb136960bf 100644 --- a/test/test-manager/src/tests/tunnel.rs +++ b/test/test-manager/src/tests/tunnel.rs @@ -847,30 +847,31 @@ pub async fn test_mul_02_002( // Step 2 - Start a network monitor snooping the outbound network interface for some // identifiable payload - // FIXME: This needs to be kept in sync with the magic payload string defined in `connection_cheker::net`. - // An easy fix would be to make the payload for `ConnCheck` configurable. - let unique_identifier = b"Hello there!"; + let unique_identifier = "Hello there!"; let identify_rogue_packet = move |packet: &ParsedPacket| { packet .payload .windows(unique_identifier.len()) - .any(|window| window == unique_identifier) + .any(|window| window == unique_identifier.as_bytes()) }; let rogue_packet_monitor = start_packet_monitor(identify_rogue_packet, MonitorOptions::default()).await; - // Step 3 - Start the rogue program which will try to leak traffic to the chosen relay endpoint + // Step 3 - Start the rogue program which will try to leak the unique identifier payload + // to the chosen relay endpoint let mut checker = ConnChecker::new(rpc.clone(), mullvad_client.clone(), target_endpoint); + checker.payload(unique_identifier); let mut conn_artist = checker.spawn().await?; // Before proceeding, assert that the method of detecting identifiable packets work. conn_artist.check_connection().await?; - let monitor_result = rogue_packet_monitor.into_result().await.unwrap(); + let monitor_result = rogue_packet_monitor.into_result().await?; log::info!("Checking that the identifiable payload was detectable without encryption"); ensure!( !monitor_result.packets.is_empty(), "Did not observe rogue packets! The method seems to be broken" ); + log::info!("The identifiable payload was detected! (that's good)"); // Step 4 - Finally, connect to a tunnel and assert that no outgoing traffic contains the // payload in plain text.