Patchwork [2/7] shadow: add a -native recipe with customized utilities

login
register
mail settings
Submitter Scott Garman
Date May 31, 2011, 7:53 p.m.
Message ID <7826575ce92090c4460c7d016e0b06441f84cff7.1306865217.git.scott.a.garman@intel.com>
Download mbox | patch
Permalink /patch/5127/
State New, archived
Headers show

Comments

Scott Garman - May 31, 2011, 7:53 p.m.
This adds a -native recipe for the shadow utilities.

The custom --root option allows the the following utilities to be
run within a chroot when invoked under pseudo:

* useradd
* groupadd
* usermod
* groupmod
* userdel
* groupdel
* passwd
* gpasswd
* pwconv
* pwunconv
* grpconv
* grpunconv

They can then be used to manipulate user and group account information
in target sysroots.

useradd was also modified to create home directories recursively when
necessary.

Signed-off-by: Scott Garman <scott.a.garman@intel.com>
---
 .../shadow/files/add_root_cmd_options.patch        | 1293 ++++++++++++++++++++
 .../shadow/shadow-native_4.1.4.3.bb                |   66 +
 2 files changed, 1359 insertions(+), 0 deletions(-)
 create mode 100644 meta/recipes-extended/shadow/files/add_root_cmd_options.patch
 create mode 100644 meta/recipes-extended/shadow/shadow-native_4.1.4.3.bb
Phil Blundell - June 1, 2011, 9:47 a.m.
On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
> This adds a -native recipe for the shadow utilities.
> 
> The custom --root option allows the the following utilities to be
> run within a chroot when invoked under pseudo:

Rather than patching the code for all these utilities, can't you just
wrap them in a call to chroot(8)?  That is, make useradd.bbclass do:

eval $PSEUDO chroot ${STAGING_DIR_TARGET} useradd ...

rather than the existing

eval $PSEUDO useradd --root ${STAGING_DIR_TARGET} ...

?

p.
Martyn Welch - June 1, 2011, 12:34 p.m.
On 01/06/11 10:47, Phil Blundell wrote:
> On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
>> This adds a -native recipe for the shadow utilities.
>>
>> The custom --root option allows the the following utilities to be
>> run within a chroot when invoked under pseudo:
> 
> Rather than patching the code for all these utilities, can't you just
> wrap them in a call to chroot(8)?  That is, make useradd.bbclass do:
> 
> eval $PSEUDO chroot ${STAGING_DIR_TARGET} useradd ...
> 
> rather than the existing
> 
> eval $PSEUDO useradd --root ${STAGING_DIR_TARGET} ...
> 
> ?
> 

You'd need root privileges to use chroot wouldn't you? I'm assuming you
wouldn't with the existing.

Martyn
Phil Blundell - June 1, 2011, 1:42 p.m.
On Wed, 2011-06-01 at 13:34 +0100, Martyn Welch wrote:
> On 01/06/11 10:47, Phil Blundell wrote:
> > On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
> >> This adds a -native recipe for the shadow utilities.
> >>
> >> The custom --root option allows the the following utilities to be
> >> run within a chroot when invoked under pseudo:
> > 
> > Rather than patching the code for all these utilities, can't you just
> > wrap them in a call to chroot(8)?  That is, make useradd.bbclass do:
> > 
> > eval $PSEUDO chroot ${STAGING_DIR_TARGET} useradd ...
> > 
> > rather than the existing
> > 
> > eval $PSEUDO useradd --root ${STAGING_DIR_TARGET} ...
> > 
> > ?
> > 
> 
> You'd need root privileges to use chroot wouldn't you? I'm assuming you
> wouldn't with the existing.

Doesn't $PSEUDO sort that out?  The --root option that Scott has patched
into all the utilities seems to just end up calling chroot(2) anyway so
you would need root privileges at that point.  That said, I'm not
entirely au fait with pseudo so I might well be misunderstanding how it
works.

p.
Scott Garman - June 1, 2011, 5:43 p.m.
On 06/01/2011 02:47 AM, Phil Blundell wrote:
> On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
>> This adds a -native recipe for the shadow utilities.
>>
>> The custom --root option allows the the following utilities to be
>> run within a chroot when invoked under pseudo:
>
> Rather than patching the code for all these utilities, can't you just
> wrap them in a call to chroot(8)?  That is, make useradd.bbclass do:
>
> eval $PSEUDO chroot ${STAGING_DIR_TARGET} useradd ...
>
> rather than the existing
>
> eval $PSEUDO useradd --root ${STAGING_DIR_TARGET} ...

That's a reasonable suggestion. I haven't tried it yet, but I have found 
that pseudo's chroot(2) implementation is not complete. One of the cases 
where it does not work is when forking child processes, which breaks the 
jail and the child processes are no longer chroot'ed.

My guess is that chroot(8) is going to call chroot(2) and then fork a 
child process to run its additional arguments.

cc'ing Mark directly in case he has additional comments or needs to 
correct me.

Scott
Mark Hatle - June 1, 2011, 9:05 p.m.
On 6/1/11 12:43 PM, Scott Garman wrote:
> On 06/01/2011 02:47 AM, Phil Blundell wrote:
>> On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
>>> This adds a -native recipe for the shadow utilities.
>>>
>>> The custom --root option allows the the following utilities to be
>>> run within a chroot when invoked under pseudo:
>>
>> Rather than patching the code for all these utilities, can't you just
>> wrap them in a call to chroot(8)?  That is, make useradd.bbclass do:
>>
>> eval $PSEUDO chroot ${STAGING_DIR_TARGET} useradd ...
>>
>> rather than the existing
>>
>> eval $PSEUDO useradd --root ${STAGING_DIR_TARGET} ...
> 
> That's a reasonable suggestion. I haven't tried it yet, but I have found 
> that pseudo's chroot(2) implementation is not complete. One of the cases 
> where it does not work is when forking child processes, which breaks the 
> jail and the child processes are no longer chroot'ed.
> 
> My guess is that chroot(8) is going to call chroot(2) and then fork a 
> child process to run its additional arguments.

chroot should be complete in pseudo, if you can reproduce any failures we should
pass them upstream.

The reason I suggested the --root option was primarily for the ease of people
who are NOT using the automated scripting, i.e. someone manually adding a
preinst (or similar) to their recipes.

The --root option is easier (to me at least) to understand that having to if-def
stuff around a chroot..  (but that is personal preference...)

--Mark

> cc'ing Mark directly in case he has additional comments or needs to 
> correct me.
> 
> Scott
>
Phil Blundell - June 2, 2011, 11:19 a.m.
On Wed, 2011-06-01 at 16:05 -0500, Mark Hatle wrote:
> The reason I suggested the --root option was primarily for the ease of people
> who are NOT using the automated scripting, i.e. someone manually adding a
> preinst (or similar) to their recipes.

I was thinking about this stuff the other day for update-rc.d.  Really,
it sucks that all the manually-written postinsts have to include all of
this garbage about checking for $D.  It also sucks a bit that this same
code (for both manually-written and auto-generated postinsts) ends up
getting deployed into the packages and onto the target system, where it
is at best a waste of space.

I think maybe a better way of dealing with that whole thing would be for
update-rc.d-native and similar tools to provide a set of wrapper scripts
which would take care of all the machinations required to support
offline root mode, be that adding "--root $D" or doing stuff with chroot
or whatever.  If those were installed into some appropriate place in the
sysroot then we could arrange for the right directory to be prepended to
$PATH during rootfs construction and then everything ought to "just
work" without the postinst scripts needing to take any special measures.

p.
Phil Blundell - Sept. 1, 2011, 2:46 p.m.
I just tried using useradd.bbclass for the first time (in an effort to
make dbus installable on a readonly-rootfs) and it doesn't seem to be
working very well for me.

The root of my problem seems to be the code below.  As far as I can
tell, what's happening is that process_root_flag() consumes all the
command line arguments to useradd, which means that the subsequent call
to getopt() in process_flags() just returns immediately because there is
nothing left for it to do.  The upshot of all this is that the switches
on the command line are simply ignored and useradd doesn't do what I
wanted.

Is anybody else using this code successfully in oe-core with a
nontrivial USERADD_PARAM?

p.

On Tue, 2011-05-31 at 12:53 -0700, Scott Garman wrote:
> + /*
> ++ * process_root_flag - chroot if given the --root option
> ++ *
> ++ * We do this outside of process_flags() because
> ++ * the is_shadow_pwd boolean needs to be set before
> ++ * process_flags(), and if we do need to chroot() we
> ++ * must do so before is_shadow_pwd gets set.
> ++ */
> ++static void process_root_flag (int argc, char **argv)
> ++{
> ++	/*
> ++	 * Parse the command line options.
> ++	 */
> ++	int flag;
> ++	int option_index = 0;
> ++	static struct option long_options[] = {
> ++		{"root", required_argument, NULL, 'Q'},
> ++		{NULL, 0, NULL, '\0'}
> ++	};
> ++
> ++	while ((flag = getopt_long (argc, argv, "a:A:d:gM:Q:rR", long_options, &option_index)) != -1) {
> ++		switch (flag) {
> ++		case 'Q':
> ++			if ('/' != optarg[0]) {
> ++				fprintf (stderr,
> ++				         _("%s: invalid chroot path '%s'\n"),
> ++				         Prog, optarg);
> ++				exit (E_BAD_ARG);
> ++			}
> ++			newroot = optarg;
> ++
> ++			if (access (newroot, F_OK) != 0) {
> ++				fprintf(stderr,
> ++				        _("%s: chroot directory %s does not exist\n"),
> ++				        Prog, newroot);
> ++				exit (E_BAD_ARG);
> ++			}
> ++			if ( chroot(newroot) != 0 ) {
> ++				fprintf(stderr,
> ++				        _("%s: unable to chroot to directory %s\n"),
> ++				        Prog, newroot);
> ++				exit (E_BAD_ARG);
> ++			}
> ++			break;
> ++		/* no-op on everything else - they will be hanled by process_flags() */
> ++		}
> ++	}
> ++}

Patch

diff --git a/meta/recipes-extended/shadow/files/add_root_cmd_options.patch b/meta/recipes-extended/shadow/files/add_root_cmd_options.patch
new file mode 100644
index 0000000..3511ebb
--- /dev/null
+++ b/meta/recipes-extended/shadow/files/add_root_cmd_options.patch
@@ -0,0 +1,1293 @@ 
+Add a --root command option to the following utilties:
+
+* useradd
+* groupadd
+* usermod
+* groupmod
+* userdel
+* groupdel
+* passwd
+* gpasswd
+* pwconv
+* pwunconv
+* grpconv
+* grpunconv
+
+This option allows the utilities to be chrooted when run under pseudo.
+They can then be used to manipulate user and group account information
+in target sysroots.
+
+Upstream-Status: Inappropriate [Other]
+Workaround is specific to our build system.
+
+Signed-off-by: Scott Garman <scott.a.garman@intel.com>
+
+diff -urN shadow-4.1.4.3.orig//src/gpasswd.c shadow-4.1.4.3//src/gpasswd.c
+--- shadow-4.1.4.3.orig//src/gpasswd.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/gpasswd.c	2011-05-28 17:09:52.346013331 -0700
+@@ -63,6 +63,7 @@
+  * (/etc/gshadow present) */
+ static bool is_shadowgrp;
+ #endif
++static const char *newroot = "";
+ 
+ /* Flags set by options */
+ static bool aflg = false;
+@@ -97,6 +98,7 @@
+ static void usage (void);
+ static RETSIGTYPE catch_signals (int killed);
+ static bool is_valid_user_list (const char *users);
++static void process_root_flag (int argc, char **argv);
+ static void process_flags (int argc, char **argv);
+ static void check_flags (int argc, int opt_index);
+ static void open_files (void);
+@@ -136,6 +138,7 @@
+ 	           "Options:\n"
+ 	           "  -a, --add USER                add USER to GROUP\n"
+ 	           "  -d, --delete USER             remove USER from GROUP\n"
++	           "  -Q  --root CHROOT_DIR         directory to chroot into\n"
+ 	           "  -r, --remove-password         remove the GROUP's password\n"
+ 	           "  -R, --restrict                restrict access to GROUP to its members\n"
+ 	           "  -M, --members USER,...        set the list of members of GROUP\n"
+@@ -226,6 +229,55 @@
+ }
+ 
+ /*
++ * process_root_flag - chroot if given the --root option
++ *
++ * We do this outside of process_flags() because
++ * the is_shadow_pwd boolean needs to be set before
++ * process_flags(), and if we do need to chroot() we
++ * must do so before is_shadow_pwd gets set.
++ */
++static void process_root_flag (int argc, char **argv)
++{
++	/*
++	 * Parse the command line options.
++	 */
++	int flag;
++	int option_index = 0;
++	static struct option long_options[] = {
++		{"root", required_argument, NULL, 'Q'},
++		{NULL, 0, NULL, '\0'}
++	};
++
++	while ((flag = getopt_long (argc, argv, "a:A:d:gM:Q:rR", long_options, &option_index)) != -1) {
++		switch (flag) {
++		case 'Q':
++			if ('/' != optarg[0]) {
++				fprintf (stderr,
++				         _("%s: invalid chroot path '%s'\n"),
++				         Prog, optarg);
++				exit (E_BAD_ARG);
++			}
++			newroot = optarg;
++
++			if (access (newroot, F_OK) != 0) {
++				fprintf(stderr,
++				        _("%s: chroot directory %s does not exist\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			if ( chroot(newroot) != 0 ) {
++				fprintf(stderr,
++				        _("%s: unable to chroot to directory %s\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			break;
++		/* no-op on everything else - they will be hanled by process_flags() */
++		}
++	}
++}
++
++/*
+  * process_flags - process the command line options and arguments
+  */
+ static void process_flags (int argc, char **argv)
+@@ -235,6 +287,7 @@
+ 	static struct option long_options[] = {
+ 		{"add", required_argument, NULL, 'a'},
+ 		{"delete", required_argument, NULL, 'd'},
++		{"root", required_argument, NULL, 'Q'},
+ 		{"remove-password", no_argument, NULL, 'r'},
+ 		{"restrict", no_argument, NULL, 'R'},
+ 		{"administrators", required_argument, NULL, 'A'},
+@@ -242,7 +295,7 @@
+ 		{NULL, 0, NULL, '\0'}
+ 		};
+ 
+-	while ((flag = getopt_long (argc, argv, "a:A:d:gM:rR", long_options, &option_index)) != -1) {
++	while ((flag = getopt_long (argc, argv, "a:A:d:gM:Q:rR", long_options, &option_index)) != -1) {
+ 		switch (flag) {
+ 		case 'a':	/* add a user */
+ 			aflg = true;
+@@ -283,6 +336,9 @@
+ 			}
+ 			Mflg = true;
+ 			break;
++		case 'Q':
++			/* no-op since we handled this in process_root_flag() earlier */
++			break;
+ 		case 'r':	/* remove group password */
+ 			rflg = true;
+ 			break;
+@@ -995,6 +1051,8 @@
+ 	setbuf (stdout, NULL);
+ 	setbuf (stderr, NULL);
+ 
++	process_root_flag (argc, argv);
++
+ #ifdef SHADOWGRP
+ 	is_shadowgrp = sgr_file_present ();
+ #endif
+diff -urN shadow-4.1.4.3.orig//src/groupadd.c shadow-4.1.4.3//src/groupadd.c
+--- shadow-4.1.4.3.orig//src/groupadd.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/groupadd.c	2011-05-28 17:09:52.346013331 -0700
+@@ -76,6 +76,7 @@
+ static gid_t group_id;
+ static /*@null@*/char *group_passwd;
+ static /*@null@*/char *empty_list = NULL;
++static const char *newroot = "";
+ 
+ static bool oflg = false;	/* permit non-unique group ID to be specified with -g */
+ static bool gflg = false;	/* ID value for the new group */
+@@ -120,6 +121,7 @@
+ 	(void) fputs (_("  -o, --non-unique              allow to create groups with duplicate\n"
+ 	                "                                (non-unique) GID\n"), stderr);
+ 	(void) fputs (_("  -p, --password PASSWORD       use this encrypted password for the new group\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
+ 	(void) fputs (_("  -r, --system                  create a system account\n"), stderr);
+ 	(void) fputs ("\n", stderr);
+ 	exit (E_USAGE);
+@@ -383,12 +385,13 @@
+ 		{"key", required_argument, NULL, 'K'},
+ 		{"non-unique", no_argument, NULL, 'o'},
+ 		{"password", required_argument, NULL, 'p'},
++		{"root", required_argument, NULL, 'R'},
+ 		{"system", no_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:R:r", long_options,
+ 		             &option_index)) != -1) {
+ 		switch (c) {
+ 		case 'f':
+@@ -440,6 +443,28 @@
+ 			pflg = true;
+ 			group_passwd = optarg;
+ 			break;
++		case 'R':
++			if ('/' != optarg[0]) {
++				fprintf (stderr,
++				         _("%s: invalid chroot path '%s'\n"),
++				         Prog, optarg);
++				exit (E_BAD_ARG);
++			}
++			newroot = optarg;
++
++			if (access (newroot, F_OK) != 0) {
++				fprintf(stderr,
++				        _("%s: chroot directory %s does not exist\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			if ( chroot(newroot) != 0 ) {
++				fprintf(stderr,
++				        _("%s: unable to chroot to directory %s\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			break;
+ 		case 'r':
+ 			rflg = true;
+ 			break;
+diff -urN shadow-4.1.4.3.orig//src/groupdel.c shadow-4.1.4.3//src/groupdel.c
+--- shadow-4.1.4.3.orig//src/groupdel.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/groupdel.c	2011-05-28 17:09:52.346013331 -0700
+@@ -36,6 +36,7 @@
+ 
+ #include <ctype.h>
+ #include <fcntl.h>
++#include <getopt.h>
+ #include <grp.h>
+ #include <pwd.h>
+ #ifdef ACCT_TOOLS_SETUID
+@@ -59,6 +60,7 @@
+ 
+ static char *group_name;
+ static gid_t group_id = -1;
++static const char *newroot = "";
+ 
+ #ifdef	SHADOWGRP
+ static bool is_shadow_grp;
+@@ -70,12 +72,14 @@
+ /*@-exitarg@*/
+ #define E_SUCCESS	0	/* success */
+ #define E_USAGE		2	/* invalid command syntax */
++#define E_BAD_ARG	3	/* invalid argument to option */
+ #define E_NOTFOUND	6	/* specified group doesn't exist */
+ #define E_GROUP_BUSY	8	/* can't remove user's primary group */
+ #define E_GRP_UPDATE	10	/* can't update group file */
+ 
+ /* local function prototypes */
+ static void usage (void);
++static void process_flags (int argc, char **argv);
+ static void grp_update (void);
+ static void close_files (void);
+ static void open_files (void);
+@@ -86,11 +90,78 @@
+  */
+ static void usage (void)
+ {
+-	fputs (_("Usage: groupdel group\n"), stderr);
++	(void) fprintf (stderr,
++					_("Usage: groupdel [options]\n"
++					  "\n"
++					  "Options:\n"),
++					Prog);
++	(void) fputs (_("  -g, --group GROUP            group name to delete\n"), stderr);
++	(void) fputs (_("  -h, --help                   display this help message and exit\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR        directory to chroot into\n"), stderr);
++	(void) fputs ("\n", stderr);
+ 	exit (E_USAGE);
+ }
+ 
+ /*
++ * process_flags - perform command line argument setting
++ *
++ *	process_flags() interprets the command line arguments and sets
++ *	the values that the user will be created with accordingly. The
++ *	values are checked for sanity.
++ */
++static void process_flags (int argc, char **argv)
++{
++	{
++		/*
++		 * Parse the command line options.
++		 */
++		int c;
++		static struct option long_options[] = {
++			{"group", required_argument, NULL, 'g'},
++			{"help", no_argument, NULL, 'h'},
++			{"root", required_argument, NULL, 'R'},
++			{NULL, 0, NULL, '\0'}
++		};
++		while ((c = getopt_long (argc, argv,
++								 "g:R:",
++								 long_options, NULL)) != -1) {
++			switch (c) {
++			case 'g':
++				group_name = optarg;
++				break;
++			case 'h':
++				usage ();
++				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++							_("%s: invalid chroot path '%s'\n"),
++							Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++							_("%s: chroot directory %s does not exist\n"),
++							Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++							_("%s: unable to chroot to directory %s\n"),
++							Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
++			default:
++				usage ();
++			}
++		}
++	}
++}
++
++/*
+  * grp_update - update group file entries
+  *
+  *	grp_update() writes the new records to the group files.
+@@ -328,14 +399,14 @@
+ 	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+ 	(void) textdomain (PACKAGE);
+ 
+-	if (argc != 2) {
++   if (argc == 1) {
+ 		usage ();
+ 	}
+ 
+-	group_name = argv[1];
+-
+ 	OPENLOG ("groupdel");
+ 
++	process_flags (argc, argv);
++
+ #ifdef ACCT_TOOLS_SETUID
+ #ifdef USE_PAM
+ 	{
+diff -urN shadow-4.1.4.3.orig//src/groupmod.c shadow-4.1.4.3//src/groupmod.c
+--- shadow-4.1.4.3.orig//src/groupmod.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/groupmod.c	2011-05-28 17:09:52.346013331 -0700
+@@ -79,6 +79,7 @@
+ static char *group_passwd;
+ static gid_t group_id;
+ static gid_t group_newid;
++static char *newroot = "";
+ 
+ struct cleanup_info_mod info_passwd;
+ struct cleanup_info_mod info_group;
+@@ -126,6 +127,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, --root CHROOT_DIR         directory to chroot into\n"), stderr);
+ 	(void) fputs ("\n", stderr);
+ 	exit (E_USAGE);
+ }
+@@ -346,10 +348,11 @@
+ 		{"new-name", required_argument, NULL, 'n'},
+ 		{"non-unique", no_argument, NULL, 'o'},
+ 		{"password", required_argument, NULL, 'p'},
++		{"root", required_argument, NULL, 'R'},
+ 		{NULL, 0, NULL, '\0'}
+ 	};
+ 	while ((c =
+-		getopt_long (argc, argv, "g:hn:op:",
++		getopt_long (argc, argv, "g:hn:op:R:",
+ 		             long_options, &option_index)) != -1) {
+ 		switch (c) {
+ 		case 'g':
+@@ -373,6 +376,28 @@
+ 			group_passwd = optarg;
+ 			pflg = true;
+ 			break;
++		case 'R':
++			if ('/' != optarg[0]) {
++				fprintf (stderr,
++				         _("%s: invalid chroot path '%s'\n"),
++				         Prog, optarg);
++				exit (E_BAD_ARG);
++			}
++			newroot = optarg;
++
++			if (access (newroot, F_OK) != 0) {
++				fprintf(stderr,
++				        _("%s: chroot directory %s does not exist\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			if ( chroot(newroot) != 0 ) {
++				fprintf(stderr,
++				        _("%s: unable to chroot to directory %s\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			break;
+ 		default:
+ 			usage ();
+ 		}
+diff -urN shadow-4.1.4.3.orig//src/grpconv.c shadow-4.1.4.3//src/grpconv.c
+--- shadow-4.1.4.3.orig//src/grpconv.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/grpconv.c	2011-05-28 17:09:52.346013331 -0700
+@@ -39,6 +39,7 @@
+ 
+ #include <errno.h>
+ #include <fcntl.h>
++#include <getopt.h>
+ #include <grp.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -50,6 +51,14 @@
+ #ifdef SHADOWGRP
+ #include "groupio.h"
+ #include "sgroupio.h"
++
++/*
++ * exit status values
++ */
++/*@-exitarg@*/
++#define E_USAGE		2	/* invalid command syntax */
++#define E_BAD_ARG	3	/* invalid argument to option */
++
+ /*
+  * Global variables
+  */
+@@ -57,9 +66,12 @@
+ 
+ static bool gr_locked  = false;
+ static bool sgr_locked = false;
++static const char *newroot = "";
+ 
+ /* local function prototypes */
+ static void fail_exit (int status);
++static void usage (void);
++static void process_flags (int argc, char **argv);
+ 
+ static void fail_exit (int status)
+ {
+@@ -82,6 +94,77 @@
+ 	exit (status);
+ }
+ 
++/*
++ * usage - display usage message and exit
++ */
++static void usage (void)
++{
++	(void) fprintf (stderr,
++					_("Usage: grpconv [options]\n"
++					  "\n"
++					  "Options:\n"),
++					Prog);
++	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
++	(void) fputs ("\n", stderr);
++	exit (E_USAGE);
++}
++
++/*
++ * process_flags - perform command line argument setting
++ *
++ *	process_flags() interprets the command line arguments and sets
++ *	the values that the user will be created with accordingly. The
++ *	values are checked for sanity.
++ */
++static void process_flags (int argc, char **argv)
++{
++	{
++		/*
++		 * Parse the command line options.
++		 */
++		int c;
++		static struct option long_options[] = {
++			{"help", no_argument, NULL, 'h'},
++			{"root", required_argument, NULL, 'R'},
++			{NULL, 0, NULL, '\0'}
++		};
++		while ((c = getopt_long (argc, argv,
++								 "R:",
++								 long_options, NULL)) != -1) {
++			switch (c) {
++			case 'h':
++				usage ();
++				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++					        _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
++			default:
++				usage ();
++			}
++		}
++	}
++}
++
+ int main (int argc, char **argv)
+ {
+ 	const struct group *gr;
+@@ -100,6 +183,8 @@
+ 
+ 	OPENLOG ("grpconv");
+ 
++	process_flags (argc, argv);
++
+ 	if (gr_lock () == 0) {
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+diff -urN shadow-4.1.4.3.orig//src/grpunconv.c shadow-4.1.4.3//src/grpunconv.c
+--- shadow-4.1.4.3.orig//src/grpunconv.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/grpunconv.c	2011-05-28 17:09:52.346013331 -0700
+@@ -43,6 +43,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <fcntl.h>
++#include <getopt.h>
+ #include <time.h>
+ #include <unistd.h>
+ #include <grp.h>
+@@ -51,6 +52,14 @@
+ #ifdef SHADOWGRP
+ #include "groupio.h"
+ #include "sgroupio.h"
++
++/*
++ * exit status values
++ */
++/*@-exitarg@*/
++#define E_USAGE		2	/* invalid command syntax */
++#define E_BAD_ARG	3	/* invalid argument to option */
++
+ /*
+  * Global variables
+  */
+@@ -58,9 +67,12 @@
+ 
+ static bool gr_locked  = false;
+ static bool sgr_locked = false;
++static const char *newroot = "";
+ 
+ /* local function prototypes */
+ static void fail_exit (int status);
++static void usage (void);
++static void process_flags (int argc, char **argv);
+ 
+ static void fail_exit (int status)
+ {
+@@ -83,6 +95,77 @@
+ 	exit (status);
+ }
+ 
++/*
++ * usage - display usage message and exit
++ */
++static void usage (void)
++{
++	(void) fprintf (stderr,
++					_("Usage: grpunconv [options]\n"
++					  "\n"
++					  "Options:\n"),
++					Prog);
++	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
++	(void) fputs ("\n", stderr);
++	exit (E_USAGE);
++}
++
++/*
++ * process_flags - perform command line argument setting
++ *
++ * process_flags() interprets the command line arguments and sets
++ * the values that the user will be created with accordingly. The
++ * values are checked for sanity.
++ */
++static void process_flags (int argc, char **argv)
++{
++	{
++		/*
++		 * Parse the command line options.
++		 */
++		int c;
++		static struct option long_options[] = {
++			{"help", no_argument, NULL, 'h'},
++			{"root", required_argument, NULL, 'R'},
++			{NULL, 0, NULL, '\0'}
++		};
++		while ((c = getopt_long (argc, argv,
++								 "R:",
++								 long_options, NULL)) != -1) {
++			switch (c) {
++			case 'h':
++				usage ();
++				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++					        _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
++			default:
++				usage ();
++			}
++		}
++	}
++}
++
+ int main (int argc, char **argv)
+ {
+ 	const struct group *gr;
+@@ -100,6 +183,8 @@
+ 
+ 	OPENLOG ("grpunconv");
+ 
++	process_flags (argc, argv);
++
+ 	if (sgr_file_present () == 0) {
+ 		exit (0);	/* no /etc/gshadow, nothing to do */
+ 	}
+diff -urN shadow-4.1.4.3.orig//src/passwd.c shadow-4.1.4.3//src/passwd.c
+--- shadow-4.1.4.3.orig//src/passwd.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/passwd.c	2011-05-28 17:09:52.346013331 -0700
+@@ -75,6 +75,7 @@
+ static char *name;		/* The name of user whose password is being changed */
+ static char *myname;		/* The current user's name */
+ static bool amroot;		/* The caller's real UID was 0 */
++static const char *newroot = "";
+ 
+ static bool
+     aflg = false,			/* -a - show status for all users */
+@@ -174,6 +175,7 @@
+ 	         "  -n, --mindays MIN_DAYS        set minimum number of days before password\n"
+ 	         "                                change to MIN_DAYS\n"
+ 	         "  -q, --quiet                   quiet mode\n"
++	         "  -R, --root CHROOT_DIR         directory to chroot into\n"
+ 	         "  -r, --repository REPOSITORY   change password in REPOSITORY repository\n"
+ 	         "  -S, --status                  report password status on the named account\n"
+ 	         "  -u, --unlock                  unlock the password of the named account\n"
+@@ -803,6 +805,7 @@
+ 			{"lock", no_argument, NULL, 'l'},
+ 			{"mindays", required_argument, NULL, 'n'},
+ 			{"quiet", no_argument, NULL, 'q'},
++			{"root", required_argument, NULL, 'R'},
+ 			{"repository", required_argument, NULL, 'r'},
+ 			{"status", no_argument, NULL, 'S'},
+ 			{"unlock", no_argument, NULL, 'u'},
+@@ -811,7 +814,7 @@
+ 			{NULL, 0, NULL, '\0'}
+ 		};
+ 
+-		while ((c = getopt_long (argc, argv, "adei:kln:qr:Suw:x:",
++		while ((c = getopt_long (argc, argv, "adei:kln:qR:r:Suw:x:",
+ 		                         long_options, &option_index)) != -1) {
+ 			switch (c) {
+ 			case 'a':
+@@ -858,6 +861,28 @@
+ 			case 'q':
+ 				qflg = true;	/* ok for users */
+ 				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++				            _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
+ 			case 'r':
+ 				/* -r repository (files|nis|nisplus) */
+ 				/* only "files" supported for now */
+diff -urN shadow-4.1.4.3.orig//src/pwconv.c shadow-4.1.4.3//src/pwconv.c
+--- shadow-4.1.4.3.orig//src/pwconv.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/pwconv.c	2011-05-28 17:09:52.346013331 -0700
+@@ -59,6 +59,7 @@
+ 
+ #include <errno.h>
+ #include <fcntl.h>
++#include <getopt.h>
+ #include <pwd.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -79,6 +80,7 @@
+ #define E_SUCCESS	0	/* success */
+ #define E_NOPERM	1	/* permission denied */
+ #define E_USAGE		2	/* invalid command syntax */
++#define E_BAD_ARG	3	/* invalid argument to option */
+ #define E_FAILURE	3	/* unexpected failure, nothing done */
+ #define E_MISSING	4	/* unexpected failure, passwd file missing */
+ #define E_PWDBUSY	5	/* passwd file(s) busy */
+@@ -90,9 +92,12 @@
+ 
+ static bool spw_locked = false;
+ static bool pw_locked = false;
++static const char *newroot = "";
+ 
+ /* local function prototypes */
+ static void fail_exit (int status);
++static void usage (void);
++static void process_flags (int argc, char **argv);
+ 
+ static void fail_exit (int status)
+ {
+@@ -115,6 +120,77 @@
+ 	exit (status);
+ }
+ 
++/*
++ * usage - display usage message and exit
++ */
++static void usage (void)
++{
++	(void) fprintf (stderr,
++					_("Usage: pwconv [options]\n"
++					  "\n"
++					  "Options:\n"),
++					Prog);
++	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
++	(void) fputs ("\n", stderr);
++	exit (E_USAGE);
++}
++
++/*
++ * process_flags - perform command line argument setting
++ *
++ *	process_flags() interprets the command line arguments and sets
++ *	the values that the user will be created with accordingly. The
++ *	values are checked for sanity.
++ */
++static void process_flags (int argc, char **argv)
++{
++	{
++		/*
++		 * Parse the command line options.
++		 */
++		int c;
++		static struct option long_options[] = {
++			{"help", no_argument, NULL, 'h'},
++			{"root", required_argument, NULL, 'R'},
++			{NULL, 0, NULL, '\0'}
++		};
++		while ((c = getopt_long (argc, argv,
++								 "R:",
++								 long_options, NULL)) != -1) {
++			switch (c) {
++			case 'h':
++				usage ();
++				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++					        _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
++			default:
++				usage ();
++			}
++		}
++	}
++}
++
+ int main (int argc, char **argv)
+ {
+ 	const struct passwd *pw;
+@@ -122,9 +198,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 +206,8 @@
+ 
+ 	OPENLOG ("pwconv");
+ 
++	process_flags (argc, argv);
++
+ 	if (pw_lock () == 0) {
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+diff -urN shadow-4.1.4.3.orig//src/pwunconv.c shadow-4.1.4.3//src/pwunconv.c
+--- shadow-4.1.4.3.orig//src/pwunconv.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/pwunconv.c	2011-05-28 17:09:52.356013600 -0700
+@@ -35,6 +35,7 @@
+ #ident "$Id: pwunconv.c 2852 2009-04-30 21:44:35Z nekral-guest $"
+ 
+ #include <fcntl.h>
++#include <getopt.h>
+ #include <pwd.h>
+ #include <stdio.h>
+ #include <sys/types.h>
+@@ -46,15 +47,24 @@
+ #include "shadowio.h"
+ 
+ /*
++ * exit status values
++ */
++/*@-exitarg@*/
++#define E_USAGE		2	/* invalid command syntax */
++#define E_BAD_ARG	3	/* invalid argument to option */
++/*
+  * Global variables
+  */
+ char *Prog;
+ 
+ static bool spw_locked = false;
+ static bool pw_locked = false;
++static const char *newroot = "";
+ 
+ /* local function prototypes */
+ static void fail_exit (int status);
++static void usage (void);
++static void process_flags (int argc, char **argv);
+ 
+ static void fail_exit (int status)
+ {
+@@ -75,6 +85,76 @@
+ 	exit (status);
+ }
+ 
++/*
++ * usage - display usage message and exit
++ */
++static void usage (void)
++{
++	(void) fprintf (stderr,
++					_("Usage: pwunconv [options]\n"
++					  "\n"
++					  "Options:\n"),
++					Prog);
++	(void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
++	(void) fputs ("\n", stderr);
++	exit (E_USAGE);
++}
++
++/*
++ * process_flags - perform command line argument setting
++ *
++ * process_flags() interprets the command line arguments and sets
++ * the values that the user will be created with accordingly. The
++ * values are checked for sanity.
++ */
++static void process_flags (int argc, char **argv)
++{
++	{
++		/*
++		 * Parse the command line options.
++		 */
++		int c;
++		static struct option long_options[] = {
++			{"help", no_argument, NULL, 'h'},
++			{"root", required_argument, NULL, 'R'},
++			{NULL, 0, NULL, '\0'}
++		};
++		while ((c = getopt_long (argc, argv,
++								 "R:",
++								 long_options, NULL)) != -1) {
++			switch (c) {
++			case 'h':
++				usage ();
++				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++					        _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
++			default:
++				usage ();
++			}
++		}
++	}
++}
+ 
+ int main (int argc, char **argv)
+ {
+@@ -93,6 +173,8 @@
+ 
+ 	OPENLOG ("pwunconv");
+ 
++	process_flags (argc, argv);
++
+ 	if (!spw_file_present ()) {
+ 		/* shadow not installed, do nothing */
+ 		exit (0);
+diff -urN shadow-4.1.4.3.orig//src/useradd.c shadow-4.1.4.3//src/useradd.c
+--- shadow-4.1.4.3.orig//src/useradd.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/useradd.c	2011-05-28 17:10:25.446909971 -0700
+@@ -112,6 +112,7 @@
+ #ifdef WITH_SELINUX
+ static const char *user_selinux = "";
+ #endif
++static const char *newroot = "";
+ 
+ static long user_expire = -1;
+ static bool is_shadow_pwd;
+@@ -189,6 +190,7 @@
+ static void new_spent (struct spwd *);
+ static void grp_update (void);
+ 
++static void process_root_flag (int argc, char **argv);
+ static void process_flags (int argc, char **argv);
+ static void close_files (void);
+ static void open_files (void);
+@@ -711,6 +713,7 @@
+ 	(void) fputs (_("  -o, --non-unique              allow to create users with duplicate\n"
+ 	                "                                (non-unique) UID\n"), stderr);
+ 	(void) fputs (_("  -p, --password PASSWORD       encrypted password of the new account\n"), stderr);
++	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), stderr);
+ 	(void) fputs (_("  -r, --system                  create a system account\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);
+@@ -943,6 +946,59 @@
+ }
+ 
+ /*
++ * process_root_flag - chroot if given the --root option
++ *
++ * We do this outside of process_flags() because
++ * the is_shadow_pwd boolean needs to be set before
++ * process_flags(), and if we do need to chroot() we
++ * must do so before is_shadow_pwd gets set.
++ */
++static void process_root_flag (int argc, char **argv)
++{
++	/*
++	 * Parse the command line options.
++	 */
++	int c;
++	static struct option long_options[] = {
++		{"root", required_argument, NULL, 'R'},
++		{NULL, 0, NULL, '\0'}
++	};
++	while ((c = getopt_long (argc, argv,
++#ifdef WITH_SELINUX
++	                         "b:c:d:De:f:g:G:k:K:lmMNop:R:rs:u:UZ:",
++#else
++	                         "b:c:d:De:f:g:G:k:K:lmMNop:R:rs:u:U",
++#endif
++	                         long_options, NULL)) != -1) {
++		switch (c) {
++		case 'R':
++			if ('/' != optarg[0]) {
++				fprintf (stderr,
++				         _("%s: invalid chroot path '%s'\n"),
++				         Prog, optarg);
++				exit (E_BAD_ARG);
++			}
++			newroot = optarg;
++
++			if (access (newroot, F_OK) != 0) {
++				fprintf(stderr,
++				        _("%s: chroot directory %s does not exist\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			if ( chroot(newroot) != 0 ) {
++				fprintf(stderr,
++				        _("%s: unable to chroot to directory %s\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			break;
++		/* no-op on everything else - they will be hanled by process_flags() */
++		}
++	}
++}
++
++/*
+  * process_flags - perform command line argument setting
+  *
+  *	process_flags() interprets the command line arguments and sets
+@@ -978,6 +1034,7 @@
+ 			{"no-user-group", no_argument, NULL, 'N'},
+ 			{"non-unique", no_argument, NULL, 'o'},
+ 			{"password", required_argument, NULL, 'p'},
++			{"root", required_argument, NULL, 'R'},
+ 			{"system", no_argument, NULL, 'r'},
+ 			{"shell", required_argument, NULL, 's'},
+ #ifdef WITH_SELINUX
+@@ -989,9 +1046,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:R:rs: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:R:rs:u:U",
+ #endif
+ 		                         long_options, NULL)) != -1) {
+ 			switch (c) {
+@@ -1156,6 +1213,9 @@
+ 				}
+ 				user_pass = optarg;
+ 				break;
++			case 'R':
++				/* no-op since we handled this in process_root_flag() earlier */
++				break;
+ 			case 'r':
+ 				rflg = true;
+ 				break;
+@@ -1748,8 +1808,16 @@
+ #ifdef WITH_SELINUX
+ 		selinux_file_context (user_home);
+ #endif
+-		/* XXX - create missing parent directories.  --marekm */
+-		if (mkdir (user_home, 0) != 0) {
++		/* shell out to invoke mkdir -p 
++		 * creating a subshell under pseudo's chroot() breaks the jail
++		 * (bug in pseudo), so make sure we include the full host path
++		 * to the sysroot when the --root option is in use.
++		 */
++		int sysroot_path_len = strlen(newroot);
++		int home_path_len = strlen(user_home);
++		char cmd[sysroot_path_len + home_path_len + 10];
++		sprintf(cmd, "mkdir -p %s%s", newroot, user_home);
++		if (system (cmd) != 0) {
+ 			fprintf (stderr,
+ 			         _("%s: cannot create directory %s\n"),
+ 			         Prog, user_home);
+@@ -1861,6 +1929,7 @@
+ 	 */
+ 	user_groups[0] = (char *) 0;
+ 
++	process_root_flag (argc, argv);
+ 
+ 	is_shadow_pwd = spw_file_present ();
+ #ifdef SHADOWGRP
+diff -urN shadow-4.1.4.3.orig//src/userdel.c shadow-4.1.4.3//src/userdel.c
+--- shadow-4.1.4.3.orig//src/userdel.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/userdel.c	2011-05-28 17:09:52.356013600 -0700
+@@ -79,6 +79,7 @@
+ static char *user_name;
+ static uid_t user_id;
+ static char *user_home;
++static const char *newroot = "";
+ 
+ static bool fflg = false;
+ static bool rflg = false;
+@@ -119,6 +120,7 @@
+ 	         "  -f, --force                   force removal of files,\n"
+ 	         "                                even if not owned by user\n"
+ 	         "  -h, --help                    display this help message and exit\n"
++	         "  -R, --root CHROOT_DIR         directory to chroot into\n"
+ 	         "  -r, --remove                  remove home directory and mail spool\n"
+ 	         "\n"), stderr);
+ 	exit (E_USAGE);
+@@ -768,12 +770,34 @@
+ 			{"remove", no_argument, NULL, 'r'},
+ 			{NULL, 0, NULL, '\0'}
+ 		};
+-		while ((c = getopt_long (argc, argv, "fhr",
++		while ((c = getopt_long (argc, argv, "fhR:r",
+ 		                         long_options, NULL)) != -1) {
+ 			switch (c) {
+ 			case 'f':	/* force remove even if not owned by user */
+ 				fflg = true;
+ 				break;
++			case 'R':
++				if ('/' != optarg[0]) {
++					fprintf (stderr,
++					         _("%s: invalid chroot path '%s'\n"),
++					         Prog, optarg);
++					exit (E_BAD_ARG);
++				}
++				newroot = optarg;
++
++				if (access (newroot, F_OK) != 0) {
++					fprintf(stderr,
++					        _("%s: chroot directory %s does not exist\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				if ( chroot(newroot) != 0 ) {
++					fprintf(stderr,
++					        _("%s: unable to chroot to directory %s\n"),
++					        Prog, newroot);
++					exit (E_BAD_ARG);
++				}
++				break;
+ 			case 'r':	/* remove home dir and mailbox */
+ 				rflg = true;
+ 				break;
+diff -urN shadow-4.1.4.3.orig//src/usermod.c shadow-4.1.4.3//src/usermod.c
+--- shadow-4.1.4.3.orig//src/usermod.c	2011-02-13 09:58:16.000000000 -0800
++++ shadow-4.1.4.3//src/usermod.c	2011-05-28 17:09:52.356013600 -0700
+@@ -110,6 +110,7 @@
+ static long user_newinactive;
+ static long sys_ngroups;
+ static char **user_groups;	/* NULL-terminated list */
++static const char *newroot = "";
+ 
+ static bool
+     aflg = false,		/* append to existing secondary group set */
+@@ -164,6 +165,7 @@
+ #endif
+ static void grp_update (void);
+ 
++static void process_root_flag (int, char **);
+ static void process_flags (int, char **);
+ static void close_files (void);
+ static void open_files (void);
+@@ -323,6 +325,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  --root CHROOT_DIR         directory to chroot into\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"
+@@ -802,6 +805,60 @@
+ }
+ 
+ /*
++ * process_root_flag - chroot if given the --root option
++ *
++ * We do this outside of process_flags() because
++ * the is_shadow_pwd boolean needs to be set before
++ * process_flags(), and if we do need to chroot() we
++ * must do so before is_shadow_pwd gets set.
++ */
++static void process_root_flag (int argc, char **argv)
++{
++	/*
++	 * Parse the command line options.
++	 */
++	int c;
++	static struct option long_options[] = {
++		{"root", required_argument, NULL, 'R'},
++		{NULL, 0, NULL, '\0'}
++	};
++	while ((c = getopt_long (argc, argv,
++#ifdef WITH_SELINUX
++                             "ac:d:e:f:g:G:hl:Lmop:R:s:u:UZ:",
++#else
++	                         "ac:d:e:f:g:G:hl:Lmop:R:s:u:U",
++#endif
++	                         long_options, NULL)) != -1) {
++		switch (c) {
++		case 'R':
++			if (    (!VALID (optarg) ) 
++				|| (   ('/' != optarg[0]) ) ) {
++				fprintf (stderr,
++				         _("%s: invalid chroot path '%s'\n"),
++				         Prog, optarg);
++				exit (E_BAD_ARG);
++			}
++			newroot = optarg;
++
++			if (access (newroot, F_OK) != 0) {
++				fprintf(stderr,
++				        _("%s: chroot directory %s does not exist\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			if ( chroot(newroot) != 0 ) {
++				fprintf(stderr,
++				        _("%s: unable to chroot to directory %s\n"),
++				        Prog, newroot);
++				exit (E_BAD_ARG);
++			}
++			break;
++		/* no-op on everything else - they will be hanled by process_flags() */
++		}
++	}
++}
++
++/*
+  * process_flags - perform command line argument setting
+  *
+  *	process_flags() interprets the command line arguments and sets the
+@@ -895,6 +952,7 @@
+ 			{"move-home", no_argument, NULL, 'm'},
+ 			{"non-unique", no_argument, NULL, 'o'},
+ 			{"password", required_argument, NULL, 'p'},
++			{"root", required_argument, NULL, 'R'},
+ #ifdef WITH_SELINUX
+ 			{"selinux-user", required_argument, NULL, 'Z'},
+ #endif
+@@ -905,9 +963,9 @@
+ 		};
+ 		while ((c = getopt_long (argc, argv,
+ #ifdef WITH_SELINUX
+-			                 "ac:d:e:f:g:G:hl:Lmop:s:u:UZ:",
++			                 "ac:d:e:f:g:G:hl:Lmop:R:s:u:UZ:",
+ #else
+-			                 "ac:d:e:f:g:G:hl:Lmop:s:u:U",
++			                 "ac:d:e:f:g:G:hl:Lmop:R:s:u:U",
+ #endif
+ 			                 long_options, NULL)) != -1) {
+ 			switch (c) {
+@@ -999,6 +1057,9 @@
+ 				user_pass = optarg;
+ 				pflg = true;
+ 				break;
++			case 'R':
++				/* no-op since we handled this in process_root_flag() earlier */
++				break;
+ 			case 's':
+ 				if (!VALID (optarg)) {
+ 					fprintf (stderr,
+@@ -1715,6 +1776,8 @@
+ 
+ 	OPENLOG ("usermod");
+ 
++	process_root_flag (argc, argv);
++
+ 	is_shadow_pwd = spw_file_present ();
+ #ifdef SHADOWGRP
+ 	is_shadow_grp = sgr_file_present ();
diff --git a/meta/recipes-extended/shadow/shadow-native_4.1.4.3.bb b/meta/recipes-extended/shadow/shadow-native_4.1.4.3.bb
new file mode 100644
index 0000000..2f93e05
--- /dev/null
+++ b/meta/recipes-extended/shadow/shadow-native_4.1.4.3.bb
@@ -0,0 +1,66 @@ 
+SUMMARY = "Tools to change and administer password and group data"
+DESCRIPTION = "Tools to change and administer password and group data"
+HOMEPAGE = "http://pkg-shadow.alioth.debian.org"
+BUGTRACKER = "https://alioth.debian.org/tracker/?group_id=30580"
+SECTION = "base utils"
+PRIORITY = "optional"
+LICENSE = "BSD | Artistic"
+LIC_FILES_CHKSUM = "file://COPYING;md5=08c553a87d4e51bbed50b20e0adcaede \
+                    file://src/passwd.c;firstline=8;endline=30;md5=2899a045e90511d0e043b85a7db7e2fe"
+
+PR = "r0"
+
+SRC_URI = "ftp://pkg-shadow.alioth.debian.org/pub/pkg-shadow/shadow-${PV}.tar.bz2 \
+           file://shadow.automake-1.11.patch \
+           file://shadow-4.1.3-dots-in-usernames.patch \
+           file://shadow-4.1.4.2-env-reset-keep-locale.patch \
+           file://add_root_cmd_options.patch"
+
+SRC_URI[md5sum] = "b8608d8294ac88974f27b20f991c0e79"
+SRC_URI[sha256sum] = "633f5bb4ea0c88c55f3642c97f9d25cbef74f82e0b4cf8d54e7ad6f9f9caa778" 
+
+inherit autotools gettext native
+
+EXTRA_OECONF += "--without-audit \
+                 --without-libcrack \
+                 --without-libpam \
+                 --without-selinux"
+
+do_install_append() {
+	# Enable CREATE_HOME by default.
+	sed -i 's/#CREATE_HOME/CREATE_HOME/g' ${D}${sysconfdir}/login.defs
+
+	# As we are on an embedded system, ensure the users mailbox is in
+	# ~/ not /var/spool/mail by default, as who knows where or how big
+	# /var is. The system MDA will set this later anyway.
+	sed -i 's/MAIL_DIR/#MAIL_DIR/g' ${D}${sysconfdir}/login.defs
+	sed -i 's/#MAIL_FILE/MAIL_FILE/g' ${D}${sysconfdir}/login.defs
+
+	# Disable checking emails.
+	sed -i 's/MAIL_CHECK_ENAB/#MAIL_CHECK_ENAB/g' ${D}${sysconfdir}/login.defs
+
+	# Now we don't have a mail system. Disable mail creation for now.
+	sed -i 's:/bin/bash:/bin/sh:g' ${D}${sysconfdir}/default/useradd
+	sed -i '/^CREATE_MAIL_SPOOL/ s:^:#:' ${D}${sysconfdir}/default/useradd
+
+	install -d ${D}${sbindir} ${D}${base_sbindir} ${D}${base_bindir} 
+	for i in passwd chfn newgrp chsh ; do
+		mv ${D}${bindir}/$i ${D}${bindir}/$i.${PN}
+	done
+
+	mv ${D}${sbindir}/chpasswd ${D}${sbindir}/chpasswd.${PN}
+}
+
+pkg_postinst_${PN} () {
+	update-alternatives --install ${bindir}/passwd passwd passwd.${PN} 200
+	update-alternatives --install ${sbindir}/chpasswd chpasswd chpasswd.${PN} 200
+	update-alternatives --install ${bindir}/chfn chfn chfn.${PN} 200
+	update-alternatives --install ${bindir}/newgrp newgrp newgrp.${PN} 200
+	update-alternatives --install ${bindir}/chsh chsh chsh.${PN} 200
+}
+
+pkg_prerm_${PN} () {
+	for i in passwd chpasswd chfn newgrp chsh ; do
+		update-alternatives --remove $i $i.${PN}
+	done
+}