Skip to content
1 change: 1 addition & 0 deletions src/uu/id/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions src/uu/id/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
27 changes: 14 additions & 13 deletions src/uu/id/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -474,31 +474,32 @@ fn pretty(possible_pw: Option<Passwd>) {
);
} 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"));
}
}

Expand Down
54 changes: 53 additions & 1 deletion tests/by-util/test_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
// 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;
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]
Expand Down Expand Up @@ -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<TempPath> {
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)]
Expand Down
Loading