]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
su: add --group and --supp-group options
authorKarel Zak <kzak@redhat.com>
Tue, 28 Aug 2012 14:32:28 +0000 (16:32 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 4 Sep 2012 15:00:30 +0000 (17:00 +0200)
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>
login-utils/su.1
login-utils/su.c

index 598cebd8d860d3e16fb01280a787a04bbbdb2f28..59e17314df5dd33e6a16820cb927c2c1da1b3a7d 100644 (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:
index 35a4277438aad7b454db1896215c09d872a616ab..5aff65b1b7212557961efd3cbf57d16041efb956 100644 (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.  */