From 6273784aa4f40121b3963b41df0986044eeaced0 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 28 Aug 2012 16:32:28 +0200 Subject: [PATCH 200/208] 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 --- login-utils/su.1 | 6 +++++ login-utils/su.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/login-utils/su.1 b/login-utils/su.1 index 598cebd..59e1731 100644 --- a/login-utils/su.1 +++ b/login-utils/su.1 @@ -59,6 +59,12 @@ Pass to the shell which may or may not be useful depending on the shell. .TP +\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR +specify the primary group, this option is allowed for root user only +.TP +\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR +specify a supplemental group, this option is allowed for root user only +.TP \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR Starts the shell as login shell with an environment similar to a real login: diff --git a/login-utils/su.c b/login-utils/su.c index c6b8bce..f11c757 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} @@ -424,11 +426,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")); @@ -535,6 +544,8 @@ usage (int status) -c, --command pass a single command to the shell with -c\n\ --session-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\ @@ -556,6 +567,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) { @@ -566,6 +590,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); @@ -575,7 +604,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) { @@ -592,6 +621,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; @@ -617,6 +666,8 @@ main (int argc, char **argv) } } + restricted = evaluate_uid (); + if (optind < argc && !strcmp (argv[optind], "-")) { simulate_login = true; @@ -625,6 +676,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); @@ -648,6 +702,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) @@ -666,7 +731,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. */ -- 1.7.11.7