diff options
author | Karel Zak | 2012-08-28 16:32:28 +0200 |
---|---|---|
committer | Karel Zak | 2012-09-04 17:00:30 +0200 |
commit | 8528ea2f81487142047a49ec6601a745f2eab365 (patch) | |
tree | 2260c6946db300c7decaaf8429f6fee9e64333f1 /login-utils/su.c | |
parent | build-sys: add configure summary (diff) | |
download | kernel-qcow2-util-linux-8528ea2f81487142047a49ec6601a745f2eab365.tar.gz kernel-qcow2-util-linux-8528ea2f81487142047a49ec6601a745f2eab365.tar.xz kernel-qcow2-util-linux-8528ea2f81487142047a49ec6601a745f2eab365.zip |
su: add --group and --supp-group options
These options allow to specify alternative groups. The command
su(1) has to be executed by root. The implementation is based on
Fedora runuser(1) command.
For example:
# su --group=kzak --supp-group=uuidd -
# id
uid=0(root) gid=1000(kzak) groups=0(root),985(uuidd),1000(kzak)
non-root user:
$ su --group=kzak -
su: only root can specify alternative groups
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'login-utils/su.c')
-rw-r--r-- | login-utils/su.c | 73 |
1 files changed, 69 insertions, 4 deletions
diff --git a/login-utils/su.c b/login-utils/su.c index 35a427743..5aff65b1b 100644 --- a/login-utils/su.c +++ b/login-utils/su.c @@ -110,6 +110,8 @@ static struct option const longopts[] = {"login", no_argument, NULL, 'l'}, {"preserve-environment", no_argument, NULL, 'p'}, {"shell", required_argument, NULL, 's'}, + {"group", required_argument, NULL, 'g'}, + {"supp-group", required_argument, NULL, 'G'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {NULL, 0, NULL, 0} @@ -548,11 +550,18 @@ modify_environment (const struct passwd *pw, const char *shell) /* Become the user and group(s) specified by PW. */ static void -init_groups (const struct passwd *pw) +init_groups (const struct passwd *pw, gid_t *groups, int num_groups) { int retval; + errno = 0; - if (initgroups (pw->pw_name, pw->pw_gid) == -1) + + if (num_groups) + retval = setgroups (num_groups, groups); + else + retval = initgroups (pw->pw_name, pw->pw_gid); + + if (retval == -1) { cleanup_pam (PAM_ABORT); err (EXIT_FAILURE, _("cannot set groups")); @@ -659,6 +668,8 @@ usage (int status) -c, --command <command> pass a single command to the shell with -c\n\ --session-command <command> pass a single command to the shell with -c\n\ and do not create a new session\n\ + -g --group=group specify the primary group\n\ + -G --supp-group=group specify a supplemental group\n\ -f, --fast pass -f to the shell (for csh or tcsh)\n\ -m, --preserve-environment do not reset environment variables\n\ -p same as -m\n\ @@ -680,6 +691,19 @@ void load_config(void) logindefs_load_file(_PATH_LOGINDEFS); } +/* + * Returns 1 if the current user is not root + */ +static int +evaluate_uid(void) +{ + uid_t ruid = getuid(); + uid_t euid = geteuid(); + + /* if we're really root and aren't running setuid */ + return (uid_t) 0 == ruid && ruid == euid ? 0 : 1; +} + int main (int argc, char **argv) { @@ -690,6 +714,11 @@ main (int argc, char **argv) char *shell = NULL; struct passwd *pw; struct passwd pw_copy; + struct group *gr; + gid_t groups[NGROUPS_MAX]; + int num_supp_groups = 0; + int use_gid = 0; + int restricted; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -699,7 +728,7 @@ main (int argc, char **argv) simulate_login = false; change_environment = true; - while ((optc = getopt_long (argc, argv, "c:flmps:hV", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "c:fg:G:lmps:hV", longopts, NULL)) != -1) { switch (optc) { @@ -716,6 +745,26 @@ main (int argc, char **argv) fast_startup = true; break; + case 'g': + gr = getgrnam(optarg); + if (!gr) + errx(EXIT_FAILURE, _("group %s does not exist"), optarg); + use_gid = 1; + groups[0] = gr->gr_gid; + break; + + case 'G': + num_supp_groups++; + if (num_supp_groups >= NGROUPS_MAX) + errx(EXIT_FAILURE, + _("can't specify more than %d supplemental groups"), + NGROUPS_MAX - 1); + gr = getgrnam(optarg); + if (!gr) + errx(EXIT_FAILURE, _("group %s does not exist"), optarg); + groups[num_supp_groups] = gr->gr_gid; + break; + case 'l': simulate_login = true; break; @@ -741,6 +790,8 @@ main (int argc, char **argv) } } + restricted = evaluate_uid (); + if (optind < argc && !strcmp (argv[optind], "-")) { simulate_login = true; @@ -749,6 +800,9 @@ main (int argc, char **argv) if (optind < argc) new_user = argv[optind++]; + if ((num_supp_groups || use_gid) && restricted) + errx(EXIT_FAILURE, _("only root can specify alternative groups")); + logindefs_load_defaults = load_config; pw = getpwnam (new_user); @@ -772,6 +826,17 @@ main (int argc, char **argv) : DEFAULT_SHELL); endpwent (); + if (num_supp_groups && !use_gid) + { + pw->pw_gid = groups[1]; + memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups); + } + else if (use_gid) + { + pw->pw_gid = groups[0]; + num_supp_groups++; + } + authenticate (pw); if (request_same_session || !command || !pw->pw_uid) @@ -790,7 +855,7 @@ main (int argc, char **argv) } shell = xstrdup (shell ? shell : pw->pw_shell); - init_groups (pw); + init_groups (pw, groups, num_supp_groups); create_watching_parent (); /* Now we're in the child. */ |