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
  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
  0 siblings, 1 reply; 26+ messages in thread
From: Ed W @ 2009-12-14 17:14 UTC (permalink / raw
  To: gentoo-embedded

Yep, seems like a known bug

Also the bug exists if you merge in a binary package I believe?

Quite annoying - workarounds would be highly appreciated!

Ed


Shinkan wrote:
> 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




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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-14 17:14 ` Ed W
@ 2009-12-14 17:47   ` Sven Rebhan
  2009-12-14 18:06     ` Peter Stuge
  0 siblings, 1 reply; 26+ messages in thread
From: Sven Rebhan @ 2009-12-14 17:47 UTC (permalink / raw
  To: gentoo-embedded

2009/12/14 Ed W <lists@wildgooses.com>:
> Yep, seems like a known bug

It's not exactly a bug but rather a missing feature in useradd and
friends. The correct way to solve this would be to add an option to
useradd, allowing to specify a passwd file other than /etc/passwd.
Afterwards patch the corresponding eclass to use this option. Patches
are welcome. ;-)

> Quite annoying - workarounds would be highly appreciated!

To install a cross-compiled system read:
http://gentoo.mindzoo.de/index.cgi/wiki/Cross Install

Especially the command:
find /var/db/pkg -name '*.ebuild' -exec grep -qF 'pkg_postinst()' {}
\; -exec ebuild {} postinst \;

which re-executes all post-install procedures on the target system
(and thus adds the users).

Regards,
    Sven



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

* Re: [gentoo-embedded] emerge --root : users not created
  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
  0 siblings, 2 replies; 26+ messages in thread
From: Peter Stuge @ 2009-12-14 18:06 UTC (permalink / raw
  To: gentoo-embedded

Sven Rebhan wrote:
> The correct way to solve this would be to add an option to useradd,
> allowing to specify a passwd file other than /etc/passwd.
> Afterwards patch the corresponding eclass to use this option.
> Patches are welcome. ;-)

Looking at useradd.c at least four filenames should be changed;
passwd, shadow, group and sgroup.

The password db abstraction does allow easy changing of the file
names, but home directory, skel directory, mail spool and probably
more should also be changed, so maybe it makes sense to simply have
useradd call chroot() according to an option early on in main()?


//Peter



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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-14 18:06     ` Peter Stuge
@ 2009-12-15  7:31       ` Sven Rebhan
  2009-12-15  8:53       ` Daniel Glaser
  1 sibling, 0 replies; 26+ messages in thread
From: Sven Rebhan @ 2009-12-15  7:31 UTC (permalink / raw
  To: gentoo-embedded

2009/12/14 Peter Stuge <peter@stuge.se>:
> The password db abstraction does allow easy changing of the file
> names, but home directory, skel directory, mail spool and probably
> more should also be changed, so maybe it makes sense to simply have
> useradd call chroot() according to an option early on in main()?

This might be an option. However, I do not know if there are certain
security implications that come up with this change!? The best would
be to discuss this upstream first.

Sven



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

* Re: [gentoo-embedded] emerge --root : users not created
  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
  1 sibling, 1 reply; 26+ messages in thread
From: Daniel Glaser @ 2009-12-15  8:53 UTC (permalink / raw
  To: gentoo-embedded

Hi,
> ...
> Looking at useradd.c at least four filenames should be changed;
> passwd, shadow, group and sgroup.
>
> The password db abstraction does allow easy changing of the file
> names, but home directory, skel directory, mail spool and probably
> more should also be changed, so maybe it makes sense to simply have
> useradd call chroot() according to an option early on in main()?
>   
 This will definitely not work on cross targets? I think, there is not a
real workaround for post-installes that need to run on the target
plattform, according to my experience. I always emerged such packages on
the target itself, leading sometimes to very high compilation times.

The cleanest way would be an execute stack that gets created/extended
while cross building and processed when the target starts the next time,
but this would mean a really huge work amount to implement in ebuilds
and the system itself.

Cheers,
Daniel



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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-15  8:53       ` Daniel Glaser
@ 2009-12-15 10:33         ` Peter Stuge
  2009-12-15 13:31           ` Ahmed Ammar
  0 siblings, 1 reply; 26+ messages in thread
From: Peter Stuge @ 2009-12-15 10:33 UTC (permalink / raw
  To: gentoo-embedded

Daniel Glaser wrote:
> > so maybe it makes sense to simply have useradd call chroot()
> > according to an option early on in main()?
> 
> This will definitely not work on cross targets?

Why not? useradd doesn't fork, it does all file IO on it's own, so I
think it could work.


> I think, there is not a real workaround for post-installes that
> need to run on the target plattform, according to my experience.

If useradd is the only problem I think it's easy enough to solve.


> The cleanest way would be an execute stack

Again, if useradd is the only problem, I think that is huge overkill.
It would be a very cool solution though. :)


How does e.g. T2SDE solve this problem?


//Peter



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

* Re: [gentoo-embedded] emerge --root : users not created
  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
  0 siblings, 2 replies; 26+ messages in thread
From: Ahmed Ammar @ 2009-12-15 13:31 UTC (permalink / raw
  To: gentoo-embedded

On Tue, 2009-12-15 at 11:33 +0100, Peter Stuge wrote:
> Daniel Glaser wrote:
> > > so maybe it makes sense to simply have useradd call chroot()
> > > according to an option early on in main()?
> > 
> > This will definitely not work on cross targets?
> 
> Why not? useradd doesn't fork, it does all file IO on it's own, so I
> think it could work.

Well how exactly do you expect chroot to succeed when the host is x86
and the ${ROOT} is arm?

A.




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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-15 13:31           ` Ahmed Ammar
@ 2009-12-15 14:00             ` Shinkan
  2009-12-15 17:37             ` Peter Stuge
  1 sibling, 0 replies; 26+ messages in thread
From: Shinkan @ 2009-12-15 14:00 UTC (permalink / raw
  To: gentoo-embedded

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

2009/12/15 Ahmed Ammar <b33fc0d3@gentoo.org>

>
> Well how exactly do you expect chroot to succeed when the host is x86
> and the ${ROOT} is arm?
>


To some questions I read :
I use a amd64 host to build amd64 targets environments.
I use cross-emerge and not crossdev or chroot because my target don't have
and WILL NOT have portage, gcc, make or any other build-related tool. My
targets will run on livecd, so they won't even have (tmp-excluded) writing
needs.
My build needs REQUIRES by process that I could not even put gcc/portage/etc
on my target, chroot and build with them, then remove them.

For now, I think of chrooting to useradd manually, or copying some
pre-generated /etc/{passwd,shadow,group,...} to my target dir.

I do think it's a bug, because man emerge says that --root is supposed to do
everything emerge could do but somewhere else.
If I emerge locally a ebuild that makes a user, I expect emerge
--root=/target to also make users on /target filesystem.


-- 
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: 1592 bytes --]

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

* Re: [gentoo-embedded] emerge --root : users not created
  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
  1 sibling, 2 replies; 26+ messages in thread
From: Peter Stuge @ 2009-12-15 17:37 UTC (permalink / raw
  To: gentoo-embedded

Ahmed Ammar wrote:
> > > > so maybe it makes sense to simply have useradd call chroot()
> > > > according to an option early on in main()?
> > > 
> > > This will definitely not work on cross targets?
> > 
> > Why not? useradd doesn't fork, it does all file IO on it's own, so I
> > think it could work.
> 
> Well how exactly do you expect chroot to succeed when the host is
> x86 and the ${ROOT} is arm?

It should work fine. Note chroot() system call, not the chroot
utility.

useradd is a C program and my idea is to make it use the chroot()
system call. This system call changes the root directory for the
calling process. The chroot utility uses this system call, and then
executes a shell or other program inside the new root. The utility
will of course not work cross platform.

As long as the useradd C program does not rely on other executables
at runtime, which I severly doubt considering the nature of the
program, calling chroot() early in useradd would work regardless of
what binaries, if any, are inside the new root dir. useradd only
touches the user database text files.


//Peter



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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-15 17:37             ` Peter Stuge
@ 2009-12-15 22:24               ` Ahmed Ammar
  2009-12-21 21:25               ` Ahmed Ammar
  1 sibling, 0 replies; 26+ messages in thread
From: Ahmed Ammar @ 2009-12-15 22:24 UTC (permalink / raw
  To: gentoo-embedded

On Tue, 2009-12-15 at 18:37 +0100, Peter Stuge wrote:
> Ahmed Ammar wrote:
> > > > > so maybe it makes sense to simply have useradd call chroot()
> > > > > according to an option early on in main()?
> > > > 
> > > > This will definitely not work on cross targets?
> > > 
> > > Why not? useradd doesn't fork, it does all file IO on it's own, so I
> > > think it could work.
> > 
> > Well how exactly do you expect chroot to succeed when the host is
> > x86 and the ${ROOT} is arm?
> 
> It should work fine. Note chroot() system call, not the chroot
> utility.

Thanks for clearing that up, makes much more sense now. 

A.




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

* Re: [gentoo-embedded] emerge --root : users not created
  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
  1 sibling, 1 reply; 26+ messages in thread
From: Ahmed Ammar @ 2009-12-21 21:25 UTC (permalink / raw
  To: gentoo-embedded

On Tue, 2009-12-15 at 18:37 +0100, Peter Stuge wrote
> useradd is a C program and my idea is to make it use the chroot()
> system call. This system call changes the root directory for the
> calling process. The chroot utility uses this system call, and then
> executes a shell or other program inside the new root. The utility
> will of course not work cross platform.
> 
> As long as the useradd C program does not rely on other executables
> at runtime, which I severly doubt considering the nature of the
> program, calling chroot() early in useradd would work regardless of
> what binaries, if any, are inside the new root dir. useradd only
> touches the user database text files.

Sounds like a simple enough idea. Patches welcome? Might be worth
looking at how Gentoo Prefix does it first though.

A.





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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-21 21:25               ` Ahmed Ammar
@ 2009-12-21 21:29                 ` Ned Ludd
  2009-12-22 11:38                   ` Peter Stuge
  0 siblings, 1 reply; 26+ messages in thread
From: Ned Ludd @ 2009-12-21 21:29 UTC (permalink / raw
  To: gentoo-embedded

On Mon, 2009-12-21 at 23:25 +0200, Ahmed Ammar wrote:
> On Tue, 2009-12-15 at 18:37 +0100, Peter Stuge wrote
> > useradd is a C program and my idea is to make it use the chroot()
> > system call. This system call changes the root directory for the
> > calling process. The chroot utility uses this system call, and then
> > executes a shell or other program inside the new root. The utility
> > will of course not work cross platform.
> > 
> > As long as the useradd C program does not rely on other executables
> > at runtime, which I severly doubt considering the nature of the
> > program, calling chroot() early in useradd would work regardless of
> > what binaries, if any, are inside the new root dir. useradd only
> > touches the user database text files.
> 
> Sounds like a simple enough idea. Patches welcome? Might be worth
> looking at how Gentoo Prefix does it first though.


FYI. This is GLEP-0027 and it's not handled anywhere properly yet afaik.

http://www.gentoo.org/proj/en/glep/glep-0027.html


-- 
Ned Ludd <solar@gentoo.org>
Gentoo Linux




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

* Re: [gentoo-embedded] emerge --root : users not created
  2009-12-21 21:29                 ` Ned Ludd
@ 2009-12-22 11:38                   ` Peter Stuge
  0 siblings, 0 replies; 26+ messages in thread
From: Peter Stuge @ 2009-12-22 11:38 UTC (permalink / raw
  To: gentoo-embedded

Ned Ludd wrote:
> > > useradd is a C program and my idea is to make it use the chroot()
> > > system call.
> > 
> > Sounds like a simple enough idea. Patches welcome? Might be worth
> > looking at how Gentoo Prefix does it first though.

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.


> FYI. This is GLEP-0027 and it's not handled anywhere properly yet
> afaik.
> 
> http://www.gentoo.org/proj/en/glep/glep-0027.html

I like it, but how to deal with two packages that want the same
username but different settings such as home directory or shell?


//Peter



^ 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

* Re: [gentoo-embedded] emerge --root : users not created
  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-03-06  0:52 ` P. Levine
  2 siblings, 1 reply; 26+ messages in thread
From: Peter Stuge @ 2010-02-22 15:19 UTC (permalink / raw
  To: gentoo-embedded

P. Levine wrote:
> It seems absurd to add support for chroot() in useradd and groupadd
> without userdel and groupdel, so the patch includes support for them.

gpasswd has also been mentioned. Please check what
portage/eclass/eutils.eclass actually uses, or ideally add the flag
to all the shadow utilities?


> xfgetXXbyYY

Why is all that required? It's a mess. Please explain?


//Peter



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

* Re: [gentoo-embedded] emerge --root : users not created
  2010-02-22 15:19 ` Peter Stuge
@ 2010-02-22 18:44   ` P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-22 18:44 UTC (permalink / raw
  To: gentoo-embedded

On 02/22/2010 10:19 AM, Peter Stuge wrote:
> P. Levine wrote:
>> It seems absurd to add support for chroot() in useradd and groupadd
>> without userdel and groupdel, so the patch includes support for them.
> 
> gpasswd has also been mentioned. Please check what
> portage/eclass/eutils.eclass actually uses, or ideally add the flag
> to all the shadow utilities?

I did check the eclasses.  Eutils calls useradd and groupadd. The only
other mention of a shadow utility is games.eclass with:

ewarn "Just run 'gpasswd -a <USER> ${GAMES_GROUP}', then have <USER>
re-login."

I would consider patching all of shadow utilities to be ideal.  But I'm
not sure whether the shadow devs would.  I was under the impression this
was for useradd and groupadd (and, consequently, userdel and groupdel).
 I'll try to get a hold of them on IRC when I get a chance.

> 
>> xfgetXXbyYY
> 
> Why is all that required? It's a mess. Please explain?
> 
> 
> //Peter
> 
> 

From my previous post:

> 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.

There are numerous calls to libc functions such as getgrgid and getpwnam
by shadow's own xgetgrgid and xgetpwnam.  These are generated by files
containing macros, and at the bottom there's #include  xgetXXbyYY.c, a
file the does the macro expansion.  In the end, they generate wrapper
functions to initialize buffers, call the function, and duplicate and
return the struct.  xgetgrgid, for instance, calls getgrgid to search
the group database for a particular gid, and returns a pointer to the
group struct if it exists.  The problem is the databases are dynamically
preloaded and chroot() will not.  The only mention in the glibc manual
about forcing related functions to use a particular database method is
by calling __nss_configure_lookup.  Even if this did work with chroot()
it would be initializing databases from $ROOT/etc as the system
databases for the duration, which would be absurdly dangerous in a
system where other utils and libs could call on the same databases.

Glibc offers fgetXXent functions (fgetpwent, for example) which, simply,
sequentially return the next struct from a file stream supplied as the
argument.  There are no fgetgrgid or fgetpwnam functions.  My original
patch supplied those functions using its own xfgetXXbyYY.c and
associated macro files by recursively calling fgetXXent functions and
comparing the struct member to the argument.  But after looking at
userdel.c and groupdel.c, I saw that they made calls to setXXent,
getXXent, and endXXent functions (which use the system database) that
would have changed too many lines of their code if patched.  So I added
fsetXXent, fgetXXent, and fendXXent functions, and changed all the
others to, very simply, call on those.

The chroot.c file might seem like a mess but it's actually quite
organized, and if you cd to the patched source directory, configure, run
"gcc -E -I ./lib -I . -o chroot.expaded.c ./libmisc/chroot.c", and
scroll to the bottom of chroot.expaded.c, you'll see what functions
those macros expand to.



^ 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
@ 2010-02-23 16:58 ` Ned Ludd
  2010-02-24  2:01   ` P. Levine
  2010-03-06  0:52 ` P. Levine
  2 siblings, 1 reply; 26+ messages in thread
From: Ned Ludd @ 2010-02-23 16:58 UTC (permalink / raw
  To: gentoo-embedded

On Mon, 2010-02-22 at 09:41 -0500, P. Levine wrote:
> 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

This seems a major improvement over the previous from quickly glancing
over the code. In no time at all I'm sure you will be ready to re hit
upstream.

Q:
If the end user is using Linux-Pam on his/her host system and they run
this. It will ignore loading extra pam modules when entering the chroot?
Or do they flat out need to disable pam on the host so they can take
advantage of this for the chroot?




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

* Re: [gentoo-embedded] emerge --root : users not created
  2010-02-23 16:58 ` Ned Ludd
@ 2010-02-24  2:01   ` P. Levine
  0 siblings, 0 replies; 26+ messages in thread
From: P. Levine @ 2010-02-24  2:01 UTC (permalink / raw
  To: gentoo-embedded

On 02/23/2010 11:58 AM, Ned Ludd wrote:
> Q:
> If the end user is using Linux-Pam on his/her host system and they run
> this. It will ignore loading extra pam modules when entering the chroot?
> Or do they flat out need to disable pam on the host so they can take
> advantage of this for the chroot?

They don't need to disable pam for useradd, groupadd, etc... for the
same reason they don't for the chroot command.  PAM is for
authenticating users (attempting to ensure that a user who wants to use
a utility that he or she already has permission to, is indeed that
user).  Also keep in mind that all the chroot() function does is
(basically) prefix the path string in any functions (within the same
process or child processes) explicitly dealing with paths (fopen, for
example) with a sub path string (as if the process is changing the
directory to the chroot() path and then treating all absolute paths from
that point on as relative paths, and paths prefixed with "./" are
treated relative to the ACTUAL directory the process exists in , i.e.
the program was called from).

The chroot() function is privileged process with the  CAP_SYS_CHROOT
capability.  Even the chroot program which uses the chroot() function
doesn't check if you're root, if it should delegate such a capability on
a particular uid or call on PAM.  It assumes it is being run as root and
if chroot() fails it simply spits out "cannot change root directory to
...".  My patch at least checks if the uid is 0 and if not emits "must
be superuser for chroot access".

PAM processes are called by shadow (at least where I've encountered them
so far) to pre-authenticate a user (and only when both
--enable-account-tools-setuid and --with-libpam were enabled at
configure time) to use the particular utility.  That it would do so even
if the user was root seems to me a redundancy (since a system
would/should not allow root to grant or deny certain access rights
to/from itself).  When the program is called (suid bit enabled) PAM
takes care of the uid, euid discrepancy.  The chroot() process however,
shouldn't be called by a non-root user, period.  There are non-standard
modules on other systems to enable suid + chroot() + PAM authentication,
they are used to mimic the chroot utility (so a user can login to their
own subdirectory jail).  It seems to me it would be an unnecessary
security risk to allow a non-root user to authenticate to add/delete
users/groups from an offset directory.  This should be left to root
only.  Otherwise a dependency on pam_chroot or some other non-standard
module would have to be created, or a new module would have to be
written just for the --chroot option.

In short, using the --chroot option if compiled with --with-libpam is
fine so long as it's actually run as root.  As far as I know, emerge has
to be run as root (except for --pretend and searching).



^ 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
  2010-02-23 16:58 ` Ned Ludd
@ 2010-03-06  0:52 ` P. Levine
  2010-03-08 11:05   ` Ed W
  2 siblings, 1 reply; 26+ messages in thread
From: P. Levine @ 2010-03-06  0:52 UTC (permalink / raw
  To: gentoo-embedded

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

Attached is, hopefully, the final version of the chroot patch for
shadow.  In it, I've included chroot support for all relevant utilities.

Given that most of the utilities that use PAM do so for authentication
only, instead of disabling it when used with the --chroot flag,  I've
moved the relevant code to run before chroot is called.  It appears less
dependent on where it is called than I had first suspected.

The exception to this is passwd, chpasswd, and newusers which use PAM to
do the actual password encryption.  I've altered these to fall back to
using shadow functions (the default when not compiled with PAM support)
while using --chroot.  I'll admit it looks a little ugly, but it doesn't
seem like it can be helped.  I have tested these, and they work fine
(though before using the --chroot flag,  ideally, $ROOT/etc/login.defs
file should define the same encryption method as
$ROOT/etc/pam.d/system-auth).

Instead of having a whole lot of "if (chroot_flg)" tests scattered
throughout the source files, I've instead made ample use of the
"--wrap=" ldflag to wrap calls to pertinent libc functions into a
wrapper that checks if the chroot flag is set (still have to use the
"if (chroot_flg)" tests in passwd, chpasswd, and newusers, though).

Having examined how selinux is used in shadow, I had to disable its use
in useradd, userdel, and usermod when using chroot.  It calls on execve
after alteration of the database files, which as far as i can tell,
would fail.  And even if it was hacked to succeed, it would likely
either alter the build system or fail after trying load cross-compiled
libs.  In any event,  --chroot with selinux could only benefit a selinux
system cross-compiling a selinux system.

I've tested all related utilities with various arguments and found them
all functional, with and without the --chroot flag.

-- 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: 137239 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-03-05 06:13:29.000000000 -0500
@@ -68,6 +68,15 @@
 /* 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 `fgetspent_r' function. */
+#undef HAVE_FGETSPENT_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-03-05 06:13:29.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
+	fgetgrent_r fgetpwent_r fgetspent_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-03-05 06:13:29.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)
+	fgetgrent_r fgetpwent_r fgetspent_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/getdef.c shadow-4.1.4.2/lib/getdef.c
--- shadow-4.1.4.2.old/lib/getdef.c	2009-04-23 07:46:47.000000000 -0400
+++ shadow-4.1.4.2/lib/getdef.c	2010-03-05 06:13:29.000000000 -0500
@@ -74,8 +74,12 @@
 	{"MAIL_FILE", NULL},
 	{"MAX_MEMBERS_PER_GROUP", NULL},
 	{"MD5_CRYPT_ENAB", NULL},
+	{"PASS_ALWAYS_WARN", NULL},
+	{"PASS_CHANGE_TRIES", NULL},
 	{"PASS_MAX_DAYS", NULL},
+	{"PASS_MAX_LEN", NULL},
 	{"PASS_MIN_DAYS", NULL},
+	{"PASS_MIN_LEN", NULL},
 	{"PASS_WARN_AGE", NULL},
 #ifdef USE_SHA_CRYPT
 	{"SHA_CRYPT_MAX_ROUNDS", NULL},
@@ -111,10 +115,6 @@
 	{"MOTD_FILE", NULL},
 	{"NOLOGINS_FILE", NULL},
 	{"OBSCURE_CHECKS_ENAB", NULL},
-	{"PASS_ALWAYS_WARN", NULL},
-	{"PASS_CHANGE_TRIES", NULL},
-	{"PASS_MAX_LEN", NULL},
-	{"PASS_MIN_LEN", NULL},
 	{"PORTTIME_CHECKS_ENAB", NULL},
 	{"QUOTAS_ENAB", NULL},
 	{"SU_WHEEL_ONLY", NULL},
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-03-05 06:13:29.000000000 -0500
@@ -80,6 +80,26 @@
 /* chowntty.c */
 extern void chown_tty (const struct passwd *);
 
+/* chroot.c */
+extern void  __wrap_setpwent (void);
+extern struct passwd *__wrap_getpwent (void);
+extern void __wrap_endpwent (void);
+extern struct passwd *__wrap_getpwnam (const char *);
+extern struct passwd *__wrap_getpwuid (uid_t);
+extern struct group *__wrap_getgrnam (const char *);
+extern struct group *__wrap_getgrgid (gid_t);
+extern struct spwd *__wrap_getspnam (const char *);
+extern void  __real_setpwent (void);
+extern struct passwd *__real_getpwent (void);
+extern void __real_endpwent (void);
+extern struct passwd *__real_getpwnam (const char *);
+extern struct passwd *__real_getpwuid (uid_t);
+extern struct group *__real_getgrnam (const char *);
+extern struct group *__real_getgrgid (gid_t);
+extern struct spwd *__real_getspnam (const char *);
+extern bool chroot_flg;
+extern void process_chroot (char flag, int *argc, char **argv);
+
 /* cleanup.c */
 typedef void (*cleanup_function) (/*@null@*/void *arg);
 void add_cleanup (cleanup_function pcf, /*@null@*/void *arg);
@@ -246,9 +266,7 @@
 #endif				/* USE_PAM */
 
 /* obscure.c */
-#ifndef USE_PAM
 extern int obscure (const char *, const char *, const struct passwd *);
-#endif
 
 /* pam_pass.c */
 #ifdef USE_PAM
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-03-05 06:13: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	2009-07-23 21:15:59.000000000 -0400
+++ shadow-4.1.4.2/libmisc/Makefile.in	2010-03-05 06:13:29.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-03-05 16:08:44.000000000 -0500
@@ -0,0 +1,568 @@
+/*
+ * 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: Headers for macro generation -->
+ *
+ * RECURS_FUNC macro sets file recursion.
+ * All macros and headers for generated functions
+ * should be placed here.
+*/
+#ifndef RECURS_FUNC
+#define RECURS_FUNC
+
+#include <config.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "prototypes.h"
+#include "pwio.h"
+#include "groupio.h"
+
+#endif
+/*
+ * <-- Headers for macro generation :END
+ */
+
+
+/* START: Generate functions -->
+ *
+ * RECURS_FUNC_foo sets the current function to generate.
+ * If foo is XXYY (such as grnam) it generates an associated
+ * __wrap_getXXbyYY function (such as __wrap_getgrnam). If foo is XX
+ * (pw, for instance), it generates associated __wrap_setXXent,
+ * __wrap_getXXent, and __wrap_endXXent functions (xfsetpwent,
+ * xfgetpwent, and xfendpwent, for instance) used by
+ * the xfgetXXbyYY functions.
+ */
+#if ! defined RECURS_FUNC_pw
+#define RECURS_FUNC_pw
+#define RECURS_pw
+#elif ! defined RECURS_FUNC_pwnam
+#define RECURS_FUNC_pwnam
+#define RECURS_pwnam
+#elif ! defined RECURS_FUNC_pwuid
+#define RECURS_FUNC_pwuid
+#define RECURS_pwuid
+/* Newer versions of Linux libc already have shadow support.  */
+#elif (! defined RECURS_FUNC_sp) && (defined HAVE_GETSPNAM)
+#define RECURS_FUNC_sp
+#define RECURS_sp
+#elif (! defined RECURS_FUNC_spnam) && (defined HAVE_GETSPNAM)
+#define RECURS_FUNC_spnam
+#define RECURS_spnam
+#elif ! defined RECURS_FUNC_gr
+#define RECURS_FUNC_gr
+#define RECURS_gr
+#elif ! defined RECURS_FUNC_grnam
+#define RECURS_FUNC_grnam
+#define RECURS_grnam
+#else
+#define RECURS_grgid
+#undef RECURS_FUNC
+#endif
+
+
+/* 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	getpwnam
+#endif
+
+#ifdef RECURS_pwuid
+#undef RECURS_pwuid
+#define RECURS_pw
+#define RECURS_id
+#define FUNCTION_NAME	getpwuid
+#endif
+
+#ifdef RECURS_grnam
+#undef RECURS_grnam
+#define RECURS_gr
+#define RECURS_name
+#define FUNCTION_NAME	getgrnam
+#endif
+
+#ifdef RECURS_grgid
+#undef RECURS_grgid
+#define RECURS_gr
+#define RECURS_id
+#define FUNCTION_NAME	getgrgid
+#endif
+
+#ifdef RECURS_spnam
+#undef RECURS_spnam
+#define RECURS_sp
+#define RECURS_name
+#define FUNCTION_NAME	getspnam
+#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 *
+#ifdef RECURS_sp
+#define ARG_NAME	namp
+#else /* !RECURS_sp */
+#define ARG_NAME	name
+#endif
+#endif /* RECURS_name */
+
+#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 /* RECURS_gr */
+#ifdef RECURS_pw
+#define ARG_TYPE	uid_t
+#define ARG_NAME	uid
+#endif /* RECURS_pw */
+#endif /* RECURS_id */
+/*
+ * <-- 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, WRAP_TO_SYS
+ * for conditionally wrapping libc functions,
+ * and CLEAN_XX which undefs them before the next recursion.
+ */
+#ifdef RECURS_pw
+#undef RECURS_pw
+#define CLEAN_XX
+#define WRAP_TO_SYS
+#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
+
+#ifdef RECURS_sp
+#undef RECURS_sp
+#define CLEAN_XX
+#define LOOKUP_TYPE	struct spwd
+#define DB_FILE SHADOW_FILE
+#define DUP_FUNCTION	__spw_dup
+#define HAVE_FUNCTION_R (defined HAVE_FGETSPENT_R)
+#define DB_TYPE	sp
+#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).
+ */
+
+/* F_COMPARE (i.e., !strcmp(result->pw_name,name)) */
+#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)
+
+/* getXXent, setXXent, endXXent */
+#define FUNCTION_GETENT_NAME FUNCTION_GETENT_NAME1 (DB_TYPE)
+#define FUNCTION_GETENT_NAME1(name) FUNCTION_GETENT_NAME2 (name)
+#define FUNCTION_GETENT_NAME2(name) get##name##ent
+#define FUNCTION_SETENT_NAME FUNCTION_SETENT_NAME1 (DB_TYPE)
+#define FUNCTION_SETENT_NAME1(name) FUNCTION_SETENT_NAME2 (name)
+#define FUNCTION_SETENT_NAME2(name) set##name##ent
+#define FUNCTION_ENDENT_NAME FUNCTION_ENDENT_NAME1 (DB_TYPE)
+#define FUNCTION_ENDENT_NAME1(name) FUNCTION_ENDENT_NAME2 (name)
+#define FUNCTION_ENDENT_NAME2(name) end##name##ent
+
+/* fgetXXent, fgetXXent_r */
+#define FUNCTION_FGETENT_NAME	FUNCTION_FGETENT_NAME1 (FUNCTION_GETENT_NAME)
+#define FUNCTION_FGETENT_NAME1(name) FUNCTION_FGETENT_NAME2 (name)
+#define FUNCTION_FGETENT_NAME2(name) f##name
+#define FUNCTION_REENTRANT_NAME FUNCTION_REENTRANT_NAME1 (FUNCTION_FGETENT_NAME)
+#define FUNCTION_REENTRANT_NAME1(name) FUNCTION_REENTRANT_NAME2 (name)
+#define FUNCTION_REENTRANT_NAME2(name) name##_r
+
+/* __wrap_getXXbyYY, __wrap_getXXent, __wrap_setXXent, __wrap_endXXent */
+#define FUNCTION_XXYY_NAME FUNCTION_REAL (FUNCTION_NAME)
+#define FUNCTION_GETENT_DUMMY FUNCTION_REAL (FUNCTION_GETENT_NAME)
+#define FUNCTION_SETENT_DUMMY FUNCTION_REAL (FUNCTION_SETENT_NAME)
+#define FUNCTION_ENDENT_DUMMY FUNCTION_REAL (FUNCTION_ENDENT_NAME)
+#define FUNCTION_REAL(name) FUNCTION_REAL1 (name)
+#define FUNCTION_REAL1(name) __wrap_##name
+
+/* __real_getXXbyYY, __real_getXXent, __real_setXXent, __real_endXXent */
+#define SYSTEM_XXYY_NAME FUNCTION_WRAP (FUNCTION_NAME)
+#define SYSTEM_GETENT_NAME FUNCTION_WRAP (FUNCTION_GETENT_NAME)
+#define SYSTEM_SETENT_NAME FUNCTION_WRAP (FUNCTION_SETENT_NAME)
+#define SYSTEM_ENDENT_NAME FUNCTION_WRAP (FUNCTION_ENDENT_NAME)
+#define FUNCTION_WRAP(name) FUNCTION_WRAP1 (name)
+#define FUNCTION_WRAP1(name) __real_##name
+
+/* stream_XX */
+#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 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 /* __wrap_getXXbyYY function */
+LOOKUP_TYPE *FUNCTION_XXYY_NAME (ARG_TYPE ARG_NAME)
+
+#else /* __wrap_fooXXent function */
+static FILE * DB_STREAM_NAME;
+
+void FUNCTION_ENDENT_DUMMY (void)
+{
+#ifdef WRAP_TO_SYS
+	if (!chroot_flg)
+		SYSTEM_ENDENT_NAME ();
+#endif /* WRAP_TO_SYS */
+	if (DB_STREAM_NAME) {
+		(void) fclose(DB_STREAM_NAME);
+		DB_STREAM_NAME = (FILE *) 0;
+	}
+}
+
+void FUNCTION_SETENT_DUMMY (void)
+{
+#ifdef WRAP_TO_SYS
+	if (!chroot_flg)
+		SYSTEM_SETENT_NAME ();
+#endif /* WRAP_TO_SYS */
+	if (DB_STREAM_NAME) {
+		rewind (DB_STREAM_NAME);
+	} else {
+		DB_STREAM_NAME = fopen (DB_FILE, "r");
+	}
+}
+
+LOOKUP_TYPE *FUNCTION_GETENT_DUMMY (void)
+#endif
+{
+#ifdef FUNCTION_NAME /* __wrap_getXXbyYY function */
+	if (!chroot_flg)
+		return SYSTEM_XXYY_NAME (ARG_NAME);
+	FUNCTION_SETENT_DUMMY();
+	LOOKUP_TYPE *result = FUNCTION_GETENT_DUMMY();
+
+	while (result) {
+		if (F_COMPARE) {
+			break;
+		} else {
+			free(result);
+			result = FUNCTION_GETENT_DUMMY();
+		}
+	}
+
+	FUNCTION_ENDENT_DUMMY();
+	return result;
+#else /* __wrap_fooXXent function */
+#ifdef WRAP_TO_SYS
+	if (!chroot_flg)
+		return SYSTEM_GETENT_NAME ();
+#endif /* WRAP_TO_SYS */
+	if (!DB_STREAM_NAME) {
+		FUNCTION_SETENT_DUMMY();
+		if (!DB_STREAM_NAME) {
+			fprintf (stderr, _("%s: Error accessing the database file.\n"),
+    		 	    STRINGIZE(FUNCTION_GETENT_DUMMY));
+			exit (1);
+		}
+	}
+#if HAVE_FUNCTION_R /* __wrap_fooXXent function && 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"),
+		         STRINGIZE(FUNCTION_GETENT_DUMMY));
+		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"),
+			         STRINGIZE(FUNCTION_GETENT_DUMMY));
+			exit (13);
+		}
+		errno = 0;
+		status = FUNCTION_REENTRANT_NAME(DB_STREAM_NAME, result, buffer,
+				                        length, &resbuf);
+		if ((0 == status) && (resbuf == result)) {
+			LOOKUP_TYPE *ret_result = DUP_FUNCTION(result);
+			free (buffer);
+			free (result);
+			return ret_result;
+		}
+
+		if (ERANGE != errno) {
+			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;
+		}
+	}
+
+	free (buffer);
+	free (result);
+	return NULL;
+#else /* __wrap_fooXXent function && !HAVE_FUNCTION_R */
+	LOOKUP_TYPE *result = FUNCTION_FGETENT_NAME (DB_STREAM_NAME);
+
+	if (result) {
+		result = DUP_FUNCTION (result);
+		if (NULL == result) {
+			fprintf (stderr, _("%s: out of memory\n"),
+			         STRINGIZE(FUNCTION_GETENT_DUMMY));
+			exit (13);
+		}
+	}
+
+	return result;
+#endif /* __wrap_fooXXent function */
+#endif
+}
+/*
+ * <-- 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 /* CLEAN_YY */
+
+#ifdef CLEAN_XX
+#undef CLEAN_XX
+#ifdef WRAP_TO_SYS
+#undef WRAP_TO_SYS
+#endif /* WRAP_TO_SYS */
+#undef LOOKUP_TYPE
+#undef DB_TYPE
+#undef DB_FILE
+#undef DUP_FUNCTION
+#undef HAVE_FUNCTION_R
+#endif /* CLEAN_XX */
+/*
+ * <-- Pre-recursion macros :END
+ */
+
+
+#ifdef RECURS_FUNC
+#include __FILE__
+#endif
+/*
+ * <-- Generate functions :END
+ */
+
+
+/* START: Define functions -->
+ *
+ * All non-generated functions and pertinent macros
+ * are defined here.
+ */
+#ifndef RECURS_FUNC
+#define RECURS_FUNC
+
+#include <getopt.h>
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
+bool chroot_flg = false;
+
+/*
+ * 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.
+ */
+
+void process_chroot (char flag, int *argc, char **argv)
+{
+	int c;
+	/* Don't emit an error message for undefined options */
+	opterr = 0;
+	char *progname;
+	/* Don't change the position of the arguments in argv */
+	/* and check for the required option argument */
+	char opt_str_chroot[] = "-:R:";
+	struct option long_option_chroot[] = {
+		{"chroot", required_argument, NULL, 'R'},
+		{NULL, 0, NULL, '\0'}
+	};
+	/* Program's chroot short option isn't -R */
+	if (flag != 'R') {
+		opt_str_chroot[2] =  flag;
+		long_option_chroot[0].val = (int) flag;
+	}
+	/* Define the program name since the Prog var */
+	/* isn't always locally defined */
+	progname = ((progname = strrchr (*argv, '/')) ? progname + 1 : *argv);
+	while ((c = getopt_long (*argc, argv,
+	                         opt_str_chroot,
+	                         long_option_chroot, NULL)) != -1) {
+		if (c == flag) {
+			if (getuid()) {
+				fprintf (stderr,
+				         _("%s: must be superuser for chroot access\n"),
+				         progname);
+				exit (1);
+			}
+			if ((!VALID(optarg))
+			    || (optarg[0] != '/')) {
+				fprintf (stderr,
+				         _("%s: invalid root directory '%s'\n"),
+				         progname, optarg);
+				exit (E_BAD_ARG);
+			}
+			if ( chroot(optarg) != 0 ) {
+				fprintf (stderr,
+				         _("%s: unable to change root directory to '%s'\n"),
+				         progname, optarg);
+				exit (1);
+			}
+			/* Erase chroot arguments for next call to getopt_long */
+			do {
+				argv[optind - 2] = argv[optind];
+				optind++;
+			} while (*argc - optind + 2);
+			*argc = *argc - 2;
+			chroot_flg = true;
+			break;
+		/* chroot was the last option and without an argument */
+		} else if (c == ':') {
+			fprintf (stderr,
+			         _("%s: chroot option requires an argument\n"),
+			         progname);
+			exit (E_USAGE);
+		}
+	}
+	/* Reset the state of getopt_long */
+	optind = 0;
+	opterr = 1;
+}
+
+#endif /* !RECURS_FUNC */
+/*
+ * <-- Define functions :END
+ */
diff -Naur shadow-4.1.4.2.old/libmisc/obscure.c shadow-4.1.4.2/libmisc/obscure.c
--- shadow-4.1.4.2.old/libmisc/obscure.c	2009-04-24 19:04:30.000000000 -0400
+++ shadow-4.1.4.2/libmisc/obscure.c	2010-03-05 06:13:29.000000000 -0500
@@ -32,8 +32,6 @@
 
 #include <config.h>
 
-#ifndef USE_PAM
-
 #ident "$Id: obscure.c 2791 2009-04-24 23:04:27Z nekral-guest $"
 
 
@@ -314,7 +312,3 @@
 	}
 	return 1;
 }
-
-#else				/* !USE_PAM */
-extern int errno;		/* warning: ANSI C forbids an empty source file */
-#endif				/* !USE_PAM */
diff -Naur shadow-4.1.4.2.old/libmisc/pwd2spwd.c shadow-4.1.4.2/libmisc/pwd2spwd.c
--- shadow-4.1.4.2.old/libmisc/pwd2spwd.c	2009-04-05 18:29:44.000000000 -0400
+++ shadow-4.1.4.2/libmisc/pwd2spwd.c	2010-03-05 06:13:29.000000000 -0500
@@ -34,8 +34,6 @@
 
 #ident "$Id: pwd2spwd.c 2595 2009-04-05 22:29:42Z nekral-guest $"
 
-#ifndef USE_PAM
-
 #include <sys/types.h>
 #include "prototypes.h"
 #include "defines.h"
@@ -85,7 +83,3 @@
 
 	return &sp;
 }
-#else				/* USE_PAM */
-extern int errno;	/* warning: ANSI C forbids an empty source file */
-#endif				/* !USE_PAM */
-
diff -Naur shadow-4.1.4.2.old/libmisc/xgetXXbyYY.c shadow-4.1.4.2/libmisc/xgetXXbyYY.c
--- shadow-4.1.4.2.old/libmisc/xgetXXbyYY.c	2009-06-11 17:21:17.000000000 -0400
+++ shadow-4.1.4.2/libmisc/xgetXXbyYY.c	2010-03-05 06:13:29.000000000 -0500
@@ -64,8 +64,15 @@
 #define STRINGIZE(name) STRINGIZE1 (name)
 #define STRINGIZE1(name) #name
 
+
+#define CHROOT_FUNCTION_NAME FUNCTION_REAL (FUNCTION_NAME)
+#define FUNCTION_REAL(name) FUNCTION_REAL1 (name)
+#define FUNCTION_REAL1(name) __wrap_##name
+
 /*@null@*/ /*@only@*/LOOKUP_TYPE *XFUNCTION_NAME (ARG_TYPE ARG_NAME)
 {
+	if (chroot_flg)
+		return CHROOT_FUNCTION_NAME (ARG_NAME);
 #if HAVE_FUNCTION_R
 	LOOKUP_TYPE *result=NULL;
 	char *buffer=NULL;
diff -Naur shadow-4.1.4.2.old/man/chage.1 shadow-4.1.4.2/man/chage.1
--- shadow-4.1.4.2.old/man/chage.1	2009-07-23 21:16:15.000000000 -0400
+++ shadow-4.1.4.2/man/chage.1	2010-03-05 06:13:29.000000000 -0500
@@ -96,6 +96,18 @@
 will remove checking a password\'s validity\&.
 .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 changing password expiry information from
+/etc/shadow, information would be changed from
+\fBROOT_DIR\fR/etc/shadow\&.
+.RE
+.PP
 \fB\-W\fR, \fB\-\-warndays\fR \fIWARN_DAYS\fR
 .RS 4
 Set the number of days of warning before a password change is required\&. The
diff -Naur shadow-4.1.4.2.old/man/chage.1.xml shadow-4.1.4.2/man/chage.1.xml
--- shadow-4.1.4.2.old/man/chage.1.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/chage.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -169,6 +169,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 changing password expiry information from
+	    <filename>/etc/shadow</filename>, information would be changed from
+	    <filename><option>ROOT_DIR</option>/etc/shadow</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>
 	  <option>-W</option>, <option>--warndays</option> <replaceable>WARN_DAYS</replaceable>
 	</term>
 	<listitem>
diff -Naur shadow-4.1.4.2.old/man/chfn.1 shadow-4.1.4.2/man/chfn.1
--- shadow-4.1.4.2.old/man/chfn.1	2009-07-23 21:16:16.000000000 -0400
+++ shadow-4.1.4.2/man/chfn.1	2010-03-05 06:13:29.000000000 -0500
@@ -22,7 +22,7 @@
 chfn \- change real user name and information
 .SH "SYNOPSIS"
 .HP \w'\fBchfn\fR\ 'u
-\fBchfn\fR [\-f\ \fIfull_name\fR] [\-r\ \fIroom_no\fR] [\-w\ \fIwork_ph\fR] [\-h\ \fIhome_ph\fR] [\-o\ \fIother\fR] [\fIuser\fR]
+\fBchfn\fR [\-f\ \fIfull_name\fR] [\-r\ \fIroom_no\fR] [\-w\ \fIwork_ph\fR] [\-h\ \fIhome_ph\fR] [\-o\ \fIother\fR] [\-R\ \fIroot_dir\fR] [\fIuser\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -47,6 +47,18 @@
 marks\&. Without options,
 \fBchfn\fR
 prompts for the current user account\&.
+.PP
+If the
+\fB\-R\fR
+or
+\fB\-\-chroot\fR
+option is used, the paths to all relevant files will be prefixed with the argument\&. For instance, instead of changing the information of
+/etc/passwd,
+\fB\-R\fR
+\fIroot_dir\fR
+would cause information on
+\fIroot_dir\fR/etc/passwd
+to be changed instead\&.
 .SH "CONFIGURATION"
 .PP
 The following configuration variables in
diff -Naur shadow-4.1.4.2.old/man/chfn.1.xml shadow-4.1.4.2/man/chfn.1.xml
--- shadow-4.1.4.2.old/man/chfn.1.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/chfn.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -55,6 +55,7 @@
       <arg choice='opt'>-w <replaceable>work_ph</replaceable></arg>
       <arg choice='opt'>-h <replaceable>home_ph</replaceable></arg>
       <arg choice='opt'>-o <replaceable>other</replaceable></arg>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
       <arg choice='opt'><replaceable>user</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -93,6 +94,15 @@
       ]</emphasis> marks. Without options, <command>chfn</command>
       prompts for the current user account.
     </para>
+
+    <para>
+      If the <option>-R</option> or <option>--chroot</option> option is used,
+      the paths to all relevant files will be prefixed with the argument.
+      For instance, instead of changing the information of <filename>/etc/passwd</filename>,
+      <option>-R</option> <emphasis remap='I'>root_dir</emphasis> would cause information
+      on <emphasis remap='I'>root_dir</emphasis><filename>/etc/passwd</filename> to be
+      changed instead.
+    </para>
   </refsect1>
 
   <refsect1 id='configuration'>
diff -Naur shadow-4.1.4.2.old/man/chgpasswd.8 shadow-4.1.4.2/man/chgpasswd.8
--- shadow-4.1.4.2.old/man/chgpasswd.8	2009-07-23 21:16:17.000000000 -0400
+++ shadow-4.1.4.2/man/chgpasswd.8	2010-03-05 06:13:29.000000000 -0500
@@ -71,6 +71,18 @@
 Use MD5 encryption instead of DES when the supplied passwords are not encrypted\&.
 .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 updating passwords in
+/etc/gshadow, passwords would be updated in
+\fBROOT_DIR\fR/etc/gshadow\&.
+.RE
+.PP
 \fB\-s\fR, \fB\-\-sha\-rounds\fR
 .RS 4
 Use the specified number of rounds to encrypt the passwords\&.
diff -Naur shadow-4.1.4.2.old/man/chgpasswd.8.xml shadow-4.1.4.2/man/chgpasswd.8.xml
--- shadow-4.1.4.2.old/man/chgpasswd.8.xml	2009-05-19 17:29:27.000000000 -0400
+++ shadow-4.1.4.2/man/chgpasswd.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -125,6 +125,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 updating passwords in
+	    <filename>/etc/gshadow</filename>, passwords would be updated in
+	    <filename><option>ROOT_DIR</option>/etc/gshadow</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
       <varlistentry condition="sha_crypt">
 	<term><option>-s</option>, <option>--sha-rounds</option></term>
 	<listitem>
diff -Naur shadow-4.1.4.2.old/man/chpasswd.8 shadow-4.1.4.2/man/chpasswd.8
--- shadow-4.1.4.2.old/man/chpasswd.8	2009-07-23 21:16:18.000000000 -0400
+++ shadow-4.1.4.2/man/chpasswd.8	2010-03-05 06:13:29.000000000 -0500
@@ -33,6 +33,20 @@
 \fIuser_name\fR:\fIpassword\fR
 .SS ""
 .PP
+The supplied passwords must be in clear\-text\&.
+.PP
+PAM is used to update the password in the system database according to the PAM chpasswd configuration\&.
+.PP
+When
+\fBchpasswd\fR
+fails to update a password, it continues updating the passwords of the next users, and will return an error code on exit\&.
+.PP
+If the
+\fB\-R\fR
+option is supplied, PAM is not used and the following conditions apply and pertain to the system offset from the
+\fIROOT_DIR\fR
+path:
+.PP
 By default the supplied password must be in clear\-text, and is encrypted by
 \fBchpasswd\fR\&. Also the password age will be updated, if present\&.
 .PP
@@ -56,14 +70,18 @@
 .PP
 \fB\-c\fR, \fB\-\-crypt\-method\fR
 .RS 4
-Use the specified method to encrypt the passwords\&.
+While using the
+\fB\-R\fR
+option, use the specified method to encrypt the passwords\&.
 .sp
 The available methods are DES, MD5, NONE, and SHA256 or SHA512 if your libc support these methods\&.
 .RE
 .PP
 \fB\-e\fR, \fB\-\-encrypted\fR
 .RS 4
-Supplied passwords are in encrypted form\&.
+While using the
+\fB\-R\fR
+option, supplied passwords are in encrypted form\&.
 .RE
 .PP
 \fB\-h\fR, \fB\-\-help\fR
@@ -73,12 +91,28 @@
 .PP
 \fB\-m\fR, \fB\-\-md5\fR
 .RS 4
-Use MD5 encryption instead of DES when the supplied passwords are not encrypted\&.
+While using the
+\fB\-R\fR
+option, use MD5 encryption instead of DES when the supplied passwords are not encrypted\&.
+.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 updating passwords in
+/etc/shadow, passwords would be updated in
+\fBROOT_DIR\fR/etc/shadow\&.
 .RE
 .PP
 \fB\-s\fR, \fB\-\-sha\-rounds\fR
 .RS 4
-Use the specified number of rounds to encrypt the passwords\&.
+While using the
+\fB\-R\fR
+option, use the specified number of rounds to encrypt the passwords\&.
 .sp
 The value 0 means that the system will choose the default number of rounds for the crypt method (5000)\&.
 .sp
@@ -93,11 +127,15 @@
 .PP
 Remember to set permissions or umask to prevent readability of unencrypted files by other users\&.
 .PP
-You should make sure the passwords and the encryption method respect the system\'s password policy\&.
+You should make sure that, while using the
+\fB\-R\fR
+option, the passwords and the encryption method respect the password policy of the offset system\&.
 .SH "CONFIGURATION"
 .PP
-The following configuration variables in
-/etc/login\&.defs
+While using the
+\fB\-R\fR
+option, the following configuration variables in
+\fBROOT_DIR\fR/etc/login\&.defs
 change the behavior of this tool:
 .PP
 \fBENCRYPT_METHOD\fR (string)
@@ -155,6 +193,8 @@
 Note: this parameter overrides the
 \fBMD5_CRYPT_ENAB\fR
 variable\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBMD5_CRYPT_ENAB\fR (boolean)
@@ -171,6 +211,8 @@
 .sp
 This variable is deprecated\&. You should use
 \fBENCRYPT_METHOD\fR\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBSHA_CRYPT_MIN_ROUNDS\fR (number), \fBSHA_CRYPT_MAX_ROUNDS\fR (number)
@@ -198,6 +240,8 @@
 \fBSHA_CRYPT_MIN_ROUNDS\fR
 >
 \fBSHA_CRYPT_MAX_ROUNDS\fR, the highest value will be used\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .SH "FILES"
 .PP
@@ -215,6 +259,12 @@
 .RS 4
 Shadow password suite configuration\&.
 .RE
+.PP
+/etc/pam\&.d/chpasswd
+.RS 4
+PAM configuration for
+\fBchpasswd\fR\&.
+.RE
 .SH "SEE ALSO"
 .PP
 
diff -Naur shadow-4.1.4.2.old/man/chpasswd.8.xml shadow-4.1.4.2/man/chpasswd.8.xml
--- shadow-4.1.4.2.old/man/chpasswd.8.xml	2009-05-25 15:41:47.000000000 -0400
+++ shadow-4.1.4.2/man/chpasswd.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -67,7 +67,25 @@
       <emphasis remap='I'>user_name</emphasis>:<emphasis
       remap='I'>password</emphasis>
     </para>
-    <refsect2 condition="no_pam">
+    <refsect2 condition="pam">
+      <para>
+	The supplied passwords must be in clear-text.
+      </para>
+      <para>
+	PAM is used to update the password in the system database
+	according to the PAM chpasswd configuration.
+      </para>
+      <para>
+	When <command>chpasswd</command> fails to update a password, it
+	continues updating the passwords of the next users, and will
+	return an error code on exit.
+      </para>
+      <para>
+    If the <option>-R</option> option is supplied, PAM is not used
+    and the following conditions apply and pertain to the system
+    offset from the <replaceable>ROOT_DIR</replaceable> path:
+      </para>
+    </refsect2>
     <para>
       By default the supplied password must be in clear-text, and is
       encrypted by <command>chpasswd</command>.
@@ -84,21 +102,6 @@
 	and then commit all the changes to disk if no errors occured for
 	any users.
       </para>
-    </refsect2>
-    <refsect2 condition="pam">
-      <para>
-	The supplied passwords must be in clear-text.
-      </para>
-      <para>
-	PAM is used to update the password in the system database
-	according to the PAM chpasswd configuration.
-      </para>
-      <para>
-	When <command>chpasswd</command> fails to update a password, it
-	continues updating the passwords of the next users, and will
-	return an error code on exit.
-      </para>
-    </refsect2>
     <para>
       This command is intended to be used in a large system environment
       where many accounts are created at a single time.
@@ -111,11 +114,17 @@
       The options which apply to the <command>chpasswd</command> command
       are:
     </para>
-    <variablelist remap='IP' condition="no_pam">
+    <variablelist remap='IP'>
       <varlistentry>
 	<term><option>-c</option>, <option>--crypt-method</option></term>
 	<listitem>
-	  <para>Use the specified method to encrypt the passwords.</para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    use the specified method to encrypt the passwords.
+	  </para>
+	  <para condition="no_pam">
+	    Use the specified method to encrypt the passwords.
+	  </para>
 	  <para condition="no_sha_crypt">
 	    The available methods are DES, MD5, and NONE.
 	  </para>
@@ -128,32 +137,63 @@
       <varlistentry>
 	<term><option>-e</option>, <option>--encrypted</option></term>
 	<listitem>
-	  <para>Supplied passwords are in encrypted form.</para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    supplied passwords are in encrypted form.
+	  </para>
+	  <para condition="no_pam">
+	    Supplied passwords are in encrypted form.
+	  </para>
 	</listitem>
       </varlistentry>
-    </variablelist>
-    <variablelist remap='IP'>
       <varlistentry>
 	<term><option>-h</option>, <option>--help</option></term>
 	<listitem>
 	  <para>Display help message and exit.</para>
 	</listitem>
       </varlistentry>
-    </variablelist>
-    <variablelist remap='IP' condition="no_pam">
       <varlistentry>
 	<term><option>-m</option>, <option>--md5</option></term>
 	<listitem>
-	  <para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    use MD5 encryption instead of DES when the supplied passwords are
+	    not encrypted.
+	  </para>
+	  <para condition="no_pam">
 	    Use MD5 encryption instead of DES when the supplied passwords are
 	    not encrypted.
 	  </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 updating passwords in
+	    <filename>/etc/shadow</filename>, passwords would be updated in
+	    <filename><option>ROOT_DIR</option>/etc/shadow</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
       <varlistentry condition="sha_crypt">
 	<term><option>-s</option>, <option>--sha-rounds</option></term>
 	<listitem>
-	  <para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    use the specified number of rounds to encrypt the passwords.
+	  </para>
+	  <para condition="no_pam">
 	    Use the specified number of rounds to encrypt the passwords.
 	  </para>
 	  <para>
@@ -184,15 +224,26 @@
       Remember to set permissions or umask to prevent readability of
       unencrypted files by other users.
     </para>
+    <para condition="pam">
+      You should make sure that, while using the <option>-R</option> option,
+      the passwords and the encryption method respect the password policy
+      of the offset system.
+    </para>
     <para condition="no_pam">
       You should make sure the passwords and the encryption method respect
       the system's password policy.
     </para>
   </refsect1>
 
-  <refsect1 id='configuration' condition="no_pam">
+  <refsect1 id='configuration'>
     <title>CONFIGURATION</title>
-    <para>
+    <para condition="pam">
+      While using the <option>-R</option> option, the following
+      configuration variables in
+      <filename><option>ROOT_DIR</option>/etc/login.defs</filename>
+      change the behavior of this tool:
+    </para>
+    <para condition="no_pam">
       The following configuration variables in
       <filename>/etc/login.defs</filename> change the behavior of this
       tool:
@@ -207,19 +258,19 @@
   <refsect1 id='files'>
     <title>FILES</title>
     <variablelist>
-      <varlistentry condition="no_pam">
+      <varlistentry>
 	<term><filename>/etc/passwd</filename></term>
 	<listitem>
 	  <para>User account information.</para>
 	</listitem>
       </varlistentry>
-      <varlistentry condition="no_pam">
+      <varlistentry>
 	<term><filename>/etc/shadow</filename></term>
 	<listitem>
 	  <para>Secure user account information.</para>
 	</listitem>
       </varlistentry>
-      <varlistentry condition="no_pam">
+      <varlistentry>
 	<term><filename>/etc/login.defs</filename></term>
 	<listitem>
 	  <para>Shadow password suite configuration.</para>
@@ -243,7 +294,7 @@
       <citerefentry>
 	<refentrytitle>newusers</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
-      <phrase condition="no_pam">
+      <phrase>
 	<citerefentry>
 	  <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
 	</citerefentry>,
diff -Naur shadow-4.1.4.2.old/man/chsh.1 shadow-4.1.4.2/man/chsh.1
--- shadow-4.1.4.2.old/man/chsh.1	2009-07-23 21:16:18.000000000 -0400
+++ shadow-4.1.4.2/man/chsh.1	2010-03-05 06:13:29.000000000 -0500
@@ -39,6 +39,18 @@
 Display help message and exit\&.
 .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 changing the shell in
+/etc/passwd, the shell would be changed in
+\fBROOT_DIR\fR/etc/passwd\&.
+.RE
+.PP
 \fB\-s\fR, \fB\-\-shell\fR \fISHELL\fR
 .RS 4
 The name of the user\'s new login shell\&. Setting this field to blank causes the system to select the default login shell\&.
diff -Naur shadow-4.1.4.2.old/man/chsh.1.xml shadow-4.1.4.2/man/chsh.1.xml
--- shadow-4.1.4.2.old/man/chsh.1.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/chsh.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -83,6 +83,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 changing the shell in
+	    <filename>/etc/passwd</filename>, the shell would be changed in
+	    <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>
 	  <option>-s</option>, <option>--shell</option> <replaceable>SHELL</replaceable>
 	</term>
         <listitem>                                                                                                                   
diff -Naur shadow-4.1.4.2.old/man/expiry.1 shadow-4.1.4.2/man/expiry.1
--- shadow-4.1.4.2.old/man/expiry.1	2009-07-23 21:16:19.000000000 -0400
+++ shadow-4.1.4.2/man/expiry.1	2010-03-05 06:13:29.000000000 -0500
@@ -22,12 +22,24 @@
 expiry \- check and enforce password expiration policy
 .SH "SYNOPSIS"
 .HP \w'\fBexpiry\fR\ 'u
-\fBexpiry\fR [\-c] [\-f]
+\fBexpiry\fR [\-c] [\-f] [\-R\ \fIroot_dir\fR]
 .SH "DESCRIPTION"
 .PP
 The
 \fBexpiry\fR
 command checks (\fB\-c\fR) the current password expiration and forces (\fB\-f\fR) changes when required\&. It is callable as a normal user command\&.
+.PP
+If the
+\fB\-R\fR
+or
+\fB\-\-chroot\fR
+option is used, the paths to all relevant files will be prefixed with the argument\&. For instance, instead of checking and changing the password expiration in
+/etc/shadow,
+\fB\-R\fR
+\fIroot_dir\fR
+would cause information in
+\fIroot_dir\fR/etc/shadow
+to be changed instead\&.
 .SH "FILES"
 .PP
 /etc/passwd
diff -Naur shadow-4.1.4.2.old/man/expiry.1.xml shadow-4.1.4.2/man/expiry.1.xml
--- shadow-4.1.4.2.old/man/expiry.1.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/expiry.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -52,6 +52,7 @@
       <command>expiry</command>
       <arg choice='opt'>-c </arg>
       <arg choice='opt'>-f </arg>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -62,6 +63,16 @@
       the current password expiration and forces (<option>-f</option>)
       changes when required. It is callable as a normal user command.
     </para>
+
+    <para>
+      If the <option>-R</option> or <option>--chroot</option> option is used,
+      the paths to all relevant files will be prefixed with the argument.
+      For instance, instead of checking and changing the password expiration in
+      <filename>/etc/shadow</filename>,
+      <option>-R</option> <emphasis remap='I'>root_dir</emphasis> would cause information
+      in <emphasis remap='I'>root_dir</emphasis><filename>/etc/shadow</filename> to be
+      changed instead.
+    </para>
   </refsect1>
 
   <refsect1 id='files'>
diff -Naur shadow-4.1.4.2.old/man/gpasswd.1 shadow-4.1.4.2/man/gpasswd.1
--- shadow-4.1.4.2.old/man/gpasswd.1	2009-07-23 21:16:23.000000000 -0400
+++ shadow-4.1.4.2/man/gpasswd.1	2010-03-05 06:13:29.000000000 -0500
@@ -54,10 +54,10 @@
 .SH "OPTIONS"
 .PP
 Except for the
-\fB\-A\fR
-and
-\fB\-M\fR
-options, the options cannot be combined\&.
+\fB\-c\fR
+option,
+or the \fB\-A\fR and \fB\-M\fR options,
+the options cannot be combined\&.
 .PP
 The options which apply to the
 \fBgpasswd\fR
@@ -71,6 +71,18 @@
 \fIgroup\fR\&.
 .RE
 .PP
+\fB\-c\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 administering groups from
+/etc/group, groups would be administered from
+\fBROOT_DIR\fR/etc/group\&.
+.RE
+.PP
 \fB\-d\fR, \fB\-\-delete\fR \fIuser\fR
 .RS 4
 Remove the
diff -Naur shadow-4.1.4.2.old/man/gpasswd.1.xml shadow-4.1.4.2/man/gpasswd.1.xml
--- shadow-4.1.4.2.old/man/gpasswd.1.xml	2009-05-26 15:23:42.000000000 -0400
+++ shadow-4.1.4.2/man/gpasswd.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -110,13 +110,13 @@
 
   <refsect1 id='options'>
     <title>OPTIONS</title>
-    <para condition="gshadow">
-      Except for the <option>-A</option> and <option>-M</option> options,
+    <para>
+      Except for the <option>-c</option> option,
+    <phrase condition="gshadow">
+      or the <option>-A</option> and <option>-M</option> options,
+    </phrase>
       the options cannot be combined.
     </para>
-    <para condition="no_gshadow">
-      The options cannot be combined.
-    </para>
     <para>
       The options which apply to the <command>gpasswd</command> command are:
     </para>
@@ -132,6 +132,28 @@
 	  </para>
 	</listitem>
       </varlistentry>
+    </variablelist>
+    <variablelist remap='IP'>
+      <varlistentry>
+	<term>
+	  <option>-c</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 administering groups from
+	    <filename>/etc/group</filename>, groups would be administered from
+	    <filename><option>ROOT_DIR</option>/etc/group</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
     </variablelist>
     <variablelist remap='IP'>
       <varlistentry>
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-03-05 06:13:29.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-03-05 06:13:29.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	2009-07-23 21:16:25.000000000 -0400
+++ shadow-4.1.4.2/man/groupdel.8	2010-03-05 06:13:29.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-03-05 06:13:29.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/groupmod.8 shadow-4.1.4.2/man/groupmod.8
--- shadow-4.1.4.2.old/man/groupmod.8	2009-07-23 21:16:27.000000000 -0400
+++ shadow-4.1.4.2/man/groupmod.8	2010-03-05 06:13:29.000000000 -0500
@@ -87,6 +87,18 @@
 .sp
 You should make sure the password respects the system\'s password policy\&.
 .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 modifying a group from
+/etc/group, the group would be modified from
+\fBROOT_DIR\fR/etc/group\&.
+.RE
 .SH "CONFIGURATION"
 .PP
 The following configuration variables in
diff -Naur shadow-4.1.4.2.old/man/groupmod.8.xml shadow-4.1.4.2/man/groupmod.8.xml
--- shadow-4.1.4.2.old/man/groupmod.8.xml	2009-05-19 18:00:14.000000000 -0400
+++ shadow-4.1.4.2/man/groupmod.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -145,6 +145,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 modifying a group from
+	    <filename>/etc/group</filename>, the group would be modified from
+	    <filename><option>ROOT_DIR</option>/etc/group</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
diff -Naur shadow-4.1.4.2.old/man/groups.1 shadow-4.1.4.2/man/groups.1
--- shadow-4.1.4.2.old/man/groups.1	2009-07-23 21:16:27.000000000 -0400
+++ shadow-4.1.4.2/man/groups.1	2010-03-05 06:13:29.000000000 -0500
@@ -22,7 +22,7 @@
 groups \- display current group names
 .SH "SYNOPSIS"
 .HP \w'\fBgroups\fR\ 'u
-\fBgroups\fR [\fIuser\fR]
+\fBgroups\fR [\-R\ \fIroot_dir\fR] [\fIuser\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -32,6 +32,18 @@
 \fIuser\fR
 parameter will display the groups for the named
 \fIuser\fR\&.
+.PP
+If the
+\fB\-R\fR
+or
+\fB\-\-chroot\fR
+option is used, the paths to all relevant files will be prefixed with the argument\&. For instance, instead of displaying the group names of
+/etc/passwd,
+\fB\-R\fR
+\fIroot_dir\fR
+would cause the group names of
+\fIroot_dir\fR/etc/passwd
+to be displayed instead\&.
 .SH "NOTE"
 .PP
 Systems which do not support concurrent group sets will have the information from
diff -Naur shadow-4.1.4.2.old/man/groups.1.xml shadow-4.1.4.2/man/groups.1.xml
--- shadow-4.1.4.2.old/man/groups.1.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/groups.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -43,6 +43,7 @@
   <refsynopsisdiv id='synopsis'>
     <cmdsynopsis>
       <command>groups</command>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
       <arg choice='opt'>
 	<replaceable>user</replaceable>
       </arg>
@@ -59,6 +60,17 @@
       remap='I'>user</emphasis> parameter will display the groups for the
       named <emphasis remap='I'>user</emphasis>.
     </para>
+
+    <para>
+      If the <option>-R</option> or <option>--chroot</option> option is used,
+      the paths to all relevant files will be prefixed with the argument.
+      For instance, instead of displaying
+      the group names of <filename>/etc/passwd</filename>,
+      <option>-R</option> <emphasis remap='I'>root_dir</emphasis>
+      would cause the group names of
+      <emphasis remap='I'>root_dir</emphasis><filename>/etc/passwd</filename>
+      to be displayed instead.
+    </para>
   </refsect1>
 
   <refsect1 id='note'>
diff -Naur shadow-4.1.4.2.old/man/newusers.8 shadow-4.1.4.2/man/newusers.8
--- shadow-4.1.4.2.old/man/newusers.8	2009-07-23 21:16:36.000000000 -0400
+++ shadow-4.1.4.2/man/newusers.8	2010-03-05 06:13:29.000000000 -0500
@@ -104,6 +104,12 @@
 \fBnewusers\fR
 first tries to create or change all the specified users, and then write these changes to the user or group databases\&. If an error occurs (except in the final writes to the databases), no changes are committed to the databases\&.
 .PP
+If the
+\fB\-R\fR
+option is set, PAM is not utilized\&. When the
+\fB\-R\fR
+option is set, during this first pass, users are created with a locked password (and passwords are not changed for the users which are not created)\&. A second pass is used to update the passwords using PAM\&. Failures to update a password are reported, but will not stop the other password updates\&.
+.PP
 This command is intended to be used in a large system environment where many accounts are updated at a single time\&.
 .SH "OPTIONS"
 .PP
@@ -113,7 +119,9 @@
 .PP
 \fB\-c\fR, \fB\-\-crypt\-method\fR
 .RS 4
-Use the specified method to encrypt the passwords\&.
+While using the
+\fB\-R\fR
+option, use the specified method to encrypt the passwords\&.
 .sp
 The available methods are DES, MD5, NONE, and SHA256 or SHA512 if your libc support these methods\&.
 .RE
@@ -138,9 +146,23 @@
 counterparts for the creation of groups)\&.
 .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 updating users in
+/etc/passwd, users would be updated in
+\fBROOT_DIR\fR/etc/passwd\&.
+.RE
+.PP
 \fB\-s\fR, \fB\-\-sha\-rounds\fR
 .RS 4
-Use the specified number of rounds to encrypt the passwords\&.
+While using the
+\fB\-R\fR
+option, use the specified number of rounds to encrypt the passwords\&.
 .sp
 The value 0 means that the system will choose the default number of rounds for the crypt method (5000)\&.
 .sp
@@ -155,13 +177,94 @@
 .PP
 The input file must be protected since it contains unencrypted passwords\&.
 .PP
-You should make sure the passwords and the encryption method respect the system\'s password policy\&.
+You should make sure that, while using the
+\fB\-R\fR
+option, the passwords and the encryption method respect the password policy of the offset system\&.
 .SH "CONFIGURATION"
 .PP
 The following configuration variables in
 /etc/login\&.defs
 change the behavior of this tool:
 .PP
+\fBGID_MAX\fR (number), \fBGID_MIN\fR (number)
+.RS 4
+Range of group IDs used for the creation of regular groups by
+\fBuseradd\fR,
+\fBgroupadd\fR, or
+\fBnewusers\fR\&.
+.RE
+.PP
+\fBMAX_MEMBERS_PER_GROUP\fR (number)
+.RS 4
+Maximum members per group entry\&. When the maximum is reached, a new group entry (line) is started in
+/etc/group
+(with the same name, same password, and same GID)\&.
+.sp
+The default value is 0, meaning that there are no limits in the number of members in a group\&.
+.sp
+This feature (split group) permits to limit the length of lines in the group file\&. This is useful to make sure that lines for NIS groups are not larger than 1024 characters\&.
+.sp
+If you need to enforce such limit, you can use 25\&.
+.sp
+Note: split groups may not be supported by all tools (even in the Shadow toolsuite)\&. You should not use this variable unless you really need it\&.
+.RE
+.PP
+\fBPASS_MAX_DAYS\fR (number)
+.RS 4
+The maximum number of days a password may be used\&. If the password is older than this, a password change will be forced\&. If not specified, \-1 will be assumed (which disables the restriction)\&.
+.RE
+.PP
+\fBPASS_MIN_DAYS\fR (number)
+.RS 4
+The minimum number of days allowed between password changes\&. Any password changes attempted sooner than this will be rejected\&. If not specified, \-1 will be assumed (which disables the restriction)\&.
+.RE
+.PP
+\fBPASS_WARN_AGE\fR (number)
+.RS 4
+The number of days warning given before a password expires\&. A zero means warning is given only upon the day of expiration, a negative value means no warning is given\&. If not specified, no warning will be provided\&.
+.RE
+.PP
+\fBSYS_GID_MAX\fR (number), \fBSYS_GID_MIN\fR (number)
+.RS 4
+Range of group IDs used for the creation of system groups by
+\fBuseradd\fR,
+\fBgroupadd\fR, or
+\fBnewusers\fR\&.
+.RE
+.PP
+\fBSYS_UID_MAX\fR (number), \fBSYS_UID_MIN\fR (number)
+.RS 4
+Range of user IDs used for the creation of system users by
+\fBuseradd\fR
+or
+\fBnewusers\fR\&.
+.RE
+.PP
+\fBUID_MAX\fR (number), \fBUID_MIN\fR (number)
+.RS 4
+Range of user IDs used for the creation of regular users by
+\fBuseradd\fR
+or
+\fBnewusers\fR\&.
+.RE
+.PP
+\fBUMASK\fR (number)
+.RS 4
+The file mode creation mask is initialized to this value\&. If not specified, the mask will be initialized to 022\&.
+.sp
+
+\fBuseradd\fR
+and
+\fBnewusers\fR
+use this mask to set the mode of the home directory they create
+.RE
+.PP
+While using the
+\fB\-R\fR
+option, the following configuration variables in
+\fBROOT_DIR\fR/etc/login\&.defs
+change the behavior of this tool:
+.PP
 \fBENCRYPT_METHOD\fR (string)
 .RS 4
 This defines the system default encryption algorithm for encrypting passwords (if no algorithm are specified on the command line)\&.
@@ -217,29 +320,8 @@
 Note: this parameter overrides the
 \fBMD5_CRYPT_ENAB\fR
 variable\&.
-.RE
-.PP
-\fBGID_MAX\fR (number), \fBGID_MIN\fR (number)
-.RS 4
-Range of group IDs used for the creation of regular groups by
-\fBuseradd\fR,
-\fBgroupadd\fR, or
-\fBnewusers\fR\&.
-.RE
-.PP
-\fBMAX_MEMBERS_PER_GROUP\fR (number)
-.RS 4
-Maximum members per group entry\&. When the maximum is reached, a new group entry (line) is started in
-/etc/group
-(with the same name, same password, and same GID)\&.
-.sp
-The default value is 0, meaning that there are no limits in the number of members in a group\&.
-.sp
-This feature (split group) permits to limit the length of lines in the group file\&. This is useful to make sure that lines for NIS groups are not larger than 1024 characters\&.
-.sp
-If you need to enforce such limit, you can use 25\&.
 .sp
-Note: split groups may not be supported by all tools (even in the Shadow toolsuite)\&. You should not use this variable unless you really need it\&.
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBMD5_CRYPT_ENAB\fR (boolean)
@@ -256,21 +338,8 @@
 .sp
 This variable is deprecated\&. You should use
 \fBENCRYPT_METHOD\fR\&.
-.RE
-.PP
-\fBPASS_MAX_DAYS\fR (number)
-.RS 4
-The maximum number of days a password may be used\&. If the password is older than this, a password change will be forced\&. If not specified, \-1 will be assumed (which disables the restriction)\&.
-.RE
-.PP
-\fBPASS_MIN_DAYS\fR (number)
-.RS 4
-The minimum number of days allowed between password changes\&. Any password changes attempted sooner than this will be rejected\&. If not specified, \-1 will be assumed (which disables the restriction)\&.
-.RE
-.PP
-\fBPASS_WARN_AGE\fR (number)
-.RS 4
-The number of days warning given before a password expires\&. A zero means warning is given only upon the day of expiration, a negative value means no warning is given\&. If not specified, no warning will be provided\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBSHA_CRYPT_MIN_ROUNDS\fR (number), \fBSHA_CRYPT_MAX_ROUNDS\fR (number)
@@ -298,50 +367,8 @@
 \fBSHA_CRYPT_MIN_ROUNDS\fR
 >
 \fBSHA_CRYPT_MAX_ROUNDS\fR, the highest value will be used\&.
-.RE
-.PP
-\fBSYS_GID_MAX\fR (number), \fBSYS_GID_MIN\fR (number)
-.RS 4
-Range of group IDs used for the creation of system groups by
-\fBuseradd\fR,
-\fBgroupadd\fR, or
-\fBnewusers\fR\&.
-.RE
-.PP
-\fBSYS_UID_MAX\fR (number), \fBSYS_UID_MIN\fR (number)
-.RS 4
-Range of user IDs used for the creation of system users by
-\fBuseradd\fR
-or
-\fBnewusers\fR\&.
-.RE
-.PP
-\fBUID_MAX\fR (number), \fBUID_MIN\fR (number)
-.RS 4
-Range of user IDs used for the creation of regular users by
-\fBuseradd\fR
-or
-\fBnewusers\fR\&.
-.RE
-.PP
-\fBUMASK\fR (number)
-.RS 4
-The file mode creation mask is initialized to this value\&. If not specified, the mask will be initialized to 022\&.
 .sp
-
-\fBuseradd\fR
-and
-\fBnewusers\fR
-use this mask to set the mode of the home directory they create
-.sp
-It is also used by
-\fBlogin\fR
-to define users\' initial umask\&. Note that this mask can be overriden by the user\'s GECOS line (if
-\fBQUOTAS_ENAB\fR
-is set) or by the specification of a limit with the
-\fIK\fR
-identifier in
-\fBlimits\fR(5)\&.
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .SH "FILES"
 .PP
@@ -369,6 +396,12 @@
 .RS 4
 Shadow password suite configuration\&.
 .RE
+.PP
+/etc/pam\&.d/newusers
+.RS 4
+PAM configuration for
+\fBnewusers\fR\&.
+.RE
 .SH "SEE ALSO"
 .PP
 
diff -Naur shadow-4.1.4.2.old/man/newusers.8.xml shadow-4.1.4.2/man/newusers.8.xml
--- shadow-4.1.4.2.old/man/newusers.8.xml	2009-05-19 18:17:55.000000000 -0400
+++ shadow-4.1.4.2/man/newusers.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -221,8 +221,11 @@
       databases. If an error occurs (except in the final writes to the
       databases), no changes are committed to the databases.
     </para>
+
     <para condition="pam">
-      During this first pass, users are created with a locked password
+      If the <option>-R</option> option is set, PAM is not utilized.
+      When the <option>-R</option> option is set,
+      during this first pass, users are created with a locked password
       (and passwords are not changed for the users which are not created).
       A second pass is used to update the passwords using PAM.  Failures
       to update a password are reported, but will not stop the other
@@ -240,19 +243,23 @@
     <para>
       The options which apply to the <command>newusers</command> command are:
     </para>
-    <variablelist remap='IP' condition="no_pam">
+    <variablelist remap='IP'>
       <varlistentry>
 	<term><option>-c</option>, <option>--crypt-method</option></term>
 	<listitem>
-	  <para>Use the specified method to encrypt the passwords.</para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    use the specified method to encrypt the passwords.
+	  </para>
+	  <para condition="no_pam">
+	    Use the specified method to encrypt the passwords.
+	  </para>
 	  <para>
 	    The available methods are DES, MD5, NONE, and SHA256 or SHA512
 	    if your libc support these methods.
 	  </para>
 	</listitem>
       </varlistentry>
-    </variablelist>
-    <variablelist remap='IP'>
       <varlistentry>
 	<term><option>-h</option>, <option>--help</option></term>
 	<listitem>
@@ -278,12 +285,34 @@
 	  </para>
 	</listitem>
       </varlistentry>
-    </variablelist>
-    <variablelist remap='IP' condition="no_pam">
+      <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 updating users in
+	    <filename>/etc/passwd</filename>, users would be updated in
+	    <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
       <varlistentry condition="sha_crypt">
 	<term><option>-s</option>, <option>--sha-rounds</option></term>
 	<listitem>
-	  <para>
+	  <para condition="pam">
+	    While using the <option>-R</option> option,
+	    use the specified number of rounds to encrypt the passwords.
+	  </para>
+	  <para condition="no_pam">
 	    Use the specified number of rounds to encrypt the passwords.
 	  </para>
 	  <para>
@@ -314,6 +343,11 @@
       The input file must be protected since it contains unencrypted
       passwords.
     </para>
+    <para condition="pam">
+      You should make sure that, while using the <option>-R</option> option,
+      the passwords and the encryption method respect the password policy
+      of the offset system.
+    </para>
     <para condition="no_pam">
       You should make sure the passwords and the encryption method respect
       the system's password policy.
@@ -327,30 +361,36 @@
       <filename>/etc/login.defs</filename> change the behavior of this
       tool:
     </para>
-    <variablelist condition="no_pam">
-      &ENCRYPT_METHOD;
-    </variablelist>
     <variablelist>
       &GID_MAX; <!-- documents also GID_MIN -->
       &MAX_MEMBERS_PER_GROUP;
     </variablelist>
-    <variablelist condition="no_pam">
-      &MD5_CRYPT_ENAB;
-    </variablelist>
     <variablelist>
       &PASS_MAX_DAYS;
       &PASS_MIN_DAYS;
       &PASS_WARN_AGE;
     </variablelist>
-    <variablelist condition="no_pam">
-      &SHA_CRYPT_MIN_ROUNDS; <!-- documents also SHA_CRYPT_MAX_ROUNDS-->
-    </variablelist>
     <variablelist>
       &SYS_GID_MAX; <!-- documents also SYS_GID_MIN -->
       &SYS_UID_MAX; <!-- documents also SYS_UID_MIN -->
       &UID_MAX; <!-- documents also UID_MIN -->
       &UMASK;
     </variablelist>
+    <para condition="pam">
+      While using the <option>-R</option> option, the following
+      configuration variables in
+      <filename><option>ROOT_DIR</option>/etc/login.defs</filename>
+      change the behavior of this tool:
+    </para>
+    <variablelist>
+      &ENCRYPT_METHOD;
+    </variablelist>
+    <variablelist>
+      &MD5_CRYPT_ENAB;
+    </variablelist>
+    <variablelist>
+      &SHA_CRYPT_MIN_ROUNDS; <!-- documents also SHA_CRYPT_MAX_ROUNDS-->
+    </variablelist>
   </refsect1>
 
   <refsect1 id='files'>
diff -Naur shadow-4.1.4.2.old/man/passwd.1 shadow-4.1.4.2/man/passwd.1
--- shadow-4.1.4.2.old/man/passwd.1	2009-07-23 21:16:37.000000000 -0400
+++ shadow-4.1.4.2/man/passwd.1	2010-03-05 06:13:29.000000000 -0500
@@ -157,6 +157,18 @@
 repository
 .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 changing a password in
+/etc/shadow, a password would be changed in
+\fBROOT_DIR\fR/etc/shadow\&.
+.RE
+.PP
 \fB\-S\fR, \fB\-\-status\fR
 .RS 4
 Display account status information\&. The status information consists of 7 fields\&. The first field is the user\'s login name\&. The second field indicates if the user account has a locked password (L), has no password (NP), or has a usable password (P)\&. The third field gives the date of the last password change\&. The next four fields are the minimum age, maximum age, warning period, and inactivity period for the password\&. These ages are expressed in days\&.
@@ -186,10 +198,18 @@
 Password complexity checking may vary from site to site\&. The user is urged to select a password as complex as he or she feels comfortable with\&.
 .PP
 Users may not be able to change their password on a system if NIS is enabled and they are not logged into the NIS server\&.
+.PP
+If the
+\fB\-R\fR
+option is not set,
+\fBpasswd\fR
+uses PAM to authenticate users and to change their passwords\&.
 .SH "CONFIGURATION"
 .PP
-The following configuration variables in
-/etc/login\&.defs
+While using the
+\fB\-R\fR
+option, the following configuration variables in
+\fBROOT_DIR\fR/etc/login\&.defs
 change the behavior of this tool:
 .PP
 \fBENCRYPT_METHOD\fR (string)
@@ -247,6 +267,8 @@
 Note: this parameter overrides the
 \fBMD5_CRYPT_ENAB\fR
 variable\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBMD5_CRYPT_ENAB\fR (boolean)
@@ -263,31 +285,8 @@
 .sp
 This variable is deprecated\&. You should use
 \fBENCRYPT_METHOD\fR\&.
-.RE
-.PP
-\fBOBSCURE_CHECKS_ENAB\fR (boolean)
-.RS 4
-Enable additional checks upon password changes\&.
-.RE
-.PP
-\fBPASS_ALWAYS_WARN\fR (boolean)
-.RS 4
-Warn about weak passwords (but still allow them) if you are root\&.
-.RE
-.PP
-\fBPASS_CHANGE_TRIES\fR (number)
-.RS 4
-Maximum number of attempts to change password if rejected (too easy)\&.
-.RE
-.PP
-\fBPASS_MAX_LEN\fR (number), \fBPASS_MIN_LEN\fR (number)
-.RS 4
-Number of significant characters in the password for crypt()\&.
-\fBPASS_MAX_LEN\fR
-is 8 by default\&. Don\'t change unless your crypt() is better\&. This is ignored if
-\fBMD5_CRYPT_ENAB\fR
-set to
-\fIyes\fR\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .PP
 \fBSHA_CRYPT_MIN_ROUNDS\fR (number), \fBSHA_CRYPT_MAX_ROUNDS\fR (number)
@@ -315,6 +314,8 @@
 \fBSHA_CRYPT_MIN_ROUNDS\fR
 >
 \fBSHA_CRYPT_MAX_ROUNDS\fR, the highest value will be used\&.
+.sp
+Note: This only affect the generation of group passwords\&. The generation of user passwords is done by PAM and subject to the PAM configuration\&. It is recommended to set this variable consistently with the PAM configuration\&.
 .RE
 .SH "FILES"
 .PP
@@ -332,6 +333,12 @@
 .RS 4
 Shadow password suite configuration\&.
 .RE
+.PP
+/etc/pam\&.d/passwd
+.RS 4
+PAM configuration for
+\fBpasswd\fR\&.
+.RE
 .SH "EXIT VALUES"
 .PP
 The
diff -Naur shadow-4.1.4.2.old/man/passwd.1.xml shadow-4.1.4.2/man/passwd.1.xml
--- shadow-4.1.4.2.old/man/passwd.1.xml	2009-06-05 17:14:15.000000000 -0400
+++ shadow-4.1.4.2/man/passwd.1.xml	2010-03-05 06:13:29.000000000 -0500
@@ -277,6 +277,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 changing a password in
+	    <filename>/etc/shadow</filename>, a password would be changed in
+	    <filename><option>ROOT_DIR</option>/etc/shadow</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>
 	  <option>-S</option>, <option>--status</option>
 	</term>
 	<listitem>
@@ -347,14 +367,21 @@
       logged into the NIS server.
     </para>
     <para condition="pam">
+      If the <option>-R</option> option is not set,
       <command>passwd</command> uses PAM to authenticate users and to
       change their passwords.
     </para>
   </refsect1>
 
-  <refsect1 id='configuration' condition="no_pam">
+  <refsect1 id='configuration'>
     <title>CONFIGURATION</title>
-    <para>
+    <para condition="pam">
+      While using the <option>-R</option> option, the following
+      configuration variables in
+      <filename><option>ROOT_DIR</option>/etc/login.defs</filename>
+      change the behavior of this tool:
+    </para>
+    <para condition="no_pam">
       The following configuration variables in
       <filename>/etc/login.defs</filename> change the behavior of this
       tool:
@@ -385,7 +412,7 @@
 	  <para>Secure user account information.</para>
 	</listitem>
       </varlistentry>
-      <varlistentry condition="no_pam">
+      <varlistentry>
 	<term><filename>/etc/login.defs</filename></term>
 	<listitem>
 	  <para>Shadow password suite configuration.</para>
@@ -460,7 +487,7 @@
       <citerefentry>
 	<refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum>
       </citerefentry>,
-      <phrase condition="no_pam">
+      <phrase>
 	<citerefentry>
 	  <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
 	</citerefentry>,
diff -Naur shadow-4.1.4.2.old/man/pwconv.8 shadow-4.1.4.2/man/pwconv.8
--- shadow-4.1.4.2.old/man/pwconv.8	2009-07-23 21:16:29.000000000 -0400
+++ shadow-4.1.4.2/man/pwconv.8	2010-03-05 06:13:29.000000000 -0500
@@ -22,13 +22,13 @@
 pwconv, pwunconv, grpconv, grpunconv \- convert to and from shadow passwords and groups
 .SH "SYNOPSIS"
 .HP \w'\fBpwconv\fR\ 'u
-\fBpwconv\fR
+\fBpwconv\fR [\-R\ \fIroot_dir\fR]
 .HP \w'\fBpwunconv\fR\ 'u
-\fBpwunconv\fR
+\fBpwunconv\fR [\-R\ \fIroot_dir\fR]
 .HP \w'\fBgrpconv\fR\ 'u
-\fBgrpconv\fR
+\fBgrpconv\fR [\-R\ \fIroot_dir\fR]
 .HP \w'\fBgrpunconv\fR\ 'u
-\fBgrpunconv\fR
+\fBgrpunconv\fR [\-R\ \fIroot_dir\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -83,6 +83,18 @@
 \fBgrpconv\fR
 are similar\&. First, entries in the shadowed file which don\'t exist in the main file are removed\&. Then, shadowed entries which don\'t have `x\' as the password in the main file are updated\&. Any missing shadowed entries are added\&. Finally, passwords in the main file are replaced with `x\'\&. These programs can be used for initial conversion as well to update the shadowed file if the main file is edited by hand\&.
 .PP
+If the
+\fB\-R\fR
+or
+\fB\-\-chroot\fR
+option is used, the paths to all relevant files will be prefixed with the argument\&. For instance, instead of converting
+/etc/passwd,
+\fB\-R\fR
+\fIroot_dir\fR
+would cause
+\fIroot_dir\fR/etc/passwd
+to be converted instead\&.
+.PP
 
 \fBpwconv\fR
 will use the values of
diff -Naur shadow-4.1.4.2.old/man/pwconv.8.xml shadow-4.1.4.2/man/pwconv.8.xml
--- shadow-4.1.4.2.old/man/pwconv.8.xml	2008-10-11 07:44:44.000000000 -0400
+++ shadow-4.1.4.2/man/pwconv.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -54,15 +54,19 @@
   <refsynopsisdiv id='synopsis'>
     <cmdsynopsis>
       <command>pwconv</command>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>pwunconv</command>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>grpconv</command>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>grpunconv</command>
+      <arg choice='opt'>-R <replaceable>root_dir</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -113,6 +117,15 @@
     </para>
 
     <para>
+      If the <option>-R</option> or <option>--chroot</option> option is used,
+      the paths to all relevant files will be prefixed with the argument.
+      For instance, instead of converting <filename>/etc/passwd</filename>,
+      <option>-R</option> <emphasis remap='I'>root_dir</emphasis> would cause
+      <emphasis remap='I'>root_dir</emphasis><filename>/etc/passwd</filename>
+      to be converted instead.
+    </para>
+
+    <para>
       <command>pwconv</command> will use the values of <emphasis
       remap='I'>PASS_MIN_DAYS</emphasis>, <emphasis
       remap='I'>PASS_MAX_DAYS</emphasis>, and <emphasis
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-03-05 06:13:29.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-03-05 06:13:29.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	2009-07-23 21:16:45.000000000 -0400
+++ shadow-4.1.4.2/man/userdel.8	2010-03-05 06:13:29.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-03-05 06:13:29.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/man/usermod.8 shadow-4.1.4.2/man/usermod.8
--- shadow-4.1.4.2.old/man/usermod.8	2009-07-23 21:16:46.000000000 -0400
+++ shadow-4.1.4.2/man/usermod.8	2010-03-05 06:13:29.000000000 -0500
@@ -133,7 +133,33 @@
 \fBNote:\fR
 This option is not recommended because the password (or encrypted password) will be visible by users listing the processes\&.
 .sp
-You should make sure the password respects the system\'s password policy\&.
+The password will be written in the local
+/etc/passwd
+or
+/etc/shadow
+file or, if the
+\fB\-R\fR
+option is used, the
+\fBROOT_DIR\fR/etc/passwd
+or
+\fBROOT_DIR\fR/etc/shadow
+file\&. This might differ from the password database configured in your local PAM configuration, or the offset system\'s PAM configuration\&.
+.sp
+You should make sure the password respects the system\'s password policy or, while using the
+\fB\-R\fR
+option, the policy of the offset system\&.
+.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 nodifying a user entry from
+/etc/passwd, the user entry would be nodified from
+\fBROOT_DIR\fR/etc/passwd\&.
 .RE
 .PP
 \fB\-s\fR, \fB\-\-shell\fR \fISHELL\fR
@@ -213,13 +239,6 @@
 \fBuserdel\fR
 to create, move, or delete the user\'s mail spool\&.
 .PP
-If
-\fBMAIL_CHECK_ENAB\fR
-is set to
-\fIyes\fR, they are also used to define the
-\fBMAIL\fR
-environment variable\&.
-.PP
 \fBMAX_MEMBERS_PER_GROUP\fR (number)
 .RS 4
 Maximum members per group entry\&. When the maximum is reached, a new group entry (line) is started in
diff -Naur shadow-4.1.4.2.old/man/usermod.8.xml shadow-4.1.4.2/man/usermod.8.xml
--- shadow-4.1.4.2.old/man/usermod.8.xml	2009-05-21 08:02:13.000000000 -0400
+++ shadow-4.1.4.2/man/usermod.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -253,12 +253,38 @@
 	  <para condition="pam">
 	    The password will be written in the local
 	    <filename>/etc/passwd</filename> or
-	    <filename>/etc/shadow</filename> file. This might differ from the
-	    password database configured in your PAM configuration.
+	    <filename>/etc/shadow</filename> file or, if the
+	    <option>-R</option> option is used, the
+	    <filename><option>ROOT_DIR</option>/etc/passwd</filename> or
+	    <filename><option>ROOT_DIR</option>/etc/shadow</filename> file.
+	    This might differ from the password database configured in your
+	    local PAM configuration, or the offset system's PAM configuration.
 	  </para>
 	  <para>
 	    You should make sure the password respects the system's
-	    password policy.
+	    password policy or, while using the <option>-R</option> option,
+        the policy of the offset system.
+	  </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 nodifying a user entry from
+	    <filename>/etc/passwd</filename>,
+	    the user entry would be nodified from
+	    <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
 	  </para>
 	</listitem>
       </varlistentry>
diff -Naur shadow-4.1.4.2.old/man/vipw.8 shadow-4.1.4.2/man/vipw.8
--- shadow-4.1.4.2.old/man/vipw.8	2009-07-23 21:16:47.000000000 -0400
+++ shadow-4.1.4.2/man/vipw.8	2010-03-05 06:13:29.000000000 -0500
@@ -71,6 +71,18 @@
 Quiet mode\&.
 .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 editing a user from
+/etc/passwd, the user would be edited from
+\fBROOT_DIR\fR/etc/passwd\&.
+.RE
+.PP
 \fB\-s\fR, \fB\-\-shadow\fR
 .RS 4
 Edit shadow or gshadow database\&.
diff -Naur shadow-4.1.4.2.old/man/vipw.8.xml shadow-4.1.4.2/man/vipw.8.xml
--- shadow-4.1.4.2.old/man/vipw.8.xml	2009-03-14 09:22:01.000000000 -0400
+++ shadow-4.1.4.2/man/vipw.8.xml	2010-03-05 06:13:29.000000000 -0500
@@ -112,6 +112,26 @@
 	</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 editing a user from
+	    <filename>/etc/passwd</filename>, the user would be edited from
+	    <filename><option>ROOT_DIR</option>/etc/passwd</filename>.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
 	<term><option>-s</option>, <option>--shadow</option></term>
 	<listitem>
 	  <para>Edit shadow or gshadow database.</para>
diff -Naur shadow-4.1.4.2.old/src/Makefile.am shadow-4.1.4.2/src/Makefile.am
--- shadow-4.1.4.2.old/src/Makefile.am	2009-05-09 17:10:18.000000000 -0400
+++ shadow-4.1.4.2/src/Makefile.am	2010-03-05 06:13:29.000000000 -0500
@@ -70,6 +70,9 @@
 LIBCRYPT_NOPAM = $(LIBCRYPT)
 endif
 
+AM_LDFLAGS = -Wl,--wrap=setpwent,--wrap=getpwent,--wrap=endpwent,--wrap=getpwnam \
+	-Wl,--wrap=getpwuid,--wrap=getgrnam,--wrap=getgrgid,--wrap=getspnam
+
 chage_LDADD    = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
 chfn_LDADD     = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
 chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT)
@@ -90,7 +93,7 @@
 newgrp_LDADD   = $(LDADD) $(LIBAUDIT) $(LIBCRYPT)
 newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT)
 nologin_LDADD  =
-passwd_LDADD   = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM)
+passwd_LDADD   = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT)
 pwck_LDADD     = $(LDADD) $(LIBSELINUX)
 pwconv_LDADD   = $(LDADD) $(LIBSELINUX)
 pwunconv_LDADD = $(LDADD) $(LIBSELINUX)
diff -Naur shadow-4.1.4.2.old/src/Makefile.in shadow-4.1.4.2/src/Makefile.in
--- shadow-4.1.4.2.old/src/Makefile.in	2009-07-23 21:16:00.000000000 -0400
+++ shadow-4.1.4.2/src/Makefile.in	2010-03-05 06:13:29.000000000 -0500
@@ -176,7 +176,7 @@
 passwd_OBJECTS = passwd.$(OBJEXT)
 passwd_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_4)
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 pwck_SOURCES = pwck.c
 pwck_OBJECTS = pwck.$(OBJEXT)
 pwck_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
@@ -399,6 +399,9 @@
 @ACCT_TOOLS_SETUID_TRUE@LIBPAM_SUID = $(LIBPAM)
 @USE_PAM_FALSE@LIBCRYPT_NOPAM = $(LIBCRYPT)
 @USE_PAM_TRUE@LIBCRYPT_NOPAM = 
+AM_LDFLAGS = -Wl,--wrap=setpwent,--wrap=getpwent,--wrap=endpwent,--wrap=getpwnam \
+	-Wl,--wrap=getpwuid,--wrap=getgrnam,--wrap=getgrgid,--wrap=getspnam
+
 chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
 chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
 chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT)
@@ -420,7 +423,7 @@
 newgrp_LDADD = $(LDADD) $(LIBAUDIT) $(LIBCRYPT)
 newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT)
 nologin_LDADD = 
-passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM)
+passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT)
 pwck_LDADD = $(LDADD) $(LIBSELINUX)
 pwconv_LDADD = $(LDADD) $(LIBSELINUX)
 pwunconv_LDADD = $(LDADD) $(LIBSELINUX)
diff -Naur shadow-4.1.4.2.old/src/chage.c shadow-4.1.4.2/src/chage.c
--- shadow-4.1.4.2.old/src/chage.c	2009-04-30 17:39:39.000000000 -0400
+++ shadow-4.1.4.2/src/chage.c	2010-03-05 06:13:29.000000000 -0500
@@ -167,6 +167,7 @@
 	         "                                change to MIN_DAYS\n"
 	         "  -M, --maxdays MAX_DAYS        set maximim number of days before password\n"
 	         "                                change to MAX_DAYS\n"
+	         "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	         "  -W, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"
 	         "\n"), stderr);
 	exit (E_USAGE);
@@ -820,12 +821,14 @@
 	 */
 	Prog = Basename (argv[0]);
 
-	process_flags (argc, argv);
-
 	OPENLOG ("chage");
 
 	check_perms ();
 
+	process_chroot ('R', &argc, argv);
+
+	process_flags (argc, argv);
+
 	if (!spw_file_present ()) {
 		fprintf (stderr,
 		         _("%s: the shadow password file is not present\n"),
diff -Naur shadow-4.1.4.2.old/src/chfn.c shadow-4.1.4.2/src/chfn.c
--- shadow-4.1.4.2.old/src/chfn.c	2009-04-30 17:39:39.000000000 -0400
+++ shadow-4.1.4.2/src/chfn.c	2010-03-05 06:13:29.000000000 -0500
@@ -116,7 +116,8 @@
 		fprintf (stderr,
 		         _("Usage: %s [-f full_name] [-r room_no] "
 		           "[-w work_ph]\n"
-		           "\t[-h home_ph] [-o other] [user]\n"), Prog);
+		           "\t[-h home_ph] [-o other] [-R root_dir] "
+		           "[user]\n"), Prog);
 	} else {
 		fprintf (stderr,
 		         _("Usage: %s [-f full_name] [-r room_no] "
@@ -628,6 +629,12 @@
 
 	OPENLOG ("chfn");
 
+	/* Check that the caller is allowed to change the gecos of the
+	 * specified user */
+	check_perms (pw);
+
+	process_chroot ('R', &argc, argv);
+
 	/* parse the command line options */
 	process_flags (argc, argv);
 
@@ -679,10 +686,6 @@
 	}
 #endif
 
-	/* Check that the caller is allowed to change the gecos of the
-	 * specified user */
-	check_perms (pw);
-
 	/* If some fields were not set on the command line, load the value from
 	 * the old gecos fields. */
 	get_old_fields (pw->pw_gecos);
diff -Naur shadow-4.1.4.2.old/src/chgpasswd.c shadow-4.1.4.2/src/chgpasswd.c
--- shadow-4.1.4.2.old/src/chgpasswd.c	2009-04-30 17:39:39.000000000 -0400
+++ shadow-4.1.4.2/src/chgpasswd.c	2010-03-05 06:13:29.000000000 -0500
@@ -124,6 +124,7 @@
 	                   "  -h, --help                    display this help message and exit\n"
 	                   "  -m, --md5                     encrypt the clear text password using\n"
 	                   "                                the MD5 algorithm\n"
+	                   "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	                   "%s"
 	                   "\n"),
 	                 Prog,
@@ -390,12 +391,14 @@
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
 	(void) textdomain (PACKAGE);
 
-	process_flags (argc, argv);
-
 	OPENLOG ("chgpasswd");
 
 	check_perms ();
 
+	process_chroot ('R', &argc, argv);
+
+	process_flags (argc, argv);
+
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
 #endif
diff -Naur shadow-4.1.4.2.old/src/chpasswd.c shadow-4.1.4.2/src/chpasswd.c
--- shadow-4.1.4.2.old/src/chpasswd.c	2009-05-10 09:49:05.000000000 -0400
+++ shadow-4.1.4.2/src/chpasswd.c	2010-03-05 06:13:29.000000000 -0500
@@ -54,7 +54,6 @@
  * Global variables
  */
 char *Prog;
-#ifndef USE_PAM
 static bool cflg   = false;
 static bool eflg   = false;
 static bool md5flg = false;
@@ -70,7 +69,6 @@
 static bool is_shadow_pwd;
 static bool pw_locked = false;
 static bool spw_locked = false;
-#endif				/* !USE_PAM */
 
 /* local function prototypes */
 static void fail_exit (int code);
@@ -78,17 +76,17 @@
 static void process_flags (int argc, char **argv);
 static void check_flags (void);
 static void check_perms (void);
-#ifndef USE_PAM
 static void open_files (void);
 static void close_files (void);
-#endif				/* !USE_PAM */
 
 /*
  * fail_exit - exit with a failure code after unlocking the files
  */
 static void fail_exit (int code)
 {
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if (pw_locked) {
 		if (pw_unlock () == 0) {
 			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
@@ -104,7 +102,9 @@
 			/* continue */
 		}
 	}
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 
 	exit (code);
 }
@@ -119,7 +119,9 @@
 	                  "\n"
 	                  "Options:\n"),
 	                Prog);
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	(void) fprintf (stderr,
 	                _("  -c, --crypt-method            the crypt method (one of %s)\n"),
 #ifndef USE_SHA_CRYPT
@@ -129,18 +131,36 @@
 #endif				/* USE_SHA_CRYPT */
 	               );
 	(void) fputs (_("  -e, --encrypted               supplied passwords are encrypted\n"), stderr);
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	(void) fputs (_("  -m, --md5                     encrypt the clear text password using\n"
 	                "                                the MD5 algorithm\n"),
 	              stderr);
+#ifdef USE_PAM
+	}
+#endif
+	(void) fputs (_("  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"), stderr);
+#ifdef USE_PAM
+	if (!chroot_flg) {
+		(void) fprintf (stderr,
+	                _("                                use \'%s --help -R [ROOT_DIR]\' to see\n"
+	                  "                                available options\n"),
+					Prog);
+	} else {
+#endif
 #ifdef USE_SHA_CRYPT
 	(void) fputs (_("  -s, --sha-rounds              number of SHA rounds for the SHA*\n"
 	                "                                crypt algorithms\n"),
 	              stderr);
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	(void) fputs ("\n", stderr);
 
 	exit (E_USAGE);
@@ -156,34 +176,30 @@
 	int option_index = 0;
 	int c;
 	static struct option long_options[] = {
-#ifndef USE_PAM
 		{"crypt-method", required_argument, NULL, 'c'},
 		{"encrypted", no_argument, NULL, 'e'},
 		{"md5", no_argument, NULL, 'm'},
 #ifdef USE_SHA_CRYPT
 		{"sha-rounds", required_argument, NULL, 's'},
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
 		{"help", no_argument, NULL, 'h'},
 		{NULL, 0, NULL, '\0'}
 	};
 
 	while ((c = getopt_long (argc, argv,
-#ifndef USE_PAM
 # ifdef USE_SHA_CRYPT
 	                         "c:ehms:",
 # else				/* !USE_SHA_CRYPT */
 	                         "c:ehm",
 # endif				/* !USE_SHA_CRYPT */
-#else
-	                         "h",
-#endif				/* !USE_PAM */
 	                         long_options, &option_index)) != -1) {
+		if (!chroot_flg && (c != 'h'))
+			usage ();
+
 		switch (c) {
 		case 'h':
 			usage ();
 			break;
-#ifndef USE_PAM
 		case 'c':
 			cflg = true;
 			crypt_method = optarg;
@@ -205,7 +221,6 @@
 			}
 			break;
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
 		default:
 			usage ();
 			break;
@@ -223,7 +238,6 @@
  */
 static void check_flags (void)
 {
-#ifndef USE_PAM
 #ifdef USE_SHA_CRYPT
 	if (sflg && !cflg) {
 		fprintf (stderr,
@@ -256,7 +270,6 @@
 			usage ();
 		}
 	}
-#endif				/* USE_PAM */
 }
 
 /*
@@ -306,7 +319,6 @@
 #endif				/* USE_PAM */
 }
 
-#ifndef USE_PAM
 /*
  * open_files - lock and open the password databases
  */
@@ -382,7 +394,6 @@
 	}
 	pw_locked = false;
 }
-#endif
 
 int main (int argc, char **argv)
 {
@@ -391,13 +402,11 @@
 	char *newpwd;
 	char *cp;
 
-#ifndef USE_PAM
 	const struct spwd *sp;
 	struct spwd newsp;
 
 	const struct passwd *pw;
 	struct passwd newpw;
-#endif				/* !USE_PAM */
 
 	int errors = 0;
 	int line = 0;
@@ -408,16 +417,22 @@
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
 	(void) textdomain (PACKAGE);
 
-	process_flags (argc, argv);
-
 	OPENLOG ("chpasswd");
 
 	check_perms ();
 
-#ifndef USE_PAM
+	process_chroot ('R', &argc, argv);
+
+	process_flags (argc, argv);
+
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	is_shadow_pwd = spw_file_present ();
 
 	open_files ();
+#ifdef USE_PAM
+	}
 #endif
 
 	/*
@@ -467,13 +482,15 @@
 		newpwd = cp;
 
 #ifdef USE_PAM
-		if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) {
-			fprintf (stderr,
-			         _("%s: (line %d, user %s) password not changed\n"),
-			         Prog, line, name);
-			errors++;
-		}
-#else				/* !USE_PAM */
+		if (!chroot_flg) {
+			if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) {
+				fprintf (stderr,
+				         _("%s: (line %d, user %s) password not changed\n"),
+				         Prog, line, name);
+				errors++;
+			}
+		} else {
+#endif
 		if (   !eflg
 		    && (   (NULL == crypt_method)
 		        || (0 != strcmp (crypt_method, "NONE")))) {
@@ -552,7 +569,9 @@
 				continue;
 			}
 		}
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	}
 
 	/*
@@ -566,16 +585,24 @@
 	 * password database.
 	 */
 	if (0 != errors) {
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 		fprintf (stderr,
 		         _("%s: error detected, changes ignored\n"), Prog);
+#ifdef USE_PAM
+	}
 #endif
 		fail_exit (1);
 	}
 
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	/* Save the changes */
 	close_files ();
+#ifdef USE_PAM
+	}
 #endif
 
 	nscd_flush_cache ("passwd");
diff -Naur shadow-4.1.4.2.old/src/chsh.c shadow-4.1.4.2/src/chsh.c
--- shadow-4.1.4.2.old/src/chsh.c	2009-04-30 17:39:39.000000000 -0400
+++ shadow-4.1.4.2/src/chsh.c	2010-03-05 06:13:29.000000000 -0500
@@ -107,6 +107,7 @@
 	         "\n"
 	         "Options:\n"
 	         "  -h, --help                    display this help message and exit\n"
+	         "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	         "  -s, --shell SHELL             new login shell for the user account\n"
 	         "\n"), stderr);
 	exit (E_USAGE);
@@ -453,6 +454,10 @@
 
 	OPENLOG ("chsh");
 
+	check_perms (pw);
+
+	process_chroot ('R', &argc, argv);
+
 	/* parse the command line options */
 	process_flags (argc, argv);
 
@@ -503,8 +508,6 @@
 	}
 #endif
 
-	check_perms (pw);
-
 	/*
 	 * Now get the login shell. Either get it from the password
 	 * file, or use the value from the command line.
diff -Naur shadow-4.1.4.2.old/src/expiry.c shadow-4.1.4.2/src/expiry.c
--- shadow-4.1.4.2.old/src/expiry.c	2009-04-11 14:37:05.000000000 -0400
+++ shadow-4.1.4.2/src/expiry.c	2010-03-05 06:13:29.000000000 -0500
@@ -61,7 +61,7 @@
  */
 static void usage (void)
 {
-	fputs (_("Usage: expiry {-f|-c}\n"), stderr);
+	fputs (_("Usage: expiry [-R root_dir] {-f|-c}\n"), stderr);
 	exit (10);
 }
 
@@ -100,6 +100,8 @@
 
 	OPENLOG ("expiry");
 
+	process_chroot ('R', &argc, argv);
+
 	if (   (argc != 2)
 	    || (   (strcmp (argv[1], "-f") != 0)
 	        && (strcmp (argv[1], "-c") != 0))) {
diff -Naur shadow-4.1.4.2.old/src/gpasswd.c shadow-4.1.4.2/src/gpasswd.c
--- shadow-4.1.4.2.old/src/gpasswd.c	2009-04-30 17:39:40.000000000 -0400
+++ shadow-4.1.4.2/src/gpasswd.c	2010-03-05 06:13:29.000000000 -0500
@@ -135,6 +135,7 @@
 	           "\n"
 	           "Options:\n"
 	           "  -a, --add USER                add USER to GROUP\n"
+	           "  -c, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	           "  -d, --delete USER             remove USER from GROUP\n"
 	           "  -r, --remove-password         remove the GROUP's password\n"
 	           "  -R, --restrict                restrict access to GROUP to its members\n"
@@ -1025,6 +1026,8 @@
 		exit (1);
 	}
 
+	process_chroot ('c', &argc, argv);
+
 	/* Parse the options */
 	process_flags (argc, argv);
 
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-03-05 06:13:29.000000000 -0500
@@ -81,6 +81,7 @@
 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 pflg = false;	/* new encrypted password */
 
 #ifdef SHADOWGRP
@@ -121,6 +122,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);
 }
@@ -578,13 +580,19 @@
 
 	OPENLOG ("groupadd");
 
+	check_perms ();
+
+	/*
+	 * Parse the chroot option before other options
+	 * but after PAM authentication.
+	 */
+	process_chroot ('R', &argc, argv);
+
 	/*
 	 * Parse the command line options.
 	 */
 	process_flags (argc, argv);
 
-	check_perms ();
-
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
 #endif
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-03-05 06:13:29.000000000 -0500
@@ -86,7 +86,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);
 }
 
@@ -328,14 +332,6 @@
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
 	(void) textdomain (PACKAGE);
 
-	if (argc != 2) {
-		usage ();
-	}
-
-	group_name = argv[1];
-
-	OPENLOG ("groupdel");
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -369,6 +365,16 @@
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
+	OPENLOG ("groupdel");
+
+	process_chroot ('R', &argc, argv);
+
+	if (argc != 2) {
+		usage ();
+	}
+
+	group_name = argv[1];
+
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
 #endif
diff -Naur shadow-4.1.4.2.old/src/groupmod.c shadow-4.1.4.2/src/groupmod.c
--- shadow-4.1.4.2.old/src/groupmod.c	2010-03-05 06:15:52.000000000 -0500
+++ shadow-4.1.4.2/src/groupmod.c	2010-03-05 06:13:29.000000000 -0500
@@ -126,6 +126,7 @@
 	(void) fputs (_("  -o, --non-unique              allow to use a duplicate (non-unique) GID\n"), stderr);
 	(void) fputs (_("  -p, --password PASSWORD       change the password to this (encrypted)\n"
 	                "                                PASSWORD\n"), stderr);
+	(void) fputs (_("  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"), stderr);
 	(void) fputs ("\n", stderr);
 	exit (E_USAGE);
 }
@@ -711,10 +712,6 @@
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
 	(void) textdomain (PACKAGE);
 
-	process_flags (argc, argv);
-
-	OPENLOG ("groupmod");
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -748,6 +745,17 @@
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
+	OPENLOG ("groupmod");
+
+	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after PAM authentication.
+	 */
+	process_chroot ('R', &argc, argv);
+
+	process_flags (argc, argv);
+
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
 #endif
diff -Naur shadow-4.1.4.2.old/src/groups.c shadow-4.1.4.2/src/groups.c
--- shadow-4.1.4.2.old/src/groups.c	2009-04-30 17:08:50.000000000 -0400
+++ shadow-4.1.4.2/src/groups.c	2010-03-05 06:13:29.000000000 -0500
@@ -130,6 +130,8 @@
 	 */
 	Prog = Basename (argv[0]);
 
+	process_chroot ('R', &argc, argv);
+
 	if (argc == 1) {
 
 		/*
diff -Naur shadow-4.1.4.2.old/src/grpconv.c shadow-4.1.4.2/src/grpconv.c
--- shadow-4.1.4.2.old/src/grpconv.c	2008-09-06 08:51:54.000000000 -0400
+++ shadow-4.1.4.2/src/grpconv.c	2010-03-05 06:13:29.000000000 -0500
@@ -89,9 +89,6 @@
 	const struct sgrp *sg;
 	struct sgrp sgent;
 
-	if (1 != argc) {
-		(void) fputs (_("Usage: grpconv\n"), stderr);
-	}
 	Prog = Basename (argv[0]);
 
 	(void) setlocale (LC_ALL, "");
@@ -100,6 +97,13 @@
 
 	OPENLOG ("grpconv");
 
+	process_chroot ('R', &argc, argv);
+
+	if (1 != argc) {
+		fprintf (stderr, _("Usage: %s [-R root_dir]\n"), Prog);
+		fail_exit (2);
+	}
+
 	if (gr_lock () == 0) {
 		fprintf (stderr,
 		         _("%s: cannot lock %s; try again later.\n"),
diff -Naur shadow-4.1.4.2.old/src/grpunconv.c shadow-4.1.4.2/src/grpunconv.c
--- shadow-4.1.4.2.old/src/grpunconv.c	2008-09-06 08:51:54.000000000 -0400
+++ shadow-4.1.4.2/src/grpunconv.c	2010-03-05 06:13:29.000000000 -0500
@@ -89,9 +89,6 @@
 	struct group grent;
 	const struct sgrp *sg;
 
-	if (1 != argc) {
-		(void) fputs (_("Usage: grpunconv\n"), stderr);
-	}
 	Prog = Basename (argv[0]);
 
 	(void) setlocale (LC_ALL, "");
@@ -100,6 +97,13 @@
 
 	OPENLOG ("grpunconv");
 
+	process_chroot ('R', &argc, argv);
+
+	if (1 != argc) {
+		fprintf (stderr, _("Usage: %s [-R root_dir]\n"), Prog);
+		fail_exit (2);
+	}
+
 	if (sgr_file_present () == 0) {
 		exit (0);	/* no /etc/gshadow, nothing to do */
 	}
diff -Naur shadow-4.1.4.2.old/src/newusers.c shadow-4.1.4.2/src/newusers.c
--- shadow-4.1.4.2.old/src/newusers.c	2009-05-10 09:49:05.000000000 -0400
+++ shadow-4.1.4.2/src/newusers.c	2010-03-05 06:13:29.000000000 -0500
@@ -73,14 +73,12 @@
 char *Prog;
 
 static bool rflg = false;	/* create a system account */
-#ifndef USE_PAM
 static bool cflg = false;
 static char *crypt_method = NULL;
 #ifdef USE_SHA_CRYPT
 static bool sflg = false;
 static long sha_rounds = 5000;
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
 
 static bool is_shadow;
 #ifdef SHADOWGRP
@@ -97,9 +95,7 @@
 static int add_group (const char *, const char *, gid_t *, gid_t);
 static int get_user_id (const char *, uid_t *);
 static int add_user (const char *, uid_t, gid_t);
-#ifndef USE_PAM
 static void update_passwd (struct passwd *, const char *);
-#endif				/* !USE_PAM */
 static int add_passwd (struct passwd *, const char *);
 static void process_flags (int argc, char **argv);
 static void check_flags (void);
@@ -117,7 +113,9 @@
 	                  "\n"
 	                  "Options:\n"),
 	                Prog);
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	(void) fprintf (stderr,
 	                _("  -c, --crypt-method            the crypt method (one of %s)\n"),
 #ifndef USE_SHA_CRYPT
@@ -126,16 +124,28 @@
 	                "NONE DES MD5 SHA256 SHA512"
 #endif				/* USE_SHA_CRYPT */
 	               );
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
 	(void) fputs (_("  -r, --system                  create system accounts\n"), stderr);
-#ifndef USE_PAM
+	(void) fputs (_("  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"), stderr);
+#ifdef USE_PAM
+	if (!chroot_flg) {
+		(void) fprintf (stderr,
+	                _("                                use \'%s --help -R [ROOT_DIR]\' to see\n"
+	                  "                                available options\n"),
+					Prog);
+	} else {
+#endif
 #ifdef USE_SHA_CRYPT
 	(void) fputs (_("  -s, --sha-rounds              number of SHA rounds for the SHA*\n"
 	                "                                crypt algorithms\n"),
 	              stderr);
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	(void) fputs ("\n", stderr);
 
 	exit (EXIT_FAILURE);
@@ -380,7 +390,6 @@
 	return (pw_update (&pwent) == 0);
 }
 
-#ifndef USE_PAM
 static void update_passwd (struct passwd *pwd, const char *password)
 {
 	void *crypt_arg = NULL;
@@ -400,7 +409,6 @@
 		                                              crypt_arg));
 	}
 }
-#endif				/* !USE_PAM */
 
 /*
  * add_passwd - add or update the encrypted password
@@ -409,9 +417,11 @@
 {
 	const struct spwd *sp;
 	struct spwd spent;
-
-#ifndef USE_PAM
 	void *crypt_arg = NULL;
+
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if (crypt_method != NULL) {
 #ifdef USE_SHA_CRYPT
 		if (sflg) {
@@ -429,14 +439,18 @@
 		update_passwd (pwd, password);
 		return 0;
 	}
-#endif				/* USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 
 	/*
 	 * Do the first and easiest shadow file case. The user already
 	 * exists in the shadow password file.
 	 */
 	sp = spw_locate (pwd->pw_name);
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if (NULL != sp) {
 		spent = *sp;
 		if (   (NULL != crypt_method)
@@ -466,7 +480,8 @@
 		update_passwd (pwd, password);
 		return 0;
 	}
-#else				/* USE_PAM */
+#ifdef USE_PAM
+	} else {
 	/*
 	 * If there is already a shadow entry, do not touch it.
 	 * If there is already a passwd entry with a password, do not
@@ -477,26 +492,31 @@
 	    || (strcmp (pwd->pw_passwd, "x") != 0)) {
 		return 0;
 	}
-#endif				/* USE_PAM */
+	}
+#endif
 
 	/*
 	 * Now the really hard case - I need to create an entirely new
 	 * shadow password file entry.
 	 */
 	spent.sp_namp = pwd->pw_name;
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) {
 		spent.sp_pwdp = (char *)password;
 	} else {
 		const char *salt = crypt_make_salt (crypt_method, crypt_arg);
 		spent.sp_pwdp = pw_encrypt (password, salt);
 	}
-#else
+#ifdef USE_PAM
+	} else {
 	/*
 	 * Lock the password.
 	 * The password will be updated later for all users using PAM.
 	 */
 	spent.sp_pwdp = "!";
+	}
 #endif
 	spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
 	if (0 == spent.sp_lstchg) {
@@ -524,28 +544,25 @@
 	int option_index = 0;
 	int c;
 	static struct option long_options[] = {
-#ifndef USE_PAM
 		{"crypt-method", required_argument, NULL, 'c'},
 #ifdef USE_SHA_CRYPT
 		{"sha-rounds", required_argument, NULL, 's'},
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
 		{"help", no_argument, NULL, 'h'},
 		{"system", no_argument, NULL, 'r'},
 		{NULL, 0, NULL, '\0'}
 	};
 
 	while ((c = getopt_long (argc, argv,
-#ifndef USE_PAM
 #ifdef USE_SHA_CRYPT
 	                         "c:hrs:",
 #else				/* !USE_SHA_CRYPT */
 	                         "c:hr",
 #endif				/* !USE_SHA_CRYPT */
-#else				/* USE_PAM */
-	                         "hr",
-#endif
 	                     long_options, &option_index)) != -1) {
+		if (!chroot_flg && (c != 'h') && (c != 'r'))
+			usage ();
+
 		switch (c) {
 		case 'h':
 			usage ();
@@ -553,7 +570,6 @@
 		case 'r':
 			rflg = true;
 			break;
-#ifndef USE_PAM
 		case 'c':
 			cflg = true;
 			crypt_method = optarg;
@@ -569,7 +585,6 @@
 			}
 			break;
 #endif				/* USE_SHA_CRYPT */
-#endif				/* !USE_PAM */
 		default:
 			usage ();
 			break;
@@ -596,7 +611,6 @@
  */
 static void check_flags (void)
 {
-#ifndef USE_PAM
 #ifdef USE_SHA_CRYPT
 	if (sflg && !cflg) {
 		fprintf (stderr,
@@ -621,7 +635,6 @@
 			usage ();
 		}
 	}
-#endif				/* !USE_PAM */
 }
 
 /*
@@ -836,10 +849,12 @@
 
 	OPENLOG ("newusers");
 
-	process_flags (argc, argv);
-
 	check_perms ();
 
+	process_chroot ('R', &argc, argv);
+
+	process_flags (argc, argv);
+
 	is_shadow = spw_file_present ();
 
 #ifdef SHADOWGRP
@@ -965,7 +980,8 @@
 		}
 		newpw = *pw;
 
-#if USE_PAM
+#ifdef USE_PAM
+	if (!chroot_flg) {
 		/* keep the list of user/password for later update by PAM */
 		nusers++;
 		lines     = realloc (lines,     sizeof (lines[0])     * nusers);
@@ -974,6 +990,7 @@
 		lines[nusers-1]     = line;
 		usernames[nusers-1] = strdup (fields[0]);
 		passwords[nusers-1] = strdup (fields[1]);
+	}
 #endif
 		if (add_passwd (&newpw, fields[1])) {
 			fprintf (stderr,
@@ -1045,6 +1062,7 @@
 	nscd_flush_cache ("group");
 
 #ifdef USE_PAM
+	if (!chroot_flg) {
 	unsigned int i;
 	/* Now update the passwords using PAM */
 	for (i = 0; i < nusers; i++) {
@@ -1055,7 +1073,8 @@
 			errors++;
 		}
 	}
-#endif				/* USE_PAM */
+	}
+#endif
 
 	return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE);
 }
diff -Naur shadow-4.1.4.2.old/src/passwd.c shadow-4.1.4.2/src/passwd.c
--- shadow-4.1.4.2.old/src/passwd.c	2009-05-22 09:32:30.000000000 -0400
+++ shadow-4.1.4.2/src/passwd.c	2010-03-05 06:13:29.000000000 -0500
@@ -101,14 +101,11 @@
 static long warn = 0;		/* Warning days before change   */
 static long inact = 0;		/* Days without change before locked */
 
-#ifndef USE_PAM
 static bool do_update_age = false;
-#endif				/* ! USE_PAM */
 
 static bool pw_locked = false;
 static bool spw_locked = false;
 
-#ifndef USE_PAM
 /*
  * Size of the biggest passwd:
  *   $6$	3
@@ -124,7 +121,6 @@
  */
 static char crypt_passwd[256];
 static bool do_update_pwd = false;
-#endif				/* !USE_PAM */
 
 /*
  * External identifiers
@@ -133,13 +129,11 @@
 /* local function prototypes */
 static void usage (int);
 
-#ifndef USE_PAM
 static int reuse (const char *, const struct passwd *);
 static int new_password (const struct passwd *);
 
 static void check_password (const struct passwd *, const struct spwd *);
 static char *insert_crypt_passwd (const char *, const char *);
-#endif				/* !USE_PAM */
 static char *date_to_str (time_t);
 static const char *pw_status (const char *);
 static void print_status (const struct passwd *);
@@ -175,6 +169,7 @@
 	         "                                change to MIN_DAYS\n"
 	         "  -q, --quiet                   quiet mode\n"
 	         "  -r, --repository REPOSITORY   change password in REPOSITORY repository\n"
+	         "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	         "  -S, --status                  report password status on the named account\n"
 	         "  -u, --unlock                  unlock the password of the named account\n"
 	         "  -w, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"
@@ -184,7 +179,6 @@
 	exit (status);
 }
 
-#ifndef USE_PAM
 static int reuse (const char *pass, const struct passwd *pw)
 {
 #ifdef HAVE_LIBCRACK_HIST
@@ -426,7 +420,6 @@
 {
 	return xstrdup (passwd);
 }
-#endif				/* !USE_PAM */
 
 static char *date_to_str (time_t t)
 {
@@ -506,11 +499,15 @@
 
 static char *update_crypt_pw (char *cp)
 {
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if (do_update_pwd) {
 		cp = insert_crypt_passwd (cp, crypt_passwd);
 	}
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 
 	if (dflg) {
 		*cp = '\0';
@@ -638,7 +635,9 @@
 	if (iflg) {
 		nsp->sp_inact = (inact * DAY) / SCALE;
 	}
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	if (do_update_age) {
 		nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
 		if (0 == nsp->sp_lstchg) {
@@ -647,7 +646,9 @@
 			nsp->sp_lstchg = -1;
 		}
 	}
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 
 	/*
 	 * Force change on next login, like SunOS 4.x passwd -e or Solaris
@@ -761,11 +762,9 @@
 {
 	const struct passwd *pw;	/* Password file entry for user      */
 
-#ifndef USE_PAM
 	char *cp;		/* Miscellaneous character pointing  */
 
 	const struct spwd *sp;	/* Shadow file entry for user   */
-#endif				/* !USE_PAM */
 
 	(void) setlocale (LC_ALL, "");
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
@@ -787,6 +786,8 @@
 
 	OPENLOG ("passwd");
 
+	process_chroot ('R', &argc, argv);
+
 	{
 		/*
 		 * Parse the command line options.
@@ -1030,7 +1031,9 @@
 		print_status (pw);
 		exit (E_SUCCESS);
 	}
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 	/*
 	 * The user name is valid, so let's get the shadow file entry.
 	 */
@@ -1070,7 +1073,9 @@
 		do_update_pwd = true;
 		do_update_age = true;
 	}
-#endif				/* !USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	/*
 	 * Before going any further, raise the ulimit to prevent colliding
 	 * into a lowered ulimit, and set the real UID to root to protect
@@ -1080,6 +1085,8 @@
 	pwd_init ();
 
 #ifdef USE_PAM
+	if (!chroot_flg) {
+#endif
 	/*
 	 * Don't set the real UID for PAM...
 	 */
@@ -1087,7 +1094,9 @@
 		do_pam_passwd (name, qflg, kflg);
 		exit (E_SUCCESS);
 	}
-#endif				/* USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 	if (setuid (0) != 0) {
 		fputs (_("Cannot change ID to root.\n"), stderr);
 		SYSLOG ((LOG_ERR, "can't setuid(0)"));
@@ -1107,9 +1116,13 @@
 	closelog ();
 	if (!qflg) {
 		if (!anyflag) {
-#ifndef USE_PAM
+#ifdef USE_PAM
+	if (chroot_flg) {
+#endif
 			printf (_("%s: password changed.\n"), Prog);
-#endif				/* USE_PAM */
+#ifdef USE_PAM
+	}
+#endif
 		} else {
 			printf (_("%s: password expiry information changed.\n"), Prog);
 		}
diff -Naur shadow-4.1.4.2.old/src/pwconv.c shadow-4.1.4.2/src/pwconv.c
--- shadow-4.1.4.2.old/src/pwconv.c	2009-04-30 17:39:40.000000000 -0400
+++ shadow-4.1.4.2/src/pwconv.c	2010-03-05 06:13:29.000000000 -0500
@@ -122,9 +122,6 @@
 	const struct spwd *sp;
 	struct spwd spent;
 
-	if (1 != argc) {
-		(void) fputs (_("Usage: pwconv\n"), stderr);
-	}
 	Prog = Basename (argv[0]);
 
 	(void) setlocale (LC_ALL, "");
@@ -133,6 +130,13 @@
 
 	OPENLOG ("pwconv");
 
+	process_chroot ('R', &argc, argv);
+
+	if (1 != argc) {
+		fprintf (stderr, _("Usage: %s [-R root_dir]\n"), Prog);
+		fail_exit (2);
+	}
+
 	if (pw_lock () == 0) {
 		fprintf (stderr,
 		         _("%s: cannot lock %s; try again later.\n"),
diff -Naur shadow-4.1.4.2.old/src/pwunconv.c shadow-4.1.4.2/src/pwunconv.c
--- shadow-4.1.4.2.old/src/pwunconv.c	2009-04-30 17:44:37.000000000 -0400
+++ shadow-4.1.4.2/src/pwunconv.c	2010-03-05 06:13:29.000000000 -0500
@@ -82,9 +82,6 @@
 	struct passwd pwent;
 	const struct spwd *spwd;
 
-	if (1 != argc) {
-		(void) fputs (_("Usage: pwunconv\n"), stderr);
-	}
 	Prog = Basename (argv[0]);
 
 	(void) setlocale (LC_ALL, "");
@@ -93,6 +90,13 @@
 
 	OPENLOG ("pwunconv");
 
+	process_chroot ('R', &argc, argv);
+
+	if (1 != argc) {
+		fprintf (stderr, _("Usage: %s [-R root_dir]\n"), Prog);
+		fail_exit (2);
+	}
+
 	if (!spw_file_present ()) {
 		/* shadow not installed, do nothing */
 		exit (0);
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-03-05 16:25:34.000000000 -0500
@@ -712,6 +712,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);
@@ -1246,7 +1247,14 @@
 		         Prog, "-m", "-M");
 		usage ();
 	}
-
+#ifdef WITH_SELINUX
+	if (Zflg && chroot_flg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-Z", "-R");
+		usage ();
+	}
+#endif
 	/*
 	 * Either -D or username is required. Defaults can be set with -D
 	 * for the -b, -e, -f, -g, -s options only.
@@ -1861,16 +1869,6 @@
 	 */
 	user_groups[0] = (char *) 0;
 
-
-	is_shadow_pwd = spw_file_present ();
-#ifdef SHADOWGRP
-	is_shadow_grp = sgr_file_present ();
-#endif
-
-	get_defaults ();
-
-	process_flags (argc, argv);
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -1905,6 +1903,22 @@
 #endif				/* ACCT_TOOLS_SETUID */
 
 	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after PAM authentication.
+	 */
+	process_chroot ('R', &argc, argv);
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+
+	get_defaults ();
+
+	process_flags (argc, argv);
+
+	/*
 	 * See if we are messing with the defaults file, or creating
 	 * a new user.
 	 */
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-03-05 16:17:30.000000000 -0500
@@ -120,6 +120,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);
 }
@@ -757,38 +758,6 @@
 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
 	(void) textdomain (PACKAGE);
 
-	{
-		/*
-		 * Parse the command line options.
-		 */
-		int c;
-		static struct option long_options[] = {
-			{"force", no_argument, NULL, 'f'},
-			{"help", no_argument, NULL, 'h'},
-			{"remove", no_argument, NULL, 'r'},
-			{NULL, 0, NULL, '\0'}
-		};
-		while ((c = getopt_long (argc, argv, "fhr",
-		                         long_options, NULL)) != -1) {
-			switch (c) {
-			case 'f':	/* force remove even if not owned by user */
-				fflg = true;
-				break;
-			case 'r':	/* remove home dir and mailbox */
-				rflg = true;
-				break;
-			default:
-				usage ();
-			}
-		}
-	}
-
-	if ((optind + 1) != argc) {
-		usage ();
-	}
-
-	OPENLOG ("userdel");
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -822,6 +791,45 @@
 #endif				/* USE_PAM */
 #endif				/* ACCT_TOOLS_SETUID */
 
+	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files
+	 * but after PAM authentication.
+	 */
+	process_chroot ('R', &argc, argv);
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"force", no_argument, NULL, 'f'},
+			{"help", no_argument, NULL, 'h'},
+			{"remove", no_argument, NULL, 'r'},
+			{NULL, 0, NULL, '\0'}
+		};
+		while ((c = getopt_long (argc, argv, "fhr",
+		                         long_options, NULL)) != -1) {
+			switch (c) {
+			case 'f':	/* force remove even if not owned by user */
+				fflg = true;
+				break;
+			case 'r':	/* remove home dir and mailbox */
+				rflg = true;
+				break;
+			default:
+				usage ();
+			}
+		}
+	}
+
+	if ((optind + 1) != argc) {
+		usage ();
+	}
+
+	OPENLOG ("userdel");
+
 	is_shadow_pwd = spw_file_present ();
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
@@ -975,7 +983,7 @@
 #endif
 
 #ifdef WITH_SELINUX
-	if (is_selinux_enabled () > 0) {
+	if ((is_selinux_enabled () > 0) && !chroot_flg) {
 		const char *args[5];
 		args[0] = "/usr/sbin/semanage";
 		args[1] = "login";
diff -Naur shadow-4.1.4.2.old/src/usermod.c shadow-4.1.4.2/src/usermod.c
--- shadow-4.1.4.2.old/src/usermod.c	2009-05-22 06:42:53.000000000 -0400
+++ shadow-4.1.4.2/src/usermod.c	2010-03-05 16:25:48.000000000 -0500
@@ -323,6 +323,7 @@
 	         "                                new location (use only with -d)\n"
 	         "  -o, --non-unique              allow using duplicate (non-unique) UID\n"
 	         "  -p, --password PASSWORD       use encrypted password for the new password\n"
+	         "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	         "  -s, --shell SHELL             new login shell for the user account\n"
 	         "  -u, --uid UID                 new UID for the user account\n"
 	         "  -U, --unlock                  unlock the user account\n"
@@ -1126,7 +1127,15 @@
 		usage ();
 		exit (E_USAGE);
 	}
-
+#ifdef WITH_SELINUX
+	if (Zflg && chroot_flg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-Z", "-R");
+		usage ();
+		exit (E_USAGE);
+	}
+#endif
 	/* local, no need for xgetpwnam */
 	if (lflg && (getpwnam (user_newname) != NULL)) {
 		fprintf (stderr,
@@ -1715,25 +1724,6 @@
 
 	OPENLOG ("usermod");
 
-	is_shadow_pwd = spw_file_present ();
-#ifdef SHADOWGRP
-	is_shadow_grp = sgr_file_present ();
-#endif
-
-	process_flags (argc, argv);
-
-	/*
-	 * The home directory, the username and the user's UID should not
-	 * be changed while the user is logged in.
-	 */
-	if (   (uflg || lflg || dflg)
-	    && (user_busy (user_name, user_id) != 0)) {
-		fprintf (stderr,
-		         _("%s: user %s is currently logged in\n"),
-		         Prog, user_name);
-		exit (E_USER_BUSY);
-	}
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -1768,6 +1758,32 @@
 #endif				/* ACCT_TOOLS_SETUID */
 
 	/*
+	 * Parse the chroot option before other options
+	 * and before accessing any database files,
+	 * but after PAM authentication.
+	 */
+	process_chroot ('R', &argc, argv);
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+
+	process_flags (argc, argv);
+
+	/*
+	 * The home directory, the username and the user's UID should not
+	 * be changed while the user is logged in.
+	 */
+	if (   (uflg || lflg || dflg)
+	    && (user_busy (user_name, user_id) != 0)) {
+		fprintf (stderr,
+		         _("%s: user %s is currently logged in\n"),
+		         Prog, user_name);
+		exit (E_USER_BUSY);
+	}
+
+	/*
 	 * Do the hard stuff - open the files, change the user entries,
 	 * change the home directory, then close and update the files.
 	 */
diff -Naur shadow-4.1.4.2.old/src/vipw.c shadow-4.1.4.2/src/vipw.c
--- shadow-4.1.4.2.old/src/vipw.c	2009-05-25 15:51:28.000000000 -0400
+++ shadow-4.1.4.2/src/vipw.c	2010-03-05 06:13:29.000000000 -0500
@@ -54,10 +54,13 @@
 	"You may need to modify %s for consistency.\n"\
 	"Please use the command '%s' to do so.\n")
 
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
 /*
  * Global variables
  */
-static const char *progname, *filename, *fileeditname;
+static const char *progname, *fileeditname, *chrootdir;
+static char *filename;
 static bool filelocked = false;
 static bool createedit = false;
 static int (*unlock) (void);
@@ -82,6 +85,7 @@
 	         "  -h, --help                    display this help message and exit\n"
 	         "  -p, --passwd                  edit passwd database\n"
 	         "  -q, --quiet                   quiet mode\n"
+	         "  -R, --chroot ROOT_DIR         access files from an alternate root directory\n"
 	         "  -s, --shadow                  edit shadow or gshadow database\n"
 	         "\n"), stderr);
 	exit (E_USAGE);
@@ -188,10 +192,18 @@
 	FILE *f;
 	char filebackup[1024], fileedit[1024];
 
-	snprintf (filebackup, sizeof filebackup, "%s-", file);
-	snprintf (fileedit, sizeof fileedit, "%s.edit", file);
+	if (!chroot_flg) {
+		filename = (char *) file;
+	} else {
+		filename = strdup (chrootdir);
+		filename = (char *) realloc (filename,
+									(strlen (filename) + strlen (file) +1 )
+									* sizeof(char));
+		filename = strcat (filename, file);
+	}
+	snprintf (filebackup, sizeof filebackup, "%s-", filename);
+	snprintf (fileedit, sizeof fileedit, "%s.edit", filename);
 	unlock = file_unlock;
-	filename = file;
 	fileeditname = fileedit;
 
 	if (access (file, F_OK) != 0) {
@@ -203,7 +215,7 @@
 	if (is_selinux_enabled ()) {
 		security_context_t passwd_context=NULL;
 		int ret = 0;
-		if (getfilecon (file, &passwd_context) < 0) {
+		if (getfilecon (filename, &passwd_context) < 0) {
 			vipwexit (_("Couldn't get file context"), errno, 1);
 		}
 		ret = setfscreatecon (passwd_context);
@@ -219,12 +231,12 @@
 	filelocked = true;
 
 	/* edited copy has same owners, perm */
-	if (stat (file, &st1) != 0) {
-		vipwexit (file, 1, 1);
+	if (stat (filename, &st1) != 0) {
+		vipwexit (filename, 1, 1);
 	}
-	f = fopen (file, "r");
+	f = fopen (filename, "r");
 	if (NULL == f) {
-		vipwexit (file, 1, 1);
+		vipwexit (filename, 1, 1);
 	}
 	if (create_backup_file (f, fileedit, &st1) != 0) {
 		vipwexit (_("Couldn't make backup"), errno, 1);
@@ -301,11 +313,11 @@
 	 */
 	createedit = false;
 	unlink (filebackup);
-	link (file, filebackup);
-	if (rename (fileedit, file) == -1) {
+	link (filename, filebackup);
+	if (rename (fileedit, filename) == -1) {
 		fprintf (stderr,
 		         _("%s: can't restore %s: %s (your changes are in %s)\n"),
-		         progname, file, strerror (errno), fileedit);
+		         progname, filename, strerror (errno), fileedit);
 		vipwexit (0, 0, 1);
 	}
 
@@ -342,11 +354,12 @@
 			{"help", no_argument, NULL, 'h'},
 			{"passwd", no_argument, NULL, 'p'},
 			{"quiet", no_argument, NULL, 'q'},
+			{"chroot", required_argument, NULL, 'R'},
 			{"shadow", no_argument, NULL, 's'},
 			{NULL, 0, NULL, '\0'}
 		};
 		while ((c =
-			getopt_long (argc, argv, "ghpqs",
+			getopt_long (argc, argv, "ghpqR:s",
 				     long_options, NULL)) != -1) {
 			switch (c) {
 			case 'g':
@@ -361,6 +374,17 @@
 			case 'q':
 				quiet = true;
 				break;
+			case 'R':
+				if (   ( !VALID (optarg) )
+				    || ( optarg[0] != '/' )) {
+					fprintf (stderr,
+					         _("%s: invalid root directory '%s'\n"),
+					         progname, optarg);
+					exit (3);
+				}
+				chroot_flg = true;
+				chrootdir = optarg;
+				break;
 			case 's':
 				editshadow = true;
 				break;

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

* Re: [gentoo-embedded] emerge --root : users not created
  2010-03-06  0:52 ` P. Levine
@ 2010-03-08 11:05   ` Ed W
  0 siblings, 0 replies; 26+ messages in thread
From: Ed W @ 2010-03-08 11:05 UTC (permalink / raw
  To: gentoo-embedded

On 06/03/2010 00:52, P. Levine wrote:
> I've tested all related utilities with various arguments and found them
> all functional, with and without the --chroot flag.
>    

This is fantastic development!

Ed W



^ 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