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>
This commit is contained in:
Karel Zak 2012-08-28 16:32:28 +02:00
parent 4e1073c23b
commit 8528ea2f81
2 changed files with 75 additions and 4 deletions

View File

@ -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:

View File

@ -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. */