Merge pull request #1893 from safing/fix/restrict_ui_process_connections
Fix/restrict UI process connections
This commit is contained in:
@@ -9,6 +9,8 @@ use crate::{portmaster::PortmasterExt, traymenu};
|
||||
const LIGHT_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_512.png");
|
||||
const DARK_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png");
|
||||
|
||||
const CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS: &str = "PORTMASTER_UI_WEBVIEW_PROCESS";
|
||||
|
||||
/// Either returns the existing "main" window or creates a new one.
|
||||
///
|
||||
/// The window is not automatically shown (i.e it starts hidden).
|
||||
@@ -24,6 +26,7 @@ pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||
} else {
|
||||
debug!("[tauri] creating main window");
|
||||
|
||||
do_before_window_create(); // required operations before window creation
|
||||
let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into()))
|
||||
.title("Portmaster")
|
||||
.visible(false)
|
||||
@@ -31,6 +34,7 @@ pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||
.min_inner_size(800.0, 600.0)
|
||||
.theme(Some(Theme::Dark))
|
||||
.build();
|
||||
do_after_window_create(); // required operations after window creation
|
||||
|
||||
match res {
|
||||
Ok(win) => {
|
||||
@@ -69,6 +73,8 @@ pub fn create_splash_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||
let _ = window.show();
|
||||
Ok(window)
|
||||
} else {
|
||||
|
||||
do_before_window_create(); // required operations before window creation
|
||||
let window = WebviewWindowBuilder::new(app, "splash", WebviewUrl::App("index.html".into()))
|
||||
.center()
|
||||
.closable(false)
|
||||
@@ -78,6 +84,7 @@ pub fn create_splash_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||
.title("Portmaster")
|
||||
.inner_size(600.0, 250.0)
|
||||
.build()?;
|
||||
do_after_window_create(); // required operations after window creation
|
||||
set_window_icon(&window);
|
||||
|
||||
let _ = window.request_user_attention(Some(UserAttentionType::Informational));
|
||||
@@ -118,6 +125,28 @@ pub fn set_window_icon(window: &WebviewWindow) {
|
||||
};
|
||||
}
|
||||
|
||||
/// This function must be called before the window is created.
|
||||
///
|
||||
/// Temporarily sets the environment variable `PORTMASTER_WEBVIEW_UI_PROCESS` to "true".
|
||||
/// This ensures that any child process (i.e., the WebView process) spawned during window creation
|
||||
/// will inherit this environment variable. This allows portmaster-core to detect that the process
|
||||
/// is a child WebView of the main process.
|
||||
///
|
||||
/// IMPORTANT: After the window is created, you must call `do_after_window_create()` to remove
|
||||
/// the environment variable from the main process environment.
|
||||
pub fn do_before_window_create() {
|
||||
std::env::set_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS, "true");
|
||||
}
|
||||
|
||||
/// This function must be called after the window is created.
|
||||
///
|
||||
/// Removes the `PORTMASTER_WEBVIEW_UI_PROCESS` environment variable from the main process.
|
||||
/// This ensures that only the child WebView process has the variable set, and the main process
|
||||
/// does not retain it.
|
||||
pub fn do_after_window_create() {
|
||||
std::env::remove_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS);
|
||||
}
|
||||
|
||||
/// Opens a window for the tauri application.
|
||||
///
|
||||
/// If the main window has already been created, it is instructed to
|
||||
|
||||
@@ -118,6 +118,8 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool {
|
||||
var previousPid int
|
||||
proc := p
|
||||
|
||||
hasPmWebviewEnvVar := false
|
||||
|
||||
for i := 0; i < checkLevels; i++ {
|
||||
if proc.Pid == UnidentifiedProcessID || proc.Pid == SystemProcessID {
|
||||
break
|
||||
@@ -125,7 +127,43 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool {
|
||||
|
||||
realPath, err := filepath.EvalSymlinks(proc.Path)
|
||||
if err == nil && realPath == module.portmasterUIPath {
|
||||
return true
|
||||
if runtime.GOOS != "windows" {
|
||||
return true
|
||||
}
|
||||
|
||||
// On Windows, avoid false positive detection of the Portmaster UI.
|
||||
// For example:
|
||||
// There may be cases where a system browser is launched from the Portmaster UI,
|
||||
// making it a child of the Portmaster UI process (e.g., user clicked a link in the UI).
|
||||
// In this case, the parent process tree may look like this:
|
||||
// Portmaster.exe
|
||||
// ├─ WebView (PM UI)
|
||||
// │ └─ WebView (PM UI child)
|
||||
// └─ System Web Browser ...
|
||||
//
|
||||
// To ensure that 'p' is the actual Portmaster UI process, we check for the presence
|
||||
// of the 'PORTMASTER_UI_WEBVIEW_PROCESS' environment variable in the process and its parents.
|
||||
// If the env var is set, we are a child (WebView window) of the Portmaster UI process.
|
||||
// Otherwise, the process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process.
|
||||
if i == 0 {
|
||||
return true // We are the main Portmaster UI process.
|
||||
}
|
||||
if hasPmWebviewEnvVar {
|
||||
return true // We are a WebView window of the Portmaster UI process.
|
||||
}
|
||||
// The process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process.
|
||||
log.Tracer(ctx).Warningf("process: %d %q is a child of the Portmaster UI, but does not have the PORTMASTER_UI_WEBVIEW_PROCESS environment variable set. Ignoring.", p.Pid, p.Path)
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the process has the environment variable set.
|
||||
//
|
||||
// It is OK to check for the existence of the environment variable in all
|
||||
// processes in the parent chain (on all loop iterations). This increases the
|
||||
// chance of correct detection, even if a child or grandchild WebView process
|
||||
// did not inherit the environment variable for some reason.
|
||||
if _, ok := proc.Env["PORTMASTER_UI_WEBVIEW_PROCESS"]; ok {
|
||||
hasPmWebviewEnvVar = true
|
||||
}
|
||||
|
||||
if i < checkLevels-1 { // no need to check parent if we are at the last level
|
||||
|
||||
@@ -237,14 +237,20 @@ func createSpecialProfile(profileID string, path string) *Profile {
|
||||
Source: SourceLocal,
|
||||
PresentationPath: path,
|
||||
Config: map[string]interface{}{
|
||||
// Block all connections by default for the Portmaster UI profile,
|
||||
// since the only required connections are to the Portmaster Core,
|
||||
// which are fast-tracked.
|
||||
//
|
||||
// This ensures that any unexpected connections —
|
||||
// possibly made by the internal WebView implementation —
|
||||
// are blocked.
|
||||
CfgOptionDefaultActionKey: DefaultActionBlockValue,
|
||||
CfgOptionBlockScopeInternetKey: false,
|
||||
CfgOptionBlockScopeLANKey: false,
|
||||
CfgOptionBlockScopeLocalKey: false,
|
||||
CfgOptionBlockP2PKey: false,
|
||||
CfgOptionBlockScopeInternetKey: false, // This is stronger than the rules, and thus must be false in order to access safing.io.
|
||||
CfgOptionBlockScopeLANKey: true,
|
||||
CfgOptionBlockScopeLocalKey: true,
|
||||
CfgOptionBlockP2PKey: true,
|
||||
CfgOptionBlockInboundKey: true,
|
||||
CfgOptionEndpointsKey: []string{
|
||||
"+ Localhost",
|
||||
"+ .safing.io",
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user