Attached is the final version of the chroot patch. I'll submit it in
the next few days.
It seems absurd to add support for chroot() in useradd and groupadd
without userdel and groupdel, so the patch includes support for them.
Also, to create a smaller footprint, I've combined all applicable
functions into one file. The downside is more complex macro expansions
(comments included, though), but it allows for a more integrated
interface (generated function xfgetXXbyYY calls generated functions
xfsetXXent, xfgetXXent, and xfendXXent), and less alteration of shadow's
own code.
PAM isn't a concern because chroot() only strictly works in a process
with an su uid. And a function to parse the chroot flag before any
others (leaving argv and argc in a pristine state) is included.
-- Peter Levine
diff -Naur shadow-4.1.4.2.old/config.h.in shadow-4.1.4.2/config.h.in
--- shadow-4.1.4.2.old/config.h.in 2010-02-22 04:04:04.000000000 -0500
+++ shadow-4.1.4.2/config.h.in 2010-02-22 04:03:46.000000000 -0500
@@ -68,6 +68,12 @@
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
+/* Define to 1 if you have the `fgetgrent_r' function. */
+#undef HAVE_FGETGRENT_R
+
+/* Define to 1 if you have the `fgetpwent_r' function. */
+#undef HAVE_FGETPWENT_R
+
/* Define to 1 if you have the `fsync' function. */
#undef HAVE_FSYNC
diff -Naur shadow-4.1.4.2.old/configure shadow-4.1.4.2/configure
--- shadow-4.1.4.2.old/configure 2010-02-22 04:04:04.000000000 -0500
+++ shadow-4.1.4.2/configure 2010-02-22 04:03:46.000000000 -0500
@@ -13536,7 +13536,8 @@
for ac_func in l64a fchmod fchown fsync futimes getgroups gethostname getspnam \
gettimeofday getusershell getutent initgroups lchown lckpwdf lstat \
lutimes memcpy memset setgroups sigaction strchr updwtmp updwtmpx innetgr \
- getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getaddrinfo
+ fgetpwent_r fgetgrent_r getpwnam_r getpwuid_r getgrnam_r getgrgid_r \
+ getspnam_r getaddrinfo
do
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
diff -Naur shadow-4.1.4.2.old/configure.in shadow-4.1.4.2/configure.in
--- shadow-4.1.4.2.old/configure.in 2009-07-23 21:10:31.000000000 -0400
+++ shadow-4.1.4.2/configure.in 2010-02-21 04:50:40.000000000 -0500
@@ -41,7 +41,8 @@
AC_CHECK_FUNCS(l64a fchmod fchown fsync futimes getgroups gethostname getspnam \
gettimeofday getusershell getutent initgroups lchown lckpwdf lstat \
lutimes memcpy memset setgroups sigaction strchr updwtmp updwtmpx innetgr \
- getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getaddrinfo)
+ fgetpwent_r fgetgrent_r getpwnam_r getpwuid_r getgrnam_r getgrgid_r \
+ getspnam_r getaddrinfo)
AC_SYS_LARGEFILE
dnl Checks for typedefs, structures, and compiler characteristics.
diff -Naur shadow-4.1.4.2.old/lib/prototypes.h shadow-4.1.4.2/lib/prototypes.h
--- shadow-4.1.4.2.old/lib/prototypes.h 2009-05-18 14:32:21.000000000 -0400
+++ shadow-4.1.4.2/lib/prototypes.h 2010-02-22 03:47:15.000000000 -0500
@@ -80,6 +80,20 @@
/* chowntty.c */
extern void chown_tty (const struct passwd *);
+/* chroot.c */
+extern bool process_chroot_flag (char flag, int argc, char **argv);
+extern struct group *fgetgr_nam_gid (const char *grname);
+extern void xfsetpwent (void);
+extern struct passwd *xfgetpwent (void);
+extern void xfendpwent (void);
+extern struct passwd *xfgetpwnam (const char *);
+extern struct passwd *xfgetpwuid (uid_t);
+extern void xfsetgrent (void);
+extern struct group *xfgetgrent (void);
+extern void xfendgrent (void);
+extern struct group *xfgetgrnam (const char *);
+extern struct group *xfgetgrgid (gid_t);
+
/* cleanup.c */
typedef void (*cleanup_function) (/*...@null@*/void *arg);
void add_cleanup (cleanup_function pcf, /*...@null@*/void *arg);
diff -Naur shadow-4.1.4.2.old/libmisc/Makefile.am shadow-4.1.4.2/libmisc/Makefile.am
--- shadow-4.1.4.2.old/libmisc/Makefile.am 2009-05-17 14:39:06.000000000 -0400
+++ shadow-4.1.4.2/libmisc/Makefile.am 2010-02-21 09:43:29.000000000 -0500
@@ -14,6 +14,7 @@
chkname.h \
chowndir.c \
chowntty.c \
+ chroot.c \
cleanup.c \
cleanup_group.c \
cleanup_user.c \
diff -Naur shadow-4.1.4.2.old/libmisc/Makefile.in shadow-4.1.4.2/libmisc/Makefile.in
--- shadow-4.1.4.2.old/libmisc/Makefile.in 2010-02-22 04:04:05.000000000 -0500
+++ shadow-4.1.4.2/libmisc/Makefile.in 2010-02-22 04:03:47.000000000 -0500
@@ -48,11 +48,11 @@
libmisc_a_LIBADD =
am_libmisc_a_OBJECTS = addgrps.$(OBJEXT) age.$(OBJEXT) \
audit_help.$(OBJEXT) basename.$(OBJEXT) chkname.$(OBJEXT) \
- chowndir.$(OBJEXT) chowntty.$(OBJEXT) cleanup.$(OBJEXT) \
- cleanup_group.$(OBJEXT) cleanup_user.$(OBJEXT) \
- console.$(OBJEXT) copydir.$(OBJEXT) entry.$(OBJEXT) \
- env.$(OBJEXT) failure.$(OBJEXT) fields.$(OBJEXT) \
- find_new_gid.$(OBJEXT) find_new_uid.$(OBJEXT) \
+ chowndir.$(OBJEXT) chowntty.$(OBJEXT) chroot.$(OBJEXT) \
+ cleanup.$(OBJEXT) cleanup_group.$(OBJEXT) \
+ cleanup_user.$(OBJEXT) console.$(OBJEXT) copydir.$(OBJEXT) \
+ entry.$(OBJEXT) env.$(OBJEXT) failure.$(OBJEXT) \
+ fields.$(OBJEXT) find_new_gid.$(OBJEXT) find_new_uid.$(OBJEXT) \
getdate.$(OBJEXT) getgr_nam_gid.$(OBJEXT) getrange.$(OBJEXT) \
hushed.$(OBJEXT) isexpired.$(OBJEXT) limits.$(OBJEXT) \
list.$(OBJEXT) log.$(OBJEXT) loginprompt.$(OBJEXT) \
@@ -244,6 +244,7 @@
chkname.h \
chowndir.c \
chowntty.c \
+ chroot.c \
cleanup.c \
cleanup_group.c \
cleanup_user.c \
@@ -351,6 +352,7 @@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/chkname...@am__quote@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/chowndir...@am__quote@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/chowntty...@am__quote@
+...@amdep_true@@am__include@ @am__qu...@./$(DEPDIR)/chroot...@am__quote@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/cleanup...@am__quote@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/cleanup_group...@am__quote@
@AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/cleanup_user...@am__quote@
diff -Naur shadow-4.1.4.2.old/libmisc/chroot.c shadow-4.1.4.2/libmisc/chroot.c
--- shadow-4.1.4.2.old/libmisc/chroot.c 1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/chroot.c 2010-02-22 03:32:09.000000000 -0500
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2007 - 2009, Nicolas François
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * According to the Linux-PAM documentation:
+ *
+ * 4.1. Care about standard library calls
+ *
+ * In general, writers of authorization-granting applications should
+ * assume that each module is likely to call any or all 'libc' functions.
+ * For 'libc' functions that return pointers to static/dynamically
+ * allocated structures (ie. the library allocates the memory and the
+ * user is not expected to 'free()' it) any module call to this function
+ * is likely to corrupt a pointer previously obtained by the application.
+ * The application programmer should either re-call such a 'libc'
+ * function after a call to the Linux-PAM library, or copy the structure
+ * contents to some safe area of memory before passing control to the
+ * Linux-PAM library.
+ *
+ * Two important function classes that fall into this category are
+ * getpwnam(3) and syslog(3).
+ *
+ * This file provide wrapper to the getpwnam or getpwnam_r functions.
+ */
+
+
+/* START: All functions not macro generated -->
+ *
+ * RECURS_FUNC macro sets file recursion.
+ * All functions and macros that are not generated,
+ * as well as headers, should be placed here.
+*/
+#ifndef RECURS_FUNC
+#define RECURS_FUNC
+
+#include <config.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <errno.h>
+#include "pwio.h"
+#include "groupio.h"
+#include "prototypes.h"
+#define VALID(s) (strcspn (s, ":\n") == strlen (s))
+#define E_BAD_ARG 3 /* invalid argument to option */
+
+/*
+ * process_chroot_flag - search for chroot command line argument
+ *
+ * process_chroot_flag() searches for the chroot flag. It calls
+ * chroot() and returns true, if found, and returns false if not.
+ * Resets the state of getopt_long when finished.
+ */
+extern bool process_chroot_flag (char flag, int argc, char **argv)
+{
+ int c;
+ bool chroot_flg = false;
+ opterr = 0;
+ static char * opt_str_chroot = "-R:";
+ static struct option long_option_chroot[] = {
+ {"chroot", required_argument, NULL, 'R'},
+ {NULL, 0, NULL, '\0'}
+ };
+
+ if (flag != 'R') {
+ opt_str_chroot[1] = flag;
+ long_option_chroot[0].val = flag;
+ }
+
+ while ((c = getopt_long (argc, argv,
+ opt_str_chroot,
+ long_option_chroot, NULL)) != -1) {
+ if (c == 'R') {
+ if (getuid()) {
+ fprintf (stderr,
+ _("%s: must be superuser for chroot access\n"),
+ Prog);
+ exit (1);
+ }
+ if ((!VALID(optarg))
+ || (optarg[0] != '/')) {
+ fprintf (stderr,
+ _("%s: invalid root directory '%s'\n"),
+ Prog, optarg);
+ exit (E_BAD_ARG);
+ }
+ if ( chroot(optarg) != 0 ) {
+ fprintf (stderr,
+ _("%s: unable to change root directory to '%s'\n"),
+ Prog, optarg);
+ exit (1);
+ }
+ chroot_flg = true;
+ break;
+ }
+ }
+ optind = 0;
+ opterr = 1;
+ return chroot_flg;
+}
+
+/*
+ * fgetgr_nam_gid - Return a pointer to the group specified by a string
+ * using recursive calls to fgetgrent. The string may be a valid GID
+ * or a valid groupname.
+ * If the group does not exist on the system, NULL is returned.
+ */
+extern /*...@null@*/struct group *fgetgr_nam_gid (const char *grname)
+{
+ long long int gid;
+ char *endptr;
+
+ if (NULL == grname) {
+ return NULL;
+ }
+
+ errno = 0;
+ gid = strtoll (grname, &endptr, 10);
+ if ( ('\0' != *grname)
+ && ('\0' == *endptr)
+ && (ERANGE != errno)
+ && (/*...@+longintegral@*/gid == (gid_t)gid)/*...@=longintegral@*/) {
+ return xfgetgrgid ((gid_t) gid);
+ }
+ return xfgetgrnam (grname);
+}
+#endif
+/*
+ * <-- All functions not macro generated :END
+ */
+
+
+/* START: Generate functions -->
+ *
+ * RECURS_FUNC_foo sets the current function to generate.
+ * If foo is XXYY (such as grnam) it generates an associated
+ * xfgetXXbyYY function (such as xfgetgrnam). If foo is XX
+ * (pw, for instance), it generates associated xfsetXXent,
+ * xfgetXXent, and xfendXXent functions (xfsetpwent,
+ * xfgetpwent, and xfendpwent, for instance) used by
+ * the xfgetXXbyYY functions.
+ */
+#if ! defined RECURS_FUNC_pwnam
+#define RECURS_FUNC_pwnam
+#define RECURS_pwnam
+#elif ! defined RECURS_FUNC_pwuid
+#define RECURS_FUNC_pwuid
+#define RECURS_pwuid
+#elif ! defined RECURS_FUNC_grnam
+#define RECURS_FUNC_grnam
+#define RECURS_grnam
+#elif ! defined RECURS_FUNC_grgid
+#define RECURS_FUNC_grgid
+#define RECURS_grgid
+#elif ! defined RECURS_FUNC_pw
+#define RECURS_FUNC_pw
+#define RECURS_pw
+#else
+#undef RECURS_FUNC
+#define RECURS_gr
+#endif
+/*
+ * <-- Generate functions :END
+ */
+
+
+/* START: xfgetXXbyYY specific macros -->
+ *
+ * Each RECURS_XXYY defines macros for it's associated
+ * structure type and structure member to search for
+ * (pw and name, for instance), and the function name.
+ */
+#ifdef RECURS_pwnam
+#undef RECURS_pwnam
+#define RECURS_pw
+#define RECURS_name
+#define FUNCTION_NAME xfgetpwnam
+#endif
+
+#ifdef RECURS_pwuid
+#undef RECURS_pwuid
+#define RECURS_pw
+#define RECURS_id
+#define FUNCTION_NAME xfgetpwuid
+#endif
+
+#ifdef RECURS_grnam
+#undef RECURS_grnam
+#define RECURS_gr
+#define RECURS_name
+#define FUNCTION_NAME xfgetgrnam
+#endif
+
+#ifdef RECURS_grgid
+#undef RECURS_grgid
+#define RECURS_gr
+#define RECURS_id
+#define FUNCTION_NAME xfgetgrgid
+#endif
+/*
+ * <-- xfgetXXbyYY specific macros :END
+ */
+
+
+/* START: YY specific macros -->
+ *
+ * Each RECURS_YY defines macros for its argument
+ * struct member type and name (name should be left
+ * the same as its struct member definition), type of
+ * comparison to use for the struct member
+ * (str or num), and CLEAN_YY which undefs them
+ * before the next recursion.
+ */
+#ifdef RECURS_name
+#undef RECURS_name
+#define CLEAN_YY
+#define CMPR_TYPE(name1,name2) (!strcmp(name1,name2))
+#define ARG_TYPE const char *
+#define ARG_NAME name
+#endif
+
+#ifdef RECURS_id
+#undef RECURS_id
+#define CLEAN_YY
+#define CMPR_TYPE(name1,name2) (name1 == name2)
+#ifdef RECURS_gr
+#define ARG_TYPE gid_t
+#define ARG_NAME gid
+#endif
+#ifdef RECURS_pw
+#define ARG_TYPE uid_t
+#define ARG_NAME uid
+#endif
+#endif
+/*
+ * <-- YY specific macros :END
+ */
+
+
+/* START: XX specific macros -->
+ *
+ * Each RECURS_XX defines macros for its return
+ * pointer type, database file, duplication function,
+ * reentrant function macro, XX itself, and CLEAN_XX
+ * which undefs them before the next recursion.
+ */
+#ifdef RECURS_pw
+#undef RECURS_pw
+#define CLEAN_XX
+#define LOOKUP_TYPE struct passwd
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION __pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+#define DB_TYPE pw
+#endif
+
+#ifdef RECURS_gr
+#undef RECURS_gr
+#define CLEAN_XX
+#define LOOKUP_TYPE struct group
+#define DB_FILE GROUP_FILE
+#define DUP_FUNCTION __gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+#define DB_TYPE gr
+#endif
+/*
+ * <-- XX specific macros :END
+ */
+
+
+/* START: Standard macros for function generation -->
+ *
+ * All macros not specific to a particular function
+ * (i.e., generated from the macros above).
+ */
+#define F_COMPARE F_COMPARE1 (DB_TYPE,ARG_NAME)
+#define F_COMPARE1(name1,name2) F_COMPARE2 (name1,name2)
+#define F_COMPARE2(name1,name2) F_COMPARE3 (name1 ## _ ## name2,name2)
+#define F_COMPARE3(name1,name2) CMPR_TYPE (result->name1,name2)
+
+#define F_FUNCTION_NAME F_FUNCTION_NAME1 (DB_TYPE)
+#define F_FUNCTION_NAME1(name) F_FUNCTION_NAME2 (name)
+#define F_FUNCTION_NAME2(name) fget##name##ent
+#define F_REENTRANT_NAME F_REENTRANT_NAME1 (F_FUNCTION_NAME)
+#define F_REENTRANT_NAME1(name) F_REENTRANT_NAME2 (name)
+#define F_REENTRANT_NAME2(name) name##_r
+
+#define FUNCTION_F_GETENT_DUMMY FUNCTION_F_GETENT_DUMMY1 (F_FUNCTION_NAME)
+#define FUNCTION_F_GETENT_DUMMY1(name) FUNCTION_F_GETENT_DUMMY2 (name)
+#define FUNCTION_F_GETENT_DUMMY2(name) x##name
+#define FUNCTION_F_SETENT_DUMMY FUNCTION_F_SETENT_DUMMY1 (DB_TYPE)
+#define FUNCTION_F_SETENT_DUMMY1(name) FUNCTION_F_SETENT_DUMMY2 (name)
+#define FUNCTION_F_SETENT_DUMMY2(name) xfset##name##ent
+#define FUNCTION_F_ENDENT_DUMMY FUNCTION_F_ENDENT_DUMMY1 (DB_TYPE)
+#define FUNCTION_F_ENDENT_DUMMY1(name) FUNCTION_F_ENDENT_DUMMY2 (name)
+#define FUNCTION_F_ENDENT_DUMMY2(name) xfend##name##ent
+
+#define DB_STREAM_NAME DB_STREAM_NAME1 (DB_TYPE)
+#define DB_STREAM_NAME1(name) DB_STREAM_NAME2 (name)
+#define DB_STREAM_NAME2(name) stream_##name
+#define DB_RESULT_NAME DB_RESULT_NAME1 (DB_TYPE)
+#define DB_RESULT_NAME1(name) DB_RESULT_NAME2 (name)
+#define DB_RESULT_NAME2(name) result_##name
+#define DB_RESBUF_NAME DB_RESBUF_NAME1 (DB_TYPE)
+#define DB_RESBUF_NAME1(name) DB_RESBUF_NAME2 (name)
+#define DB_RESBUF_NAME2(name) resbuf_##name
+#define DB_BUFFER_NAME DB_BUFFER_NAME1 (DB_TYPE)
+#define DB_BUFFER_NAME1(name) DB_BUFFER_NAME2 (name)
+#define DB_BUFFER_NAME2(name) buffer_##name
+#define DB_LENGTH_NAME DB_LENGTH_NAME1 (DB_TYPE)
+#define DB_LENGTH_NAME1(name) DB_LENGTH_NAME2 (name)
+#define DB_LENGTH_NAME2(name) length_##name
+
+#define STRINGIZE(name) STRINGIZE1 (name)
+#define STRINGIZE1(name) #name
+/*
+ * <-- Standard macros for functions generation :END
+ */
+
+
+/* START: Function body -->
+ *
+ * All functions to be generated go here.
+ */
+#ifdef FUNCTION_NAME /* xfgetXXbyYY function */
+LOOKUP_TYPE *FUNCTION_NAME (ARG_TYPE ARG_NAME)
+
+#else /* xffooXXent function */
+static FILE * DB_STREAM_NAME;
+static LOOKUP_TYPE * DB_RESULT_NAME;
+#if HAVE_FUNCTION_R /* xffooXXent function && HAVE_FUNCTION_R */
+static LOOKUP_TYPE * DB_RESBUF_NAME;
+static char * DB_BUFFER_NAME;
+static size_t DB_LENGTH_NAME;
+#endif /* xffooXXent function && HAVE_FUNCTION_R */
+
+LOOKUP_TYPE *FUNCTION_F_GETENT_DUMMY (void)
+#endif /* All functions */
+{
+#ifdef FUNCTION_NAME /* xfgetXXbyYY function */
+ FUNCTION_F_SETENT_DUMMY();
+ LOOKUP_TYPE *result = FUNCTION_F_GETENT_DUMMY();
+
+ while (result) {
+ if (F_COMPARE) {
+ break;
+ } else {
+ free(result);
+ result = FUNCTION_F_GETENT_DUMMY();
+ }
+ }
+
+ FUNCTION_F_ENDENT_DUMMY();
+ return result;
+#else /* xffooXXent function */
+ if (DB_STREAM_NAME == NULL) {
+ FUNCTION_F_SETENT_DUMMY();
+ if (DB_STREAM_NAME == NULL) {
+ fprintf (stderr, _("%s: Error accessing the database file.\n"),
+ STRINGIZE(FUNCTION_F_GETENT_DUMMY));
+ exit (1);
+ }
+ }
+#if HAVE_FUNCTION_R /* xffooXXent function && HAVE_FUNCTION_R */
+ if (DB_RESULT_NAME == NULL) {
+ DB_RESULT_NAME = malloc(sizeof(LOOKUP_TYPE));
+ if (DB_RESULT_NAME == NULL) {
+ fprintf (stderr, _("%s: out of memory\n"),
+ STRINGIZE(FUNCTION_F_GETENT_DUMMY));
+ exit (13);
+ }
+ }
+ if (DB_LENGTH_NAME < 0x100)
+ DB_LENGTH_NAME = 0x100;
+
+ while (true) {
+ int status;
+ if (DB_BUFFER_NAME == NULL) {
+ DB_BUFFER_NAME = (char *)realloc (DB_BUFFER_NAME, DB_LENGTH_NAME);
+ if (NULL == DB_BUFFER_NAME) {
+ fprintf (stderr, _("%s: out of memory\n"),
+ STRINGIZE(FUNCTION_F_GETENT_DUMMY));
+ exit (13);
+ }
+ }
+ errno = 0;
+ status = F_REENTRANT_NAME(DB_STREAM_NAME, DB_RESULT_NAME, DB_BUFFER_NAME,
+ DB_LENGTH_NAME, &DB_RESBUF_NAME);
+ if ((0 == status) && (DB_RESBUF_NAME == DB_RESULT_NAME)) {
+ LOOKUP_TYPE *ret_result = DUP_FUNCTION(DB_RESULT_NAME);
+ if (NULL == ret_result) {
+ fprintf (stderr, _("%s: out of memory\n"),
+ STRINGIZE(FUNCTION_F_GETENT_DUMMY));
+ exit (13);
+ }
+ return ret_result;
+ }
+
+ if (ERANGE != errno) {
+ return NULL;
+ }
+
+ if (DB_LENGTH_NAME <= ((size_t)-1 / 4)) {
+ DB_LENGTH_NAME *= 4;
+ } else if (DB_LENGTH_NAME == (size_t) -1) {
+ break;
+ } else {
+ DB_LENGTH_NAME = (size_t) -1;
+ }
+ }
+
+ return NULL;
+#else /* xffooXXent function && !HAVE_FUNCTION_R */
+ DB_RESULT_NAME = F_FUNCTION_NAME(DB_STREAM_NAME);
+ if (DB_RESULT_NAME) {
+ DB_RESULT_NAME = DUP_FUNCTION(DB_RESULT_NAME);
+ if (NULL == DB_RESULT_NAME) {
+ fprintf (stderr, _("%s: out of memory\n"),
+ STRINGIZE(FUNCTION_F_GETENT_DUMMY));
+ exit (13);
+ }
+ }
+
+ return DB_RESULT_NAME;
+#endif /* xffooXXent function */
+#endif /* All functions */
+}
+
+#ifndef FUNCTION_NAME /* xffooXXent function */
+void FUNCTION_F_SETENT_DUMMY (void)
+{
+ FUNCTION_F_ENDENT_DUMMY();
+ DB_STREAM_NAME = fopen (DB_FILE, "r");
+}
+
+void FUNCTION_F_ENDENT_DUMMY (void)
+{
+ if (DB_STREAM_NAME != NULL) {
+ fclose(DB_STREAM_NAME);
+ DB_STREAM_NAME = NULL;
+ }
+#if HAVE_FUNCTION_R /* xffooXXent function && HAVE_FUNCTION_R */
+ if (DB_BUFFER_NAME != NULL) {
+ free(DB_BUFFER_NAME);
+ DB_BUFFER_NAME = NULL;
+ }
+ if (DB_RESULT_NAME != NULL) {
+ free(DB_RESULT_NAME);
+ DB_RESULT_NAME = NULL;
+ }
+ DB_LENGTH_NAME = 0x100;
+ DB_RESBUF_NAME = NULL;
+#endif /* xffooXXent function && HAVE_FUNCTION_R */
+}
+#endif /* xffooXXent function */
+/*
+ * <-- Function body :END
+ */
+
+
+/* START: Pre-recursion macros -->
+ *
+ * CLEAN_FOO undefs macros for the next file recursion.
+ */
+#ifdef CLEAN_YY
+#undef CLEAN_YY
+#undef FUNCTION_NAME
+#undef ARG_TYPE
+#undef ARG_NAME
+#undef CMPR_TYPE
+#endif
+
+#ifdef CLEAN_XX
+#undef CLEAN_XX
+#undef LOOKUP_TYPE
+#undef DB_TYPE
+#undef DB_FILE
+#undef DUP_FUNCTION
+#undef HAVE_FUNCTION_R
+#endif
+/*
+ * <-- Pre-recursion macros :END
+ */
+
+
+#ifdef RECURS_FUNC
+#include __FILE__
+#endif
+
+
diff -Naur shadow-4.1.4.2.old/man/groupadd.8 shadow-4.1.4.2/man/groupadd.8
--- shadow-4.1.4.2.old/man/groupadd.8 2010-02-21 05:12:08.000000000 -0500
+++ shadow-4.1.4.2/man/groupadd.8 2010-02-21 04:27:47.000000000 -0500
@@ -99,6 +99,18 @@
login\&.defs, instead of
\fBGID_MIN\fR\-\fBGID_MAX\fR\&.
.RE
+.PP
+\fB\-R\fR, \fB\-\-chroot\fR \fIROOT_DIR\fR
+.RS 4
+Access files from a root directory other than /\&.
+.sp
+Instead of accessing or altering files from the usual directory, paths will be prefixed with
+\fBROOT_DIR\fR\&.
+.sp
+For instance, instead of adding a group to
+/etc/group, the group would be added to
+\fBROOT_DIR\fR/etc/group\&.
+.RE
.SH "CONFIGURATION"
.PP
The following configuration variables in
diff -Naur shadow-4.1.4.2.old/man/groupadd.8.xml shadow-4.1.4.2/man/groupadd.8.xml
--- shadow-4.1.4.2.old/man/groupadd.8.xml 2009-05-21 08:02:13.000000000 -0400
+++ shadow-4.1.4.2/man/groupadd.8.xml 2010-02-16 18:48:50.000000000 -0500
@@ -179,6 +179,26 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>-R</option>, <option>--chroot</option>
+ <replaceable>ROOT_DIR</replaceable>
+ </term>
+ <listitem>
+ <para>
+ Access files from a root directory other than /.
+ </para>
+ <para>
+ Instead of accessing or altering files from the usual directory,
+ paths will be prefixed with <option>ROOT_DIR</option>.
+ </para>
+ <para>
+ For instance, instead of adding a group to
+ <filename>/etc/group</filename>, the group would be added to
+ <filename><option>ROOT_DIR</option>/etc/group</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff -Naur shadow-4.1.4.2.old/man/groupdel.8 shadow-4.1.4.2/man/groupdel.8
--- shadow-4.1.4.2.old/man/groupdel.8 2010-02-21 05:12:10.000000000 -0500
+++ shadow-4.1.4.2/man/groupdel.8 2010-02-21 04:27:48.000000000 -0500
@@ -22,13 +22,30 @@
groupdel \- delete a group
.SH "SYNOPSIS"
.HP \w'\fBgroupdel\fR\ 'u
-\fBgroupdel\fR \fIgroup\fR
+\fBgroupdel\fR [options] \fIgroup\fR
.SH "DESCRIPTION"
.PP
The
\fBgroupdel\fR
command modifies the system account files, deleting all entries that refer to
\fIgroup\fR\&. The named group must exist\&.
+.SH "OPTIONS"
+.PP
+The options which apply to the
+\fBgroupdel\fR
+command are:
+.PP
+\fB\-R\fR, \fB\-\-chroot\fR \fIROOT_DIR\fR
+.RS 4
+Access files from a root directory other than /\&.
+.sp
+Instead of accessing or altering files from the usual directory, paths will be prefixed with
+\fBROOT_DIR\fR\&.
+.sp
+For instance, instead of deleting a group from
+/etc/group, the user would be deleted from
+\fBROOT_DIR\fR/etc/group\&.
+.RE
.SH "CAVEATS"
.PP
You may not remove the primary group of any existing user\&. You must remove the user before you remove the group\&.
diff -Naur shadow-4.1.4.2.old/man/groupdel.8.xml shadow-4.1.4.2/man/groupdel.8.xml
--- shadow-4.1.4.2.old/man/groupdel.8.xml 2009-05-19 16:31:45.000000000 -0400
+++ shadow-4.1.4.2/man/groupdel.8.xml 2010-02-21 01:39:14.000000000 -0500
@@ -47,6 +47,7 @@
<refsynopsisdiv id='synopsis'>
<cmdsynopsis>
<command>groupdel</command>
+ <arg choice='opt'>options</arg>
<arg choice='plain'>
<replaceable>group</replaceable>
</arg>
@@ -61,6 +62,35 @@
</para>
</refsect1>
+ <refsect1 id='options'>
+ <title>OPTIONS</title>
+ <para>
+ The options which apply to the <command>groupdel</command> command are:
+ </para>
+ <variablelist remap='IP'>
+ <varlistentry>
+ <term>
+ <option>-R</option>, <option>--chroot</option>
+ <replaceable>ROOT_DIR</replaceable>
+ </term>
+ <listitem>
+ <para>
+ Access files from a root directory other than /.
+ </para>
+ <para>
+ Instead of accessing or altering files from the usual directory,
+ paths will be prefixed with <option>ROOT_DIR</option>.
+ </para>
+ <para>
+ For instance, instead of deleting a group from
+ <filename>/etc/group</filename>, the user would be deleted from
+ <filename><option>ROOT_DIR</option>/etc/group</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1 id='caveats'>
<title>CAVEATS</title>
<para>
diff -Naur shadow-4.1.4.2.old/man/useradd.8 shadow-4.1.4.2/man/useradd.8
--- shadow-4.1.4.2.old/man/useradd.8 2010-02-21 05:12:48.000000000 -0500
+++ shadow-4.1.4.2/man/useradd.8 2010-02-21 04:28:07.000000000 -0500
@@ -285,6 +285,18 @@
options if you want a home directory for a system account to be created\&.
.RE
.PP
+\fB\-R\fR, \fB\-\-chroot\fR \fIROOT_DIR\fR
+.RS 4
+Access files from a root directory other than /\&.
+.sp
+Instead of accessing or altering files from the usual directory, paths will be prefixed with
+\fBROOT_DIR\fR\&.
+.sp
+For instance, instead of adding a user to
+/etc/passwd, the user would be added to
+\fBROOT_DIR\fR/etc/passwd\&.
+.RE
+.PP
\fB\-s\fR, \fB\-\-shell\fR \fISHELL\fR
.RS 4
The name of the user\'s login shell\&. The default is to leave this field blank, which causes the system to select the default login shell specified by the
diff -Naur shadow-4.1.4.2.old/man/useradd.8.xml shadow-4.1.4.2/man/useradd.8.xml
--- shadow-4.1.4.2.old/man/useradd.8.xml 2009-05-21 08:02:13.000000000 -0400
+++ shadow-4.1.4.2/man/useradd.8.xml 2010-02-16 18:48:50.000000000 -0500
@@ -425,6 +425,26 @@
</varlistentry>
<varlistentry>
<term>
+ <option>-R</option>, <option>--chroot</option>
+ <replaceable>ROOT_DIR</replaceable>
+ </term>
+ <listitem>
+ <para>
+ Access files from a root directory other than /.
+ </para>
+ <para>
+ Instead of accessing or altering files from the usual directory,
+ paths will be prefixed with <option>ROOT_DIR</option>.
+ </para>
+ <para>
+ For instance, instead of adding a user to
+ <filename>/etc/passwd</filename>, the user would be added to
+ <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
<option>-s</option>, <option>--shell</option>
<replaceable>SHELL</replaceable>
</term>
diff -Naur shadow-4.1.4.2.old/man/userdel.8 shadow-4.1.4.2/man/userdel.8
--- shadow-4.1.4.2.old/man/userdel.8 2010-02-21 05:12:50.000000000 -0500
+++ shadow-4.1.4.2/man/userdel.8 2010-02-21 04:28:08.000000000 -0500
@@ -67,6 +67,18 @@
login\&.defs
file\&.
.RE
+.PP
+\fB\-R\fR, \fB\-\-chroot\fR \fIROOT_DIR\fR
+.RS 4
+Access files from a root directory other than /\&.
+.sp
+Instead of accessing or altering files from the usual directory, paths will be prefixed with
+\fBROOT_DIR\fR\&.
+.sp
+For instance, instead of deleting a user from
+/etc/passwd, the user would be deleted from
+\fBROOT_DIR\fR/etc/passwd\&.
+.RE
.SH "CONFIGURATION"
.PP
The following configuration variables in
diff -Naur shadow-4.1.4.2.old/man/userdel.8.xml shadow-4.1.4.2/man/userdel.8.xml
--- shadow-4.1.4.2.old/man/userdel.8.xml 2009-05-21 08:02:13.000000000 -0400
+++ shadow-4.1.4.2/man/userdel.8.xml 2010-02-21 01:27:25.000000000 -0500
@@ -118,6 +118,26 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>-R</option>, <option>--chroot</option>
+ <replaceable>ROOT_DIR</replaceable>
+ </term>
+ <listitem>
+ <para>
+ Access files from a root directory other than /.
+ </para>
+ <para>
+ Instead of accessing or altering files from the usual directory,
+ paths will be prefixed with <option>ROOT_DIR</option>.
+ </para>
+ <para>
+ For instance, instead of deleting a user from
+ <filename>/etc/passwd</filename>, the user would be deleted from
+ <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff -Naur shadow-4.1.4.2.old/src/groupadd.c shadow-4.1.4.2/src/groupadd.c
--- shadow-4.1.4.2.old/src/groupadd.c 2009-06-05 18:16:58.000000000 -0400
+++ shadow-4.1.4.2/src/groupadd.c 2010-02-22 03:51:46.000000000 -0500
@@ -81,6 +83,8 @@
static bool gflg = false; /* ID value for the new group */
static bool fflg = false; /* if group already exists, do nothing and exit(0) */
static bool rflg = false; /* create a system account */
+static bool Rflg = false; /* root directory offset to access files from */
+
static bool pflg = false; /* new encrypted password */
#ifdef SHADOWGRP
@@ -121,6 +125,7 @@
" (non-unique) GID\n"), stderr);
(void) fputs (_(" -p, --password PASSWORD use this encrypted password for the new group\n"), stderr);
(void) fputs (_(" -r, --system create a system account\n"), stderr);
+ (void) fputs (_(" -R, --chroot ROOT_DIR access files from an alternate root directory\n"), stderr);
(void) fputs ("\n", stderr);
exit (E_USAGE);
}
@@ -384,11 +389,12 @@
{"non-unique", no_argument, NULL, 'o'},
{"password", required_argument, NULL, 'p'},
{"system", no_argument, NULL, 'r'},
+ {"chroot", required_argument, NULL, 'R'},
{NULL, 0, NULL, '\0'}
};
while ((c =
- getopt_long (argc, argv, "fg:hK:op:r", long_options,
+ getopt_long (argc, argv, "fg:hK:op:rR:", long_options,
&option_index)) != -1) {
switch (c) {
case 'f':
@@ -443,6 +449,8 @@
case 'r':
rflg = true;
break;
+ case 'R':
+ break;
default:
usage ();
}
@@ -476,8 +484,10 @@
/*
* Check if the group already exist.
*/
- /* local, no need for xgetgrnam */
- if (getgrnam (group_name) != NULL) {
+ /* no chroot flag, local, no need for xgetgrnam */
+ if (((!Rflg) && (getgrnam (group_name) != NULL)) ||
+ /* chroot flag set, use compatible xfgetgrnam */
+ ((Rflg) && (xfgetgrnam (group_name) != NULL))) {
/* The group already exist */
if (fflg) {
/* OK, no need to do anything */
@@ -489,7 +499,8 @@
exit (E_NAME_IN_USE);
}
- if (gflg && (getgrgid (group_id) != NULL)) {
+ if (((gflg) && (!Rflg) && (getgrgid (group_id) != NULL)) ||
+ ((gflg) && (Rflg) && (xfgetgrgid (group_id) != NULL))) {
/* A GID was specified, and a group already exist with that GID
* - either we will use this GID anyway (-o)
* - either we ignore the specified GID and
@@ -524,6 +535,8 @@
{
#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
+ /* chroot doesn't work with PAM libs */
+ if (!Rflg) {
pam_handle_t *pamh = NULL;
int retval;
struct passwd *pampw;
@@ -553,6 +566,7 @@
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
exit (1);
}
+ }
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
}
@@ -579,6 +593,12 @@
OPENLOG ("groupadd");
/*
+ * Parse the chroot option before other options
+ * but after opening the log.
+ */
+ Rflg = process_chroot_flag ('R', argc, argv);
+
+ /*
* Parse the command line options.
*/
process_flags (argc, argv);
diff -Naur shadow-4.1.4.2.old/src/groupdel.c shadow-4.1.4.2/src/groupdel.c
--- shadow-4.1.4.2.old/src/groupdel.c 2009-04-30 17:39:40.000000000 -0400
+++ shadow-4.1.4.2/src/groupdel.c 2010-02-21 09:41:48.000000000 -0500
@@ -60,6 +60,7 @@
static char *group_name;
static gid_t group_id = -1;
+static bool Rflg = false;
#ifdef SHADOWGRP
static bool is_shadow_grp;
#endif
@@ -86,7 +87,11 @@
*/
static void usage (void)
{
- fputs (_("Usage: groupdel group\n"), stderr);
+ fputs (_("Usage: groupdel [options] group\n"
+ "\n"
+ "Options:\n"
+ " -R, --chroot ROOT_DIR access files from an alternate root directory\n"
+ "\n"), stderr);
exit (E_USAGE);
}
@@ -266,16 +271,29 @@
static void group_busy (gid_t gid)
{
struct passwd *pwd;
+ static void (*setpwent_func)(void);
+ static struct passwd * (*getpwent_func)(void);
+ static void (*endpwent_func)(void);
+
+ if (!Rflg) {
+ setpwent_func = &setpwent;
+ getpwent_func = &getpwent;
+ endpwent_func = &endpwent;
+ } else {
+ setpwent_func = &xfsetpwent;
+ getpwent_func = &xfgetpwent;
+ endpwent_func = &xfendpwent;
+ }
/*
* Nice slow linear search.
*/
- setpwent ();
+ (*setpwent_func) ();
- while ( ((pwd = getpwent ()) != NULL) && (pwd->pw_gid != gid) );
+ while ( ((pwd = (*getpwent_func) ()) != NULL) && (pwd->pw_gid != gid) );
- endpwent ();
+ (*endpwent_func) ();
/*
* If pwd isn't NULL, it stopped because the gid's matched.
@@ -327,17 +345,21 @@
(void) setlocale (LC_ALL, "");
(void) bindtextdomain (PACKAGE, LOCALEDIR);
(void) textdomain (PACKAGE);
-
- if (argc != 2) {
+
+ Rflg = process_chroot_flag ('R', argc, argv);
+ if ((!Rflg && argc != 2) ||
+ (Rflg && argc != 4)) {
usage ();
}
- group_name = argv[1];
+ group_name = argv[argc - 1];
OPENLOG ("groupdel");
#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
+ /* chroot doesn't work with PAM libs */
+ if (!Rflg) {
{
struct passwd *pampw;
pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
@@ -366,6 +388,7 @@
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
exit (1);
}
+ }
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
@@ -378,7 +401,11 @@
/*
* Start with a quick check to see if the group exists.
*/
- grp = getgrnam (group_name); /* local, no need for xgetgrnam */
+ if (!Rflg) {
+ grp = getgrnam (group_name); /* no chroot flag, local, no need for xgetgrnam */
+ } else {
+ grp = xfgetgrnam (group_name); /* chroot flag set, use compatible xfgetgrnam */
+ }
if (NULL == grp) {
fprintf (stderr,
_("%s: group '%s' does not exist\n"),
diff -Naur shadow-4.1.4.2.old/src/useradd.c shadow-4.1.4.2/src/useradd.c
--- shadow-4.1.4.2.old/src/useradd.c 2009-06-05 18:16:58.000000000 -0400
+++ shadow-4.1.4.2/src/useradd.c 2010-02-22 03:48:52.000000000 -0500
@@ -143,6 +143,7 @@
Nflg = false, /* do not create a group having the same name as the user, but add the user to def_group (or the group specified with -g) */
oflg = false, /* permit non-unique user ID to be specified with -u */
rflg = false, /* create a system account */
+ Rflg = false, /* root directory offset to access files from */
sflg = false, /* shell program for new account */
uflg = false, /* specify user ID for new account */
Uflg = false, /* create a group having the same name as the user */
@@ -286,6 +287,7 @@
FILE *fp;
char buf[1024];
char *cp;
+ const struct group *grp;
/*
* Open the defaults file for reading.
@@ -317,7 +319,11 @@
* Primary GROUP identifier
*/
if (MATCH (buf, DGROUP)) {
- const struct group *grp = getgr_nam_gid (cp);
+ if (!Rflg) {
+ grp = getgr_nam_gid (cp);
+ } else {
+ grp = fgetgr_nam_gid (cp);
+ }
if (NULL == grp) {
fprintf (stderr,
_("%s: group '%s' does not exist\n"),
@@ -618,7 +624,11 @@
* Names starting with digits are treated as numerical
* GID values, otherwise the string is looked up as is.
*/
- grp = getgr_nam_gid (list);
+ if (!Rflg) {
+ grp = getgr_nam_gid (list);
+ } else {
+ grp = fgetgr_nam_gid (list);
+ }
/*
* There must be a match, either by GID value or by
@@ -712,6 +722,7 @@
" (non-unique) UID\n"), stderr);
(void) fputs (_(" -p, --password PASSWORD encrypted password of the new account\n"), stderr);
(void) fputs (_(" -r, --system create a system account\n"), stderr);
+ (void) fputs (_(" -R, --chroot ROOT_DIR access files from an alternate root directory\n"), stderr);
(void) fputs (_(" -s, --shell SHELL login shell of the new account\n"), stderr);
(void) fputs (_(" -u, --uid UID user ID of the new account\n"), stderr);
(void) fputs (_(" -U, --user-group create a group with the same name as the user\n"), stderr);
@@ -979,6 +990,7 @@
{"non-unique", no_argument, NULL, 'o'},
{"password", required_argument, NULL, 'p'},
{"system", no_argument, NULL, 'r'},
+ {"chroot", required_argument, NULL, 'R'},
{"shell", required_argument, NULL, 's'},
#ifdef WITH_SELINUX
{"selinux-user", required_argument, NULL, 'Z'},
@@ -989,9 +1001,9 @@
};
while ((c = getopt_long (argc, argv,
#ifdef WITH_SELINUX
- "b:c:d:De:f:g:G:k:K:lmMNop:rs:u:UZ:",
+ "b:c:d:De:f:g:G:k:K:lmMNop:rR:s:u:UZ:",
#else
- "b:c:d:De:f:g:G:k:K:lmMNop:rs:u:U",
+ "b:c:d:De:f:g:G:k:K:lmMNop:rR:s:u:U",
#endif
long_options, NULL)) != -1) {
switch (c) {
@@ -1081,7 +1093,11 @@
fflg = true;
break;
case 'g':
- grp = getgr_nam_gid (optarg);
+ if (!Rflg) {
+ grp = getgr_nam_gid (optarg);
+ } else {
+ grp = fgetgr_nam_gid (optarg);
+ }
if (NULL == grp) {
fprintf (stderr,
_("%s: group '%s' does not exist\n"),
@@ -1159,6 +1175,8 @@
case 'r':
rflg = true;
break;
+ case 'R':
+ break;
case 's':
if ( ( !VALID (optarg) )
|| ( ('\0' != optarg[0])
@@ -1663,8 +1681,10 @@
* no user with this UID exists yet (entries for shared UIDs
* are left unchanged). --marekm
*/
- /* local, no need for xgetpwuid */
- if ((!lflg) && (getpwuid (user_id) == NULL)) {
+ /* no chroot flag, local, no need for xgetpwuid */
+ if ((((!Rflg) && (!lflg)) && (getpwuid (user_id) == NULL)) ||
+ /* chroot flag set, use compatible xfgetpwuid */
+ ((!lflg) && (xfgetpwuid (user_id) == NULL))) {
faillog_reset (user_id);
lastlog_reset (user_id);
}
@@ -1806,7 +1826,11 @@
return;
}
- gr = getgrnam ("mail"); /* local, no need for xgetgrnam */
+ if (!Rflg) {
+ gr = getgrnam ("mail"); /* no chroot flag, local, no need for xgetgrnam */
+ } else {
+ gr = xfgetgrnam ("mail"); /* chroot flag set, use compatible xfgetgrnam */
+ }
if (NULL == gr) {
fputs (_("Group 'mail' not found. Creating the user mailbox file with 0600 mode.\n"),
stderr);
@@ -1861,6 +1885,12 @@
*/
user_groups[0] = (char *) 0;
+ /*
+ * Parse the chroot option before other options
+ * and before accessing any database files,
+ * but after opening the log.
+ */
+ Rflg = process_chroot_flag ('R', argc, argv);
is_shadow_pwd = spw_file_present ();
#ifdef SHADOWGRP
@@ -1873,6 +1903,8 @@
#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
+ /* chroot doesn't work with PAM libs */
+ if (!Rflg) {
{
struct passwd *pampw;
pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
@@ -1901,6 +1933,7 @@
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
fail_exit (1);
}
+ }
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
@@ -1920,7 +1953,8 @@
/*
* Start with a quick check to see if the user exists.
*/
- if (getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */
+ if (((!Rflg) && (getpwnam (user_name) != NULL)) || /* no chroot flag, local, no need for xgetpwnam */
+ ((Rflg) && (xfgetpwnam (user_name) != NULL))) { /* chroot flag set, use compatible xfgetpwnam */
fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name);
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, Prog,
@@ -1938,8 +1972,10 @@
* --bero
*/
if (Uflg) {
- /* local, no need for xgetgrnam */
- if (getgrnam (user_name) != NULL) {
+ /* no chroot flag, local, no need for xgetgrnam */
+ if (((!Rflg) && (getgrnam (user_name) != NULL)) ||
+ /* chroot flag set, use compatible xfgetgrnam */
+ ((Rflg) && (xfgetgrnam (user_name) != NULL))) {
fprintf (stderr,
_("%s: group %s exists - if you want to add this user to that group, use -g.\n"),
Prog, user_name);
@@ -1974,7 +2010,8 @@
fail_exit (E_UID_IN_USE);
}
} else {
- if (getpwuid (user_id) != NULL) {
+ if (((!Rflg) && (getpwuid (user_id) != NULL)) ||
+ ((Rflg) && (xfgetpwuid (user_id) != NULL))) {
fprintf (stderr,
_("%s: UID %lu is not unique\n"),
Prog, (unsigned long) user_id);
diff -Naur shadow-4.1.4.2.old/src/userdel.c shadow-4.1.4.2/src/userdel.c
--- shadow-4.1.4.2.old/src/userdel.c 2009-05-22 06:41:12.000000000 -0400
+++ shadow-4.1.4.2/src/userdel.c 2010-02-22 03:50:55.000000000 -0500
@@ -76,12 +76,16 @@
*/
char *Prog;
+static void (*setpwent_func)(void);
+static struct passwd * (*getpwent_func)(void);
+static void (*endpwent_func)(void);
static char *user_name;
static uid_t user_id;
static char *user_home;
static bool fflg = false;
static bool rflg = false;
+static bool Rflg = false;
static bool is_shadow_pwd;
@@ -120,6 +124,7 @@
" even if not owned by user\n"
" -h, --help display this help message and exit\n"
" -r, --remove remove home directory and mail spool\n"
+ " -R, --chroot ROOT_DIR access files from an alternate root directory\n"
"\n"), stderr);
exit (E_USAGE);
}
@@ -200,7 +205,11 @@
* We should retrieve the group with gr_locate and check
* that gr_mem is empty.
*/
- grp = xgetgrnam (user_name);
+ if (!Rflg) {
+ grp = xgetgrnam (user_name);
+ } else {
+ grp = xfgetgrnam (user_name);
+ }
if ( (NULL != grp)
&& getdef_bool ("USERGROUPS_ENAB")
&& ( (NULL == grp->gr_mem[0])
@@ -213,8 +222,8 @@
* Scan the passwd file to check if this group is still
* used as a primary group.
*/
- setpwent ();
- while ((pwd = getpwent ()) != NULL) {
+ (*setpwent_func) ();
+ while ((pwd = (*getpwent_func) ()) != NULL) {
if (strcmp (pwd->pw_name, user_name) == 0) {
continue;
}
@@ -225,7 +234,7 @@
break;
}
}
- endpwent ();
+ (*endpwent_func) ();
}
if (NULL == pwd) {
@@ -750,6 +759,12 @@
#endif
/*
+ * Parse the chroot option before other options
+ * and before accessing any database files.
+ */
+ Rflg = process_chroot_flag ('R', argc, argv);
+
+ /*
* Get my name so that I can use it to report errors.
*/
Prog = Basename (argv[0]);
@@ -766,9 +781,10 @@
{"force", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"remove", no_argument, NULL, 'r'},
+ {"chroot", required_argument, NULL, 'R'},
{NULL, 0, NULL, '\0'}
};
- while ((c = getopt_long (argc, argv, "fhr",
+ while ((c = getopt_long (argc, argv, "fhrR:",
long_options, NULL)) != -1) {
switch (c) {
case 'f': /* force remove even if not owned by user */
@@ -777,6 +793,8 @@
case 'r': /* remove home dir and mailbox */
rflg = true;
break;
+ case 'R':
+ break;
default:
usage ();
}
@@ -791,6 +809,8 @@
#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
+ /* chroot doesn't work with PAM libs */
+ if (!Rflg) {
{
struct passwd *pampw;
pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
@@ -819,6 +839,7 @@
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
exit (E_PW_UPDATE);
}
+ }
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
@@ -833,7 +854,11 @@
user_name = argv[argc - 1];
{
struct passwd *pwd;
- pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
+ if (!Rflg) {
+ pwd = getpwnam (user_name); /* no chroot flag, local, no need for xgetpwnam */
+ } else {
+ pwd = xfgetpwnam (user_name); /* chroot flag set, use compatible xfgetpwnam */
+ }
if (NULL == pwd) {
fprintf (stderr, _("%s: user '%s' does not exist\n"),
Prog, user_name);
@@ -888,6 +913,16 @@
}
}
+ if (!Rflg) {
+ setpwent_func = &setpwent;
+ getpwent_func = &getpwent;
+ endpwent_func = &endpwent;
+ } else {
+ setpwent_func = &xfsetpwent;
+ getpwent_func = &xfgetpwent;
+ endpwent_func = &xfendpwent;
+ }
+
/*
* Do the hard stuff - open the files, create the user entries,
* create the home directory, then close and update the files.
@@ -927,8 +962,8 @@
* prevent accidents if someone has /home or / as home
* directory... --marekm
*/
- setpwent ();
- while ((pwd = getpwent ())) {
+ (*setpwent_func) ();
+ while ((pwd = (*getpwent_func) ())) {
if (strcmp (pwd->pw_name, user_name) == 0) {
continue;
}
@@ -943,7 +978,7 @@
break;
}
}
- endpwent ();
+ (*endpwent_func) ();
}
#endif