diff --git a/userspace/ksud/src/ksu.rs b/userspace/ksud/src/ksu.rs index 6045a6118dc3..3753e53809b7 100644 --- a/userspace/ksud/src/ksu.rs +++ b/userspace/ksud/src/ksu.rs @@ -58,10 +58,12 @@ fn print_usage(program: &str, opts: Options) { print!("{}", opts.usage(&brief)); } -fn set_identity(uid: u32, gid: u32) { +fn set_identity(uid: u32, gid: u32, groups: &Vec) { #[cfg(any(target_os = "linux", target_os = "android"))] unsafe { - libc::seteuid(uid); + if !groups.is_empty() { + libc::setgroups(groups.len(), groups.as_ptr()); + } libc::setresgid(gid, gid, gid); libc::setresuid(uid, uid, uid); } @@ -117,6 +119,13 @@ pub fn root_shell() -> Result<()> { "mount-master", "force run in the global mount namespace", ); + opts.optopt("g", "group", "Specify the primary group", "GROUP"); + opts.optmulti( + "G", + "supp-group", + "Specify a supplementary group. The first specified supplementary group is also used as a primary group if the option -g is not specified.", + "GROUP", + ); // Replace -cn with -z, -mm with -M for supporting getopt_long let args = args @@ -160,6 +169,31 @@ pub fn root_shell() -> Result<()> { let mut is_login = matches.opt_present("l"); let preserve_env = matches.opt_present("p"); let mount_master = matches.opt_present("M"); + let mut groups = Vec::::new(); + for g in matches.opt_strs("G") { + if let core::result::Result::Ok(id) = g.parse::() { + groups.push(id); + } else { + println!("Invalid GID: {g}"); + print_usage(&program, opts); + return Ok(()); + } + } + let mut gid: Option = None; + // if -g provided, use it. + if let Some(g) = matches.opt_str("g") { + if let core::result::Result::Ok(id) = g.parse::() { + gid = Some(id); + } else { + println!("Invalid GID: {g}"); + print_usage(&program, opts); + return Ok(()); + } + } + // otherwise, use the first gid of groups. + if gid.is_none() && !groups.is_empty() { + gid = Some(groups[0]); + } // we've make sure that -c is the last option and it already contains the whole command, no need to construct it again let args = matches @@ -175,7 +209,6 @@ pub fn root_shell() -> Result<()> { // use current uid if no user specified, these has been done in kernel! let mut uid = unsafe { libc::getuid() }; - let gid = unsafe { libc::getgid() }; if free_idx < matches.free.len() { let name = &matches.free[free_idx]; uid = unsafe { @@ -191,6 +224,8 @@ pub fn root_shell() -> Result<()> { } } + // if there is no gid provided, use uid. + let gid = gid.unwrap_or(uid); // https://github.com/topjohnwu/Magisk/blob/master/native/src/su/su_daemon.cpp#L408 let arg0 = if is_login { "-" } else { &shell }; @@ -241,7 +276,7 @@ pub fn root_shell() -> Result<()> { let _ = utils::unshare_mnt_ns(); } - set_identity(uid, gid); + set_identity(uid, gid, &groups); std::result::Result::Ok(()) })