From bd4791dc17d6ce2e1fb94578786bc37542883ee3 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 10 Nov 2025 00:33:53 +0900 Subject: [PATCH] Check that executable is not SUID/SGID for unrelated subcommands --- src/core/common.rs | 17 +++++++++++++++++ src/main.rs | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/src/core/common.rs b/src/core/common.rs index ad7aa47..151cd5d 100644 --- a/src/core/common.rs +++ b/src/core/common.rs @@ -20,6 +20,23 @@ fn get_unix_groups(_user: &LibcUser) -> anyhow::Result> { Ok(vec![]) } +/// Check if the current executable is SUID or SGID. +/// +/// If the check fails, an error is returned. +pub fn executable_is_suid_or_sgid() -> anyhow::Result { + 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) +} + #[cfg(not(target_os = "macos"))] fn get_unix_groups(user: &LibcUser) -> anyhow::Result> { let user_cstr = diff --git a/src/main.rs b/src/main.rs index 9603a73..2782011 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use futures::StreamExt; use crate::{ core::{ bootstrap::bootstrap_server_connection_and_drop_privileges, + common::executable_is_suid_or_sgid, protocol::{Response, create_client_to_server_message_stream}, }, server::command::ServerArgs, @@ -152,6 +153,10 @@ fn handle_mysql_admutils_command() -> anyhow::Result> { fn handle_server_command(args: &Args) -> anyhow::Result> { match args.command { Command::Server(ref command) => { + assert!( + !executable_is_suid_or_sgid()?, + "The executable should not be SUID or SGID when running the server manually" + ); tokio_start_server( args.server_socket_path.to_owned(), args.config.to_owned(), @@ -167,6 +172,10 @@ fn handle_server_command(args: &Args) -> anyhow::Result> { fn handle_generate_completions_command(args: &Args) -> anyhow::Result> { match args.command { Command::GenerateCompletions(ref completion_args) => { + assert!( + !executable_is_suid_or_sgid()?, + "The executable should not be SUID or SGID when generating completions" + ); let mut cmd = match completion_args.command { ToplevelCommands::Mysqladm => Args::command(), #[cfg(feature = "mysql-admutils-compatibility")]