Check that executable is not SUID/SGID for unrelated subcommands

This commit is contained in:
2025-11-10 00:33:53 +09:00
parent 05a4f9ad1d
commit bd4791dc17
2 changed files with 26 additions and 0 deletions

View File

@@ -20,6 +20,23 @@ fn get_unix_groups(_user: &LibcUser) -> anyhow::Result<Vec<LibcGroup>> {
Ok(vec![]) 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<bool> {
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"))] #[cfg(not(target_os = "macos"))]
fn get_unix_groups(user: &LibcUser) -> anyhow::Result<Vec<LibcGroup>> { fn get_unix_groups(user: &LibcUser) -> anyhow::Result<Vec<LibcGroup>> {
let user_cstr = let user_cstr =

View File

@@ -15,6 +15,7 @@ use futures::StreamExt;
use crate::{ use crate::{
core::{ core::{
bootstrap::bootstrap_server_connection_and_drop_privileges, bootstrap::bootstrap_server_connection_and_drop_privileges,
common::executable_is_suid_or_sgid,
protocol::{Response, create_client_to_server_message_stream}, protocol::{Response, create_client_to_server_message_stream},
}, },
server::command::ServerArgs, server::command::ServerArgs,
@@ -152,6 +153,10 @@ fn handle_mysql_admutils_command() -> anyhow::Result<Option<()>> {
fn handle_server_command(args: &Args) -> anyhow::Result<Option<()>> { fn handle_server_command(args: &Args) -> anyhow::Result<Option<()>> {
match args.command { match args.command {
Command::Server(ref 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( tokio_start_server(
args.server_socket_path.to_owned(), args.server_socket_path.to_owned(),
args.config.to_owned(), args.config.to_owned(),
@@ -167,6 +172,10 @@ fn handle_server_command(args: &Args) -> anyhow::Result<Option<()>> {
fn handle_generate_completions_command(args: &Args) -> anyhow::Result<Option<()>> { fn handle_generate_completions_command(args: &Args) -> anyhow::Result<Option<()>> {
match args.command { match args.command {
Command::GenerateCompletions(ref completion_args) => { 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 { let mut cmd = match completion_args.command {
ToplevelCommands::Mysqladm => Args::command(), ToplevelCommands::Mysqladm => Args::command(),
#[cfg(feature = "mysql-admutils-compatibility")] #[cfg(feature = "mysql-admutils-compatibility")]