From caa0018590df464514bc8396a68edf8728696964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Mon, 21 Apr 2025 13:40:04 +0300 Subject: [PATCH 1/6] id: Print EUID when it is different from real UID --- src/uu/id/src/id.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 314d12d6804..952cad3bf26 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -465,8 +465,8 @@ fn pretty(possible_pw: Option) { println!("uid\t{rid}"); } - let eid = getegid(); - if eid == rid { + let eid = geteuid(); + if eid != rid { if let Ok(p) = Passwd::locate(eid) { println!("euid\t{}", p.name); } else { From a284a93dfd128f5379d0a831371f4261977e0f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Mon, 21 Apr 2025 13:50:18 +0300 Subject: [PATCH 2/6] id: Remove extra printing of euid --- src/uu/id/src/id.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 952cad3bf26..35f6b371379 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -474,15 +474,6 @@ fn pretty(possible_pw: Option) { } } - let rid = getgid(); - if rid != eid { - if let Ok(g) = Group::locate(rid) { - println!("euid\t{}", g.name); - } else { - println!("euid\t{rid}"); - } - } - println!( "groups\t{}", entries::get_groups_gnu(None) From 8800d65af09ff668076d935763363c6003270900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Mon, 21 Apr 2025 13:51:11 +0300 Subject: [PATCH 3/6] id: Print real GID if it is different from EGID --- src/uu/id/src/id.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 35f6b371379..90664045889 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -474,6 +474,16 @@ fn pretty(possible_pw: Option) { } } + let rgid = getgid(); + let egid = getegid(); + if egid != rgid { + if let Ok(g) = Group::locate(rgid) { + println!("rgid\t{}", g.name); + } else { + println!("rgid\t{rgid}"); + } + } + println!( "groups\t{}", entries::get_groups_gnu(None) From 2d30c16e2c5af0382d747ed1549a6396454c2615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Mon, 21 Apr 2025 14:09:14 +0300 Subject: [PATCH 4/6] id: Spell-checker ignore rgid --- src/uu/id/src/id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 90664045889..538ce4d9ce9 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` From 2109daddbeb0e32b7ec5f856450eadd9f4f3c8b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Sun, 8 Jun 2025 15:33:52 +0300 Subject: [PATCH 5/6] id: Rename `uid` and `euid` for consistency --- src/uu/id/src/id.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index d0baccbe64e..faf94e10818 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -473,22 +473,22 @@ 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}", get_message("id-output-login")); } println!("{}\t{}", get_message("id-output-uid"), p.name); } else { - println!("{}\t{rid}", get_message("id-output-uid")); + println!("{}\t{uid}", get_message("id-output-uid")); } - let eid = geteuid(); - 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{}", get_message("id-output-euid"), p.name); } else { - println!("{}\t{eid}", get_message("id-output-euid")); + println!("{}\t{euid}", get_message("id-output-euid")); } } From e78eda1413fced2e7afedde0cc8e34e4eee7340b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Sat, 14 Jun 2025 12:26:36 +0300 Subject: [PATCH 6/6] id: Test `-p` with root-owned suid binary --- tests/by-util/test_id.rs | 54 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index 7a7d5e9a169..d6144b054da 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -3,13 +3,16 @@ // 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 uutests::new_ucmd; 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] @@ -470,3 +473,52 @@ fn test_id_pretty_print_password_record() { .fails() .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) +}