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 <gscriv...@gnu.org> 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}, +...@option{--userspec} 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 _______________________________________________ Bug-coreutils mailing list Bug-coreutils@gnu.org http://lists.gnu.org/mailman/listinfo/bug-coreutils