Skip to content

Commit 78cf565

Browse files
feat(desktop): relaunch elevated when starting TUN unprivileged
A proxy-only/system/pac launch runs without root/UAC, so if the user switches to tun and starts it, the tun bring-up would just fail. Detect that at start (`is_privileged` is false) and relaunch elevated via the existing whole-process model — pkexec on Linux, UAC on Windows. On success the elevated copy takes over; only a declined/failed prompt falls through to a clear "TUN mode needs administrator rights" failure the UI surfaces.
1 parent c93cbff commit 78cf565

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

src-tauri/src/desktop/elevate.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,29 @@ const PASS_ENV: &[&str] = &[
3737
"KASUMI_SKIP_ELEVATION",
3838
];
3939

40+
/// Whether the process already has the privileges the tun data-path needs (root on
41+
/// Linux, admin on Windows), or elevation was explicitly waived. Drives the
42+
/// relaunch-into-tun decision: a `false` here on a tun start means we must re-exec.
43+
#[cfg(target_os = "linux")]
44+
pub fn is_privileged() -> bool {
45+
let root = unsafe { libc::geteuid() == 0 };
46+
root || std::env::var_os("KASUMI_SKIP_ELEVATION").is_some()
47+
}
48+
49+
/// See the Linux variant.
50+
#[cfg(target_os = "windows")]
51+
pub fn is_privileged() -> bool {
52+
use windows_sys::Win32::UI::Shell::IsUserAnAdmin;
53+
let admin = unsafe { IsUserAnAdmin() != 0 };
54+
admin || std::env::var_os("KASUMI_SKIP_ELEVATION").is_some()
55+
}
56+
57+
/// Other desktops (macOS) have no elevation path yet; treat as privileged.
58+
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
59+
pub fn is_privileged() -> bool {
60+
true
61+
}
62+
4063
/// Re-exec the process as root if it isn't already. Returns on the elevated side
4164
/// (or when no elevator is available — the data-path then fails loudly at start,
4265
/// which the UI surfaces). Must run before any GTK/Tauri init.

src-tauri/src/desktop/linux/platform.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@ impl Platform for DesktopPlatform {
270270
// Only `tun` mode brings up a tun device + routing; the other modes run the
271271
// core with just its local socks/http inbound.
272272
let tun = mode == ProxyMode::Tun;
273+
// Tun needs root. If the process started unprivileged (a proxy-only/system/pac
274+
// launch) and the user switched to tun, relaunch elevated now — this prompts
275+
// pkexec and, on success, never returns (the elevated copy takes over). We only
276+
// reach the line below if elevation was declined/unavailable.
277+
if tun && !crate::desktop::elevate::is_privileged() {
278+
crate::desktop::elevate::ensure_elevated();
279+
return self.fail("TUN mode needs administrator rights").await;
280+
}
273281
let result = if engine == CoreEngine::SingBox {
274282
self.start_singbox(tun).await
275283
} else {

src-tauri/src/desktop/windows/platform.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ impl Platform for DesktopPlatform {
255255
// Only `tun` mode brings up a wintun device + routing; the other modes run
256256
// the core with just its local socks/http inbound.
257257
let tun = mode == ProxyMode::Tun;
258+
// Tun needs admin. If the process started unprivileged (a proxy-only/system/pac
259+
// launch) and the user switched to tun, relaunch elevated now — this prompts
260+
// UAC and, on success, never returns (the elevated copy takes over). We only
261+
// reach the line below if elevation was declined/unavailable.
262+
if tun && !crate::desktop::elevate::is_privileged() {
263+
crate::desktop::elevate::ensure_elevated();
264+
return self.fail("TUN mode needs administrator rights").await;
265+
}
258266
let result = if engine == CoreEngine::SingBox {
259267
self.start_singbox(tun).await
260268
} else {

0 commit comments

Comments
 (0)