From 222941509d9da21c717bad2fb30e9ee038020912 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Thu, 4 Dec 2025 20:29:44 +0900 Subject: [PATCH] core: check suid/sgid dynamically instead of checking file --- nix/nixos-configurations/vm-suid.nix | 2 +- src/core/bootstrap.rs | 8 ++++---- src/core/common.rs | 26 +++++++++----------------- src/main.rs | 6 +++--- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/nix/nixos-configurations/vm-suid.nix b/nix/nixos-configurations/vm-suid.nix index 86e7fbf..020fa90 100644 --- a/nix/nixos-configurations/vm-suid.nix +++ b/nix/nixos-configurations/vm-suid.nix @@ -90,7 +90,7 @@ nixpkgs.lib.nixosSystem { defaultEditor = true; }; - environment.systemPackages = with pkgs; [ jq ]; + environment.systemPackages = with pkgs; [ jq pkgs.muscl ]; }) ]; } diff --git a/src/core/bootstrap.rs b/src/core/bootstrap.rs index 6ebe84c..cabfbac 100644 --- a/src/core/bootstrap.rs +++ b/src/core/bootstrap.rs @@ -10,7 +10,7 @@ use tracing_subscriber::prelude::*; use crate::{ core::common::{ - DEFAULT_CONFIG_PATH, DEFAULT_SOCKET_PATH, UnixUser, executable_is_suid_or_sgid, + DEFAULT_CONFIG_PATH, DEFAULT_SOCKET_PATH, UnixUser, executing_in_suid_sgid_mode, }, server::{ config::{MysqlConfig, ServerConfig}, @@ -81,7 +81,7 @@ pub fn bootstrap_server_connection_and_drop_privileges( ) -> anyhow::Result { if will_connect_to_external_server(server_socket_path.as_ref(), config.as_ref())? { assert!( - !executable_is_suid_or_sgid()?, + !executing_in_suid_sgid_mode()?, "The executable should not be SUID or SGID when connecting to an external server" ); @@ -178,7 +178,7 @@ fn bootstrap_internal_server_and_drop_privs( config_path: Option, ) -> anyhow::Result { if let Some(config_path) = config_path { - if !executable_is_suid_or_sgid()? { + if !executing_in_suid_sgid_mode()? { anyhow::bail!("Executable is not SUID/SGID - refusing to start internal sever"); } @@ -195,7 +195,7 @@ fn bootstrap_internal_server_and_drop_privs( let config_path = PathBuf::from(DEFAULT_CONFIG_PATH); if fs::metadata(&config_path).is_ok() { - if !executable_is_suid_or_sgid()? { + if !executing_in_suid_sgid_mode()? { anyhow::bail!("Executable is not SUID/SGID - refusing to start internal sever"); } tracing::debug!("Starting server with default config at {:?}", config_path); diff --git a/src/core/common.rs b/src/core/common.rs index fb56f28..90f217a 100644 --- a/src/core/common.rs +++ b/src/core/common.rs @@ -67,28 +67,20 @@ fn get_unix_groups(user: &LibcUser) -> anyhow::Result> { Ok(groups) } -/// Check if the current executable is SUID or SGID. -/// -/// If the check fails, an error is returned. +/// Check if the current executable is running in SUID/SGID mode #[cfg(feature = "suid-sgid-mode")] -pub fn executable_is_suid_or_sgid() -> anyhow::Result { - use std::{fs, os::unix::fs::PermissionsExt}; - let result = std::env::current_exe() - .context("Failed to get current executable path") - .and_then(|executable| { - fs::metadata(executable).context("Failed to get executable metadata") - }) - .context("Failed to check SUID/SGID bits on executable") - .map(|metadata| { - let mode = metadata.permissions().mode(); - mode & 0o4000 != 0 || mode & 0o2000 != 0 - })?; - Ok(result) +pub fn executing_in_suid_sgid_mode() -> anyhow::Result { + let euid = nix::unistd::geteuid(); + let uid = nix::unistd::getuid(); + let egid = nix::unistd::getegid(); + let gid = nix::unistd::getgid(); + + Ok(euid != uid || egid != gid) } #[cfg(not(feature = "suid-sgid-mode"))] #[inline] -pub fn executable_is_suid_or_sgid() -> anyhow::Result { +pub fn executing_in_suid_sgid_mode() -> anyhow::Result { Ok(false) } diff --git a/src/main.rs b/src/main.rs index 042cb36..0b8d42b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use futures_util::StreamExt; use crate::{ core::{ bootstrap::bootstrap_server_connection_and_drop_privileges, - common::{ASCII_BANNER, KIND_REGARDS, executable_is_suid_or_sgid}, + common::{ASCII_BANNER, KIND_REGARDS, executing_in_suid_sgid_mode}, protocol::{Response, create_client_to_server_message_stream}, }, server::{command::ServerArgs, landlock::landlock_restrict_server}, @@ -151,7 +151,7 @@ fn main() -> anyhow::Result<()> { fn handle_dynamic_completion() -> anyhow::Result> { if std::env::var_os("COMPLETE").is_some() { #[cfg(feature = "suid-sgid-mode")] - if executable_is_suid_or_sgid()? { + if executing_in_suid_sgid_mode()? { use crate::core::bootstrap::drop_privs; drop_privs()? } @@ -202,7 +202,7 @@ fn handle_server_command(args: &Args) -> anyhow::Result> { match args.command { Command::Server(ref command) => { assert!( - !executable_is_suid_or_sgid()?, + !executing_in_suid_sgid_mode()?, "The executable should not be SUID or SGID when running the server manually" );