summaryrefslogtreecommitdiff
path: root/src/chroot.c
diff options
context:
space:
mode:
authorGiuseppe Scrivano <gscrivano@gnu.org>2009-05-01 23:50:11 +0200
committerJim Meyering <meyering@redhat.com>2009-05-26 15:10:22 +0200
commitc45c51fe97193898f3909dcf5e4c0e117ab239a2 (patch)
tree9a146adbde39cce97dbd4e1ee5aa3eb296fe02a6 /src/chroot.c
parent39285f6008a26ff1d5facbffcbf12f57d60564d1 (diff)
downloadcoreutils-c45c51fe97193898f3909dcf5e4c0e117ab239a2.tar.xz
chroot: accept new options --userspec=U:G and --groups=G1,G2,G3
* NEWS: Note chroot's new options. * doc/coreutils.texi: Document them. * src/chroot.c (main): Add support for --userspec and --groups. * tests/Makefile.am (root-tests): Add chroot/credentials. * tests/chroot/credentials: New file. * tests/test-lib.sh: Define NON_ROOT_GROUP to a default value.
Diffstat (limited to 'src/chroot.c')
-rw-r--r--src/chroot.c125
1 files changed, 122 insertions, 3 deletions
diff --git a/src/chroot.c b/src/chroot.c
index 6d3fddf77..788a1fc41 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -21,17 +21,88 @@
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
+#include <grp.h>
#include "system.h"
#include "error.h"
#include "long-options.h"
#include "quote.h"
+#include "userspec.h"
+#include "xstrtol.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "chroot"
#define AUTHORS proper_name ("Roland McGrath")
+#ifndef MAXGID
+# define MAXGID GID_T_MAX
+#endif
+
+enum
+{
+ GROUPS = UCHAR_MAX + 1,
+ USERSPEC
+};
+
+static struct option const long_opts[] =
+{
+ {"groups", required_argument, NULL, GROUPS},
+ {"userspec", required_argument, NULL, USERSPEC},
+ {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 (char const *groups)
+{
+ GETGROUPS_T *gids = NULL;
+ size_t n_gids_allocated = 0;
+ size_t n_gids = 0;
+ char *buffer = xstrdup (groups);
+ char const *tmp;
+ int ret;
+
+ 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;
+ }
+
+ if (n_gids == n_gids_allocated)
+ gids = x2nrealloc (gids, &n_gids_allocated, sizeof *gids);
+ gids[n_gids++] = value;
+ }
+
+ free (buffer);
+
+ ret = setgroups (n_gids, gids);
+
+ free (gids);
+
+ return ret;
+}
+
+
void
usage (int status)
{
@@ -41,13 +112,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=USER:GROUP specify user and group (ID or name) to use\n\
+ --groups=G_LIST specify supplementary groups as g1,g2,..,gN\n\
+"), stdout);
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
@@ -62,6 +140,10 @@ If no command is given, run ``${SHELL} -i'' (default: /bin/sh).\n\
int
main (int argc, char **argv)
{
+ int c;
+ char const *userspec = NULL;
+ char const *groups = NULL;
+
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -73,8 +155,21 @@ 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 +200,30 @@ main (int argc, char **argv)
argv += optind + 1;
}
+ if (userspec)
+ {
+ uid_t uid;
+ gid_t gid;
+ char *user;
+ char *group;
+ char const *err = parse_user_spec (userspec, &uid, &gid, &user, &group);
+
+ if (err)
+ error (EXIT_FAILURE, errno, "%s", err);
+
+ free (user);
+ free (group);
+
+ if (groups && set_additional_groups (groups))
+ error (0, errno, _("failed to set additional groups"));
+
+ if (gid && setgid (gid))
+ error (0, errno, _("failed to set group-ID"));
+
+ if (uid && setuid (uid))
+ error (0, errno, _("failed to set user-ID"));
+ }
+
/* Execute the given command. */
execvp (argv[0], argv);