Compare commits

...

4 Commits

Author SHA1 Message Date
fufesou
bb51c6aa42 fix(ipc): cmdline, unit tests (#15069)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-05-18 17:03:04 +08:00
fufesou
78e8134ad5 fix(ipc): cmdline, use scope, deploy (#15068)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-05-18 16:52:22 +08:00
fufesou
bc2c36215d fix(ipc): scope active-user IPC routing to root CLI main requests (#15058)
* fix(ipc): scope active-user IPC routing to root CLI main requests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): cmdline, comments fails close

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): cmdline, better check

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): cmdline, try active uid when no --server processes

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): cmdline, select active uid

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): remove unused import

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-05-18 16:32:46 +08:00
IronCodeStudios
377547fa11 scrap/wayland: insert videoconvert to fix screencast on COSMIC / DMA-BUF portals (#15063)
On Wayland compositors whose xdg-desktop-portal backend exposes screencast
frames as DMA-BUF buffers — notably xdg-desktop-portal-cosmic 0.1.0 on
Pop!_OS 24.04 / COSMIC — inbound screen capture fails. PipeWireRecorder
links pipewiresrc directly to an appsink whose caps only accept
video/x-raw BGRx/RGBx in system memory. That format set is too narrow for
the portal's buffer-type / modifier negotiation, which collapses with:

  pw.link: negotiating -> error no more output formats (-22)
  gstpipewiresrc: stream error: no more output formats
  gstbasesrc: streaming stopped, reason not-negotiated (-4)
  ERROR src/server/wayland.rs: Failed scrap Element failed to change its state

Inserting a videoconvert element between pipewiresrc and appsink widens
the negotiable format set to any system-memory video/x-raw format, giving
the portal room to settle on a format it can deliver via its SHM path.
videoconvert then converts to the BGRx/RGBx the appsink expects.

Verified on Pop!_OS 24.04 / COSMIC with gst-launch, before and after:

  # fails (current behaviour):
  gst-launch-1.0 pipewiresrc path=N ! video/x-raw,format=BGRx ! fakesink
  # works (with this change):
  gst-launch-1.0 pipewiresrc path=N ! videoconvert ! video/x-raw,format=BGRx ! fakesink

After the change, inbound connections capture and stream the desktop
normally and the "Failed scrap" error no longer occurs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 16:02:23 +08:00
6 changed files with 330 additions and 32 deletions

View File

@@ -276,12 +276,21 @@ impl PipeWireRecorder {
// see: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/982 // see: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/982
src.set_property("always-copy", &true)?; src.set_property("always-copy", &true)?;
// COSMIC/Wayland fix: insert videoconvert between pipewiresrc and appsink.
// xdg-desktop-portal-cosmic's modifier negotiation fails when the downstream
// format set is too narrow (appsink only accepts BGRx/RGBx), producing
// "no more output formats" / not-negotiated (-4). videoconvert accepts any
// system-memory video/x-raw format, widening negotiation so the portal can
// settle on a format it can deliver via its SHM path.
let convert = gst::ElementFactory::make("videoconvert", None)?;
let sink = gst::ElementFactory::make("appsink", None)?; let sink = gst::ElementFactory::make("appsink", None)?;
sink.set_property("drop", &true)?; sink.set_property("drop", &true)?;
sink.set_property("max-buffers", &1u32)?; sink.set_property("max-buffers", &1u32)?;
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &convert, &sink])?;
src.link(&sink)?; src.link(&convert)?;
convert.link(&sink)?;
let appsink = sink let appsink = sink
.dynamic_cast::<AppSink>() .dynamic_cast::<AppSink>()

View File

@@ -199,6 +199,20 @@ pub fn core_main() -> Option<Vec<String>> {
} }
std::thread::spawn(move || crate::start_server(false, no_server)); std::thread::spawn(move || crate::start_server(false, no_server));
} else { } else {
#[cfg(any(target_os = "linux", target_os = "macos"))]
// Root CLI management commands must talk to the user `--server` main IPC.
// Example: `sudo rustdesk --option custom-rendezvous-server` should query the
// user's IPC instead of root's `/tmp/<app>-0/ipc`; `connect()` still limits this
// routing to empty-postfix main IPC only.
let _user_main_ipc_scope = if crate::platform::is_installed()
&& is_root()
&& is_user_main_ipc_scope_cli_command(&args)
{
Some(crate::ipc::UserMainIpcScope::new())
} else {
None
};
#[cfg(windows)] #[cfg(windows)]
{ {
use crate::platform; use crate::platform;
@@ -938,6 +952,57 @@ fn is_root() -> bool {
crate::platform::is_root() crate::platform::is_root()
} }
#[cfg(any(target_os = "linux", target_os = "macos", test))]
fn is_user_main_ipc_scope_cli_command(args: &[String]) -> bool {
matches!(
args.first().map(String::as_str),
Some("--password")
| Some("--set-unlock-pin")
| Some("--get-id")
| Some("--set-id")
| Some("--config")
| Some("--option")
| Some("--assign")
| Some("--deploy")
)
}
#[cfg(test)]
mod tests {
use super::*;
fn args(values: &[&str]) -> Vec<String> {
values.iter().map(|value| value.to_string()).collect()
}
#[test]
fn user_main_ipc_scope_cli_command_matches_management_commands_only() {
for command in [
"--password",
"--set-unlock-pin",
"--get-id",
"--set-id",
"--config",
"--option",
"--assign",
"--deploy",
] {
assert!(is_user_main_ipc_scope_cli_command(&args(&[command])));
}
for command in [
"--service",
"--server",
"--tray",
"--cm",
"--check-hwcodec-config",
"--connect",
] {
assert!(!is_user_main_ipc_scope_cli_command(&args(&[command])));
}
}
}
/// Check if the executable is a Quick Support version. /// Check if the executable is a Quick Support version.
/// Note: This function must be kept in sync with `libs/portable/src/main.rs`. /// Note: This function must be kept in sync with `libs/portable/src/main.rs`.
#[cfg(windows)] #[cfg(windows)]

View File

@@ -33,25 +33,25 @@ use hbb_common::{
tokio_util::codec::Framed, tokio_util::codec::Framed,
ResultType, ResultType,
}; };
#[cfg(any(target_os = "linux", target_os = "macos"))]
use ipc_auth::authorize_service_scoped_ipc_connection;
#[cfg(windows)] #[cfg(windows)]
pub(crate) use ipc_auth::authorize_windows_portable_service_ipc_connection; pub(crate) use ipc_auth::authorize_windows_portable_service_ipc_connection;
#[cfg(windows)] #[cfg(windows)]
pub(crate) use ipc_auth::ensure_peer_executable_matches_current_by_pid_opt; pub(crate) use ipc_auth::ensure_peer_executable_matches_current_by_pid_opt;
#[cfg(windows)] #[cfg(windows)]
pub(crate) use ipc_auth::log_rejected_windows_ipc_connection; pub(crate) use ipc_auth::log_rejected_windows_ipc_connection;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) use ipc_auth::{ use ipc_auth::{active_uid, authorize_service_scoped_ipc_connection};
active_uid, ensure_peer_executable_matches_current_by_fd, is_allowed_service_peer_uid,
log_rejected_uinput_connection, peer_uid_from_fd,
};
#[cfg(windows)] #[cfg(windows)]
use ipc_auth::{ use ipc_auth::{
authorize_windows_main_ipc_connection, portable_service_listener_security_attributes, authorize_windows_main_ipc_connection, portable_service_listener_security_attributes,
should_allow_everyone_create_on_windows, should_allow_everyone_create_on_windows,
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub(crate) use ipc_auth::{
ensure_peer_executable_matches_current_by_fd, is_allowed_service_peer_uid,
log_rejected_uinput_connection, peer_uid_from_fd,
};
#[cfg(target_os = "linux")]
use ipc_fs::terminal_count_candidate_uids; use ipc_fs::terminal_count_candidate_uids;
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use ipc_fs::{ use ipc_fs::{
@@ -63,6 +63,8 @@ use parity_tokio_ipc::{
}; };
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use std::cell::Cell;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::{ use std::{
collections::HashMap, collections::HashMap,
@@ -71,12 +73,47 @@ use std::{
// IPC actions here. // IPC actions here.
pub const IPC_ACTION_CLOSE: &str = "close"; pub const IPC_ACTION_CLOSE: &str = "close";
#[cfg(target_os = "windows")]
const PORTABLE_SERVICE_IPC_HANDSHAKE_TIMEOUT_MS: u64 = 3_000; const PORTABLE_SERVICE_IPC_HANDSHAKE_TIMEOUT_MS: u64 = 3_000;
#[cfg(target_os = "windows")]
pub(crate) const IPC_TOKEN_LEN: usize = 64; pub(crate) const IPC_TOKEN_LEN: usize = 64;
#[cfg(target_os = "windows")]
const IPC_TOKEN_RANDOM_BYTES: usize = IPC_TOKEN_LEN / 2; const IPC_TOKEN_RANDOM_BYTES: usize = IPC_TOKEN_LEN / 2;
#[cfg(target_os = "windows")]
const _: () = assert!(IPC_TOKEN_LEN % 2 == 0); const _: () = assert!(IPC_TOKEN_LEN % 2 == 0);
pub static EXIT_RECV_CLOSE: AtomicBool = AtomicBool::new(true); pub static EXIT_RECV_CLOSE: AtomicBool = AtomicBool::new(true);
#[cfg(any(target_os = "linux", target_os = "macos"))]
thread_local! {
static USE_USER_MAIN_IPC: Cell<bool> = Cell::new(false);
}
#[must_use = "bind this guard to a local variable to keep the IPC scope active"]
/// Thread-local guard for routing root main IPC to the active user on Linux/macOS.
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) struct UserMainIpcScope {
previous: bool,
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
impl UserMainIpcScope {
pub(crate) fn new() -> Self {
let previous = USE_USER_MAIN_IPC.with(|use_user_main| {
let previous = use_user_main.get();
use_user_main.set(true);
previous
});
Self { previous }
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
impl Drop for UserMainIpcScope {
fn drop(&mut self) {
USE_USER_MAIN_IPC.with(|use_user_main| use_user_main.set(self.previous));
}
}
#[inline] #[inline]
pub async fn connect_service(ms_timeout: u64) -> ResultType<ConnectionTmpl<ConnClient>> { pub async fn connect_service(ms_timeout: u64) -> ResultType<ConnectionTmpl<ConnClient>> {
connect(ms_timeout, crate::POSTFIX_SERVICE).await connect(ms_timeout, crate::POSTFIX_SERVICE).await
@@ -1112,11 +1149,7 @@ async fn handle(data: Data, stream: &mut Connection) {
}; };
} }
pub async fn connect(ms_timeout: u64, postfix: &str) -> ResultType<ConnectionTmpl<ConnClient>> { #[cfg(target_os = "windows")]
let path = Config::ipc_path(postfix);
connect_with_path(ms_timeout, &path).await
}
pub(crate) fn generate_one_time_ipc_token() -> ResultType<String> { pub(crate) fn generate_one_time_ipc_token() -> ResultType<String> {
use hbb_common::rand::{rngs::OsRng, RngCore as _}; use hbb_common::rand::{rngs::OsRng, RngCore as _};
use std::fmt::Write as _; use std::fmt::Write as _;
@@ -1137,6 +1170,7 @@ pub(crate) fn generate_one_time_ipc_token() -> ResultType<String> {
Ok(token) Ok(token)
} }
#[cfg(target_os = "windows")]
pub(crate) fn constant_time_ipc_token_eq(expected: &str, candidate: &str) -> bool { pub(crate) fn constant_time_ipc_token_eq(expected: &str, candidate: &str) -> bool {
if expected.len() != IPC_TOKEN_LEN || candidate.len() != IPC_TOKEN_LEN { if expected.len() != IPC_TOKEN_LEN || candidate.len() != IPC_TOKEN_LEN {
return false; return false;
@@ -1149,6 +1183,7 @@ pub(crate) fn constant_time_ipc_token_eq(expected: &str, candidate: &str) -> boo
== 0 == 0
} }
#[cfg(target_os = "windows")]
pub(crate) async fn portable_service_ipc_handshake_as_client<T>( pub(crate) async fn portable_service_ipc_handshake_as_client<T>(
stream: &mut ConnectionTmpl<T>, stream: &mut ConnectionTmpl<T>,
token: &str, token: &str,
@@ -1173,6 +1208,7 @@ where
} }
} }
#[cfg(target_os = "windows")]
pub(crate) async fn portable_service_ipc_handshake_as_server<T, F>( pub(crate) async fn portable_service_ipc_handshake_as_server<T, F>(
stream: &mut ConnectionTmpl<T>, stream: &mut ConnectionTmpl<T>,
mut validate_token: F, mut validate_token: F,
@@ -1209,6 +1245,103 @@ async fn connect_with_path(ms_timeout: u64, path: &str) -> ResultType<Connection
Ok(ConnectionTmpl::new(client)) Ok(ConnectionTmpl::new(client))
} }
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[inline]
fn select_server_uid_for_user_main_ipc(
server_uids: &[u32],
active_uid: Option<u32>,
prefer_root: bool,
) -> ResultType<u32> {
let mut server_uids = server_uids.to_vec();
server_uids.sort_unstable();
server_uids.dedup();
match server_uids.as_slice() {
[] => {
if let Some(uid) = active_uid {
// If no `--server` processes are found but the active user is identifiable,
// try the active user anyway because the main process may also listen on "" IPC.
return Ok(uid);
} else {
bail!("No --server process found for user main IPC")
}
}
[uid] => return Ok(*uid),
_ => {}
}
if prefer_root && server_uids.contains(&0) {
return Ok(0);
}
if let Some(active_uid) = active_uid.filter(|uid| server_uids.contains(uid)) {
return Ok(active_uid);
}
bail!("Multiple --server processes found for user main IPC");
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn running_server_uids_for_current_exe() -> ResultType<Vec<u32>> {
let current_exe = std::env::current_exe()?;
let current_exe_path = std::fs::canonicalize(&current_exe)?;
let current_pid = hbb_common::sysinfo::Pid::from_u32(std::process::id());
let mut sys = hbb_common::sysinfo::System::new();
sys.refresh_processes();
let mut server_uids = Vec::new();
for process in sys.processes().values() {
if process.pid() == current_pid {
continue;
}
if process.cmd().get(1).map_or(true, |arg| arg != "--server") {
continue;
}
let Ok(process_path) = std::fs::canonicalize(process.exe()) else {
continue;
};
if process_path != current_exe_path {
continue;
}
let Some(uid) = process.user_id().map(|uid| **uid as u32) else {
// Root CLI management commands need a stable matching `--server` target.
// If this key process races during enumeration, failing the command is clearer
// than silently skipping it; `--server` is not expected to exit frequently.
bail!("Failed to read --server process uid");
};
server_uids.push(uid);
}
Ok(server_uids)
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn user_main_ipc_server_uid() -> ResultType<u32> {
let server_uids = running_server_uids_for_current_exe()?;
#[cfg(target_os = "linux")]
let prefer_root = crate::platform::linux::is_login_screen_wayland();
#[cfg(target_os = "macos")]
let prefer_root = false;
select_server_uid_for_user_main_ipc(&server_uids, active_uid(), prefer_root)
}
pub async fn connect(ms_timeout: u64, postfix: &str) -> ResultType<ConnectionTmpl<ConnClient>> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let use_user_main_ipc = USE_USER_MAIN_IPC.with(|use_user_main| use_user_main.get());
let is_root_main_ipc =
unsafe { hbb_common::libc::geteuid() == 0 } && postfix.is_empty() && use_user_main_ipc;
if is_root_main_ipc {
let uid = user_main_ipc_server_uid()?;
let path = Config::ipc_path_for_uid(uid, postfix);
return connect_with_path(ms_timeout, &path).await;
}
let path = Config::ipc_path(postfix);
return connect_with_path(ms_timeout, &path).await;
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let path = Config::ipc_path(postfix);
connect_with_path(ms_timeout, &path).await
}
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub async fn connect_for_uid( pub async fn connect_for_uid(
ms_timeout: u64, ms_timeout: u64,
@@ -2002,7 +2135,16 @@ mod test {
assert!(std::mem::size_of::<Data>() <= 120); assert!(std::mem::size_of::<Data>() <= 120);
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_service_ipc_path_is_shared_across_uids() {
assert_eq!(
Config::ipc_path_for_uid(0, crate::POSTFIX_SERVICE),
Config::ipc_path_for_uid(501, crate::POSTFIX_SERVICE)
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test] #[test]
fn test_ipc_path_differs_by_uid_for_cm() { fn test_ipc_path_differs_by_uid_for_cm() {
let effective_uid = unsafe { hbb_common::libc::geteuid() as u32 }; let effective_uid = unsafe { hbb_common::libc::geteuid() as u32 };
@@ -2021,4 +2163,46 @@ mod test {
Config::ipc_path_for_uid(other_uid, postfix) Config::ipc_path_for_uid(other_uid, postfix)
); );
} }
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_select_server_uid_uses_active_uid_when_no_server_found() {
assert_eq!(
select_server_uid_for_user_main_ipc(&[], Some(501), false).unwrap(),
501
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_select_server_uid_uses_single_server_uid() {
assert_eq!(
select_server_uid_for_user_main_ipc(&[501], None, false).unwrap(),
501
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_select_server_uid_prefers_active_uid_with_multiple_servers() {
assert_eq!(
select_server_uid_for_user_main_ipc(&[0, 501], Some(501), false).unwrap(),
501
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_select_server_uid_prefers_root_on_wayland_login_screen() {
assert_eq!(
select_server_uid_for_user_main_ipc(&[0, 501], Some(501), true).unwrap(),
0
);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_select_server_uid_fails_when_multiple_servers_are_ambiguous() {
assert!(select_server_uid_for_user_main_ipc(&[501, 502], None, false).is_err());
}
} }

View File

@@ -607,27 +607,30 @@ pub(crate) fn log_rejected_windows_ipc_connection(
peer_session_id: Option<u32>, peer_session_id: Option<u32>,
expected_session_id: Option<u32>, expected_session_id: Option<u32>,
peer_is_system: Option<bool>, peer_is_system: Option<bool>,
peer_is_elevated: Option<bool>,
) { ) {
static LOG_THROTTLE: OnceLock<Mutex<UnauthorizedIpcLogThrottle>> = OnceLock::new(); static LOG_THROTTLE: OnceLock<Mutex<UnauthorizedIpcLogThrottle>> = OnceLock::new();
throttled_unauthorized_ipc_log(&LOG_THROTTLE, |suppressed| { throttled_unauthorized_ipc_log(&LOG_THROTTLE, |suppressed| {
if suppressed > 0 { if suppressed > 0 {
log::warn!( log::warn!(
"Rejected unauthorized connection on ipc channel: postfix={}, peer_pid={:?}, peer_session_id={:?}, expected_session_id={:?}, peer_is_system={:?} (suppressed {} similar events)", "Rejected unauthorized connection on ipc channel: postfix={}, peer_pid={:?}, peer_session_id={:?}, expected_session_id={:?}, peer_is_system={:?}, peer_is_elevated={:?} (suppressed {} similar events)",
postfix, postfix,
peer_pid, peer_pid,
peer_session_id, peer_session_id,
expected_session_id, expected_session_id,
peer_is_system, peer_is_system,
peer_is_elevated,
suppressed suppressed
); );
} else { } else {
log::warn!( log::warn!(
"Rejected unauthorized connection on ipc channel: postfix={}, peer_pid={:?}, peer_session_id={:?}, expected_session_id={:?}, peer_is_system={:?}", "Rejected unauthorized connection on ipc channel: postfix={}, peer_pid={:?}, peer_session_id={:?}, expected_session_id={:?}, peer_is_system={:?}, peer_is_elevated={:?}",
postfix, postfix,
peer_pid, peer_pid,
peer_session_id, peer_session_id,
expected_session_id, expected_session_id,
peer_is_system peer_is_system,
peer_is_elevated
); );
} }
}); });
@@ -655,8 +658,14 @@ pub(crate) fn authorize_service_scoped_ipc_connection(stream: &Connection, postf
#[cfg(windows)] #[cfg(windows)]
pub(crate) fn authorize_windows_main_ipc_connection(stream: &Connection, postfix: &str) -> bool { pub(crate) fn authorize_windows_main_ipc_connection(stream: &Connection, postfix: &str) -> bool {
let (authorized, peer_pid, peer_session_id, server_session_id, peer_is_system) = let (
stream.server_authorization_status(); authorized,
peer_pid,
peer_session_id,
server_session_id,
peer_is_system,
peer_is_elevated,
) = stream.server_authorization_status();
if !authorized { if !authorized {
log_rejected_windows_ipc_connection( log_rejected_windows_ipc_connection(
postfix, postfix,
@@ -664,6 +673,7 @@ pub(crate) fn authorize_windows_main_ipc_connection(stream: &Connection, postfix
peer_session_id, peer_session_id,
server_session_id, server_session_id,
peer_is_system, peer_is_system,
peer_is_elevated,
); );
return false; return false;
} }
@@ -776,7 +786,14 @@ impl ConnectionTmpl<parity_tokio_ipc::Connection> {
fn server_authorization_status( fn server_authorization_status(
&self, &self,
) -> (bool, Option<u32>, Option<u32>, Option<u32>, Option<bool>) { ) -> (
bool,
Option<u32>,
Option<u32>,
Option<u32>,
Option<bool>,
Option<bool>,
) {
let peer_pid = self.peer_pid(); let peer_pid = self.peer_pid();
let server_session_id = crate::platform::windows::get_current_process_session_id(); let server_session_id = crate::platform::windows::get_current_process_session_id();
let peer_session_id = let peer_session_id =
@@ -786,20 +803,34 @@ impl ConnectionTmpl<parity_tokio_ipc::Connection> {
let peer_is_system = peer_is_system_result let peer_is_system = peer_is_system_result
.as_ref() .as_ref()
.and_then(|r| r.as_ref().ok().copied()); .and_then(|r| r.as_ref().ok().copied());
if server_session_id.is_none() && !peer_is_system.unwrap_or(false) { let session_authorized = is_allowed_windows_session_scoped_peer(
// When the server session id cannot be determined, the session-id allow-path is
// disabled and only SYSTEM peers can be authorized.
log::debug!(
"IPC authorization: server session id unavailable; rejecting non-SYSTEM peer, peer_pid={:?}, peer_session_id={:?}",
peer_pid,
peer_session_id
);
}
let authorized = is_allowed_windows_session_scoped_peer(
peer_is_system.unwrap_or(false), peer_is_system.unwrap_or(false),
peer_session_id, peer_session_id,
server_session_id, server_session_id,
); );
let peer_is_elevated_result = if session_authorized {
None
} else {
peer_pid.map(|pid| crate::platform::windows::is_elevated(Some(pid)))
};
let peer_is_elevated = peer_is_elevated_result
.as_ref()
.and_then(|r| r.as_ref().ok().copied());
if server_session_id.is_none()
&& !peer_is_system.unwrap_or(false)
&& !peer_is_elevated.unwrap_or(false)
{
// When the server session id cannot be determined, the session-id allow-path is
// disabled and only privileged peers can be authorized.
log::debug!(
"IPC authorization: server session id unavailable; rejecting non-privileged peer, peer_pid={:?}, peer_session_id={:?}",
peer_pid,
peer_session_id
);
}
// Main IPC trusts same-session peers, LocalSystem, and elevated administrators.
// Service-scoped IPC channels keep their own stricter authorization paths.
let authorized = session_authorized || peer_is_elevated.unwrap_or(false);
if !authorized { if !authorized {
if let (Some(pid), Some(Err(err))) = (peer_pid, peer_is_system_result.as_ref()) { if let (Some(pid), Some(Err(err))) = (peer_pid, peer_is_system_result.as_ref()) {
log::debug!( log::debug!(
@@ -808,6 +839,13 @@ impl ConnectionTmpl<parity_tokio_ipc::Connection> {
err err
); );
} }
if let (Some(pid), Some(Err(err))) = (peer_pid, peer_is_elevated_result.as_ref()) {
log::debug!(
"Failed to determine whether peer process is elevated, pid={}, err={}",
pid,
err
);
}
} }
( (
authorized, authorized,
@@ -815,6 +853,7 @@ impl ConnectionTmpl<parity_tokio_ipc::Connection> {
peer_session_id, peer_session_id,
server_session_id, server_session_id,
peer_is_system, peer_is_system,
peer_is_elevated,
) )
} }

View File

@@ -614,6 +614,7 @@ fn authorize_service_scoped_ipc_connection(
peer_session_id, peer_session_id,
expected_active_session_id, expected_active_session_id,
peer_is_system, peer_is_system,
None,
); );
return false; return false;
} }