bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] chroot specify user/group feature


From: Giuseppe Scrivano
Subject: Re: [PATCH] chroot specify user/group feature
Date: Tue, 19 May 2009 22:32:04 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.93 (gnu/linux)

Hi Jim,

this is an updated version for the previous patch.  I added
documentation and new tests.

Since I don't use short-named options, there are not conflicts with -u,
-g and -G used by different chroot implementations.
In my first version -g has a different meaning than the -g on FreeBSD
and OpenBSD; now I removed this option and I kept only the long-named
version --groups.

Regards,
Giuseppe



>From 60834e55e2826d41de545794123a7007536873e4 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Fri, 1 May 2009 23:50:11 +0200
Subject: [PATCH] Chroot now accepts the options --userspec and --groups.

* NEWS: Added note about the new chroot features.
* doc/coreutils.texi: Likewise.
* src/chroot.c (main): Added support for --userspec and --groups.
* tests/Makefile.am: Added tests for chroot.
* tests/chroot/credentials: Likewise.
* tests/test-lib.sh: Likewise.
---
 NEWS                     |    5 ++
 doc/coreutils.texi       |   13 ++++-
 src/chroot.c             |  125 ++++++++++++++++++++++++++++++++++++++++++++-
 tests/Makefile.am        |    1 +
 tests/chroot/credentials |   41 +++++++++++++++
 tests/test-lib.sh        |    1 +
 6 files changed, 181 insertions(+), 5 deletions(-)
 create mode 100644 tests/chroot/credentials

diff --git a/NEWS b/NEWS
index 31f1b1a..3af06e4 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,11 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   truncate -s failed to skip all whitespace in the option argument in
   some locales.
 
+** New features
+
+  chroot now accepts the options --userspec and --groups.
+
+
 * Noteworthy changes in release 7.4 (2009-05-07) [stable]
 
 ** Bug fixes
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b96fdb2..29745b1 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -14157,8 +14157,17 @@ variable or @command{/bin/sh} if not set, invoked with 
the @option{-i} option.
 @var{command} must not be a special built-in utility
 (@pxref{Special built-in utilities}).
 
-The only options are @option{--help} and @option{--version}.  @xref{Common
-options}.  Options must precede operands.
+Options accepted by chroot are @option{--help}, @option{--version},
address@hidden and @option{--groups}.  @xref{Common options}.
+Options must precede operands.
+
+If not specified differently, @command{chroot} keeps the user
+credentials in the new environment, that usually is the super-user.
+This behaviour can be changed by the @option{--userspec} option.  It
+allows to specify new credentials in the form @var{user:group}.
+Additional groups can be configured using the @option{--groups}
+option.  If any of @option{--userspec} or  @option{--groups} are not
+specified then the original values are kept.
 
 Here are a few tips to help avoid common problems in using chroot.
 To start with a simple example, make @var{command} refer to a statically
diff --git a/src/chroot.c b/src/chroot.c
index 6d3fddf..ca9ac00 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -21,17 +21,80 @@
 #include <getopt.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <grp.h>
 
 #include "system.h"
 #include "error.h"
 #include "long-options.h"
+#include "userspec.h"
 #include "quote.h"
+#include "xstrtol.h"
+
+#ifndef GID_T_MAX
+# define GID_T_MAX TYPE_MAXIMUM (gid_t)
+#endif
+
+#ifndef MAXGID
+# define MAXGID GID_T_MAX
+#endif
+
 
 /* The official name of this program (e.g., no `g' prefix).  */
 #define PROGRAM_NAME "chroot"
 
 #define AUTHORS proper_name ("Roland McGrath")
 
+enum { USERSPEC = UCHAR_MAX + 1, GROUPS};
+
+static struct option const long_opts[] =
+{
+  {"userspec", required_argument, NULL, USERSPEC},
+  {"groups", required_argument, NULL, GROUPS},
+  {GETOPT_HELP_OPTION_DECL},
+  {GETOPT_VERSION_OPTION_DECL},
+  {NULL, 0, NULL, 0}
+};
+
+/* Groups is a comma separated list of additional groups.  */
+static int set_additional_groups (const char *groups)
+{
+  gid_t groups_id [NGROUPS];
+  int ngroups = 0;
+  char *buffer = xstrdup (groups);
+  const char *tmp;
+
+  for (tmp = strtok (buffer, ","); tmp; tmp = strtok (NULL, ","))
+    {
+      struct group *g;
+      unsigned long int value;
+
+      if (xstrtoul (tmp, NULL, 10, &value, "") == LONGINT_OK && value <= 
MAXGID)
+        {
+          g = getgrgid (value);
+        }
+      else
+        {
+          g = getgrnam (tmp);
+          if (g != NULL)
+            value = g->gr_gid;
+        }
+
+      if (g == NULL)
+        {
+          error (0, errno, _("cannot find group %s"), tmp);
+          free (buffer);
+          return 1;
+        }
+
+      groups_id[ngroups++] = value;
+    }
+
+  free (buffer);
+
+  return setgroups (ngroups, groups_id);
+}
+
+
 void
 usage (int status)
 {
@@ -41,13 +104,20 @@ usage (int status)
   else
     {
       printf (_("\
-Usage: %s NEWROOT [COMMAND [ARG]...]\n\
+Usage: %s [OPTION] NEWROOT [COMMAND [ARG]...]\n\
   or:  %s OPTION\n\
 "), program_name, program_name);
+
       fputs (_("\
 Run COMMAND with root directory set to NEWROOT.\n\
 \n\
 "), stdout);
+
+      fputs (_("\
+  --userspec,  specify the userspec in the form USER:GROUP\n\
+  --groups,  specify the additional groups as g1,g2,..,gn\n\
+"), stdout);
+
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
       fputs (_("\
@@ -62,6 +132,10 @@ If no command is given, run ``${SHELL} -i'' (default: 
/bin/sh).\n\
 int
 main (int argc, char **argv)
 {
+  int c;
+  const char *userspec = NULL;
+  const char *groups = NULL;
+
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
   setlocale (LC_ALL, "");
@@ -73,8 +147,20 @@ main (int argc, char **argv)
 
   parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
                      usage, AUTHORS, (char const *) NULL);
-  if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
-    usage (EXIT_FAILURE);
+
+  while ((c = getopt_long (argc, argv, "+", long_opts, NULL))   != -1)
+    {
+      switch (c) {
+      case USERSPEC:
+        userspec = optarg;
+        break;
+      case GROUPS:
+        groups = optarg;
+        break;
+      default:
+        usage (EXIT_FAILURE);
+      }
+    }
 
   if (argc <= optind)
     {
@@ -105,6 +191,39 @@ main (int argc, char **argv)
       argv += optind + 1;
     }
 
+  if (userspec)
+    {
+      uid_t uid;
+      gid_t gid;
+      char *user;
+      char *group;
+      const char *err = parse_user_spec (userspec, &uid, &gid, &user, &group);
+      if (err)
+        {
+          perror (err);
+          exit (1);
+        }
+
+      free (user);
+      free (group);
+
+      if (groups)
+        {
+          if (set_additional_groups (groups))
+            error (0, errno, _("cannot set additional groups"));
+        }
+
+      if (gid && setgid (gid))
+        {
+          error (0, errno, _("cannot setgid"));
+        }
+
+      if (uid && setuid (uid))
+        {
+          error (0, errno, _("cannot setgid"));
+        }
+    }
+
   /* Execute the given command.  */
   execvp (argv[0], argv);
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7fe74c0..c3746e2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,6 +24,7 @@ root_tests =                                  \
   cp/preserve-gid                              \
   cp/special-bits                              \
   cp/cp-mv-enotsup-xattr                       \
+       chroot/credentials \
   dd/skip-seek-past-dev                                \
   install/install-C-root                       \
   ls/capability                                        \
diff --git a/tests/chroot/credentials b/tests/chroot/credentials
new file mode 100644
index 0000000..f3e7a32
--- /dev/null
+++ b/tests/chroot/credentials
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Verify that the credentials are changed correctly.
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+if test "$VERBOSE" = yes; then
+  set -x
+  chroot --version
+fi
+
+. $srcdir/test-lib.sh
+
+require_root_
+
+fail=0
+
+# Verify that root credentials are kept.
+test `chroot / whoami` == root || fail=1
+test "`groups`" == "`chroot / groups`" || fail=1
+
+# Verify that credentials are changed correctly.
+test "`chroot --userspec=$NON_ROOT_USERNAME:$NON_ROOT_GROUP / whoami`" != root 
|| fail=1
+
+# Verify that there are no additional groups.
+test "`chroot --userspec=$NON_ROOT_USERNAME:$NON_ROOT_GROUP --groups="" / id 
-nG`" == $NON_ROOT_GROUP || fail=1
+
+Exit $fail
diff --git a/tests/test-lib.sh b/tests/test-lib.sh
index 17a3538..a765bd6 100644
--- a/tests/test-lib.sh
+++ b/tests/test-lib.sh
@@ -204,6 +204,7 @@ require_root_()
 {
   uid_is_privileged_ || skip_test_ "must be run as root"
   NON_ROOT_USERNAME=${NON_ROOT_USERNAME=nobody}
+  NON_ROOT_GROUP=${NON_ROOT_GROUP=nogroup}
 }
 
 skip_if_root_() { uid_is_privileged_ && skip_test_ "must be run as non-root"; }
-- 
1.6.2.4





reply via email to

[Prev in Thread] Current Thread [Next in Thread]