public inbox for gentoo-embedded@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-embedded] emerge --root : users not created
@ 2009-12-14 16:17 Shinkan
  2009-12-14 17:14 ` Ed W
  0 siblings, 1 reply; 26+ messages in thread
From: Shinkan @ 2009-12-14 16:17 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 1856 bytes --]

Hi everyone,

I posted this on gentoo-user initially, but someone answered and advised to
post this to embedded :

I wanted to submit this as a bug on bugzilla, but I must be sure there is
nothing that I miss.

Let's say I have a /target dir.
If I do 'emerge --root=/target <someport>' (cross-emerge), and that
<someport> is supposed to create users (like vixie-cron, clamav or many
others), users are not created on /target. I can verify that by chrooting on
/target and making something that requires this user (such as launching
clamd for clamav), or simply by looking at /target/etc/passwd to see that
there's no expected users.

Am I missing somethings or is this really a bug ?


Here is "Willie Wong" answer :

If you don't get a better answer here, you should ask the embedded
group. But I think it maybe a bug:

Looking at eutils.eclass, in function enewuser, it explicitly checks
for whether the shell specified is available in ${ROOT}, but when it
comes time to create the actual user, it calls the system useradd,
which I think will add the user to /etc, and not ${ROOT}/etc...

Though, I cannot right now think of how to actually change it so that
it will create the appropriate accounts in a modified ${ROOT}. AFAIK
useradd does not support this. It may require re-implementing useradd
in portage? Which will just be silly.

Perhaps ${ROOT} is not designed to be used the way you intend to use
it? It looks like you are building embedded or cross-compiled, right?
Maybe a work-around is to do everything in a CHROOT?

Anyway, ask gentoo-embedded to see if there's any work arounds, and
maybe ask gentoo-dev to clarify on what $ROOT is used for?

Thanks in advance.

-- 
Pierre.
"Sometimes when I'm talking, my words can't keep up with my thoughts. I
wonder why we think faster than we speak. Probably so we can think twice." -
Bill Watterson

[-- Attachment #2: Type: text/html, Size: 2107 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-16 15:04 P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-16 15:04 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 1780 bytes --]

Peter Stuge wrote:
> I talked to upstream on freenode/#shadow and they welcome a patch for
> adding --chroot
>
> chroot() needs to happen really early since useradd and friends read
> some configuration files to know e.g. which password encryption
> method to use.

Attached is a tentative patch to add a chroot flag to useradd and
groupadd (via --chroot or -R).  It compiles and works on my end
(--chroot /usr/armv4tl-softfloat-linux-gnueabi) with various other flags
enabled.  I'm hoping for others to test it and get some feedback before
I submit it to shadow upstream.

There do exist a couple of issues:

sysconf(_SC_NGROUPS_MAX) is called by useradd early on.  This would
report the maximum allowable number of groups per user on the build
system, not the target.  To my knowledge, this is set by the kernel and
would have to be used.  However, this tends to be a very high number for
linux kernel >= 2.6.3 (65536) so it seems like a mute point (for linux
kernel >= 2.6.3).

There are a number of calls to "getXXbyYY" functions (i.e., getgrgid,
getpwnam, etc...).  These seem to be dynamically preloaded and access
preloaded databases.  They are unaffected by chroot() (even after
setting __nss_configure_lookup(foo, files)).  I've instead used shadow's
own method of macro expansion to generate functions doing the
equivalent, with recursive calls to fgetXXent functions.

And PAM functionality doesn't work and has to be disabled while using
chroot().  I don't know very much about PAM.  Would this be a problem?

Also,  the chroot functionality could probably be easily extended to
other modules but I'm not sure if this would be acceptable upstream.

There are a couple of cosmetic changes I'm considering as well (such as
how --chroot flag is parsed).

-- Peter Levine

[-- Attachment #2: shadow-4.1.4.2-chroot.patch.old --]
[-- Type: application/x-trash, Size: 42414 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-16 15:20 P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-16 15:20 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 1966 bytes --]

On 02/16/2010 10:04 AM, P. Levine wrote:
> Peter Stuge wrote:
>> I talked to upstream on freenode/#shadow and they welcome a patch for
>> adding --chroot
>>
>> chroot() needs to happen really early since useradd and friends read
>> some configuration files to know e.g. which password encryption
>> method to use.
> 
> Attached is a tentative patch to add a chroot flag to useradd and
> groupadd (via --chroot or -R).  It compiles and works on my end
> (--chroot /usr/armv4tl-softfloat-linux-gnueabi) with various other flags
> enabled.  I'm hoping for others to test it and get some feedback before
> I submit it to shadow upstream.
> 
> There do exist a couple of issues:
> 
> sysconf(_SC_NGROUPS_MAX) is called by useradd early on.  This would
> report the maximum allowable number of groups per user on the build
> system, not the target.  To my knowledge, this is set by the kernel and
> would have to be used.  However, this tends to be a very high number for
> linux kernel >= 2.6.3 (65536) so it seems like a mute point (for linux
> kernel >= 2.6.3).
> 
> There are a number of calls to "getXXbyYY" functions (i.e., getgrgid,
> getpwnam, etc...).  These seem to be dynamically preloaded and access
> preloaded databases.  They are unaffected by chroot() (even after
> setting __nss_configure_lookup(foo, files)).  I've instead used shadow's
> own method of macro expansion to generate functions doing the
> equivalent, with recursive calls to fgetXXent functions.
> 
> And PAM functionality doesn't work and has to be disabled while using
> chroot().  I don't know very much about PAM.  Would this be a problem?
> 
> Also,  the chroot functionality could probably be easily extended to
> other modules but I'm not sure if this would be acceptable upstream.
> 
> There are a couple of cosmetic changes I'm considering as well (such as
> how --chroot flag is parsed).
> 
> -- Peter Levine

Sorry, wrong patch.

I've attached the correct one.

-- Peter Levine


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: shadow-4.1.4.2-chroot.patch --]
[-- Type: text/x-patch; name=shadow-4.1.4.2-chroot.patch, Size: 41959 bytes --]

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	2009-07-23 21:15:58.000000000 -0400
+++ shadow-4.1.4.2/config.h.in	2010-02-16 07:19:48.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	2009-07-23 21:15:56.000000000 -0400
+++ shadow-4.1.4.2/configure	2010-02-16 07:19:48.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-16 07:19:48.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-16 07:19:48.000000000 -0500
@@ -154,6 +154,7 @@
 
 /* getgr_nam_gid.c */
 extern /*@null@*/struct group *getgr_nam_gid (const char *grname);
+extern /*@null@*/struct group *fgetgr_nam_gid (const char *grname);
 
 /* getlong.c */
 extern int getlong (const char *numstr, /*@out@*/long int *result);
@@ -400,6 +401,15 @@
 /* xgetspnam.c */
 extern /*@null@*/ /*@only@*/struct spwd *xgetspnam(const char *);
 
+/* xfgetpwnam.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwnam (const char *);
+/* xfgetpwuid.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwuid (uid_t);
+/* xfgetgrnam.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrnam (const char *);
+/* xfgetgrgid.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrgid (gid_t);
+
 /* yesno.c */
 extern bool yes_or_no (bool read_only);
 
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-16 07:19:48.000000000 -0500
@@ -1,5 +1,5 @@
 
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 
 INCLUDES = -I$(top_srcdir)/lib
 
@@ -59,6 +59,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.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	2009-07-23 21:15:59.000000000 -0400
+++ shadow-4.1.4.2/libmisc/Makefile.in	2010-02-16 07:19:48.000000000 -0500
@@ -64,9 +64,10 @@
 	shell.$(OBJEXT) system.$(OBJEXT) strtoday.$(OBJEXT) \
 	sub.$(OBJEXT) sulog.$(OBJEXT) ttytype.$(OBJEXT) tz.$(OBJEXT) \
 	ulimit.$(OBJEXT) user_busy.$(OBJEXT) utmp.$(OBJEXT) \
-	valid.$(OBJEXT) xgetpwnam.$(OBJEXT) xgetpwuid.$(OBJEXT) \
-	xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) xgetspnam.$(OBJEXT) \
-	xmalloc.$(OBJEXT) yesno.$(OBJEXT)
+	valid.$(OBJEXT) xfgetpwnam.$(OBJEXT) xfgetpwuid.$(OBJEXT) \
+	xfgetgrnam.$(OBJEXT) xfgetgrgid.$(OBJEXT) xgetpwnam.$(OBJEXT) \
+	xgetpwuid.$(OBJEXT) xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) \
+	xgetspnam.$(OBJEXT) xmalloc.$(OBJEXT) yesno.$(OBJEXT)
 libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -232,7 +233,7 @@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 INCLUDES = -I$(top_srcdir)/lib
 noinst_LIBRARIES = libmisc.a
 libmisc_a_SOURCES = \
@@ -289,6 +290,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.c \
@@ -395,6 +400,10 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_busy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utmp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrgid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwuid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrgid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrnam.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwnam.Po@am__quote@
diff -Naur shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c shadow-4.1.4.2/libmisc/getgr_nam_gid.c
--- shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c	2009-04-29 09:50:14.000000000 -0400
+++ shadow-4.1.4.2/libmisc/getgr_nam_gid.c	2010-02-16 07:19:48.000000000 -0500
@@ -64,3 +64,29 @@
 	return xgetgrnam (grname);
 }
 
+/*
+ * 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);
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c shadow-4.1.4.2/libmisc/xfgetXXbyYY.c
--- shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetXXbyYY.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "prototypes.h"
+
+#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_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) F_COMPARE_## name2 (result->name1,name2)
+#define F_COMPARE_gid	F_COMPARE_num
+#define F_COMPARE_uid	F_COMPARE_num
+#define F_COMPARE_namp	F_COMPARE_str
+#define F_COMPARE_name	F_COMPARE_str
+#define F_COMPARE_num(name1,name2)	(name1 == name2)
+#define F_COMPARE_str(name1,name2)	(!strcmp(name1,name2))
+
+#define XFUNCTION_NAME XPREFIX (FUNCTION_NAME)
+#define XPREFIX(name) XPREFIX1 (name)
+#define XPREFIX1(name) x##name
+#define REENTRANT_NAME APPEND_R (F_FUNCTION_NAME)
+#define APPEND_R(name) APPEND_R1 (name)
+#define APPEND_R1(name) name##_r
+#define STRINGIZE(name) STRINGIZE1 (name)
+#define STRINGIZE1(name) #name
+
+/*@null@*/ /*@only@*/LOOKUP_TYPE *XFUNCTION_NAME (ARG_TYPE ARG_NAME)
+{
+	FILE *stream = NULL;
+	if ((stream = fopen (DB_FILE, "r")) == NULL) {
+		fprintf (stderr, _("%s: Error opening the database file.\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (1);
+	}
+#if HAVE_FUNCTION_R
+	LOOKUP_TYPE *result=NULL;
+	char *buffer=NULL;
+	/* we have to start with something */
+	size_t length = 0x100;
+
+	result = malloc(sizeof(LOOKUP_TYPE));
+	if (NULL == result) {
+		fprintf (stderr, _("%s: out of memory\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (13);
+	}
+
+	while (true) {
+		int status;
+		LOOKUP_TYPE *resbuf = NULL;
+		buffer = (char *)realloc (buffer, length);
+		if (NULL == buffer) {
+			fprintf (stderr, _("%s: out of memory\n"),
+			         "x" STRINGIZE(FUNCTION_NAME));
+			exit (13);
+		}
+		errno = 0;
+		status = REENTRANT_NAME(stream, result, buffer,
+	                        length, &resbuf);
+		while ((0 ==status) && (resbuf == result)) {
+			if (F_COMPARE) {
+				/* Build a result structure that can be freed by
+				 * the shadow *_free functions. */
+				LOOKUP_TYPE *ret_result = DUP_FUNCTION(result);
+				(void) fclose (stream);
+				free(buffer);
+				free(result);
+				return ret_result;
+			} else {
+				status = REENTRANT_NAME(stream, result, buffer,
+		                	        length, &resbuf);
+			}
+		}
+
+		if (ERANGE != errno) {
+			(void) fclose (stream);
+			free (buffer);
+			free (result);
+			return NULL;
+		}
+
+		if (length <= ((size_t)-1 / 4)) {
+			length *= 4;
+		} else if (length == (size_t) -1) {
+			break;
+		} else {
+			length = (size_t) -1;
+		}
+	}
+
+	(void) fclose (stream);
+	free(buffer);
+	free(result);
+	return NULL;
+
+#else /* !HAVE_FUNCTION_R */
+
+	/* No reentrant function.
+	 * Duplicate the structure to avoid other call to overwrite it.
+	 *
+	 * We should also restore the initial structure. But that would be
+	 * overkill.
+	 */
+	LOOKUP_TYPE *result = F_FUNCTION_NAME(stream);
+
+	while (result) {
+		if (F_COMPARE) {
+			result = DUP_FUNCTION(result);
+			if (NULL == result) {
+				fprintf (stderr, _("%s: out of memory\n"),
+				         "x" STRINGIZE(FUNCTION_NAME));
+				exit (13);
+			}
+			break;
+		} else {
+			result = F_FUNCTION_NAME(stream);
+		}
+	}
+
+	(void) fclose (stream);
+	return result;
+#endif
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrgid.c shadow-4.1.4.2/libmisc/xfgetgrgid.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrgid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrgid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrgid
+#define ARG_TYPE	gid_t
+#define ARG_NAME	gid
+#define DB_TYPE	gr
+#define DB_FILE GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrnam.c shadow-4.1.4.2/libmisc/xfgetgrnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	gr
+#define DB_FILE	GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwnam.c shadow-4.1.4.2/libmisc/xfgetpwnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwuid.c shadow-4.1.4.2/libmisc/xfgetpwuid.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwuid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwuid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwuid
+#define ARG_TYPE	uid_t
+#define ARG_NAME	uid
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
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	2009-07-23 21:16:24.000000000 -0400
+++ shadow-4.1.4.2/man/groupadd.8	2010-02-16 07:19:48.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 07:19:48.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/useradd.8 shadow-4.1.4.2/man/useradd.8
--- shadow-4.1.4.2.old/man/useradd.8	2009-07-23 21:16:44.000000000 -0400
+++ shadow-4.1.4.2/man/useradd.8	2010-02-16 07:19:48.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 07:19:48.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/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-16 08:18:58.000000000 -0500
@@ -72,6 +72,8 @@
  */
 char *Prog;
 
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
 static /*@null@*/char *group_name;
 static gid_t group_id;
 static /*@null@*/char *group_passwd;
@@ -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);
 }
@@ -373,6 +378,8 @@
 	/*
 	 * Parse the command line options.
 	 */
+	int argc_saved = argc;
+	static bool parse_chroot =  false;
 	char *cp;
 	int option_index = 0;
 	int c;
@@ -384,12 +391,35 @@
 		{"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) {
+		if (!parse_chroot) {
+		switch (c) {
+		case 'R':
+			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': %s\n"),
+			         Prog, optarg, strerror (errno));
+				exit (1);
+			}
+			Rflg = true;
+			break;
+		default:
+			break;
+		}
+		} else {
 		switch (c) {
 		case 'f':
 			/*
@@ -443,9 +473,20 @@
 		case 'r':
 			rflg = true;
 			break;
+		case 'R':
+			break;
 		default:
 			usage ();
 		}
+		}
+	}
+
+	/* Finished parsing for chroot flag */
+	if (!parse_chroot) {
+		parse_chroot = true;
+		argc = argc_saved;
+		optind = 0;
+		return;
 	}
 
 	/*
@@ -476,8 +517,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 +532,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 +568,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 +599,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 }
@@ -579,6 +626,12 @@
 	OPENLOG ("groupadd");
 
 	/*
+	 * Parse the chroot option before other options
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
+
+	/*
 	 * Parse the command line options.
 	 */
 	process_flags (argc, argv);
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-16 08:17:32.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);
@@ -960,6 +971,8 @@
 		 * Parse the command line options.
 		 */
 		int c;
+		int argc_saved = argc;
+		static bool parse_chroot =  false;
 		static struct option long_options[] = {
 			{"base-dir", required_argument, NULL, 'b'},
 			{"comment", required_argument, NULL, 'c'},
@@ -979,6 +992,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,11 +1003,33 @@
 		};
 		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) {
+			if (!parse_chroot) {
+			switch (c) {
+			case 'R':
+				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': %s\n"),
+				         Prog, optarg, strerror (errno));
+					fail_exit (1);
+				}
+				Rflg = true;
+				break;
+			default:
+				break;
+			}
+			} else {
 			switch (c) {
 			case 'b':
 				if (   ( !VALID (optarg) )
@@ -1081,7 +1117,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 +1199,8 @@
 			case 'r':
 				rflg = true;
 				break;
+			case 'R':
+				break;
 			case 's':
 				if (   ( !VALID (optarg) )
 				    || (   ('\0' != optarg[0])
@@ -1204,6 +1246,14 @@
 				usage ();
 			}
 			anyflag = true;
+			}
+		}
+		/* Finished parsing for chroot flag */
+		if (!parse_chroot) {
+			parse_chroot = true;
+			argc = argc_saved;
+			optind = 0;
+			return;
 		}
 	}
 
@@ -1663,8 +1713,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 +1858,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 +1917,12 @@
 	 */
 	user_groups[0] = (char *) 0;
 
+	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
 
 	is_shadow_pwd = spw_file_present ();
 #ifdef SHADOWGRP
@@ -1873,6 +1935,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 +1965,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		fail_exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
@@ -1920,7 +1985,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 +2004,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 +2042,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);


^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-16 15:42 P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-16 15:42 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 1967 bytes --]

On 02/16/2010 10:04 AM, P. Levine wrote:
> Peter Stuge wrote:
>> I talked to upstream on freenode/#shadow and they welcome a patch for
>> adding --chroot
>>
>> chroot() needs to happen really early since useradd and friends read
>> some configuration files to know e.g. which password encryption
>> method to use.
> 
> Attached is a tentative patch to add a chroot flag to useradd and
> groupadd (via --chroot or -R).  It compiles and works on my end
> (--chroot /usr/armv4tl-softfloat-linux-gnueabi) with various other flags
> enabled.  I'm hoping for others to test it and get some feedback before
> I submit it to shadow upstream.
> 
> There do exist a couple of issues:
> 
> sysconf(_SC_NGROUPS_MAX) is called by useradd early on.  This would
> report the maximum allowable number of groups per user on the build
> system, not the target.  To my knowledge, this is set by the kernel and
> would have to be used.  However, this tends to be a very high number for
> linux kernel >= 2.6.3 (65536) so it seems like a mute point (for linux
> kernel >= 2.6.3).
> 
> There are a number of calls to "getXXbyYY" functions (i.e., getgrgid,
> getpwnam, etc...).  These seem to be dynamically preloaded and access
> preloaded databases.  They are unaffected by chroot() (even after
> setting __nss_configure_lookup(foo, files)).  I've instead used shadow's
> own method of macro expansion to generate functions doing the
> equivalent, with recursive calls to fgetXXent functions.
> 
> And PAM functionality doesn't work and has to be disabled while using
> chroot().  I don't know very much about PAM.  Would this be a problem?
> 
> Also,  the chroot functionality could probably be easily extended to
> other modules but I'm not sure if this would be acceptable upstream.
> 
> There are a couple of cosmetic changes I'm considering as well (such as
> how --chroot flag is parsed).
> 
> -- Peter Levine

Sorry, wrong patch.

I've attached the correct one.

-- Peter Levine



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: shadow-4.1.4.2-chroot.patch --]
[-- Type: text/x-patch; name=shadow-4.1.4.2-chroot.patch, Size: 41961 bytes --]

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	2009-07-23 21:15:58.000000000 -0400
+++ shadow-4.1.4.2/config.h.in	2010-02-16 07:19:48.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	2009-07-23 21:15:56.000000000 -0400
+++ shadow-4.1.4.2/configure	2010-02-16 07:19:48.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-16 07:19:48.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-16 07:19:48.000000000 -0500
@@ -154,6 +154,7 @@
 
 /* getgr_nam_gid.c */
 extern /*@null@*/struct group *getgr_nam_gid (const char *grname);
+extern /*@null@*/struct group *fgetgr_nam_gid (const char *grname);
 
 /* getlong.c */
 extern int getlong (const char *numstr, /*@out@*/long int *result);
@@ -400,6 +401,15 @@
 /* xgetspnam.c */
 extern /*@null@*/ /*@only@*/struct spwd *xgetspnam(const char *);
 
+/* xfgetpwnam.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwnam (const char *);
+/* xfgetpwuid.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwuid (uid_t);
+/* xfgetgrnam.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrnam (const char *);
+/* xfgetgrgid.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrgid (gid_t);
+
 /* yesno.c */
 extern bool yes_or_no (bool read_only);
 
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-16 07:19:48.000000000 -0500
@@ -1,5 +1,5 @@
 
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 
 INCLUDES = -I$(top_srcdir)/lib
 
@@ -59,6 +59,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.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	2009-07-23 21:15:59.000000000 -0400
+++ shadow-4.1.4.2/libmisc/Makefile.in	2010-02-16 07:19:48.000000000 -0500
@@ -64,9 +64,10 @@
 	shell.$(OBJEXT) system.$(OBJEXT) strtoday.$(OBJEXT) \
 	sub.$(OBJEXT) sulog.$(OBJEXT) ttytype.$(OBJEXT) tz.$(OBJEXT) \
 	ulimit.$(OBJEXT) user_busy.$(OBJEXT) utmp.$(OBJEXT) \
-	valid.$(OBJEXT) xgetpwnam.$(OBJEXT) xgetpwuid.$(OBJEXT) \
-	xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) xgetspnam.$(OBJEXT) \
-	xmalloc.$(OBJEXT) yesno.$(OBJEXT)
+	valid.$(OBJEXT) xfgetpwnam.$(OBJEXT) xfgetpwuid.$(OBJEXT) \
+	xfgetgrnam.$(OBJEXT) xfgetgrgid.$(OBJEXT) xgetpwnam.$(OBJEXT) \
+	xgetpwuid.$(OBJEXT) xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) \
+	xgetspnam.$(OBJEXT) xmalloc.$(OBJEXT) yesno.$(OBJEXT)
 libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -232,7 +233,7 @@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 INCLUDES = -I$(top_srcdir)/lib
 noinst_LIBRARIES = libmisc.a
 libmisc_a_SOURCES = \
@@ -289,6 +290,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.c \
@@ -395,6 +400,10 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_busy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utmp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrgid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwuid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrgid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrnam.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwnam.Po@am__quote@
diff -Naur shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c shadow-4.1.4.2/libmisc/getgr_nam_gid.c
--- shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c	2009-04-29 09:50:14.000000000 -0400
+++ shadow-4.1.4.2/libmisc/getgr_nam_gid.c	2010-02-16 07:19:48.000000000 -0500
@@ -64,3 +64,29 @@
 	return xgetgrnam (grname);
 }
 
+/*
+ * 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);
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c shadow-4.1.4.2/libmisc/xfgetXXbyYY.c
--- shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetXXbyYY.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "prototypes.h"
+
+#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_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) F_COMPARE_## name2 (result->name1,name2)
+#define F_COMPARE_gid	F_COMPARE_num
+#define F_COMPARE_uid	F_COMPARE_num
+#define F_COMPARE_namp	F_COMPARE_str
+#define F_COMPARE_name	F_COMPARE_str
+#define F_COMPARE_num(name1,name2)	(name1 == name2)
+#define F_COMPARE_str(name1,name2)	(!strcmp(name1,name2))
+
+#define XFUNCTION_NAME XPREFIX (FUNCTION_NAME)
+#define XPREFIX(name) XPREFIX1 (name)
+#define XPREFIX1(name) x##name
+#define REENTRANT_NAME APPEND_R (F_FUNCTION_NAME)
+#define APPEND_R(name) APPEND_R1 (name)
+#define APPEND_R1(name) name##_r
+#define STRINGIZE(name) STRINGIZE1 (name)
+#define STRINGIZE1(name) #name
+
+/*@null@*/ /*@only@*/LOOKUP_TYPE *XFUNCTION_NAME (ARG_TYPE ARG_NAME)
+{
+	FILE *stream = NULL;
+	if ((stream = fopen (DB_FILE, "r")) == NULL) {
+		fprintf (stderr, _("%s: Error opening the database file.\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (1);
+	}
+#if HAVE_FUNCTION_R
+	LOOKUP_TYPE *result=NULL;
+	char *buffer=NULL;
+	/* we have to start with something */
+	size_t length = 0x100;
+
+	result = malloc(sizeof(LOOKUP_TYPE));
+	if (NULL == result) {
+		fprintf (stderr, _("%s: out of memory\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (13);
+	}
+
+	while (true) {
+		int status;
+		LOOKUP_TYPE *resbuf = NULL;
+		buffer = (char *)realloc (buffer, length);
+		if (NULL == buffer) {
+			fprintf (stderr, _("%s: out of memory\n"),
+			         "x" STRINGIZE(FUNCTION_NAME));
+			exit (13);
+		}
+		errno = 0;
+		status = REENTRANT_NAME(stream, result, buffer,
+	                        length, &resbuf);
+		while ((0 ==status) && (resbuf == result)) {
+			if (F_COMPARE) {
+				/* Build a result structure that can be freed by
+				 * the shadow *_free functions. */
+				LOOKUP_TYPE *ret_result = DUP_FUNCTION(result);
+				(void) fclose (stream);
+				free(buffer);
+				free(result);
+				return ret_result;
+			} else {
+				status = REENTRANT_NAME(stream, result, buffer,
+		                	        length, &resbuf);
+			}
+		}
+
+		if (ERANGE != errno) {
+			(void) fclose (stream);
+			free (buffer);
+			free (result);
+			return NULL;
+		}
+
+		if (length <= ((size_t)-1 / 4)) {
+			length *= 4;
+		} else if (length == (size_t) -1) {
+			break;
+		} else {
+			length = (size_t) -1;
+		}
+	}
+
+	(void) fclose (stream);
+	free(buffer);
+	free(result);
+	return NULL;
+
+#else /* !HAVE_FUNCTION_R */
+
+	/* No reentrant function.
+	 * Duplicate the structure to avoid other call to overwrite it.
+	 *
+	 * We should also restore the initial structure. But that would be
+	 * overkill.
+	 */
+	LOOKUP_TYPE *result = F_FUNCTION_NAME(stream);
+
+	while (result) {
+		if (F_COMPARE) {
+			result = DUP_FUNCTION(result);
+			if (NULL == result) {
+				fprintf (stderr, _("%s: out of memory\n"),
+				         "x" STRINGIZE(FUNCTION_NAME));
+				exit (13);
+			}
+			break;
+		} else {
+			result = F_FUNCTION_NAME(stream);
+		}
+	}
+
+	(void) fclose (stream);
+	return result;
+#endif
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrgid.c shadow-4.1.4.2/libmisc/xfgetgrgid.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrgid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrgid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrgid
+#define ARG_TYPE	gid_t
+#define ARG_NAME	gid
+#define DB_TYPE	gr
+#define DB_FILE GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrnam.c shadow-4.1.4.2/libmisc/xfgetgrnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	gr
+#define DB_FILE	GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwnam.c shadow-4.1.4.2/libmisc/xfgetpwnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwuid.c shadow-4.1.4.2/libmisc/xfgetpwuid.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwuid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwuid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwuid
+#define ARG_TYPE	uid_t
+#define ARG_NAME	uid
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
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	2009-07-23 21:16:24.000000000 -0400
+++ shadow-4.1.4.2/man/groupadd.8	2010-02-16 07:19:48.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 07:19:48.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/useradd.8 shadow-4.1.4.2/man/useradd.8
--- shadow-4.1.4.2.old/man/useradd.8	2009-07-23 21:16:44.000000000 -0400
+++ shadow-4.1.4.2/man/useradd.8	2010-02-16 07:19:48.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 07:19:48.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/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-16 08:18:58.000000000 -0500
@@ -72,6 +72,8 @@
  */
 char *Prog;
 
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
 static /*@null@*/char *group_name;
 static gid_t group_id;
 static /*@null@*/char *group_passwd;
@@ -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);
 }
@@ -373,6 +378,8 @@
 	/*
 	 * Parse the command line options.
 	 */
+	int argc_saved = argc;
+	static bool parse_chroot =  false;
 	char *cp;
 	int option_index = 0;
 	int c;
@@ -384,12 +391,35 @@
 		{"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) {
+		if (!parse_chroot) {
+		switch (c) {
+		case 'R':
+			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': %s\n"),
+			         Prog, optarg, strerror (errno));
+				exit (1);
+			}
+			Rflg = true;
+			break;
+		default:
+			break;
+		}
+		} else {
 		switch (c) {
 		case 'f':
 			/*
@@ -443,9 +473,20 @@
 		case 'r':
 			rflg = true;
 			break;
+		case 'R':
+			break;
 		default:
 			usage ();
 		}
+		}
+	}
+
+	/* Finished parsing for chroot flag */
+	if (!parse_chroot) {
+		parse_chroot = true;
+		argc = argc_saved;
+		optind = 0;
+		return;
 	}
 
 	/*
@@ -476,8 +517,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 +532,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 +568,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 +599,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 }
@@ -579,6 +626,12 @@
 	OPENLOG ("groupadd");
 
 	/*
+	 * Parse the chroot option before other options
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
+
+	/*
 	 * Parse the command line options.
 	 */
 	process_flags (argc, argv);
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-16 08:17:32.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);
@@ -960,6 +971,8 @@
 		 * Parse the command line options.
 		 */
 		int c;
+		int argc_saved = argc;
+		static bool parse_chroot =  false;
 		static struct option long_options[] = {
 			{"base-dir", required_argument, NULL, 'b'},
 			{"comment", required_argument, NULL, 'c'},
@@ -979,6 +992,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,11 +1003,33 @@
 		};
 		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) {
+			if (!parse_chroot) {
+			switch (c) {
+			case 'R':
+				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': %s\n"),
+				         Prog, optarg, strerror (errno));
+					fail_exit (1);
+				}
+				Rflg = true;
+				break;
+			default:
+				break;
+			}
+			} else {
 			switch (c) {
 			case 'b':
 				if (   ( !VALID (optarg) )
@@ -1081,7 +1117,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 +1199,8 @@
 			case 'r':
 				rflg = true;
 				break;
+			case 'R':
+				break;
 			case 's':
 				if (   ( !VALID (optarg) )
 				    || (   ('\0' != optarg[0])
@@ -1204,6 +1246,14 @@
 				usage ();
 			}
 			anyflag = true;
+			}
+		}
+		/* Finished parsing for chroot flag */
+		if (!parse_chroot) {
+			parse_chroot = true;
+			argc = argc_saved;
+			optind = 0;
+			return;
 		}
 	}
 
@@ -1663,8 +1713,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 +1858,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 +1917,12 @@
 	 */
 	user_groups[0] = (char *) 0;
 
+	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
 
 	is_shadow_pwd = spw_file_present ();
 #ifdef SHADOWGRP
@@ -1873,6 +1935,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 +1965,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		fail_exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
@@ -1920,7 +1985,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 +2004,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 +2042,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);



^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-16 15:56 P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-16 15:56 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 215 bytes --]

On 02/16/2010 10:04 AM, P. Levine wrote:
> Attached is a tentative patch to add a chroot flag to useradd and
> groupadd (via --chroot or -R).

Sorry, wrong patch.

I've attached the correct one.

-- Peter Levine




[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: shadow-4.1.4.2-chroot.patch --]
[-- Type: text/x-patch; name=shadow-4.1.4.2-chroot.patch, Size: 41957 bytes --]

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	2009-07-23 21:15:58.000000000 -0400
+++ shadow-4.1.4.2/config.h.in	2010-02-16 07:19:48.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	2009-07-23 21:15:56.000000000 -0400
+++ shadow-4.1.4.2/configure	2010-02-16 07:19:48.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-16 07:19:48.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-16 07:19:48.000000000 -0500
@@ -154,6 +154,7 @@
 
 /* getgr_nam_gid.c */
 extern /*@null@*/struct group *getgr_nam_gid (const char *grname);
+extern /*@null@*/struct group *fgetgr_nam_gid (const char *grname);
 
 /* getlong.c */
 extern int getlong (const char *numstr, /*@out@*/long int *result);
@@ -400,6 +401,15 @@
 /* xgetspnam.c */
 extern /*@null@*/ /*@only@*/struct spwd *xgetspnam(const char *);
 
+/* xfgetpwnam.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwnam (const char *);
+/* xfgetpwuid.c */
+extern /*@null@*/ /*@only@*/struct passwd *xfgetpwuid (uid_t);
+/* xfgetgrnam.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrnam (const char *);
+/* xfgetgrgid.c */
+extern /*@null@*/ /*@only@*/struct group *xfgetgrgid (gid_t);
+
 /* yesno.c */
 extern bool yes_or_no (bool read_only);
 
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-16 07:19:48.000000000 -0500
@@ -1,5 +1,5 @@
 
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 
 INCLUDES = -I$(top_srcdir)/lib
 
@@ -59,6 +59,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.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	2009-07-23 21:15:59.000000000 -0400
+++ shadow-4.1.4.2/libmisc/Makefile.in	2010-02-16 07:19:48.000000000 -0500
@@ -64,9 +64,10 @@
 	shell.$(OBJEXT) system.$(OBJEXT) strtoday.$(OBJEXT) \
 	sub.$(OBJEXT) sulog.$(OBJEXT) ttytype.$(OBJEXT) tz.$(OBJEXT) \
 	ulimit.$(OBJEXT) user_busy.$(OBJEXT) utmp.$(OBJEXT) \
-	valid.$(OBJEXT) xgetpwnam.$(OBJEXT) xgetpwuid.$(OBJEXT) \
-	xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) xgetspnam.$(OBJEXT) \
-	xmalloc.$(OBJEXT) yesno.$(OBJEXT)
+	valid.$(OBJEXT) xfgetpwnam.$(OBJEXT) xfgetpwuid.$(OBJEXT) \
+	xfgetgrnam.$(OBJEXT) xfgetgrgid.$(OBJEXT) xgetpwnam.$(OBJEXT) \
+	xgetpwuid.$(OBJEXT) xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) \
+	xgetspnam.$(OBJEXT) xmalloc.$(OBJEXT) yesno.$(OBJEXT)
 libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -232,7 +233,7 @@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = .indent.pro xgetXXbyYY.c
+EXTRA_DIST = .indent.pro xgetXXbyYY.c xfgetXXbyYY.c
 INCLUDES = -I$(top_srcdir)/lib
 noinst_LIBRARIES = libmisc.a
 libmisc_a_SOURCES = \
@@ -289,6 +290,10 @@
 	user_busy.c \
 	utmp.c \
 	valid.c \
+	xfgetpwnam.c \
+	xfgetpwuid.c \
+	xfgetgrnam.c \
+	xfgetgrgid.c \
 	xgetpwnam.c \
 	xgetpwuid.c \
 	xgetgrnam.c \
@@ -395,6 +400,10 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_busy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utmp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrgid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetgrnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwnam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfgetpwuid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrgid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrnam.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwnam.Po@am__quote@
diff -Naur shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c shadow-4.1.4.2/libmisc/getgr_nam_gid.c
--- shadow-4.1.4.2.old/libmisc/getgr_nam_gid.c	2009-04-29 09:50:14.000000000 -0400
+++ shadow-4.1.4.2/libmisc/getgr_nam_gid.c	2010-02-16 07:19:48.000000000 -0500
@@ -64,3 +64,29 @@
 	return xgetgrnam (grname);
 }
 
+/*
+ * 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);
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c shadow-4.1.4.2/libmisc/xfgetXXbyYY.c
--- shadow-4.1.4.2.old/libmisc/xfgetXXbyYY.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetXXbyYY.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "prototypes.h"
+
+#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_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) F_COMPARE_## name2 (result->name1,name2)
+#define F_COMPARE_gid	F_COMPARE_num
+#define F_COMPARE_uid	F_COMPARE_num
+#define F_COMPARE_namp	F_COMPARE_str
+#define F_COMPARE_name	F_COMPARE_str
+#define F_COMPARE_num(name1,name2)	(name1 == name2)
+#define F_COMPARE_str(name1,name2)	(!strcmp(name1,name2))
+
+#define XFUNCTION_NAME XPREFIX (FUNCTION_NAME)
+#define XPREFIX(name) XPREFIX1 (name)
+#define XPREFIX1(name) x##name
+#define REENTRANT_NAME APPEND_R (F_FUNCTION_NAME)
+#define APPEND_R(name) APPEND_R1 (name)
+#define APPEND_R1(name) name##_r
+#define STRINGIZE(name) STRINGIZE1 (name)
+#define STRINGIZE1(name) #name
+
+/*@null@*/ /*@only@*/LOOKUP_TYPE *XFUNCTION_NAME (ARG_TYPE ARG_NAME)
+{
+	FILE *stream = NULL;
+	if ((stream = fopen (DB_FILE, "r")) == NULL) {
+		fprintf (stderr, _("%s: Error opening the database file.\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (1);
+	}
+#if HAVE_FUNCTION_R
+	LOOKUP_TYPE *result=NULL;
+	char *buffer=NULL;
+	/* we have to start with something */
+	size_t length = 0x100;
+
+	result = malloc(sizeof(LOOKUP_TYPE));
+	if (NULL == result) {
+		fprintf (stderr, _("%s: out of memory\n"),
+		         "x" STRINGIZE(FUNCTION_NAME));
+		exit (13);
+	}
+
+	while (true) {
+		int status;
+		LOOKUP_TYPE *resbuf = NULL;
+		buffer = (char *)realloc (buffer, length);
+		if (NULL == buffer) {
+			fprintf (stderr, _("%s: out of memory\n"),
+			         "x" STRINGIZE(FUNCTION_NAME));
+			exit (13);
+		}
+		errno = 0;
+		status = REENTRANT_NAME(stream, result, buffer,
+	                        length, &resbuf);
+		while ((0 ==status) && (resbuf == result)) {
+			if (F_COMPARE) {
+				/* Build a result structure that can be freed by
+				 * the shadow *_free functions. */
+				LOOKUP_TYPE *ret_result = DUP_FUNCTION(result);
+				(void) fclose (stream);
+				free(buffer);
+				free(result);
+				return ret_result;
+			} else {
+				status = REENTRANT_NAME(stream, result, buffer,
+		                	        length, &resbuf);
+			}
+		}
+
+		if (ERANGE != errno) {
+			(void) fclose (stream);
+			free (buffer);
+			free (result);
+			return NULL;
+		}
+
+		if (length <= ((size_t)-1 / 4)) {
+			length *= 4;
+		} else if (length == (size_t) -1) {
+			break;
+		} else {
+			length = (size_t) -1;
+		}
+	}
+
+	(void) fclose (stream);
+	free(buffer);
+	free(result);
+	return NULL;
+
+#else /* !HAVE_FUNCTION_R */
+
+	/* No reentrant function.
+	 * Duplicate the structure to avoid other call to overwrite it.
+	 *
+	 * We should also restore the initial structure. But that would be
+	 * overkill.
+	 */
+	LOOKUP_TYPE *result = F_FUNCTION_NAME(stream);
+
+	while (result) {
+		if (F_COMPARE) {
+			result = DUP_FUNCTION(result);
+			if (NULL == result) {
+				fprintf (stderr, _("%s: out of memory\n"),
+				         "x" STRINGIZE(FUNCTION_NAME));
+				exit (13);
+			}
+			break;
+		} else {
+			result = F_FUNCTION_NAME(stream);
+		}
+	}
+
+	(void) fclose (stream);
+	return result;
+#endif
+}
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrgid.c shadow-4.1.4.2/libmisc/xfgetgrgid.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrgid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrgid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrgid
+#define ARG_TYPE	gid_t
+#define ARG_NAME	gid
+#define DB_TYPE	gr
+#define DB_FILE GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetgrnam.c shadow-4.1.4.2/libmisc/xfgetgrnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetgrnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetgrnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "groupio.h"
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	fgetgrnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	gr
+#define DB_FILE	GROUP_FILE
+#define DUP_FUNCTION	__gr_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETGRENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwnam.c shadow-4.1.4.2/libmisc/xfgetpwnam.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwnam.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwnam.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwnam
+#define ARG_TYPE	const char *
+#define ARG_NAME	name
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
diff -Naur shadow-4.1.4.2.old/libmisc/xfgetpwuid.c shadow-4.1.4.2/libmisc/xfgetpwuid.c
--- shadow-4.1.4.2.old/libmisc/xfgetpwuid.c	1969-12-31 19:00:00.000000000 -0500
+++ shadow-4.1.4.2/libmisc/xfgetpwuid.c	2010-02-16 07:19:48.000000000 -0500
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "pwio.h"
+
+#define LOOKUP_TYPE	struct passwd
+#define FUNCTION_NAME	fgetpwuid
+#define ARG_TYPE	uid_t
+#define ARG_NAME	uid
+#define DB_TYPE	pw
+#define DB_FILE PASSWD_FILE
+#define DUP_FUNCTION	__pw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETPWENT_R)
+
+#include "xfgetXXbyYY.c"
+
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	2009-07-23 21:16:24.000000000 -0400
+++ shadow-4.1.4.2/man/groupadd.8	2010-02-16 07:19:48.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 07:19:48.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/useradd.8 shadow-4.1.4.2/man/useradd.8
--- shadow-4.1.4.2.old/man/useradd.8	2009-07-23 21:16:44.000000000 -0400
+++ shadow-4.1.4.2/man/useradd.8	2010-02-16 07:19:48.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 07:19:48.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/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-16 08:18:58.000000000 -0500
@@ -72,6 +72,8 @@
  */
 char *Prog;
 
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
 static /*@null@*/char *group_name;
 static gid_t group_id;
 static /*@null@*/char *group_passwd;
@@ -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);
 }
@@ -373,6 +378,8 @@
 	/*
 	 * Parse the command line options.
 	 */
+	int argc_saved = argc;
+	static bool parse_chroot =  false;
 	char *cp;
 	int option_index = 0;
 	int c;
@@ -384,12 +391,35 @@
 		{"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) {
+		if (!parse_chroot) {
+		switch (c) {
+		case 'R':
+			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': %s\n"),
+			         Prog, optarg, strerror (errno));
+				exit (1);
+			}
+			Rflg = true;
+			break;
+		default:
+			break;
+		}
+		} else {
 		switch (c) {
 		case 'f':
 			/*
@@ -443,9 +473,20 @@
 		case 'r':
 			rflg = true;
 			break;
+		case 'R':
+			break;
 		default:
 			usage ();
 		}
+		}
+	}
+
+	/* Finished parsing for chroot flag */
+	if (!parse_chroot) {
+		parse_chroot = true;
+		argc = argc_saved;
+		optind = 0;
+		return;
 	}
 
 	/*
@@ -476,8 +517,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 +532,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 +568,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 +599,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 }
@@ -579,6 +626,12 @@
 	OPENLOG ("groupadd");
 
 	/*
+	 * Parse the chroot option before other options
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
+
+	/*
 	 * Parse the command line options.
 	 */
 	process_flags (argc, argv);
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-16 08:17:32.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);
@@ -960,6 +971,8 @@
 		 * Parse the command line options.
 		 */
 		int c;
+		int argc_saved = argc;
+		static bool parse_chroot =  false;
 		static struct option long_options[] = {
 			{"base-dir", required_argument, NULL, 'b'},
 			{"comment", required_argument, NULL, 'c'},
@@ -979,6 +992,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,11 +1003,33 @@
 		};
 		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) {
+			if (!parse_chroot) {
+			switch (c) {
+			case 'R':
+				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': %s\n"),
+				         Prog, optarg, strerror (errno));
+					fail_exit (1);
+				}
+				Rflg = true;
+				break;
+			default:
+				break;
+			}
+			} else {
 			switch (c) {
 			case 'b':
 				if (   ( !VALID (optarg) )
@@ -1081,7 +1117,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 +1199,8 @@
 			case 'r':
 				rflg = true;
 				break;
+			case 'R':
+				break;
 			case 's':
 				if (   ( !VALID (optarg) )
 				    || (   ('\0' != optarg[0])
@@ -1204,6 +1246,14 @@
 				usage ();
 			}
 			anyflag = true;
+			}
+		}
+		/* Finished parsing for chroot flag */
+		if (!parse_chroot) {
+			parse_chroot = true;
+			argc = argc_saved;
+			optind = 0;
+			return;
 		}
 	}
 
@@ -1663,8 +1713,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 +1858,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 +1917,12 @@
 	 */
 	user_groups[0] = (char *) 0;
 
+	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after opening the log.
+	 */
+	process_flags (argc, argv);
 
 	is_shadow_pwd = spw_file_present ();
 #ifdef SHADOWGRP
@@ -1873,6 +1935,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 +1965,7 @@
 		fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
 		fail_exit (1);
 	}
+	}
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
@@ -1920,7 +1985,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 +2004,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 +2042,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);

^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-16 16:14 P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-16 16:14 UTC (permalink / raw
  To: gentoo-embedded

On 02/16/2010 10:56 AM, P. Levine wrote:
> Sorry, wrong patch.
>
> I've attached the correct one.
>

Sorry for the reposts.
Problems on my end.

-- Peter Levine







^ permalink raw reply	[flat|nested] 26+ messages in thread
* Re: [gentoo-embedded] emerge --root : users not created
@ 2010-02-22 14:41 P. Levine
  2010-02-22 15:19 ` Peter Stuge
                   ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: P. Levine @ 2010-02-22 14:41 UTC (permalink / raw
  To: gentoo-embedded

[-- Attachment #1: Type: text/plain, Size: 801 bytes --]

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

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: shadow-4.1.4.2-chroot.patch --]
[-- Type: text/x-patch; name=shadow-4.1.4.2-chroot.patch, Size: 45898 bytes --]

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__quote@./$(DEPDIR)/chkname.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowndir.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowntty.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chroot.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_group.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_user.Po@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
 

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2010-03-08 11:14 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-14 16:17 [gentoo-embedded] emerge --root : users not created Shinkan
2009-12-14 17:14 ` Ed W
2009-12-14 17:47   ` Sven Rebhan
2009-12-14 18:06     ` Peter Stuge
2009-12-15  7:31       ` Sven Rebhan
2009-12-15  8:53       ` Daniel Glaser
2009-12-15 10:33         ` Peter Stuge
2009-12-15 13:31           ` Ahmed Ammar
2009-12-15 14:00             ` Shinkan
2009-12-15 17:37             ` Peter Stuge
2009-12-15 22:24               ` Ahmed Ammar
2009-12-21 21:25               ` Ahmed Ammar
2009-12-21 21:29                 ` Ned Ludd
2009-12-22 11:38                   ` Peter Stuge
  -- strict thread matches above, loose matches on Subject: below --
2010-02-16 15:04 P. Levine
2010-02-16 15:20 P. Levine
2010-02-16 15:42 P. Levine
2010-02-16 15:56 P. Levine
2010-02-16 16:14 P. Levine
2010-02-22 14:41 P. Levine
2010-02-22 15:19 ` Peter Stuge
2010-02-22 18:44   ` P. Levine
2010-02-23 16:58 ` Ned Ludd
2010-02-24  2:01   ` P. Levine
2010-03-06  0:52 ` P. Levine
2010-03-08 11:05   ` Ed W

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox