Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion windows_kext/driver/src/ale_callouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,17 @@ fn ale_layer_auth(mut data: CalloutData, ale_data: AleLayerData) {
Verdict::PermanentAccept
| Verdict::Accept
| Verdict::RedirectNameServer
| Verdict::RedirectTunnel => {
| Verdict::RedirectTunnel
| Verdict::RedirectSplitTunnel => {
// Continue to packet layer.
data.action_permit();

if device.is_owner_pid(ale_data.process_id as u32) && matches!(ale_data.direction, Direction::Outbound) {
// If this is Portmaster's own outbound connection, clear the write flag
// to prevent subsequent filters in the chain from overriding the permit action.
// This prevents other firewall applications from blocking Portmaster's own connections.
data.clear_write_flag();
}
}
Verdict::PermanentBlock | Verdict::Undeterminable | Verdict::Failed => {
// Packet layer will not see this connection.
Expand Down
52 changes: 28 additions & 24 deletions windows_kext/driver/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use smoltcp::wire::{IpAddress, IpProtocol, Ipv4Address, Ipv6Address};

use crate::connection_map::Key;

pub static PM_DNS_PORT: u16 = 53;
pub static PM_SPN_PORT: u16 = 717;
pub static PM_DNS_PORT: u16 = 53;
pub static PM_SPN_PORT: u16 = 717;
pub static PM_SPLIT_TUN_PORT: u16 = 719;

// Make sure this in sync with the Go version
#[derive(Copy, Clone, FromPrimitive)]
Expand All @@ -27,9 +28,11 @@ pub enum Verdict {
PermanentBlock = 5,
Drop = 6,
PermanentDrop = 7,
RedirectNameServer = 8,
RedirectTunnel = 9,
RedirectNameServer = 8, // redirect to PM_DNS_PORT port
RedirectTunnel = 9, // redirect to PM_SPN_PORT port
Failed = 10,
RedirectSplitTunnel= 11, // redirect to PM_SPLIT_TUN_PORT port
// RedirectSplitTunnel must stay last: older Portmaster versions only know verdicts 0–10 and would never send this value.
}

impl Display for Verdict {
Expand All @@ -46,31 +49,12 @@ impl Display for Verdict {
Verdict::PermanentDrop => write!(f, "PermanentDrop"),
Verdict::RedirectNameServer => write!(f, "RedirectNameServer"),
Verdict::RedirectTunnel => write!(f, "RedirectTunnel"),
Verdict::RedirectSplitTunnel=> write!(f, "RedirectSplitTunnel"),
Verdict::Failed => write!(f, "Failed"),
}
}
}

#[allow(dead_code)]
impl Verdict {
/// Returns true if the verdict is a redirect.
pub fn is_redirect(&self) -> bool {
matches!(self, Verdict::RedirectNameServer | Verdict::RedirectTunnel)
}

/// Returns true if the verdict is a permanent verdict.
pub fn is_permanent(&self) -> bool {
matches!(
self,
Verdict::PermanentAccept
| Verdict::PermanentBlock
| Verdict::PermanentDrop
| Verdict::RedirectNameServer
| Verdict::RedirectTunnel
)
}
}

/// Direction of the connection.
#[derive(Copy, Clone, FromPrimitive)]
#[repr(u8)]
Expand Down Expand Up @@ -125,6 +109,14 @@ pub trait Connection {
unify: true,
redirect_address,
}),
Verdict::RedirectSplitTunnel => Some(RedirectInfo {
local_address: self.get_local_address(),
remote_address: self.get_remote_address(),
remote_port: self.get_remote_port(),
redirect_port: PM_SPLIT_TUN_PORT,
unify: true,
redirect_address,
}),
_ => None,
}
}
Expand Down Expand Up @@ -279,6 +271,12 @@ impl Connection for ConnectionV4 {
}
key.local_address.eq(&key.remote_address)
}
Verdict::RedirectSplitTunnel => {
if key.remote_port != PM_SPLIT_TUN_PORT {
return false;
}
key.local_address.eq(&key.remote_address)
}
_ => false,
}
}
Expand Down Expand Up @@ -422,6 +420,12 @@ impl Connection for ConnectionV6 {
}
key.local_address.eq(&key.remote_address)
}
Verdict::RedirectSplitTunnel => {
if key.remote_port != PM_SPLIT_TUN_PORT {
return false;
}
key.local_address.eq(&key.remote_address)
}
_ => false,
}
}
Expand Down
21 changes: 17 additions & 4 deletions windows_kext/driver/src/device.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use alloc::string::String;
use core::sync::atomic::{AtomicU32, Ordering};
use num_traits::FromPrimitive;
use protocol::{command::CommandType, info::Info};
use smoltcp::wire::{IpAddress, IpProtocol, Ipv4Address, Ipv6Address};
Expand Down Expand Up @@ -28,12 +29,16 @@ pub enum Packet {
pub struct Device {
pub(crate) filter_engine: FilterEngine,
pub(crate) read_leftover: ArrayHolder,
pub(crate) event_queue: IOQueue<Info>,
pub(crate) packet_cache: IdCache,
pub(crate) connection_cache: ConnectionCache,
pub(crate) event_queue: IOQueue<Info>, // Queue for events to user-space
pub(crate) packet_cache: IdCache, // Cache of pending packets waiting for verdict
pub(crate) connection_cache: ConnectionCache, // Cache of connections and their verdicts
pub(crate) injector: Injector,
pub(crate) network_allocator: NetworkAllocator,
pub(crate) bandwidth_stats: Bandwidth,
/// PID of the user-space process that currently holds the device handle open.
/// Written once on IRP_MJ_CREATE, cleared on IRP_MJ_CLEANUP.
/// AtomicU32 gives lock-free reads in callouts with zero overhead.
pub(crate) owner_pid: AtomicU32,
}

impl Device {
Expand All @@ -57,9 +62,16 @@ impl Device {
injector: Injector::new(),
network_allocator: NetworkAllocator::new(),
bandwidth_stats: Bandwidth::new(),
owner_pid: AtomicU32::new(0),
})
}

/// Returns the PID of the process that currently has the device handle open, or 0 if none.
pub fn is_owner_pid(&self, pid: u32) -> bool {
let p = self.owner_pid.load(Ordering::Acquire);
p != 0 && p == pid
}

/// Cleanup is called just before drop.
// pub fn cleanup(&mut self) {}

Expand Down Expand Up @@ -162,7 +174,8 @@ impl Device {
}
}
crate::connection::Verdict::RedirectNameServer
| crate::connection::Verdict::RedirectTunnel => {
| crate::connection::Verdict::RedirectTunnel
| crate::connection::Verdict::RedirectSplitTunnel => {
if let Some(redirect_info) = redirect_info {
// Will not redirect packets from ALE layer
if let Err(err) = packet.redirect(redirect_info) {
Expand Down
33 changes: 32 additions & 1 deletion windows_kext/driver/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::common::ControlCode;
use crate::device;
use alloc::boxed::Box;
use num_traits::FromPrimitive;
use wdk::irp_helpers::{DeviceControlRequest, ReadRequest, WriteRequest};
use wdk::irp_helpers::{CleanupRequest, CreateRequest, DeviceControlRequest, ReadRequest, WriteRequest};
use wdk::{err, info, interface};
use windows_sys::Wdk::Foundation::{DEVICE_OBJECT, DRIVER_OBJECT, IRP};
use windows_sys::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS};
Expand Down Expand Up @@ -39,6 +39,8 @@ pub extern "system" fn driver_entry(

// Set driver functions.
driver.set_driver_unload(Some(driver_unload));
driver.set_create_fn(Some(driver_create));
driver.set_cleanup_fn(Some(driver_cleanup));
driver.set_read_fn(Some(driver_read));
driver.set_write_fn(Some(driver_write));
driver.set_device_control_fn(Some(device_control));
Expand Down Expand Up @@ -68,6 +70,35 @@ unsafe extern "system" fn driver_unload(_object: *const DRIVER_OBJECT) {
}
}

/// driver_create is triggered when user-space opens a handle to the device (CreateFile).
unsafe extern "system" fn driver_create(
_device_object: *const DEVICE_OBJECT,
irp: *mut IRP,
) -> NTSTATUS {
let mut create_request = CreateRequest::new(irp.as_mut().unwrap());
if let Some(device) = get_device() {
let pid = create_request.get_requestor_pid();
device.owner_pid.store(pid, core::sync::atomic::Ordering::Release);
info!("Device opened by PID {}", pid);
}
create_request.complete();
create_request.get_status()
}

/// driver_cleanup is triggered when user-space closes the last handle to the device.
unsafe extern "system" fn driver_cleanup(
_device_object: *const DEVICE_OBJECT,
irp: *mut IRP,
) -> NTSTATUS {
let mut cleanup_request = CleanupRequest::new(irp.as_mut().unwrap());
if let Some(device) = get_device() {
let old_pid = device.owner_pid.swap(0, core::sync::atomic::Ordering::Release);
info!("Device closed by PID {}", old_pid);
}
cleanup_request.complete();
cleanup_request.get_status()
}

// driver_read event triggered from user-space on file.Read.
unsafe extern "system" fn driver_read(
_device_object: *const DEVICE_OBJECT,
Expand Down
2 changes: 1 addition & 1 deletion windows_kext/driver/src/packet_callouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ fn ip_packet_layer(
send_request_to_portmaster = false;
data.block_and_absorb();
}
Verdict::RedirectNameServer | Verdict::RedirectTunnel => {
Verdict::RedirectNameServer | Verdict::RedirectTunnel | Verdict::RedirectSplitTunnel => {
if let Some(redirect_info) = conn_info.redirect_info.take() {
match clone_packet(
device,
Expand Down
Loading
Loading