mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-04-20 02:36:16 +02:00
chroot: don't chdir() if not changing root
This allows chroot to be used as a light weight tool to change user identification for a command, while not changing the current working directory. It also makes `chroot / true` consistently succeed on all platforms for non root users. * src/chroot.c (main): If the same root is specified. i.e. '/' then don't change the current working directory, and avoid the overhead of the other redundant calls. * tests/misc/chroot-fail.sh: Remove failure guard previously needed on some systems. Also add an explicit case to ensure we don't change directory. * NEWS: Mention the change in behavior.
This commit is contained in:
3
NEWS
3
NEWS
@@ -82,6 +82,9 @@ GNU coreutils NEWS -*- outline -*-
|
||||
|
||||
** Changes in behavior
|
||||
|
||||
chroot with an argument of "/" no longer implicitly changes the current
|
||||
directory to "/", allowing changing only user credentials for a command.
|
||||
|
||||
ls with none of LS_COLORS or COLORTERM environment variables set,
|
||||
will now honor an empty or unknown TERM environment variable,
|
||||
and not output colors even with --colors=always.
|
||||
|
||||
32
src/chroot.c
32
src/chroot.c
@@ -225,21 +225,27 @@ main (int argc, char **argv)
|
||||
usage (EXIT_CANCELED);
|
||||
}
|
||||
|
||||
/* We have to look up users and groups twice.
|
||||
- First, outside the chroot to load potentially necessary passwd/group
|
||||
parsing plugins (e.g. NSS);
|
||||
- Second, inside chroot to redo the parsing in case IDs are different. */
|
||||
if (userspec)
|
||||
ignore_value (parse_user_spec (userspec, &uid, &gid, NULL, NULL));
|
||||
if (groups && *groups)
|
||||
ignore_value (parse_additional_groups (groups, &out_gids, &n_gids, false));
|
||||
/* Only do chroot specific actions if actually changing root.
|
||||
The main difference here is that we don't change working dir. */
|
||||
if (! STREQ (argv[optind], "/"))
|
||||
{
|
||||
/* We have to look up users and groups twice.
|
||||
- First, outside the chroot to load potentially necessary passwd/group
|
||||
parsing plugins (e.g. NSS);
|
||||
- Second, inside chroot to redo parsing in case IDs are different. */
|
||||
if (userspec)
|
||||
ignore_value (parse_user_spec (userspec, &uid, &gid, NULL, NULL));
|
||||
if (groups && *groups)
|
||||
ignore_value (parse_additional_groups (groups, &out_gids, &n_gids,
|
||||
false));
|
||||
|
||||
if (chroot (argv[optind]) != 0)
|
||||
error (EXIT_CANCELED, errno, _("cannot change root directory to %s"),
|
||||
argv[optind]);
|
||||
if (chroot (argv[optind]) != 0)
|
||||
error (EXIT_CANCELED, errno, _("cannot change root directory to %s"),
|
||||
argv[optind]);
|
||||
|
||||
if (chdir ("/"))
|
||||
error (EXIT_CANCELED, errno, _("cannot chdir to root directory"));
|
||||
if (chdir ("/"))
|
||||
error (EXIT_CANCELED, errno, _("cannot chdir to root directory"));
|
||||
}
|
||||
|
||||
if (argc == optind + 1)
|
||||
{
|
||||
|
||||
@@ -28,16 +28,18 @@ test $? = 125 || fail=1
|
||||
chroot --- / true # unknown option
|
||||
test $? = 125 || fail=1
|
||||
|
||||
# chroot("/") succeeds for non-root users on some systems, but not all.
|
||||
if chroot / true ; then
|
||||
chroot / sh -c 'exit 2' # exit status propagation
|
||||
test $? = 2 || fail=1
|
||||
chroot / . # invalid command
|
||||
test $? = 126 || fail=1
|
||||
chroot / no_such # no such command
|
||||
test $? = 127 || fail=1
|
||||
else
|
||||
test $? = 125 || fail=1
|
||||
fi
|
||||
# Note chroot("/") succeeds for non-root users on some systems, but not all,
|
||||
# however we avoid the chroot() with "/" to have common behvavior.
|
||||
chroot / sh -c 'exit 2' # exit status propagation
|
||||
test $? = 2 || fail=1
|
||||
chroot / . # invalid command
|
||||
test $? = 126 || fail=1
|
||||
chroot / no_such # no such command
|
||||
test $? = 127 || fail=1
|
||||
|
||||
# Ensure we don't chdir("/") when not changing root
|
||||
# to allow only changing user ids for a command.
|
||||
curdir=$(chroot / env pwd) || fail=1
|
||||
test "$curdir" = '/' && fail=1
|
||||
|
||||
Exit $fail
|
||||
|
||||
Reference in New Issue
Block a user