diff --git a/src/uu/id/locales/en-US.ftl b/src/uu/id/locales/en-US.ftl index a6b4ac2256f..49264b30ed6 100644 --- a/src/uu/id/locales/en-US.ftl +++ b/src/uu/id/locales/en-US.ftl @@ -48,4 +48,5 @@ id-output-uid = uid id-output-groups = groups id-output-login = login id-output-euid = euid +id-output-rgid = rgid id-output-context = context diff --git a/src/uu/id/locales/fr-FR.ftl b/src/uu/id/locales/fr-FR.ftl index b606f520757..2e799ae37bc 100644 --- a/src/uu/id/locales/fr-FR.ftl +++ b/src/uu/id/locales/fr-FR.ftl @@ -48,4 +48,5 @@ id-output-uid = uid id-output-groups = groupes id-output-login = connexion id-output-euid = euid +id-output-rgid = rgid id-output-context = contexte diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index dcdc692435d..b1cb7be39d4 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag cflag +// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid rgid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag cflag // README: // This was originally based on BSD's `id` @@ -474,31 +474,32 @@ fn pretty(possible_pw: Option) { ); } else { let login = cstr2cow!(getlogin().cast_const()); - let rid = getuid(); - if let Ok(p) = Passwd::locate(rid) { + let uid = getuid(); + if let Ok(p) = Passwd::locate(uid) { if let Some(user_name) = login { println!("{}\t{user_name}", translate!("id-output-login")); } println!("{}\t{}", translate!("id-output-uid"), p.name); } else { - println!("{}\t{rid}", translate!("id-output-uid")); + println!("{}\t{uid}", translate!("id-output-uid")); } - let eid = getegid(); - if eid == rid { - if let Ok(p) = Passwd::locate(eid) { + let euid = geteuid(); + if euid != uid { + if let Ok(p) = Passwd::locate(euid) { println!("{}\t{}", translate!("id-output-euid"), p.name); } else { - println!("{}\t{eid}", translate!("id-output-euid")); + println!("{}\t{euid}", translate!("id-output-euid")); } } - let rid = getgid(); - if rid != eid { - if let Ok(g) = Group::locate(rid) { - println!("{}\t{}", translate!("id-output-euid"), g.name); + let rgid = getgid(); + let egid = getegid(); + if egid != rgid { + if let Ok(g) = Group::locate(rgid) { + println!("{}\t{}", translate!("id-output-rgid"), g.name); } else { - println!("{}\t{rid}", translate!("id-output-euid")); + println!("{}\t{rgid}", translate!("id-output-rgid")); } } diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index aaef4e7785f..ec96e3c23e8 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) coreutil +// spell-checker:ignore (ToDO) coreutil euid rgid use std::process::{Command, Stdio}; use uutests::new_ucmd; @@ -11,6 +11,9 @@ use uutests::unwrap_or_return; use uutests::util::{TestScenario, check_coreutil_version, expected_result, is_ci, whoami}; use uutests::util_name; +#[cfg(all(feature = "chmod", feature = "chown"))] +use tempfile::TempPath; + const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; // this feature was introduced in GNU's coreutils 8.31 #[test] @@ -477,6 +480,55 @@ fn test_id_pretty_print_password_record() { .stderr_contains("the argument '-p' cannot be used with '-P'"); } +#[test] +#[cfg(all(feature = "chmod", feature = "chown"))] +fn test_id_pretty_print_suid_binary() { + use uucore::process::{getgid, getuid}; + + if let Some(suid_coreutils_path) = create_root_owned_suid_coreutils_binary() { + let result = TestScenario::new(util_name!()) + .cmd(suid_coreutils_path.to_str().unwrap()) + .args(&[util_name!(), "-p"]) + .succeeds(); + + // The `euid` line should be present only if the real UID does not belong to `root` + if getuid() == 0 { + result.stdout_does_not_contain("euid\t"); + } else { + result.stdout_contains_line("euid\troot"); + } + + // The `rgid` line should be present only if the real GID does not belong to `root` + if getgid() == 0 { + result.stdout_does_not_contain("rgid\t"); + } else { + result.stdout_contains("rgid\t"); + } + } else { + print!("Test skipped; requires root user"); + } +} + +/// Create SUID temp file owned by `root:root` with the contents of the `coreutils` binary +#[cfg(all(feature = "chmod", feature = "chown"))] +fn create_root_owned_suid_coreutils_binary() -> Option { + use std::fs::read; + use std::io::Write; + use tempfile::NamedTempFile; + use uutests::util::{get_tests_binary, run_ucmd_as_root}; + + let mut temp_file = NamedTempFile::new().unwrap(); + let coreutils_binary = read(get_tests_binary()).unwrap(); + temp_file.write_all(&coreutils_binary).unwrap(); + let temp_path = temp_file.into_temp_path(); + let temp_path_str = temp_path.to_str().unwrap(); + + run_ucmd_as_root(&TestScenario::new("chown"), &["root:root", temp_path_str]).ok()?; + run_ucmd_as_root(&TestScenario::new("chmod"), &["+xs", temp_path_str]).ok()?; + + Some(temp_path) +} + /// This test requires user with username 200 on system #[test] #[cfg(unix)]