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.

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

Upstream-Status: Inappropriate [Other]
Workaround is specific to our build system.

Signed-off-by: Scott Garman <scott.a.garman@intel.com>

2011-09-29 Fix the parsing of the --root option in gpasswd, useradd, usermod:

In programs which need to scan the command line in two passes to handle 
--root option separately from the rest of the arguments, replace the first
calls to getopt_long with a simple iteration over the argument list since 
getopt_long has the bad habit of reordering arguments on the command line.

Signed-off-by: Julian Pidancet <julian.pidancet@gmail.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-09-29 12:00:45.211000091 +0100
+++ shadow-4.1.4.3//src/gpasswd.c	2011-09-29 12:09:54.590000090 +0100
@@ -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,57 @@
 }
 
 /*
+ * 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 i;
+        char *root;
+
+        for (i = 0; i < argc; i++) {
+		if (!strcmp (argv[i], "--root") || !strcmp (argv[i], "-Q")) {
+			if (i + 1 == argc) {
+				fprintf (stderr,
+				         _("%s: option '%s' requires an argument\n"),
+				         Prog, argv[i]);
+				exit (E_BAD_ARG);
+			}
+			root = argv[i + 1];
+
+			if ('/' != root[0]) {
+				fprintf (stderr,
+				         _("%s: invalid chroot path '%s'\n"),
+				         Prog, root);
+				exit (E_BAD_ARG);
+			}
+			newroot = root;
+
+			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;
+		}
+	}
+}
+
+/*
  * process_flags - process the command line options and arguments
  */
 static void process_flags (int argc, char **argv)
@@ -235,6 +289,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 +297,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 +338,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 +1053,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-09-29 12:00:45.212000091 +0100
+++ shadow-4.1.4.3//src/groupadd.c	2011-09-29 11:59:28.386000092 +0100
@@ -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-09-29 12:00:45.212000091 +0100
+++ shadow-4.1.4.3//src/groupdel.c	2011-09-29 11:59:28.386000092 +0100
@@ -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-09-29 12:00:45.212000091 +0100
+++ shadow-4.1.4.3//src/groupmod.c	2011-09-29 11:59:28.387000092 +0100
@@ -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-09-29 12:00:45.213000091 +0100
+++ shadow-4.1.4.3//src/grpconv.c	2011-09-29 11:59:28.387000092 +0100
@@ -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-09-29 12:00:45.213000091 +0100
+++ shadow-4.1.4.3//src/grpunconv.c	2011-09-29 11:59:28.387000092 +0100
@@ -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-09-29 12:00:45.214000091 +0100
+++ shadow-4.1.4.3//src/passwd.c	2011-09-29 11:59:28.388000092 +0100
@@ -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-09-29 12:00:45.214000091 +0100
+++ shadow-4.1.4.3//src/pwconv.c	2011-09-29 11:59:28.388000092 +0100
@@ -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-09-29 12:00:45.214000091 +0100
+++ shadow-4.1.4.3//src/pwunconv.c	2011-09-29 11:59:28.388000092 +0100
@@ -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-09-29 12:00:45.215000091 +0100
+++ shadow-4.1.4.3//src/useradd.c	2011-09-29 11:59:28.520000092 +0100
@@ -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,57 @@
 }
 
 /*
+ * 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 i;
+        char *root;
+
+        for (i = 0; i < argc; i++) {
+		if (!strcmp (argv[i], "--root") || !strcmp (argv[i], "-R")) {
+			if (i + 1 == argc) {
+				fprintf (stderr,
+				         _("%s: option '%s' requires an argument\n"),
+				         Prog, argv[i]);
+				exit (E_BAD_ARG);
+			}
+			root = argv[i + 1];
+
+			if ('/' != root[0]) {
+				fprintf (stderr,
+				         _("%s: invalid chroot path '%s'\n"),
+				         Prog, root);
+				exit (E_BAD_ARG);
+			}
+			newroot = root;
+
+			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;
+		}
+	}
+}
+
+/*
  * process_flags - perform command line argument setting
  *
  *	process_flags() interprets the command line arguments and sets
@@ -978,6 +1032,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 +1044,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 +1211,9 @@
 				}
 				user_pass = optarg;
 				break;
+			case 'R':
+				/* no-op since we handled this in process_root_flag() earlier */
+				break;
 			case 'r':
 				rflg = true;
 				break;
@@ -1735,6 +1793,36 @@
 	}
 }
 #endif
+
+/*
+ * mkdir_p - create directories, including parent directories when needed
+ *
+ * similar to mkdir -p
+ */
+void mkdir_p(const char *path) {
+	int len = strlen(path);
+	char newdir[len + 1];
+	mode_t mode = 0755;
+	int i = 0;
+
+	if (path[i] == '\0') {
+		return;
+	}
+
+	/* skip the leading '/' */
+	i++;
+
+	while(path[i] != '\0') {
+		if (path[i] == '/') {
+			strncpy(newdir, path, i);
+			newdir[i] = '\0';
+			mkdir(newdir, mode);
+		}
+		i++;
+	}
+	mkdir(path, mode);
+}
+
 /*
  * create_home - create the user's home directory
  *
@@ -1748,34 +1836,31 @@
 #ifdef WITH_SELINUX
 		selinux_file_context (user_home);
 #endif
-		/* XXX - create missing parent directories.  --marekm */
-		if (mkdir (user_home, 0) != 0) {
-			fprintf (stderr,
-			         _("%s: cannot create directory %s\n"),
-			         Prog, user_home);
-#ifdef WITH_AUDIT
-			audit_logger (AUDIT_ADD_USER, Prog,
-			              "adding home directory",
-			              user_name, (unsigned int) user_id,
-			              SHADOW_AUDIT_FAILURE);
-#endif
-			fail_exit (E_HOMEDIR);
-		}
-		chown (user_home, user_id, user_gid);
-		chmod (user_home,
-		       0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
-		home_added = true;
+		mkdir_p(user_home);
+	}
+	if (access (user_home, F_OK) != 0) {
 #ifdef WITH_AUDIT
 		audit_logger (AUDIT_ADD_USER, Prog,
 		              "adding home directory",
 		              user_name, (unsigned int) user_id,
-		              SHADOW_AUDIT_SUCCESS);
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_HOMEDIR);
+	}
+	chown (user_home, user_id, user_gid);
+	chmod (user_home,
+	       0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
+	home_added = true;
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_USER, Prog,
+	              "adding home directory",
+	              user_name, (unsigned int) user_id,
+	              SHADOW_AUDIT_SUCCESS);
 #endif
 #ifdef WITH_SELINUX
-		/* Reset SELinux to create files with default contexts */
-		setfscreatecon (NULL);
+	/* Reset SELinux to create files with default contexts */
+	setfscreatecon (NULL);
 #endif
-	}
 }
 
 /*
@@ -1861,6 +1946,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-09-29 12:00:45.216000091 +0100
+++ shadow-4.1.4.3//src/userdel.c	2011-09-29 11:59:28.389000092 +0100
@@ -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-09-29 12:00:45.216000091 +0100
+++ shadow-4.1.4.3//src/usermod.c	2011-09-29 11:59:28.390000092 +0100
@@ -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,58 @@
 }
 
 /*
+ * 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 i;
+        char *root;
+
+        for (i = 0; i < argc; i++) {
+		if (!strcmp (argv[i], "--root") || !strcmp (argv[i], "-R")) {
+			if (i + 1 == argc) {
+				fprintf (stderr,
+				         _("%s: option '%s' requires an argument\n"),
+				         Prog, argv[i]);
+				exit (E_BAD_ARG);
+			}
+			root = argv[i + 1];
+
+			if (    (!VALID (root) ) 
+				|| (   ('/' != root[0]) ) ) {
+				fprintf (stderr,
+				         _("%s: invalid chroot path '%s'\n"),
+				         Prog, root);
+				exit (E_BAD_ARG);
+			}
+			newroot = root;
+
+			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;
+		}
+	}
+}
+
+/*
  * process_flags - perform command line argument setting
  *
  *	process_flags() interprets the command line arguments and sets the
@@ -895,6 +950,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 +961,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 +1055,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 +1774,8 @@
 
 	OPENLOG ("usermod");
 
+	process_root_flag (argc, argv);
+
 	is_shadow_pwd = spw_file_present ();
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
