[Pkg-shadow-devel] Ubuntu (new upstream) shadow 1:4.2-3.2ubuntu1

Ubuntu Merge-o-Matic mom at ubuntu.com
Tue Sep 20 17:15:04 UTC 2016


This e-mail has been sent due to an upload to Ubuntu of a new upstream
version which still contains Ubuntu changes.  It contains the difference
between the Ubuntu version and the equivalent base version in Debian, note
that this difference may include the upstream changes.
-------------- next part --------------
Format: 1.8
Date: Tue, 20 Sep 2016 09:43:54 +0200
Source: shadow
Binary: passwd login uidmap
Architecture: source
Version: 1:4.2-3.2ubuntu1
Distribution: yakkety
Urgency: medium
Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
Changed-By: Matthias Klose <doko at ubuntu.com>
Description: 
 login      - system login tools
 passwd     - change and administer password and group data
 uidmap     - programs to help use subuids
Closes: 836653
Launchpad-Bugs-Fixed: 1304505 1368864 1495580 1545884 1562872
Changes: 
 shadow (1:4.2-3.2ubuntu1) yakkety; urgency=medium
 .
   * Merge with Debian; remaining changes:
     - debian/passwd.upstart: Add an upstart job to clear locks on
       [shadow-]passwd/group.
     - debian/login.defs:
       + Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
         handling does not only apply to "former (pre-PAM) uses".
       + Update documentation of UMASK: Explain that USERGROUPS_ENAB
         will modify this default for UPGs.
     - debian/{source_shadow.py,rules}: Add apport hook
     - Pass noupdate to pam_motd call for /run/motd.dynamic, to avoid running
       /etc/update-motd.d/* scripts twice.
     - debian/patches/1010_extrausers.patch: Add support to passwd for
       libnss-extrausers
     - debian/patches/1011_extrausers_toggle.patch: extrausers support for
       useradd and groupadd
     - debian/patches/userns/subuids-nonlocal-users: Don't limit
       subuid/subgid support to local users.
   * Dropped changes, included in Debian:
     - Allow LXC devices (lxc/console, lxc/tty[1234]), used from precise on.
     - Add uidmap package based on upstream patches that introduce
       newuidmap/newgidmap as well as /etc/subuid and /etc/subgid. Additional
       updates on those to widen the default allocation to 65536 uids and gids
       and only assign ranges to non-system users.
     - debian/patches/1020_fix_user_busy_errors: Call sub_uid_close in all
       error cases.
   * Dropped changes, included upstream:
     - debian/patches/495_stdout-encrypted-password: chpasswd can report
       password hashes on stdout.
     - debian/patches/496_su_kill_process_group: Kill the child process group,
       rather than just the immediate child.
   * Fix pam_motd calls so that the second pam_motd is the noupdate one rather
     than the first, ensuring /run/motd.dynamic is always populated and shown
     on the first login after boot.  LP: #1368864.
   * Don't call 'pam_exec uname', a change adopted in Debian without
     coordination with the Debian PAM maintainer
   * Use dh_installinit now for installing the upstart job, as we no longer
     generate a dependency on upstart-job.
   * Include /etc/sub[ug]id in the list of files to clear locks for on boot.
     LP: #1304505
   * Add a systemd unit to go with the upstart job, so that lock clearing works
     on newer Ubuntu releases.
   * add support for "chfn --extrausers" (LP: #1495580)
   * debian/patches/1010_extrausers.patch:
     - Fix usermod to handle a readonly /etc gracefully (LP: #1562872)
   * debian/patches/1010_extrausers.patch:
     - Fix usermod to look in extrausers location for basic changes to a
       user's passwd info.  Fixes changing user's real name in Touch via
       AccountsService.  (Does not address updating groups yet, since that's
       less useful now, as we can't update any system groups.)
   * d/p/1021_no_subuids_for_system_users.patch: fix the not creating subuids
     for system users.  (LP: #1545884)
   * Replace debian/passwd.service with debian/passwd.tmpfile, systemd tmpfile
     handling has support for removing files for us on boot.  Thanks to
     Martin Pitt <pitti at ubuntu.com> for the hint.
 .
 shadow (1:4.2-3.2) unstable; urgency=medium
 .
   * Non-maintainer upload.
   * Use HTTPS in Vcs-Git.
   * Stop using hardening-wrapper and instead use /usr/share/dpkg/buildflags.mk.
     Closes: #836653
Checksums-Sha1: 
 a083b982ab2ebd4c70ed6ad70d537ef352891378 2432 shadow_4.2-3.2ubuntu1.dsc
 440b3c40b3f18983e11e86ac17a5b055f97723dc 504272 shadow_4.2-3.2ubuntu1.debian.tar.xz
Checksums-Sha256: 
 a5d12dae23f17fa0ab7360b7164f1069e2df885a6d5139aead3ffac706b1c5b1 2432 shadow_4.2-3.2ubuntu1.dsc
 8cf6c74b2c41c436e98ca63e229002363a5f62b31f3c5698cfdee49361a7bdf5 504272 shadow_4.2-3.2ubuntu1.debian.tar.xz
Files: 
 5c7eef9b04271408507bdf51dba630c5 2432 admin required shadow_4.2-3.2ubuntu1.dsc
 2e395183ee42941aa7c56e549bea0157 504272 admin required shadow_4.2-3.2ubuntu1.debian.tar.xz
Original-Maintainer: Shadow package maintainers <pkg-shadow-devel at lists.alioth.debian.org>
-------------- next part --------------
diff -pruN 1:4.2-3.2/debian/changelog 1:4.2-3.2ubuntu1/debian/changelog
--- 1:4.2-3.2/debian/changelog	2016-09-18 14:42:16.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/changelog	2016-09-20 07:43:54.000000000 +0000
@@ -1,3 +1,62 @@
+shadow (1:4.2-3.2ubuntu1) yakkety; urgency=medium
+
+  * Merge with Debian; remaining changes:
+    - debian/passwd.upstart: Add an upstart job to clear locks on
+      [shadow-]passwd/group.
+    - debian/login.defs:
+      + Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
+        handling does not only apply to "former (pre-PAM) uses".
+      + Update documentation of UMASK: Explain that USERGROUPS_ENAB
+        will modify this default for UPGs.
+    - debian/{source_shadow.py,rules}: Add apport hook
+    - Pass noupdate to pam_motd call for /run/motd.dynamic, to avoid running
+      /etc/update-motd.d/* scripts twice.
+    - debian/patches/1010_extrausers.patch: Add support to passwd for
+      libnss-extrausers
+    - debian/patches/1011_extrausers_toggle.patch: extrausers support for
+      useradd and groupadd
+    - debian/patches/userns/subuids-nonlocal-users: Don't limit
+      subuid/subgid support to local users.
+  * Dropped changes, included in Debian:
+    - Allow LXC devices (lxc/console, lxc/tty[1234]), used from precise on.
+    - Add uidmap package based on upstream patches that introduce
+      newuidmap/newgidmap as well as /etc/subuid and /etc/subgid. Additional
+      updates on those to widen the default allocation to 65536 uids and gids
+      and only assign ranges to non-system users.
+    - debian/patches/1020_fix_user_busy_errors: Call sub_uid_close in all
+      error cases.
+  * Dropped changes, included upstream:
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout.
+    - debian/patches/496_su_kill_process_group: Kill the child process group,
+      rather than just the immediate child.
+  * Fix pam_motd calls so that the second pam_motd is the noupdate one rather
+    than the first, ensuring /run/motd.dynamic is always populated and shown
+    on the first login after boot.  LP: #1368864.
+  * Don't call 'pam_exec uname', a change adopted in Debian without
+    coordination with the Debian PAM maintainer
+  * Use dh_installinit now for installing the upstart job, as we no longer
+    generate a dependency on upstart-job.
+  * Include /etc/sub[ug]id in the list of files to clear locks for on boot.
+    LP: #1304505
+  * Add a systemd unit to go with the upstart job, so that lock clearing works
+    on newer Ubuntu releases.
+  * add support for "chfn --extrausers" (LP: #1495580)
+  * debian/patches/1010_extrausers.patch:
+    - Fix usermod to handle a readonly /etc gracefully (LP: #1562872)
+  * debian/patches/1010_extrausers.patch:
+    - Fix usermod to look in extrausers location for basic changes to a
+      user's passwd info.  Fixes changing user's real name in Touch via
+      AccountsService.  (Does not address updating groups yet, since that's
+      less useful now, as we can't update any system groups.)
+  * d/p/1021_no_subuids_for_system_users.patch: fix the not creating subuids
+    for system users.  (LP: #1545884)
+  * Replace debian/passwd.service with debian/passwd.tmpfile, systemd tmpfile
+    handling has support for removing files for us on boot.  Thanks to
+    Martin Pitt <pitti at ubuntu.com> for the hint.
+
+ -- Matthias Klose <doko at ubuntu.com>  Tue, 20 Sep 2016 09:43:54 +0200
+
 shadow (1:4.2-3.2) unstable; urgency=medium
 
   * Non-maintainer upload.
@@ -7,6 +66,93 @@ shadow (1:4.2-3.2) unstable; urgency=med
 
  -- Mattia Rizzolo <mattia at debian.org>  Sun, 18 Sep 2016 14:42:16 +0000
 
+shadow (1:4.2-3.1ubuntu6) yakkety; urgency=medium
+
+  * add support for "chfn --extrausers" (LP: #1495580)
+
+ -- Michael Vogt <michael.vogt at ubuntu.com>  Thu, 23 Jun 2016 08:02:00 +0200
+
+shadow (1:4.2-3.1ubuntu5) xenial; urgency=medium
+
+  * debian/patches/1010_extrausers.patch:
+    - Fix usermod to handle a readonly /etc gracefully (LP: #1562872)
+
+ -- Michael Terry <mterry at ubuntu.com>  Mon, 28 Mar 2016 09:44:23 -0400
+
+shadow (1:4.2-3.1ubuntu4) xenial; urgency=medium
+
+  * debian/patches/1010_extrausers.patch:
+    - Fix usermod to look in extrausers location for basic changes to a
+      user's passwd info.  Fixes changing user's real name in Touch via
+      AccountsService.  (Does not address updating groups yet, since that's
+      less useful now, as we can't update any system groups.)
+
+ -- Michael Terry <mterry at ubuntu.com>  Wed, 02 Mar 2016 15:01:19 -0500
+
+shadow (1:4.2-3.1ubuntu3) xenial; urgency=medium
+
+  * d/p/1021_no_subuids_for_system_users.patch: fix the not creating subuids
+    for system users.  (LP: #1545884)
+
+ -- Serge Hallyn <serge.hallyn at ubuntu.com>  Wed, 17 Feb 2016 20:57:59 -0800
+
+shadow (1:4.2-3.1ubuntu2) xenial; urgency=medium
+
+  * Replace debian/passwd.service with debian/passwd.tmpfile, systemd tmpfile
+    handling has support for removing files for us on boot.  Thanks to
+    Martin Pitt <pitti at ubuntu.com> for the hint.
+
+ -- Steve Langasek <steve.langasek at ubuntu.com>  Thu, 04 Feb 2016 14:01:27 -0800
+
+shadow (1:4.2-3.1ubuntu1) xenial; urgency=low
+
+  * Merge from Debian unstable.
+    - Includes pam_loginuid in login PAM config.  LP: #1067779.
+    - Fixes typo in usermod -h output.  LP: #1348873.
+  * Remaining changes:
+    - debian/passwd.upstart: Add an upstart job to clear locks on
+      [shadow-]passwd/group.
+    - debian/login.defs:
+      + Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
+        handling does not only apply to "former (pre-PAM) uses".
+      + Update documentation of UMASK: Explain that USERGROUPS_ENAB
+        will modify this default for UPGs.
+    - debian/{source_shadow.py,rules}: Add apport hook
+    - Pass noupdate to pam_motd call for /run/motd.dynamic, to avoid running
+      /etc/update-motd.d/* scripts twice.
+    - debian/patches/1010_extrausers.patch: Add support to passwd for
+      libnss-extrausers
+    - debian/patches/1011_extrausers_toggle.patch: extrausers support for
+      useradd and groupadd
+    - debian/patches/userns/subuids-nonlocal-users: Don't limit
+      subuid/subgid support to local users.
+  * Dropped changes, included in Debian:
+    - Allow LXC devices (lxc/console, lxc/tty[1234]), used from precise on.
+    - Add uidmap package based on upstream patches that introduce
+      newuidmap/newgidmap as well as /etc/subuid and /etc/subgid. Additional
+      updates on those to widen the default allocation to 65536 uids and gids
+      and only assign ranges to non-system users.
+    - debian/patches/1020_fix_user_busy_errors: Call sub_uid_close in all
+      error cases.
+  * Dropped changes, included upstream:
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout.
+    - debian/patches/496_su_kill_process_group: Kill the child process group,
+      rather than just the immediate child.
+  * Fix pam_motd calls so that the second pam_motd is the noupdate one rather
+    than the first, ensuring /run/motd.dynamic is always populated and shown
+    on the first login after boot.  LP: #1368864.
+  * Don't call 'pam_exec uname', a change adopted in Debian without
+    coordination with the Debian PAM maintainer
+  * Use dh_installinit now for installing the upstart job, as we no longer
+    generate a dependency on upstart-job.
+  * Include /etc/sub[ug]id in the list of files to clear locks for on boot.
+    LP: #1304505
+  * Add a systemd unit to go with the upstart job, so that lock clearing works
+    on newer Ubuntu releases.
+
+ -- Steve Langasek <steve.langasek at ubuntu.com>  Thu, 28 Jan 2016 22:21:41 -0800
+
 shadow (1:4.2-3.1) unstable; urgency=medium
 
   * Non-maintainer upload.
@@ -117,6 +263,79 @@ shadow (1:4.2-1) experimental; urgency=l
 
  -- Christian Perrier <bubulle at debian.org>  Tue, 22 Apr 2014 09:01:42 +0200
 
+shadow (1:4.1.5.1-1.1ubuntu7) wily; urgency=medium
+
+  * debian/patches/userns/subuids-nonlocal-users: Don't limit
+    subuid/subgid support to local users.  Closes LP: #1475749.
+
+ -- Steve Langasek <steve.langasek at ubuntu.com>  Mon, 20 Jul 2015 18:44:12 -0700
+
+shadow (1:4.1.5.1-1.1ubuntu6) wily; urgency=medium
+
+  * extrausers support for useradd and groupadd (LP: #1323732)
+
+ -- Sergio Schvezov <sergio.schvezov at canonical.com>  Thu, 25 Jun 2015 15:26:55 -0300
+
+shadow (1:4.1.5.1-1.1ubuntu5) wily; urgency=medium
+
+  * debian/rules: Re-enable audit support. (LP: #1414817)
+  * debian/control: add libaudit-dev to Build-Depends.
+
+ -- Mathieu Trudel-Lapierre <mathieu-tl at ubuntu.com>  Tue, 02 Jun 2015 10:46:18 -0400
+
+shadow (1:4.1.5.1-1.1ubuntu4) vivid; urgency=medium
+
+  * debian/patches/1020_fix_user_busy_errors:
+    - libmisc/user_busy.c: Call sub_uid_close in all error cases, otherwise
+      code that later opens it as RW fails obscurely. (LP: #1436937)
+
+ -- William Grant <wgrant at ubuntu.com>  Mon, 20 Apr 2015 18:41:47 +0100
+
+shadow (1:4.1.5.1-1.1ubuntu3) vivid; urgency=medium
+
+  * No change rebuild to get debug symbols for all architectures.
+
+ -- Brian Murray <brian at ubuntu.com>  Tue, 02 Dec 2014 11:39:38 -0800
+
+shadow (1:4.1.5.1-1.1ubuntu2) utopic; urgency=medium
+
+  * debian/patches/1010_extrausers.patch:
+    - Add support to passwd for libnss-extrausers by falling back to the
+      /var/lib/extrausers/ locations if it exists when updating
+      passwd or shadow.
+
+ -- Michael Terry <mterry at ubuntu.com>  Fri, 18 Jul 2014 10:00:44 -0400
+
+shadow (1:4.1.5.1-1.1ubuntu1) utopic; urgency=medium
+
+  * Merge from Debian unstable.  Remaining changes:
+     - debian/passwd.upstart: Add an upstrat job to clear locks on
+       [shadow-]passwd/group. (LP: #523896).
+     - Allow LXC devices (lxc/console, lxc/tty[1234]) that we'll start using
+       in LXC with Precise.
+     - debian/login.defs:
+       + Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
+         handling does not only apply to "former (pre-PAM) uses".
+       + Update documentation of UMASK: Explain that USERGROUPS_ENAB
+         will modify this default for UPGs. (Closes: #583971)
+     - debian/{source_shadow.py,rules}: Add apport hook
+     - debian/patches/495_stdout-encrypted-password: chpasswd can report
+       password hashes on stdout (Debian bug 505640).
+     - Install upstart job by-hand, instead of using dh_installinit to avoid
+       dependency on upstart-job.
+     - Pass noupdate to pam_motd call for /run/motd.dynamic, to avoid running
+       /etc/update-motd.d/* scripts twice (LP: #1169558).
+     - debian/patches/496_su_kill_process_group: Kill the child process group,
+       rather than just the immediate child; this is needed now that su no
+       longer starts a controlling terminal when not running an interactive
+       shell (closes: #713979).
+     - Add uidmap package based on upstream patches that introduce
+       newuidmap/newgidmap as well as /etc/subuid and /etc/subgid. Additional
+       updates on those to widen the default allocation to 65536 uids and gids
+       and only assign ranges to non-system users.
+
+ -- Stéphane Graber <stgraber at ubuntu.com>  Fri, 02 May 2014 15:17:15 -0400
+
 shadow (1:4.1.5.1-1.1) unstable; urgency=medium
 
   * Non-maintainer upload.
@@ -130,6 +349,103 @@ shadow (1:4.1.5.1-1.1) unstable; urgency
 
  -- Samuel Thibault <sthibault at debian.org>  Sun, 16 Mar 2014 20:58:24 +0100
 
+shadow (1:4.1.5.1-1ubuntu9) trusty; urgency=medium
+
+  * Set our subuid and subgid range to 65536 uids by default.
+  * Patch newusers to not add subuids and subgids to system users.
+  * Patch useradd to not add subuids and subgids to system users and to
+    regular users who don't fit between uid_min and uid_max.
+    (This is needed due to adduser not passing --system...)
+
+ -- Stéphane Graber <stgraber at ubuntu.com>  Sun, 16 Feb 2014 19:33:48 -0500
+
+shadow (1:4.1.5.1-1ubuntu8) trusty; urgency=medium
+
+  * Fix postinst to create subuid and subgid when missing as those won't
+    get created by usermod or any of the other tools.
+
+ -- Stéphane Graber <stgraber at ubuntu.com>  Fri, 17 Jan 2014 16:15:13 -0500
+
+shadow (1:4.1.5.1-1ubuntu7) trusty; urgency=medium
+
+  * Don't ship subuid/subgid as conffiles as that'll just cause problems
+    on upgrades. Instead simply touch them if they're not already present.
+
+ -- Stéphane Graber <stgraber at ubuntu.com>  Sun, 12 Jan 2014 12:59:46 -0500
+
+shadow (1:4.1.5.1-1ubuntu6) saucy; urgency=low
+
+  * debian/patches/496_su_kill_process_group: Kill the child process group,
+    rather than just the immediate child; this is needed now that su no
+    longer starts a controlling terminal when not running an interactive
+    shell (closes: #713979).
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Fri, 26 Jul 2013 16:55:52 +0100
+
+shadow (1:4.1.5.1-1ubuntu5) saucy; urgency=low
+
+  [ Serge Hallyn ]
+  * debian/patches/userns: patches from Eric Biederman to enable use of
+    subuids, plus some bugfix patches on top of them. (LP: #1192864)
+  * passwd.install: add new manpages
+  * debian/control, debian/uidmap.install: create new uidmap package
+    containing the new setuid-root binaries newuidmap and newgidmap 
+  * debian/subuid, debian/rules: install a default /etc/subuid and /etc/subgid
+  * debian/patches/userns/16_add-argument-sanity-checking.patch: address
+    three sanity checking concerns brought up by sarnold at
+    http://lists.alioth.debian.org/pipermail/pkg-shadow-devel/2013-June/ \
+    009752.html.
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov at ubuntu.com>  Fri, 28 Jun 2013 11:31:51 +0100
+
+shadow (1:4.1.5.1-1ubuntu4) raring; urgency=low
+
+  * Pass noupdate to pam_motd call for /run/motd.dynamic, to avoid running
+    /etc/update-motd.d/* scripts twice (LP: #1169558).
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Thu, 18 Apr 2013 01:01:45 +0100
+
+shadow (1:4.1.5.1-1ubuntu3) raring; urgency=low
+
+  * Install upstart job by-hand, instead of using dh_installinit to avoid
+    dependency on upstart-job.
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov at ubuntu.com>  Mon, 18 Mar 2013 03:23:31 +0000
+
+shadow (1:4.1.5.1-1ubuntu2) raring; urgency=low
+
+  * Revert build-dependency from gettext:any to gettext, now that gettext is
+    Multi-Arch: foreign.
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Thu, 29 Nov 2012 15:27:11 +0000
+
+shadow (1:4.1.5.1-1ubuntu1) raring; urgency=low
+
+  * The "Yorkshire Blue" release.
+  * Merge from Debian unstable.  Remaining changes:  
+     - debian/passwd.upstart: Add an upstrat job to clear locks on
+       [shadow-]passwd/group. (LP: #523896).
+     - Build-depend on gettext:any for cross-building support.
+     - Allow LXC devices (lxc/console, lxc/tty[1234]) that we'll start using
+       in LXC with Precise.
+     - debian/login.defs:
+       + Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
+         handling does not only apply to "former (pre-PAM) uses".
+       + Update documentation of UMASK: Explain that USERGROUPS_ENAB will modify
+         this default for UPGs. (Closes: #583971)
+     - debian/{source_shadow.py,rules}: Add apport hook
+     - debian/patches/495_stdout-encrypted-password: chpasswd can report
+       password hashes on stdout (Debian bug 505640).
+
+  * Dropped changes, merged in Debian:
+     - Fix case of ttyAMA0-3 devices and move them near the ttyAM0-15 ones;
+       Debian #544184; fixes console on Vexpress boards (e.g. in QEMU).
+     - use SHA512 by default for password crypt routine.
+     - debian/rules: fix FTBFS from newer libtools
+     - Mark passwd Multi-Arch: foreign.
+  
+ -- Dmitrijs Ledkovs <dmitrij.ledkov at ubuntu.com>  Tue, 23 Oct 2012 09:59:19 +0100
+
 shadow (1:4.1.5.1-1) unstable; urgency=low
 
   * The "Gruyère" release.
@@ -273,6 +589,68 @@ shadow (1:4.1.5-1) unstable; urgency=low
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Sun, 12 Feb 2012 22:27:03 +0100
 
+shadow (1:4.1.4.2+svn3283-3ubuntu7) quantal; urgency=low
+
+  * debian/passwd.upstart: Add an upstrat job to clear locks on
+    [shadow-]passwd/group. (LP: #523896).
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov at ubuntu.com>  Fri, 31 Aug 2012 13:00:33 +0100
+
+shadow (1:4.1.4.2+svn3283-3ubuntu6) quantal; urgency=low
+
+  * debian/source_shadow.py: Fix compatibility with python3. Thanks Edward
+    Donovan! (LP: #1013171)
+
+ -- Martin Pitt <martin.pitt at ubuntu.com>  Mon, 18 Jun 2012 15:09:54 +0200
+
+shadow (1:4.1.4.2+svn3283-3ubuntu5) precise; urgency=low
+
+  * Build-depend on gettext:any for cross-building support.
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Mon, 09 Apr 2012 00:28:03 +0100
+
+shadow (1:4.1.4.2+svn3283-3ubuntu4) precise; urgency=low
+
+  * Allow LXC devices (lxc/console, lxc/tty[1234]) that we'll start using
+    in LXC with Precise.
+
+ -- Stéphane Graber <stgraber at ubuntu.com>  Fri, 10 Feb 2012 15:34:05 -0500
+
+shadow (1:4.1.4.2+svn3283-3ubuntu3) precise; urgency=low
+
+  * Fix case of ttyAMA0-3 devices and move them near the ttyAM0-15 ones;
+    Debian #544184; fixes console on Vexpress boards (e.g. in QEMU).
+
+ -- Loïc Minier <loic.minier at ubuntu.com>  Wed, 30 Nov 2011 22:47:47 +0100
+
+shadow (1:4.1.4.2+svn3283-3ubuntu2) oneiric; urgency=low
+
+  * debian/login.defs:
+    - Update documentation of USERGROUPS_ENAB: with pam_umask, the UPG
+      handling does not only apply to "former (pre-PAM) uses".
+    - Update documentation of UMASK: Explain that USERGROUPS_ENAB will modify
+      this default for UPGs. (Closes: #583971)
+
+ -- Martin Pitt <martin.pitt at ubuntu.com>  Fri, 24 Jun 2011 11:07:34 +0200
+
+shadow (1:4.1.4.2+svn3283-3ubuntu1) natty; urgency=low
+
+  * The "string cheese" release.
+  * Merge from Debian unstable.  Remaining changes:
+    - Ubuntu specific:
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+    - debian/{source_shadow.py,rules}: Add apport hook
+    - debian/rules: fix FTBFS from newer libtools
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout (Debian bug 505640).
+  * Dropped changes, merged in Debian:
+    - debian/patches/300_CVE-2011-0721: reject newlines in GECOS updates.
+    - CVE-2011-0721
+  * Mark passwd Multi-Arch: foreign, so packages that aren't of the same
+    arch can depend on it.
+
+ -- Steve Langasek <steve.langasek at ubuntu.com>  Sun, 20 Feb 2011 15:59:15 -0800
+
 shadow (1:4.1.4.2+svn3283-3) unstable; urgency=high
 
   * The "Trappe d'Echourgnac" release.
@@ -283,6 +661,34 @@ shadow (1:4.1.4.2+svn3283-3) unstable; u
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Mon, 13 Feb 2011 23:20:05 +0100
 
+shadow (1:4.1.4.2+svn3283-2ubuntu3) natty; urgency=low
+
+  * SECURITY UPDATE: could inject NIS groups memberships into /etc/passwd.
+    - debian/patches/300_CVE-2011-0721: reject newlines in GECOS updates.
+    - CVE-2011-0721
+
+ -- Kees Cook <kees at ubuntu.com>  Tue, 15 Feb 2011 13:57:01 -0800
+
+shadow (1:4.1.4.2+svn3283-2ubuntu2) natty; urgency=low
+
+  * debian/patches/495_stdout-encrypted-password: adjust patch for changes 
+    in src/chpasswd.c to fix FTBFS
+
+ -- Oliver Grawert <ogra at ubuntu.com>  Tue, 04 Jan 2011 15:48:49 +0100
+
+shadow (1:4.1.4.2+svn3283-2ubuntu1) natty; urgency=low
+
+  * Merge from debian unstable.  Remaining changes:
+    - Ubuntu specific:
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+    - debian/{source_shadow.py,rules}: Add apport hook
+    - debian/rules: fix FTBFS from newer libtools
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout (Debian bug 505640).
+    - Rework 495_stdout-encrypted-password to cope with chpasswd using PAM.
+
+ -- Oliver Grawert <ogra at ubuntu.com>  Wed, 24 Nov 2010 13:42:42 +0100
+
 shadow (1:4.1.4.2+svn3283-2) unstable; urgency=low
 
   * The "Bleu du Vercors-Sassenage" release.
@@ -354,6 +760,32 @@ shadow (1:4.1.4.2+svn3283-1) unstable; u
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Sun, 29 Aug 2010 21:14:12 +0200
 
+shadow (1:4.1.4.2-1ubuntu3) maverick; urgency=low
+
+  * add ttyO0-3 to debian/securetty.linux, if OMAP kernels are built with
+    TI's DMA-offloaded driver instead of the default 8250 one the serial tty's
+    are called like that (LP: #512845).
+
+ -- Oliver Grawert <ogra at ubuntu.com>  Tue, 31 Aug 2010 14:45:17 +0200
+
+shadow (1:4.1.4.2-1ubuntu2) lucid; urgency=low
+
+  * debian/{source_shadow.py,rules}: Add apport hook
+  * debian/rules: fix FTBFS from newer libtools
+
+ -- Marc Deslauriers <marc.deslauriers at ubuntu.com>  Tue, 26 Jan 2010 08:54:59 -0500
+
+shadow (1:4.1.4.2-1ubuntu1) lucid; urgency=low
+
+  * Merged with debian unstable. Remaning changes (LP: #477299):
+    - Ubuntu specific:
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout (Debian bug 505640).
+    - Rework 495_stdout-encrypted-password to cope with chpasswd using PAM.
+
+ -- Nicolas Valcárcel Scerpella (Canonical) <nvalcarcel at canonical.com>  Sat, 07 Nov 2009 04:55:18 -0500
+
 shadow (1:4.1.4.2-1) unstable; urgency=low
 
   * The "Tome des Bauges" release.
@@ -381,6 +813,25 @@ shadow (1:4.1.4.2-1) unstable; urgency=l
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Fri, 24 Jul 2009 05:03:23 +0200
 
+shadow (1:4.1.4.1-1ubuntu2) karmic; urgency=low
+
+  * debian/securetty.linux: also list ttyS2 and ttyS3; beagleboard uses ttyS2
+    as serial port.
+
+ -- Loïc Minier <loic.minier at ubuntu.com>  Fri, 31 Jul 2009 15:34:56 +0200
+
+shadow (1:4.1.4.1-1ubuntu1) karmic; urgency=low
+
+  * Resynchronise with Debian. Remaining changes:
+    - Ubuntu specific:
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+    - debian/patches/495_stdout-encrypted-password: chpasswd can report
+      password hashes on stdout (Debian bug 505640).
+  * Rework 495_stdout-encrypted-password to cope with chpasswd using PAM.
+    It's looking a bit ugly now ...
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Wed, 03 Jun 2009 11:16:51 +0100
+
 shadow (1:4.1.4.1-1) unstable; urgency=low
 
   * The "Chevrotin" release.
@@ -468,6 +919,21 @@ shadow (1:4.1.4-1) unstable; urgency=low
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Mon, 11 May 2009 00:25:11 +0200
 
+shadow (1:4.1.3.1-1ubuntu1) karmic; urgency=low
+
+  * Merge from debian unstable, remaining changes:
+    - Ubuntu specific:
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+    - debian/patches/stdout-encrypted-password.patch: chpasswd can report
+      password hashes on stdout (debian bug 505640).
+    - debian/login.pam: Enable SELinux support (debian bug 527106).
+    - debian/securetty.linux: support Freescale MX-series (debian bug 527095).
+  * Add debian/patches/300_lastlog_failure: fixed upstream (debian bug 524873).
+  * Drop debian/patches/593_omit_lastchange_field_if_clock_is_misset: fixed
+    upstream.
+
+ -- Kees Cook <kees at ubuntu.com>  Tue, 05 May 2009 09:45:21 -0700
+
 shadow (1:4.1.3.1-1) unstable; urgency=low
 
   * The "Le Puant Macéré" release.
@@ -563,6 +1029,108 @@ shadow (1:4.1.3-1) unstable; urgency=low
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Tue, 14 Apr 2009 23:33:22 +0200
 
+shadow (1:4.1.1-6ubuntu6) jaunty; urgency=low
+
+  * debian/login.preinst: fix typo in grep (LP: #354887).
+
+ -- Kees Cook <kees at ubuntu.com>  Fri, 03 Apr 2009 22:12:07 -0700
+
+shadow (1:4.1.1-6ubuntu5) jaunty; urgency=low
+
+  * debian/login.preinst: add special-case handling to restore the
+    original white-space in /etc/login.defs that is changed by
+    system-tools-backends (LP: #316756).
+
+ -- Kees Cook <kees at ubuntu.com>  Fri, 03 Apr 2009 14:33:43 -0700
+
+shadow (1:4.1.1-6ubuntu4) jaunty; urgency=low
+
+  * debian/patches/593_omit_lastchange_field_if_clock_is_misset (LP: #349504)
+    - If the system clock is set to Jan 01, 1970, and a new user is created
+      the last changed field gets set to 0, which tells login that the 
+      password is expired and must be changed. During installation, 
+      this can cause autologin to fail. Having the clock set to 01/01/1970
+      on a fresh install is common on the ARM architecture, so this is a high
+      priority bug since its likely to affect most ARM users on first install
+
+ -- Michael Casadevall <mcasadevall at ubuntu.com>  Thu, 02 Apr 2009 14:05:31 -0400
+
+shadow (1:4.1.1-6ubuntu3) jaunty; urgency=low
+
+  [ Bryan McLellan ]
+  * Don't do the vm-builder root password check on fresh installations
+    (LP: #340841).
+
+ -- Colin Watson <cjwatson at ubuntu.com>  Tue, 17 Mar 2009 13:32:55 +0000
+
+shadow (1:4.1.1-6ubuntu2) jaunty; urgency=low
+
+  * debian/securetty.linux (LP: #316841)
+    - Updated securetty support for Freescale MX-series boards
+
+ -- Michael Casadevall <sonicmctails at gmail.com>  Tue, 13 Jan 2009 12:56:38 -0500
+
+shadow (1:4.1.1-6ubuntu1) jaunty; urgency=low
+
+  * Merge from debian unstable, remaining changes:
+    - Ubuntu specific:
+      + debian/login.pam: Enable SELinux support in login.pam.
+      + debian/rules: regenerate autoconf to avoid libtool-caused FTBFS.
+      + debian/login.defs: use SHA512 by default for password crypt routine.
+      + debian/passwd.postinst: disable the root password for virtual
+        machines created with vm-builder on Ubuntu 8.10.
+    - debian/patches/stdout-encrypted-password.patch: allow chpasswd to
+      report encrypted passwords to stdout for tools needing encrypted
+      passwords (debian bug 505640).
+
+ -- Kees Cook <kees at ubuntu.com>  Mon, 08 Dec 2008 00:44:46 -0800
+
+shadow (1:4.1.1-6) unstable; urgency=medium
+
+  * The "Rollot" release.
+  * debian/patches/303_login_symlink_attack: Fix a race condition that could
+    lead to gaining ownership or changing mode of arbitrary files.
+    Closes: #505271 
+  * debian/patches/304_su.1_synopsis: Fix the su synopsis. username is
+    referenced in the manpage, not LOGIN. Closes: #501830
+  * debian/patches/305_login.1_japanese: Fix the path of the utmp and wtmp
+    files. Closes: #501353
+
+ -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Fri, 14 Nov 2008 21:52:42 +0100
+
+shadow (1:4.1.1-5ubuntu3) jaunty; urgency=low
+
+  * disable the root password for virtual machines created with vm-builder
+    on Ubuntu 8.10. (LP: #296841)
+
+ -- Jamie Strandboge <jamie at ubuntu.com>  Thu, 13 Nov 2008 20:32:42 -0600
+
+shadow (1:4.1.1-5ubuntu2) jaunty; urgency=low
+
+  * debian/login.defs: use SHA512 by default for password crypt routine
+    (LP: #51551, currently Ubuntu specific).
+  * debian/patches/stdout-encrypted-password.patch: allow chpasswd to report
+    encrypted passwords to stdout for tools needing encrypted passwords
+    (debian bug 505640).
+  * debian/rules: regenerate autoconf to avoid libtool-caused FTBFS.
+
+ -- Kees Cook <kees at ubuntu.com>  Thu, 13 Nov 2008 16:43:48 -0800
+
+shadow (1:4.1.1-5ubuntu1) jaunty; urgency=low
+
+  * Merge from debian unstable, remaining changes:
+    - debian/login.pam: Enable SELinux support in login.pam.
+
+ -- Scott James Remnant <scott at ubuntu.com>  Wed, 05 Nov 2008 07:26:43 +0000
+
+shadow (1:4.1.1-5) unstable; urgency=low
+
+  * The "Bergues" release.
+  * debian/login.pam: restore the Etch behavior of pam_securetty.so in case of
+    unknown user. Closes: #443322, #495831
+
+ -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Sun, 14 Sep 2008 19:13:34 +0200
+
 shadow (1:4.1.1-4) unstable; urgency=low
 
   * The "Rocamadour" release.
@@ -640,6 +1208,13 @@ shadow (1:4.1.1-2) unstable; urgency=low
 
  -- Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>  Fri, 13 Jun 2008 01:27:16 +0200
 
+shadow (1:4.1.1-1ubuntu1) intrepid; urgency=low
+
+  * Merge from debian unstable, remaining changes:
+    - debian/login.pam: Enable SELinux support in login.pam.
+
+ -- Kees Cook <kees at ubuntu.com>  Mon, 09 Jun 2008 10:08:38 -0700
+
 shadow (1:4.1.1-1) unstable; urgency=low
 
   * New upstream release. This closes the following bugs:
@@ -765,6 +1340,20 @@ shadow (1:4.1.0-1) unstable; urgency=low
 
  -- Christian Perrier <bubulle at debian.org>  Sat, 12 Jan 2008 20:40:02 +0100
 
+shadow (1:4.0.18.2-1ubuntu2) hardy; urgency=low
+
+  * Add 498_make_useradd_faster_with_ldap: make useradd faster when
+    nsswitch uses LDAP or some other remote names database (LP: #120015),
+    thanks to Vince Busam.
+
+ -- Matt T. Proud <mtp at google.com>  Fri, 08 Feb 2008 18:30:51 -0800
+
+shadow (1:4.0.18.2-1ubuntu1) hardy; urgency=low
+
+  * debian/login.pam: Enable SELinux support in login.pam (LP: #191326).
+
+ -- Caleb Case <ccase at tresys.com>  Fri, 08 Feb 2008 02:20:06 -0500
+
 shadow (1:4.0.18.2-1) unstable; urgency=low
 
   * The "Vacherin" release.
@@ -1707,7 +2296,7 @@ shadow (1:4.0.12-5) unstable; urgency=lo
   * Really add /etc/pam.d/su. Closes: #330291
   
  -- Christian Perrier <bubulle at debian.org>  Wed, 28 Sep 2005 19:59:31 +0200
-   
+
 shadow (1:4.0.12-4) unstable; urgency=low
 
   * The "Epoisses" release
@@ -3039,7 +3628,7 @@ shadow (20000902-6.1) unstable; urgency=
   * Upgrade to latest config.sub and config.guess.  Closes: #88547
  
  -- Gerhard Tonn <gt at debian.org>  Fri,  1 Jun 2001 20:38:43 +0200
-                                                              
+
 shadow (20000902-6) unstable; urgency=medium
 
   * actually set root's password when appropriate
diff -pruN 1:4.2-3.2/debian/control 1:4.2-3.2ubuntu1/debian/control
--- 1:4.2-3.2/debian/control	2016-09-18 14:32:24.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/control	2016-09-20 07:43:54.000000000 +0000
@@ -1,10 +1,11 @@
 Source: shadow
 Section: admin
 Priority: required
-Maintainer: Shadow package maintainers <pkg-shadow-devel at lists.alioth.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
+XSBC-Original-Maintainer: Shadow package maintainers <pkg-shadow-devel at lists.alioth.debian.org>
 Standards-Version: 3.9.5
 Uploaders: Christian Perrier <bubulle at debian.org>, Nicolas FRANCOIS (Nekral) <nicolas.francois at centraliens.net>
-Build-Depends: dh-autoreconf, gettext, libpam0g-dev, debhelper (>= 6.0.7~), quilt, dpkg-dev (>= 1.13.5), xsltproc, docbook-xsl, docbook-xml, libxml2-utils, cdbs, libselinux1-dev [linux-any], libsemanage1-dev [linux-any], gnome-doc-utils (>= 0.4.3), bison, libaudit-dev [linux-any]
+Build-Depends: dh-autoreconf, gettext, libpam0g-dev, debhelper (>= 6.0.7~), dh-systemd, quilt, dpkg-dev (>= 1.13.5), xsltproc, docbook-xsl, docbook-xml, libxml2-utils, cdbs, libselinux1-dev [linux-any], libsemanage1-dev [linux-any], gnome-doc-utils (>= 0.4.3), bison, libaudit-dev [linux-any]
 Vcs-Git: https://anonscm.debian.org/git/pkg-shadow/shadow.git
 Vcs-Browser: https://anonscm.debian.org/git/pkg-shadow/shadow.git
 Homepage: http://pkg-shadow.alioth.debian.org/
diff -pruN 1:4.2-3.2/debian/login.defs 1:4.2-3.2ubuntu1/debian/login.defs
--- 1:4.2-3.2/debian/login.defs	2014-11-19 20:41:19.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/login.defs	2016-09-20 07:43:54.000000000 +0000
@@ -214,13 +214,14 @@ DEFAULT_HOME	yes
 #USERDEL_CMD	/usr/sbin/userdel_local
 
 #
+# Enable setting of the umask group bits to be the same as owner bits
+# (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid is
+# the same as gid, and username is the same as the primary group name.
+#
 # If set to yes, userdel will remove the user´s group if it contains no
 # more members, and useradd will create by default a group with the name
 # of the user.
 #
-# Other former uses of this variable such as setting the umask when
-# user==primary group are not used in PAM environments, such as Debian
-#
 USERGROUPS_ENAB yes
 
 #
diff -pruN 1:4.2-3.2/debian/login.pam 1:4.2-3.2ubuntu1/debian/login.pam
--- 1:4.2-3.2/debian/login.pam	2014-11-19 20:48:40.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/login.pam	2016-09-20 07:43:54.000000000 +0000
@@ -82,8 +82,10 @@ session    optional   pam_lastlog.so
 
 # Prints the message of the day upon succesful login.
 # (Replaces the `MOTD_FILE' option in login.defs)
-session    optional   pam_exec.so type=open_session stdout /bin/uname -snrvm
-session    optional   pam_motd.so
+# This includes a dynamically generated part from /run/motd.dynamic
+# and a static (admin-editable) part from /etc/motd.
+session    optional   pam_motd.so motd=/run/motd.dynamic
+session    optional   pam_motd.so noupdate
 
 # Prints the status of the user's mailbox upon succesful login
 # (Replaces the `MAIL_CHECK_ENAB' option from login.defs). 
diff -pruN 1:4.2-3.2/debian/login.postinst 1:4.2-3.2ubuntu1/debian/login.postinst
--- 1:4.2-3.2/debian/login.postinst	2014-11-19 20:41:19.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/login.postinst	2016-09-20 07:43:54.000000000 +0000
@@ -16,14 +16,26 @@ then
 fi
 rm -f /etc/pam.d/login.pre-upgrade 2>/dev/null
 
-if [ "$1" = "configure" ] && [ "$2" = "" ]
-then
+if [ "$1" = "configure" ]; then
 	# Install faillog during initial installs only
-	if [ ! -f /var/log/faillog ] ; then
+	if [ "$2" = "" ] && [ ! -f /var/log/faillog ] ; then
 		touch /var/log/faillog
 		chown root:root /var/log/faillog
 		chmod 644 /var/log/faillog
 	fi
+
+	# Create subuid/subgid if missing
+	if [ ! -e /etc/subuid ]; then
+		touch /etc/subuid
+		chown root:root /etc/subuid
+		chmod 644 /etc/subuid
+	fi
+
+	if [ ! -e /etc/subgid ]; then
+		touch /etc/subgid
+		chown root:root /etc/subgid
+		chmod 644 /etc/subgid
+	fi
 fi
 
         # Create subuid/subgid if missing
diff -pruN 1:4.2-3.2/debian/passwd.tmpfile 1:4.2-3.2ubuntu1/debian/passwd.tmpfile
--- 1:4.2-3.2/debian/passwd.tmpfile	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/passwd.tmpfile	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,8 @@
+# If a password operation is in progress and we lose power, stale lockfiles
+# can be left behind.  Clear them on boot.
+r! /etc/gshadow.lock
+r! /etc/shadow.lock
+r! /etc/passwd.lock
+r! /etc/group.lock
+r! /etc/subuid.lock
+r! /etc/subgid.lock
diff -pruN 1:4.2-3.2/debian/passwd.upstart 1:4.2-3.2ubuntu1/debian/passwd.upstart
--- 1:4.2-3.2/debian/passwd.upstart	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/passwd.upstart	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,18 @@
+# passwd - clear locks on passwd and related files
+#
+# Copyright 2012 Canonical Ltd.
+# Author: Dmitrijs Ledkovs
+#
+# This helper clears locks on passwd to avoid million duplicate bug reports
+# like this one: https://launchpad.net/bugs/523896
+# Ideally we'd know what lock-up, and doesn't clear the lock, and fix that.
+# But it appears to be safe enough to clear them unconditionally on boot.
+#
+
+description	"Clear passwd locks"
+
+start on filesystem
+
+task
+
+exec rm -f /etc/gshadow.lock /etc/shadow.lock /etc/passwd.lock /etc/group.lock /etc/subuid.lock /etc/subgid.lock
diff -pruN 1:4.2-3.2/debian/patches/1010_extrausers.patch 1:4.2-3.2ubuntu1/debian/patches/1010_extrausers.patch
--- 1:4.2-3.2/debian/patches/1010_extrausers.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/1010_extrausers.patch	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,268 @@
+Description: Add support to passwd for updating libnss-extrausers locations
+Author: Michael Terry <michael.terry at canonical.com>
+
+Index: shadow-4.2/lib/defines.h
+===================================================================
+--- shadow-4.2.orig/lib/defines.h
++++ shadow-4.2/lib/defines.h
+@@ -316,6 +316,14 @@ char *strchr (), *strrchr (), *strtok ()
+ #endif
+ #endif
+ 
++#ifndef EXTRAUSERS_PASSWD_FILE
++#define EXTRAUSERS_PASSWD_FILE "/var/lib/extrausers/passwd"
++#endif
++
++#ifndef EXTRAUSERS_SHADOW_FILE
++#define EXTRAUSERS_SHADOW_FILE "/var/lib/extrausers/shadow"
++#endif
++
+ #ifndef NULL
+ #define NULL ((void *) 0)
+ #endif
+Index: shadow-4.2/src/passwd.c
+===================================================================
+--- shadow-4.2.orig/src/passwd.c
++++ shadow-4.2/src/passwd.c
+@@ -565,8 +565,15 @@ static void update_noshadow (void)
+ {
+ 	const struct passwd *pw;
+ 	struct passwd *npw;
++	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
++	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
+ 
+ 	if (pw_lock () == 0) {
++		if (try_extrausers) {
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			update_noshadow ();
++			return;
++		}
+ 		(void) fprintf (stderr,
+ 		                _("%s: cannot lock %s; try again later.\n"),
+ 		                Prog, pw_dbname ());
+@@ -574,6 +581,20 @@ static void update_noshadow (void)
+ 	}
+ 	pw_locked = true;
+ 	if (pw_open (O_RDWR) == 0) {
++		if (try_extrausers) {
++			if (pw_unlock () == 0) {
++				(void) fprintf (stderr,
++				                _("%s: failed to unlock %s\n"),
++				                Prog, pw_dbname ());
++				SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
++				/* continue */
++			}
++			pw_locked = false;
++
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			update_noshadow ();
++			return;
++		}
+ 		(void) fprintf (stderr,
+ 		                _("%s: cannot open %s\n"),
+ 		                Prog, pw_dbname ());
+@@ -582,6 +603,21 @@ static void update_noshadow (void)
+ 	}
+ 	pw = pw_locate (name);
+ 	if (NULL == pw) {
++		if (try_extrausers) {
++			(void) pw_close ();
++			if (pw_unlock () == 0) {
++				(void) fprintf (stderr,
++				                _("%s: failed to unlock %s\n"),
++				                Prog, pw_dbname ());
++				SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
++				/* continue */
++			}
++			pw_locked = false;
++
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			update_noshadow ();
++			return;
++		}
+ 		(void) fprintf (stderr,
+ 		                _("%s: user '%s' does not exist in %s\n"),
+ 		                Prog, name, pw_dbname ());
+@@ -619,8 +655,15 @@ static void update_shadow (void)
+ {
+ 	const struct spwd *sp;
+ 	struct spwd *nsp;
++	bool try_extrausers = strcmp (spw_dbname (), EXTRAUSERS_SHADOW_FILE) != 0 &&
++	                      access (EXTRAUSERS_SHADOW_FILE, F_OK) == 0;
+ 
+ 	if (spw_lock () == 0) {
++		if (try_extrausers) {
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			update_shadow ();
++			return;
++		}
+ 		(void) fprintf (stderr,
+ 		                _("%s: cannot lock %s; try again later.\n"),
+ 		                Prog, spw_dbname ());
+@@ -628,6 +671,20 @@ static void update_shadow (void)
+ 	}
+ 	spw_locked = true;
+ 	if (spw_open (O_RDWR) == 0) {
++		if (try_extrausers) {
++			if (spw_unlock () == 0) {
++				(void) fprintf (stderr,
++						        _("%s: failed to unlock %s\n"),
++						        Prog, spw_dbname ());
++				SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
++				/* continue */
++			}
++			spw_locked = false;
++
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			update_shadow ();
++			return;
++		}
+ 		(void) fprintf (stderr,
+ 		                _("%s: cannot open %s\n"),
+ 		                Prog, spw_dbname ());
+@@ -638,7 +695,9 @@ static void update_shadow (void)
+ 	if (NULL == sp) {
+ 		/* Try to update the password in /etc/passwd instead. */
+ 		(void) spw_close ();
+-		update_noshadow ();
++		if (!try_extrausers) {
++			update_noshadow ();
++		}
+ 		if (spw_unlock () == 0) {
+ 			(void) fprintf (stderr,
+ 			                _("%s: failed to unlock %s\n"),
+@@ -647,6 +706,10 @@ static void update_shadow (void)
+ 			/* continue */
+ 		}
+ 		spw_locked = false;
++		if (try_extrausers) {
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			update_shadow ();
++		}
+ 		return;
+ 	}
+ 	nsp = __spw_dup (sp);
+Index: shadow-4.2/lib/commonio.c
+===================================================================
+--- shadow-4.2.orig/lib/commonio.c
++++ shadow-4.2/lib/commonio.c
+@@ -401,6 +401,7 @@ int commonio_lock_nowait (struct commoni
+ int commonio_lock (struct commonio_db *db)
+ {
+ #ifdef HAVE_LCKPWDF
++  if (strncmp(db->filename, "/etc/", 5) == 0) {
+ 	/*
+ 	 * only if the system libc has a real lckpwdf() - the one from
+ 	 * lockpw.c calls us and would cause infinite recursion!
+@@ -428,7 +429,9 @@ int commonio_lock (struct commonio_db *d
+ 
+ 	ulckpwdf ();
+ 	return 0;		/* failure */
+-#else				/* !HAVE_LCKPWDF */
++  } else /* strncmp(db->filename, "/etc/", 5) == 0 */
++#endif				/* HAVE_LCKPWDF */
++  {
+ 	int i;
+ 
+ 	/*
+@@ -456,7 +459,7 @@ int commonio_lock (struct commonio_db *d
+ 		}
+ 	}
+ 	return 0;		/* failure */
+-#endif				/* !HAVE_LCKPWDF */
++  }
+ }
+ 
+ static void dec_lock_count (void)
+Index: shadow-4.2/src/usermod.c
+===================================================================
+--- shadow-4.2.orig/src/usermod.c
++++ shadow-4.2/src/usermod.c
+@@ -1523,7 +1523,16 @@ static void close_files (void)
+  */
+ static void open_files (void)
+ {
++	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
++	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
++
+ 	if (pw_lock () == 0) {
++		if (try_extrausers) {
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			open_files ();
++			return;
++		}
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, pw_dbname ());
+@@ -1531,12 +1540,29 @@ static void open_files (void)
+ 	}
+ 	pw_locked = true;
+ 	if (pw_open (O_RDWR) == 0) {
++		if (try_extrausers) {
++			pw_unlock ();
++			pw_locked = false;
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			open_files ();
++			return;
++		}
+ 		fprintf (stderr,
+ 		         _("%s: cannot open %s\n"),
+ 		         Prog, pw_dbname ());
+ 		fail_exit (E_PW_UPDATE);
+ 	}
+ 	if (is_shadow_pwd && (spw_lock () == 0)) {
++		if (try_extrausers) {
++			pw_close ();
++			pw_unlock ();
++			pw_locked = false;
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			open_files ();
++			return;
++		}
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, spw_dbname ());
+@@ -1544,6 +1570,17 @@ static void open_files (void)
+ 	}
+ 	spw_locked = true;
+ 	if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
++		if (try_extrausers) {
++			pw_close ();
++			pw_unlock ();
++			spw_unlock ();
++			pw_locked = false;
++			spw_locked = false;
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			open_files ();
++			return;
++		}
+ 		fprintf (stderr,
+ 		         _("%s: cannot open %s\n"),
+ 		         Prog, spw_dbname ());
+@@ -1632,11 +1669,22 @@ static void usr_update (void)
+ 	struct spwd spent;
+ 	const struct spwd *spwd = NULL;
+ 
++	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
++	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
++
+ 	/*
+ 	 * Locate the entry in /etc/passwd, which MUST exist.
+ 	 */
+ 	pwd = pw_locate (user_name);
+ 	if (NULL == pwd) {
++		if (try_extrausers) {
++			close_files ();
++			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++			open_files ();
++			usr_update ();
++			return;
++		}
+ 		fprintf (stderr,
+ 		         _("%s: user '%s' does not exist in %s\n"),
+ 		         Prog, user_name, pw_dbname ());
diff -pruN 1:4.2-3.2/debian/patches/1011_extrausers_toggle.patch 1:4.2-3.2ubuntu1/debian/patches/1011_extrausers_toggle.patch
--- 1:4.2-3.2/debian/patches/1011_extrausers_toggle.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/1011_extrausers_toggle.patch	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,150 @@
+Index: shadow-4.2/lib/defines.h
+===================================================================
+--- shadow-4.2.orig/lib/defines.h
++++ shadow-4.2/lib/defines.h
+@@ -324,6 +324,22 @@
+ #define EXTRAUSERS_SHADOW_FILE "/var/lib/extrausers/shadow"
+ #endif
+ 
++#ifndef EXTRAUSERS_GROUP_FILE
++#define EXTRAUSERS_GROUP_FILE "/var/lib/extrausers/group"
++#endif
++
++#ifndef EXTRAUSERS_SHADOWGROUP_FILE
++#define EXTRAUSERS_SHADOWGROUP_FILE "/var/lib/extrausers/gshadow"
++#endif
++
++#ifndef EXTRAUSERS_SUBUID_FILE
++#define EXTRAUSERS_SUBUID_FILE "/var/lib/extrausers/subuid"
++#endif
++
++#ifndef EXTRAUSERS_SUBGID_FILE
++#define EXTRAUSERS_SUBGID_FILE "/var/lib/extrausers/subgid"
++#endif
++
+ #ifndef NULL
+ #define NULL ((void *) 0)
+ #endif
+Index: shadow-4.2/src/groupadd.c
+===================================================================
+--- shadow-4.2.orig/src/groupadd.c
++++ shadow-4.2/src/groupadd.c
+@@ -102,6 +102,12 @@
+ static void check_flags (void);
+ static void check_perms (void);
+ 
++#ifndef EXTRAUSERS_OPT
++#define EXTRAUSERS_OPT 100000
++#endif
++
++static bool use_extrausers = false;
++
+ /*
+  * usage - display usage message and exit
+  */
+@@ -123,6 +129,7 @@
+ 	(void) fputs (_("  -p, --password PASSWORD       use this encrypted password for the new group\n"), usageout);
+ 	(void) fputs (_("  -r, --system                  create a system account\n"), usageout);
+ 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
++	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);
+ 	(void) fputs ("\n", usageout);
+ 	exit (status);
+ }
+@@ -386,12 +393,16 @@
+ 		{"password",   required_argument, NULL, 'p'},
+ 		{"system",     no_argument,       NULL, 'r'},
+ 		{"root",       required_argument, NULL, 'R'},
++        {"extrausers", no_argument,       NULL, EXTRAUSERS_OPT},
+ 		{NULL, 0, NULL, '\0'}
+ 	};
+ 
+ 	while ((c = getopt_long (argc, argv, "fg:hK:op:rR:",
+ 		                 long_options, NULL)) != -1) {
+ 		switch (c) {
++        case EXTRAUSERS_OPT:
++            use_extrausers = true;
++            break;
+ 		case 'f':
+ 			/*
+ 			 * "force" - do nothing, just exit(0), if the
+@@ -598,7 +609,18 @@
+ 
+ 	check_perms ();
+ 
++    if (use_extrausers) {
++		fprintf (stderr, "ENTER EXTRAUSERS_GROUP_FILE");
++        gr_setdbname (EXTRAUSERS_GROUP_FILE);
++		fprintf (stderr, "EXIT EXTRAUSERS_GROUP_FILE");
++    }
++
+ #ifdef SHADOWGRP
++    if (use_extrausers) {
++		fprintf (stderr, "ENTER EXTRAUSERS_SHADOWGROUP_FILE");
++        sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
++		fprintf (stderr, "EXIT EXTRAUSERS_SHADOWGROUP_FILE");
++    }
+ 	is_shadow_grp = sgr_file_present ();
+ #endif
+ 
+Index: shadow-4.2/src/useradd.c
+===================================================================
+--- shadow-4.2.orig/src/useradd.c
++++ shadow-4.2/src/useradd.c
+@@ -141,6 +141,12 @@
+ static long sys_ngroups;
+ static bool do_grp_update = false;	/* group files need to be updated */
+ 
++#ifndef EXTRAUSERS_OPT
++#define EXTRAUSERS_OPT 100000
++#endif
++
++static bool use_extrausers = false;
++
+ static bool
+     bflg = false,		/* new default root of home directory */
+     cflg = false,		/* comment (GECOS) field for new account */
+@@ -778,6 +784,7 @@
+ #ifdef WITH_SELINUX
+ 	(void) fputs (_("  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping\n"), usageout);
+ #endif				/* WITH_SELINUX */
++	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);
+ 	(void) fputs ("\n", usageout);
+ 	exit (status);
+ }
+@@ -1052,6 +1059,7 @@
+ #ifdef WITH_SELINUX
+ 			{"selinux-user",   required_argument, NULL, 'Z'},
+ #endif				/* WITH_SELINUX */
++			{"extrausers",     no_argument,       NULL, EXTRAUSERS_OPT},
+ 			{NULL, 0, NULL, '\0'}
+ 		};
+ 		while ((c = getopt_long (argc, argv,
+@@ -1062,6 +1070,9 @@
+ #endif				/* !WITH_SELINUX */
+ 		                         long_options, NULL)) != -1) {
+ 			switch (c) {
++			case EXTRAUSERS_OPT:
++                use_extrausers = true;
++                break;
+ 			case 'b':
+ 				if (   ( !VALID (optarg) )
+ 				    || ( optarg[0] != '/' )) {
+@@ -2122,6 +2133,18 @@
+ 		}
+ 	}
+ 
++    if (use_extrausers) {
++        pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++        spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++        gr_setdbname (EXTRAUSERS_GROUP_FILE);
++        /* TODO expose this information in other tools */
++        sub_uid_setdbname(EXTRAUSERS_SUBUID_FILE);
++        sub_gid_setdbname(EXTRAUSERS_SUBGID_FILE);
++#ifdef SHADOWGRP
++        sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
++#endif
++    }
++
+ 	/*
+ 	 * Do the hard stuff:
+ 	 * - open the files,
diff -pruN 1:4.2-3.2/debian/patches/1012_extrausers_chfn.patch 1:4.2-3.2ubuntu1/debian/patches/1012_extrausers_chfn.patch
--- 1:4.2-3.2/debian/patches/1012_extrausers_chfn.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/1012_extrausers_chfn.patch	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,64 @@
+Description: add support for --extrausers for chfn
+ This add support for --extrausers to the chfn tool.
+Author: Michael Vogt <mvo at ubuntu.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1495580
+
+--- shadow-4.2.orig/src/chfn.c
++++ shadow-4.2/src/chfn.c
+@@ -74,6 +74,11 @@ static bool hflg = false;		/* -h - set h
+ static bool oflg = false;		/* -o - set other information        */
+ static bool pw_locked = false;
+ 
++#ifndef EXTRAUSERS_OPT
++#define EXTRAUSERS_OPT 100000
++#endif
++static bool use_extrausers = false;
++
+ /*
+  * External identifiers
+  */
+@@ -126,6 +131,7 @@ static /*@noreturn@*/void usage (int sta
+ 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+ 	(void) fputs (_("  -u, --help                    display this help message and exit\n"), usageout);
+ 	(void) fputs (_("  -w, --work-phone WORK_PHONE   change user's office phone number\n"), usageout);
++	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);        
+ 	(void) fputs ("\n", usageout);
+ 	exit (status);
+ }
+@@ -276,6 +282,7 @@ static void process_flags (int argc, cha
+ 		{"root",       required_argument, NULL, 'R'},
+ 		{"help",       no_argument,       NULL, 'u'},
+ 		{"work-phone", required_argument, NULL, 'w'},
++                {"extrausers", no_argument, NULL, EXTRAUSERS_OPT},
+ 		{NULL, 0, NULL, '\0'}
+ 	};
+ 
+@@ -289,6 +296,9 @@ static void process_flags (int argc, cha
+ 	while ((c = getopt_long (argc, argv, "f:h:o:r:R:uw:",
+ 	                         long_options, NULL)) != -1) {
+ 		switch (c) {
++                case EXTRAUSERS_OPT:
++                   use_extrausers = true;
++                   break;
+ 		case 'f':
+ 			if (!may_change_field ('f')) {
+ 				fprintf (stderr,
+@@ -657,6 +667,18 @@ int main (int argc, char **argv)
+ 	/* parse the command line options */
+ 	process_flags (argc, argv);
+ 
++        if (use_extrausers) {
++           pw_setdbname (EXTRAUSERS_PASSWD_FILE);
++           spw_setdbname (EXTRAUSERS_SHADOW_FILE);
++           gr_setdbname (EXTRAUSERS_GROUP_FILE);
++           /* TODO expose this information in other tools */
++           sub_uid_setdbname(EXTRAUSERS_SUBUID_FILE);
++           sub_gid_setdbname(EXTRAUSERS_SUBGID_FILE);
++#ifdef SHADOWGRP
++           sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
++#endif
++        }
++        
+ 	/*
+ 	 * Get the name of the user to check. It is either the command line
+ 	 * name, or the name getlogin() returns.
diff -pruN 1:4.2-3.2/debian/patches/1021_no_subuids_for_system_users.patch 1:4.2-3.2ubuntu1/debian/patches/1021_no_subuids_for_system_users.patch
--- 1:4.2-3.2/debian/patches/1021_no_subuids_for_system_users.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/1021_no_subuids_for_system_users.patch	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,32 @@
+Description: useradd: process flags before testing them
+ We check rflg for requested system user and do not grant subuids in that
+ case - however we were doing so before actually processing command line
+ flags.  Process flags first.
+ This needs to go upstream.
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1545884
+Author: Serge Hallyn <serge.hallyn at gmail.com>
+Forwarded: no
+
+Index: shadow-4.2/src/useradd.c
+===================================================================
+--- shadow-4.2.orig/src/useradd.c
++++ shadow-4.2/src/useradd.c
+@@ -2037,6 +2037,9 @@ int main (int argc, char **argv)
+ #ifdef SHADOWGRP
+ 	is_shadow_grp = sgr_file_present ();
+ #endif
++
++	process_flags (argc, argv);
++
+ #ifdef ENABLE_SUBIDS
+ 	is_sub_uid = sub_uid_file_present () && !rflg &&
+ 	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
+@@ -2046,8 +2049,6 @@ int main (int argc, char **argv)
+ 
+ 	get_defaults ();
+ 
+-	process_flags (argc, argv);
+-
+ #ifdef ACCT_TOOLS_SETUID
+ #ifdef USE_PAM
+ 	{
diff -pruN 1:4.2-3.2/debian/patches/series 1:4.2-3.2ubuntu1/debian/patches/series
--- 1:4.2-3.2/debian/patches/series	2015-11-12 14:24:49.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/series	2016-09-20 07:43:54.000000000 +0000
@@ -33,5 +33,10 @@
 #userns/manpagetypo
 #userns/16_add-argument-sanity-checking.patch
 1000_configure_userns
+1010_extrausers.patch
 1010_vietnamese_translation
 1020_fix_user_busy_errors
+userns/subuids-nonlocal-users
+1011_extrausers_toggle.patch
+1021_no_subuids_for_system_users.patch
+1012_extrausers_chfn.patch
diff -pruN 1:4.2-3.2/debian/patches/userns/subuids-nonlocal-users 1:4.2-3.2ubuntu1/debian/patches/userns/subuids-nonlocal-users
--- 1:4.2-3.2/debian/patches/userns/subuids-nonlocal-users	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/patches/userns/subuids-nonlocal-users	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,143 @@
+Description: Don't limit subuid/subgid support to local users
+ The current implementation of subuid/subgid support in usermod requires the
+ user to be a local user present in /etc/passwd.  There doesn't seem to be a
+ good reason for this; subuids should work equally well for users whose
+ records are in other NSS databases.
+Author: Steve Langasek <steve.langasek at ubuntu.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1475749
+
+Index: shadow-4.2/src/usermod.c
+===================================================================
+--- shadow-4.2.orig/src/usermod.c
++++ shadow-4.2/src/usermod.c
+@@ -1717,60 +1717,6 @@
+ 			fail_exit (E_PW_UPDATE);
+ 		}
+ 	}
+-#ifdef ENABLE_SUBIDS
+-	if (Vflg) {
+-		struct ulong_range_list_entry *ptr;
+-		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
+-			unsigned long count = ptr->range.last - ptr->range.first + 1;
+-			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
+-				fprintf (stderr,
+-					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
+-					Prog, ptr->range.first, ptr->range.last, 
+-					sub_uid_dbname ());
+-				fail_exit (E_SUB_UID_UPDATE);
+-			}
+-		}
+-	}
+-	if (vflg) {
+-		struct ulong_range_list_entry *ptr;
+-		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
+-			unsigned long count = ptr->range.last - ptr->range.first + 1;
+-			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
+-				fprintf (stderr,
+-					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
+-					Prog, ptr->range.first, ptr->range.last, 
+-					sub_uid_dbname ());
+-				fail_exit (E_SUB_UID_UPDATE);
+-			}
+-		}
+-	}
+-	if (Wflg) {
+-		struct ulong_range_list_entry *ptr;
+-		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
+-			unsigned long count = ptr->range.last - ptr->range.first + 1;
+-			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
+-				fprintf (stderr,
+-					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
+-					Prog, ptr->range.first, ptr->range.last, 
+-					sub_gid_dbname ());
+-				fail_exit (E_SUB_GID_UPDATE);
+-			}
+-		}
+-	}
+-	if (wflg) {
+-		struct ulong_range_list_entry *ptr;
+-		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
+-			unsigned long count = ptr->range.last - ptr->range.first + 1;
+-			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
+-				fprintf (stderr,
+-					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
+-					Prog, ptr->range.first, ptr->range.last, 
+-					sub_gid_dbname ());
+-				fail_exit (E_SUB_GID_UPDATE);
+-			}
+-		}
+-	}
+-#endif				/* ENABLE_SUBIDS */
+ }
+ 
+ /*
+@@ -2175,15 +2121,66 @@
+ 	open_files ();
+ 	if (   cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
+ 	    || sflg || uflg || Uflg
+-#ifdef ENABLE_SUBIDS
+-	    || vflg || Vflg || wflg || Wflg
+-#endif				/* ENABLE_SUBIDS */
+ 	    ) {
+ 		usr_update ();
+ 	}
+ 	if (Gflg || lflg) {
+ 		grp_update ();
+ 	}
++#ifdef ENABLE_SUBIDS
++	if (Vflg) {
++		struct ulong_range_list_entry *ptr;
++		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
++			unsigned long count = ptr->range.last - ptr->range.first + 1;
++			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
++				fprintf (stderr,
++					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
++					Prog, ptr->range.first, ptr->range.last, 
++					sub_uid_dbname ());
++				fail_exit (E_SUB_UID_UPDATE);
++			}
++		}
++	}
++	if (vflg) {
++		struct ulong_range_list_entry *ptr;
++		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
++			unsigned long count = ptr->range.last - ptr->range.first + 1;
++			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
++				fprintf (stderr,
++					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
++					Prog, ptr->range.first, ptr->range.last, 
++					sub_uid_dbname ());
++				fail_exit (E_SUB_UID_UPDATE);
++			}
++		}
++	}
++	if (Wflg) {
++		struct ulong_range_list_entry *ptr;
++		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
++			unsigned long count = ptr->range.last - ptr->range.first + 1;
++			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
++				fprintf (stderr,
++					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
++					Prog, ptr->range.first, ptr->range.last, 
++					sub_gid_dbname ());
++				fail_exit (E_SUB_GID_UPDATE);
++			}
++		}
++	}
++	if (wflg) {
++		struct ulong_range_list_entry *ptr;
++		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
++			unsigned long count = ptr->range.last - ptr->range.first + 1;
++			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
++				fprintf (stderr,
++					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
++					Prog, ptr->range.first, ptr->range.last, 
++					sub_gid_dbname ());
++				fail_exit (E_SUB_GID_UPDATE);
++			}
++		}
++	}
++#endif				/* ENABLE_SUBIDS */
+ 	close_files ();
+ 
+ #ifdef WITH_TCB
diff -pruN 1:4.2-3.2/debian/rules 1:4.2-3.2ubuntu1/debian/rules
--- 1:4.2-3.2/debian/rules	2016-09-18 14:41:28.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/rules	2016-09-20 07:43:54.000000000 +0000
@@ -8,6 +8,9 @@ export DEB_BUILD_MAINT_OPTIONS = hardeni
 DPKG_EXPORT_BUILDFLAGS = 1
 include /usr/share/dpkg/buildflags.mk
 
+DEB_DH_INSTALLINIT_ARGS = --no-start
+DEB_DH_SYSTEMD_START_ARGS = --no-start
+
 # Call autoreconf since we need to regenerate all the autofoo files
 include /usr/share/cdbs/1/rules/autoreconf.mk
 include /usr/share/cdbs/1/rules/debhelper.mk
@@ -39,6 +42,8 @@ endif
 	dh_installpam -p login --name=su
 	install -c -m 444 debian/login.defs debian/login/etc/login.defs
 	install -c -m 444 debian/securetty.$(DEB_HOST_ARCH_OS) debian/login/etc/securetty
+	install -d debian/login/usr/share/apport/package-hooks
+	install -c -m 644 debian/source_shadow.py debian/login/usr/share/apport/package-hooks/source_shadow.py
 	dh_lintian -p login
 
 binary-install/passwd::
diff -pruN 1:4.2-3.2/debian/source_shadow.py 1:4.2-3.2ubuntu1/debian/source_shadow.py
--- 1:4.2-3.2/debian/source_shadow.py	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/debian/source_shadow.py	2016-09-20 07:43:54.000000000 +0000
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+'''Apport package hook for shadow
+
+(c) 2010 Canonical Ltd.
+Contributors:
+Marc Deslauriers <marc.deslauriers at canonical.com>
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
+the full text of the license.
+'''
+
+from apport.hookutils import *
+
+def add_info(report):
+
+    attach_file_if_exists(report, '/etc/login.defs', 'LoginDefs')
+
+if __name__ == '__main__':
+    report = {}
+    add_info(report)
+    for key in report:
+        print('[%s]\n%s' % (key, report[key]))
diff -pruN 1:4.2-3.2/lib/commonio.c 1:4.2-3.2ubuntu1/lib/commonio.c
--- 1:4.2-3.2/lib/commonio.c	2016-09-20 17:11:18.000000000 +0000
+++ 1:4.2-3.2ubuntu1/lib/commonio.c	2016-09-20 17:11:18.000000000 +0000
@@ -401,6 +401,7 @@ int commonio_lock_nowait (struct commoni
 int commonio_lock (struct commonio_db *db)
 {
 #ifdef HAVE_LCKPWDF
+  if (strncmp(db->filename, "/etc/", 5) == 0) {
 	/*
 	 * only if the system libc has a real lckpwdf() - the one from
 	 * lockpw.c calls us and would cause infinite recursion!
@@ -428,7 +429,9 @@ int commonio_lock (struct commonio_db *d
 
 	ulckpwdf ();
 	return 0;		/* failure */
-#else				/* !HAVE_LCKPWDF */
+  } else /* strncmp(db->filename, "/etc/", 5) == 0 */
+#endif				/* HAVE_LCKPWDF */
+  {
 	int i;
 
 	/*
@@ -456,7 +459,7 @@ int commonio_lock (struct commonio_db *d
 		}
 	}
 	return 0;		/* failure */
-#endif				/* !HAVE_LCKPWDF */
+  }
 }
 
 static void dec_lock_count (void)
diff -pruN 1:4.2-3.2/lib/defines.h 1:4.2-3.2ubuntu1/lib/defines.h
--- 1:4.2-3.2/lib/defines.h	2014-03-01 17:56:04.000000000 +0000
+++ 1:4.2-3.2ubuntu1/lib/defines.h	2016-09-20 17:11:18.000000000 +0000
@@ -316,6 +316,30 @@ char *strchr (), *strrchr (), *strtok ()
 #endif
 #endif
 
+#ifndef EXTRAUSERS_PASSWD_FILE
+#define EXTRAUSERS_PASSWD_FILE "/var/lib/extrausers/passwd"
+#endif
+
+#ifndef EXTRAUSERS_SHADOW_FILE
+#define EXTRAUSERS_SHADOW_FILE "/var/lib/extrausers/shadow"
+#endif
+
+#ifndef EXTRAUSERS_GROUP_FILE
+#define EXTRAUSERS_GROUP_FILE "/var/lib/extrausers/group"
+#endif
+
+#ifndef EXTRAUSERS_SHADOWGROUP_FILE
+#define EXTRAUSERS_SHADOWGROUP_FILE "/var/lib/extrausers/gshadow"
+#endif
+
+#ifndef EXTRAUSERS_SUBUID_FILE
+#define EXTRAUSERS_SUBUID_FILE "/var/lib/extrausers/subuid"
+#endif
+
+#ifndef EXTRAUSERS_SUBGID_FILE
+#define EXTRAUSERS_SUBGID_FILE "/var/lib/extrausers/subgid"
+#endif
+
 #ifndef NULL
 #define NULL ((void *) 0)
 #endif
diff -pruN 1:4.2-3.2/.pc/1010_extrausers.patch/lib/commonio.c 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/lib/commonio.c
--- 1:4.2-3.2/.pc/1010_extrausers.patch/lib/commonio.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/lib/commonio.c	2016-09-20 17:11:18.000000000 +0000
@@ -0,0 +1,1282 @@
+/*
+ * Copyright (c) 1990 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2001, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include "defines.h"
+#include <assert.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <grp.h>
+#include "nscd.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#endif				/* WITH_TCB */
+#include "prototypes.h"
+#include "commonio.h"
+
+/* local function prototypes */
+static int lrename (const char *, const char *);
+static int check_link_count (const char *file);
+static int do_lock_file (const char *file, const char *lock, bool log);
+static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
+	const char *name,
+	const char *mode,
+	const struct stat *sb);
+static int create_backup (const char *, FILE *);
+static void free_linked_list (struct commonio_db *);
+static void add_one_entry (
+	struct commonio_db *db,
+	/*@owned@*/struct commonio_entry *p);
+static bool name_is_nis (const char *name);
+static int write_all (const struct commonio_db *);
+static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
+	struct commonio_db *,
+	const char *);
+static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
+	struct commonio_db *,
+	/*@null@*/struct commonio_entry *pos,
+	const char *);
+
+static int lock_count = 0;
+static bool nscd_need_reload = false;
+
+/*
+ * Simple rename(P) alternative that attempts to rename to symlink
+ * target.
+ */
+int lrename (const char *old, const char *new)
+{
+	int res;
+	char *r = NULL;
+
+#if defined(S_ISLNK)
+#ifndef __GLIBC__
+	char resolved_path[PATH_MAX];
+#endif				/* !__GLIBC__ */
+	struct stat sb;
+	if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) {
+#ifdef __GLIBC__ /* now a POSIX.1-2008 feature */
+		r = realpath (new, NULL);
+#else				/* !__GLIBC__ */
+		r = realpath (new, resolved_path);
+#endif				/* !__GLIBC__ */
+		if (NULL == r) {
+			perror ("realpath in lrename()");
+		} else {
+			new = r;
+		}
+	}
+#endif				/* S_ISLNK */
+
+	res = rename (old, new);
+
+#ifdef __GLIBC__
+	if (NULL != r) {
+		free (r);
+	}
+#endif				/* __GLIBC__ */
+
+	return res;
+}
+
+static int check_link_count (const char *file)
+{
+	struct stat sb;
+
+	if (stat (file, &sb) != 0) {
+		return 0;
+	}
+
+	if (sb.st_nlink != 2) {
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int do_lock_file (const char *file, const char *lock, bool log)
+{
+	int fd;
+	pid_t pid;
+	ssize_t len;
+	int retval;
+	char buf[32];
+
+	fd = open (file, O_CREAT | O_EXCL | O_WRONLY, 0600);
+	if (-1 == fd) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: %s: %s\n",
+			                Prog, file, strerror (errno));
+		}
+		return 0;
+	}
+
+	pid = getpid ();
+	snprintf (buf, sizeof buf, "%lu", (unsigned long) pid);
+	len = (ssize_t) strlen (buf) + 1;
+	if (write (fd, buf, (size_t) len) != len) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: %s: %s\n",
+			                Prog, file, strerror (errno));
+		}
+		(void) close (fd);
+		unlink (file);
+		return 0;
+	}
+	close (fd);
+
+	if (link (file, lock) == 0) {
+		retval = check_link_count (file);
+		if ((0==retval) && log) {
+			(void) fprintf (stderr,
+			                "%s: %s: lock file already used\n",
+			                Prog, file);
+		}
+		unlink (file);
+		return retval;
+	}
+
+	fd = open (lock, O_RDWR);
+	if (-1 == fd) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: %s: %s\n",
+			                Prog, lock, strerror (errno));
+		}
+		unlink (file);
+		errno = EINVAL;
+		return 0;
+	}
+	len = read (fd, buf, sizeof (buf) - 1);
+	close (fd);
+	if (len <= 0) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: existing lock file %s without a PID\n",
+			                Prog, lock);
+		}
+		unlink (file);
+		errno = EINVAL;
+		return 0;
+	}
+	buf[len] = '\0';
+	if (get_pid (buf, &pid) == 0) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: existing lock file %s with an invalid PID '%s'\n",
+			                Prog, lock, buf);
+		}
+		unlink (file);
+		errno = EINVAL;
+		return 0;
+	}
+	if (kill (pid, 0) == 0) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: lock %s already used by PID %lu\n",
+			                Prog, lock, (unsigned long) pid);
+		}
+		unlink (file);
+		errno = EEXIST;
+		return 0;
+	}
+	if (unlink (lock) != 0) {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: cannot get lock %s: %s\n",
+			                Prog, lock, strerror (errno));
+		}
+		unlink (file);
+		return 0;
+	}
+
+	retval = 0;
+	if (link (file, lock) == 0) {
+		retval = check_link_count (file);
+		if ((0==retval) && log) {
+			(void) fprintf (stderr,
+			                "%s: %s: lock file already used\n",
+			                Prog, file);
+		}
+	} else {
+		if (log) {
+			(void) fprintf (stderr,
+			                "%s: cannot get lock %s: %s\n",
+			                Prog, lock, strerror (errno));
+		}
+	}
+
+	unlink (file);
+	return retval;
+}
+
+
+static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
+	const char *name,
+	const char *mode,
+	const struct stat *sb)
+{
+	FILE *fp;
+	mode_t mask;
+
+	mask = umask (0777);
+	fp = fopen (name, mode);
+	(void) umask (mask);
+	if (NULL == fp) {
+		return NULL;
+	}
+
+#ifdef HAVE_FCHOWN
+	if (fchown (fileno (fp), sb->st_uid, sb->st_gid) != 0) {
+		goto fail;
+	}
+#else				/* !HAVE_FCHOWN */
+	if (chown (name, sb->st_mode) != 0) {
+		goto fail;
+	}
+#endif				/* !HAVE_FCHOWN */
+
+#ifdef HAVE_FCHMOD
+	if (fchmod (fileno (fp), sb->st_mode & 0664) != 0) {
+		goto fail;
+	}
+#else				/* !HAVE_FCHMOD */
+	if (chmod (name, sb->st_mode & 0664) != 0) {
+		goto fail;
+	}
+#endif				/* !HAVE_FCHMOD */
+	return fp;
+
+      fail:
+	(void) fclose (fp);
+	/* fopen_set_perms is used for intermediate files */
+	(void) unlink (name);
+	return NULL;
+}
+
+
+static int create_backup (const char *backup, FILE * fp)
+{
+	struct stat sb;
+	struct utimbuf ub;
+	FILE *bkfp;
+	int c;
+	mode_t mask;
+
+	if (fstat (fileno (fp), &sb) != 0) {
+		return -1;
+	}
+
+	mask = umask (077);
+	bkfp = fopen (backup, "w");
+	(void) umask (mask);
+	if (NULL == bkfp) {
+		return -1;
+	}
+
+	/* TODO: faster copy, not one-char-at-a-time.  --marekm */
+	c = 0;
+	if (fseek (fp, 0, SEEK_SET) == 0) {
+		while ((c = getc (fp)) != EOF) {
+			if (putc (c, bkfp) == EOF) {
+				break;
+			}
+		}
+	}
+	if ((c != EOF) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
+		(void) fclose (bkfp);
+		/* FIXME: unlink the backup file? */
+		return -1;
+	}
+	if (   (fsync (fileno (bkfp)) != 0)
+	    || (fclose (bkfp) != 0)) {
+		/* FIXME: unlink the backup file? */
+		return -1;
+	}
+
+	ub.actime = sb.st_atime;
+	ub.modtime = sb.st_mtime;
+	(void) utime (backup, &ub);
+	return 0;
+}
+
+
+static void free_linked_list (struct commonio_db *db)
+{
+	struct commonio_entry *p;
+
+	while (NULL != db->head) {
+		p = db->head;
+		db->head = p->next;
+
+		if (NULL != p->line) {
+			free (p->line);
+		}
+
+		if (NULL != p->eptr) {
+			db->ops->free (p->eptr);
+		}
+
+		free (p);
+	}
+	db->tail = NULL;
+}
+
+
+int commonio_setname (struct commonio_db *db, const char *name)
+{
+	snprintf (db->filename, sizeof (db->filename), "%s", name);
+	return 1;
+}
+
+
+bool commonio_present (const struct commonio_db *db)
+{
+	return (access (db->filename, F_OK) == 0);
+}
+
+
+int commonio_lock_nowait (struct commonio_db *db, bool log)
+{
+	char file[1024];
+	char lock[1024];
+
+	if (db->locked) {
+		return 1;
+	}
+
+	snprintf (file, sizeof file, "%s.%lu",
+	          db->filename, (unsigned long) getpid ());
+	snprintf (lock, sizeof lock, "%s.lock", db->filename);
+	if (do_lock_file (file, lock, log) != 0) {
+		db->locked = true;
+		lock_count++;
+		return 1;
+	}
+	return 0;
+}
+
+
+int commonio_lock (struct commonio_db *db)
+{
+#ifdef HAVE_LCKPWDF
+	/*
+	 * only if the system libc has a real lckpwdf() - the one from
+	 * lockpw.c calls us and would cause infinite recursion!
+	 */
+
+	/*
+	 * Call lckpwdf() on the first lock.
+	 * If it succeeds, call *_lock() only once
+	 * (no retries, it should always succeed).
+	 */
+	if (0 == lock_count) {
+		if (lckpwdf () == -1) {
+			if (geteuid () != 0) {
+				(void) fprintf (stderr,
+				                "%s: Permission denied.\n",
+				                Prog);
+			}
+			return 0;	/* failure */
+		}
+	}
+
+	if (commonio_lock_nowait (db, true) != 0) {
+		return 1;	/* success */
+	}
+
+	ulckpwdf ();
+	return 0;		/* failure */
+#else				/* !HAVE_LCKPWDF */
+	int i;
+
+	/*
+	 * lckpwdf() not used - do it the old way.
+	 */
+#ifndef LOCK_TRIES
+#define LOCK_TRIES 15
+#endif
+
+#ifndef LOCK_SLEEP
+#define LOCK_SLEEP 1
+#endif
+	for (i = 0; i < LOCK_TRIES; i++) {
+		if (i > 0) {
+			sleep (LOCK_SLEEP);	/* delay between retries */
+		}
+		if (commonio_lock_nowait (db, i==LOCK_TRIES-1) != 0) {
+			return 1;	/* success */
+		}
+		/* no unnecessary retries on "permission denied" errors */
+		if (geteuid () != 0) {
+			(void) fprintf (stderr, "%s: Permission denied.\n",
+			                Prog);
+			return 0;
+		}
+	}
+	return 0;		/* failure */
+#endif				/* !HAVE_LCKPWDF */
+}
+
+static void dec_lock_count (void)
+{
+	if (lock_count > 0) {
+		lock_count--;
+		if (lock_count == 0) {
+			/* Tell nscd when lock count goes to zero,
+			   if any of the files were changed.  */
+			if (nscd_need_reload) {
+				nscd_flush_cache ("passwd");
+				nscd_flush_cache ("group");
+				nscd_need_reload = false;
+			}
+#ifdef HAVE_LCKPWDF
+			ulckpwdf ();
+#endif				/* HAVE_LCKPWDF */
+		}
+	}
+}
+
+
+int commonio_unlock (struct commonio_db *db)
+{
+	char lock[1024];
+
+	if (db->isopen) {
+		db->readonly = true;
+		if (commonio_close (db) == 0) {
+			if (db->locked) {
+				dec_lock_count ();
+			}
+			return 0;
+		}
+	}
+	if (db->locked) {
+		/*
+		 * Unlock in reverse order: remove the lock file,
+		 * then call ulckpwdf() (if used) on last unlock.
+		 */
+		db->locked = false;
+		snprintf (lock, sizeof lock, "%s.lock", db->filename);
+		unlink (lock);
+		dec_lock_count ();
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * Add an entry at the end.
+ *
+ * defines p->next, p->prev
+ * (unfortunately, owned special are not supported)
+ */
+static void add_one_entry (struct commonio_db *db,
+                           /*@owned@*/struct commonio_entry *p)
+{
+	/*@-mustfreeonly@*/
+	p->next = NULL;
+	p->prev = db->tail;
+	/*@=mustfreeonly@*/
+	if (NULL == db->head) {
+		db->head = p;
+	}
+	if (NULL != db->tail) {
+		db->tail->next = p;
+	}
+	db->tail = p;
+}
+
+
+static bool name_is_nis (const char *name)
+{
+	return (('+' == name[0]) || ('-' == name[0]));
+}
+
+
+/*
+ * New entries are inserted before the first NIS entry.  Order is preserved
+ * when db is written out.
+ */
+#ifndef KEEP_NIS_AT_END
+#define KEEP_NIS_AT_END 1
+#endif
+
+#if KEEP_NIS_AT_END
+static void add_one_entry_nis (struct commonio_db *db,
+                               /*@owned@*/struct commonio_entry *newp);
+
+/*
+ * Insert an entry between the regular entries, and the NIS entries.
+ *
+ * defines newp->next, newp->prev
+ * (unfortunately, owned special are not supported)
+ */
+static void add_one_entry_nis (struct commonio_db *db,
+                               /*@owned@*/struct commonio_entry *newp)
+{
+	struct commonio_entry *p;
+
+	for (p = db->head; NULL != p; p = p->next) {
+		if (name_is_nis (p->eptr ? db->ops->getname (p->eptr)
+		                         : p->line)) {
+			/*@-mustfreeonly@*/
+			newp->next = p;
+			newp->prev = p->prev;
+			/*@=mustfreeonly@*/
+			if (NULL != p->prev) {
+				p->prev->next = newp;
+			} else {
+				db->head = newp;
+			}
+			p->prev = newp;
+			return;
+		}
+	}
+	add_one_entry (db, newp);
+}
+#endif				/* KEEP_NIS_AT_END */
+
+/* Initial buffer size, as well as increment if not sufficient
+   (for reading very long lines in group files).  */
+#define BUFLEN 4096
+
+int commonio_open (struct commonio_db *db, int mode)
+{
+	char *buf;
+	char *cp;
+	char *line;
+	struct commonio_entry *p;
+	void *eptr = NULL;
+	int flags = mode;
+	size_t buflen;
+	int fd;
+	int saved_errno;
+
+	mode &= ~O_CREAT;
+
+	if (   db->isopen
+	    || (   (O_RDONLY != mode)
+	        && (O_RDWR != mode))) {
+		errno = EINVAL;
+		return 0;
+	}
+	db->readonly = (mode == O_RDONLY);
+	if (!db->readonly && !db->locked) {
+		errno = EACCES;
+		return 0;
+	}
+
+	db->head = NULL;
+	db->tail = NULL;
+	db->cursor = NULL;
+	db->changed = false;
+
+	fd = open (db->filename,
+	             (db->readonly ? O_RDONLY : O_RDWR)
+	           | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
+	saved_errno = errno;
+	db->fp = NULL;
+	if (fd >= 0) {
+#ifdef WITH_TCB
+		if (tcb_is_suspect (fd) != 0) {
+			(void) close (fd);
+			errno = EINVAL;
+			return 0;
+		}
+#endif				/* WITH_TCB */
+		db->fp = fdopen (fd, db->readonly ? "r" : "r+");
+		saved_errno = errno;
+		if (NULL == db->fp) {
+			(void) close (fd);
+		}
+	}
+	errno = saved_errno;
+
+	/*
+	 * If O_CREAT was specified and the file didn't exist, it will be
+	 * created by commonio_close().  We have no entries to read yet.  --marekm
+	 */
+	if (NULL == db->fp) {
+		if (((flags & O_CREAT) != 0) && (ENOENT == errno)) {
+			db->isopen = true;
+			return 1;
+		}
+		return 0;
+	}
+
+	/* Do not inherit fd in spawned processes (e.g. nscd) */
+	fcntl (fileno (db->fp), F_SETFD, FD_CLOEXEC);
+
+	buflen = BUFLEN;
+	buf = (char *) malloc (buflen);
+	if (NULL == buf) {
+		goto cleanup_ENOMEM;
+	}
+
+	while (db->ops->fgets (buf, (int) buflen, db->fp) == buf) {
+		while (   ((cp = strrchr (buf, '\n')) == NULL)
+		       && (feof (db->fp) == 0)) {
+			size_t len;
+
+			buflen += BUFLEN;
+			cp = (char *) realloc (buf, buflen);
+			if (NULL == cp) {
+				goto cleanup_buf;
+			}
+			buf = cp;
+			len = strlen (buf);
+			if (db->ops->fgets (buf + len,
+			                    (int) (buflen - len),
+			                    db->fp) == NULL) {
+				goto cleanup_buf;
+			}
+		}
+		cp = strrchr (buf, '\n');
+		if (NULL != cp) {
+			*cp = '\0';
+		}
+
+		line = strdup (buf);
+		if (NULL == line) {
+			goto cleanup_buf;
+		}
+
+		if (name_is_nis (line)) {
+			eptr = NULL;
+		} else {
+			eptr = db->ops->parse (line);
+			if (NULL != eptr) {
+				eptr = db->ops->dup (eptr);
+				if (NULL == eptr) {
+					goto cleanup_line;
+				}
+			}
+		}
+
+		p = (struct commonio_entry *) malloc (sizeof *p);
+		if (NULL == p) {
+			goto cleanup_entry;
+		}
+
+		p->eptr = eptr;
+		p->line = line;
+		p->changed = false;
+
+		add_one_entry (db, p);
+	}
+
+	free (buf);
+
+	if (ferror (db->fp) != 0) {
+		goto cleanup_errno;
+	}
+
+	if ((NULL != db->ops->open_hook) && (db->ops->open_hook () == 0)) {
+		goto cleanup_errno;
+	}
+
+	db->isopen = true;
+	return 1;
+
+      cleanup_entry:
+	if (NULL != eptr) {
+		db->ops->free (eptr);
+	}
+      cleanup_line:
+	free (line);
+      cleanup_buf:
+	free (buf);
+      cleanup_ENOMEM:
+	errno = ENOMEM;
+      cleanup_errno:
+	saved_errno = errno;
+	free_linked_list (db);
+	fclose (db->fp);
+	db->fp = NULL;
+	errno = saved_errno;
+	return 0;
+}
+
+/*
+ * Sort given db according to cmp function (usually compares uids)
+ */
+int
+commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
+{
+	struct commonio_entry **entries, *ptr;
+	size_t n = 0, i;
+#if KEEP_NIS_AT_END
+	struct commonio_entry *nis = NULL;
+#endif
+
+	for (ptr = db->head;
+	        (NULL != ptr)
+#if KEEP_NIS_AT_END
+	     && (NULL != ptr->line)
+	     && (   ('+' != ptr->line[0])
+	         && ('-' != ptr->line[0]))
+#endif
+	     ;
+	     ptr = ptr->next) {
+		n++;
+	}
+#if KEEP_NIS_AT_END
+	if ((NULL != ptr) && (NULL != ptr->line)) {
+		nis = ptr;
+	}
+#endif
+
+	if (n <= 1) {
+		return 0;
+	}
+
+	entries = malloc (n * sizeof (struct commonio_entry *));
+	if (entries == NULL) {
+		return -1;
+	}
+
+	n = 0;
+	for (ptr = db->head;
+#if KEEP_NIS_AT_END
+	     nis != ptr;
+#else
+	     NULL != ptr;
+#endif
+/*@ -nullderef @*/
+	     ptr = ptr->next
+/*@ +nullderef @*/
+	    ) {
+		entries[n] = ptr;
+		n++;
+	}
+	qsort (entries, n, sizeof (struct commonio_entry *), cmp);
+
+	/* Take care of the head and tail separately */
+	db->head = entries[0];
+	n--;
+#if KEEP_NIS_AT_END
+	if (NULL == nis)
+#endif
+	{
+		db->tail = entries[n];
+	}
+	db->head->prev = NULL;
+	db->head->next = entries[1];
+	entries[n]->prev = entries[n - 1];
+#if KEEP_NIS_AT_END
+	entries[n]->next = nis;
+#else
+	entries[n]->next = NULL;
+#endif
+
+	/* Now other elements have prev and next entries */
+	for (i = 1; i < n; i++) {
+		entries[i]->prev = entries[i - 1];
+		entries[i]->next = entries[i + 1];
+	}
+
+	free (entries);
+	db->changed = true;
+
+	return 0;
+}
+
+/*
+ * Sort entries in db according to order in another.
+ */
+int commonio_sort_wrt (struct commonio_db *shadow,
+                       const struct commonio_db *passwd)
+{
+	struct commonio_entry *head = NULL, *pw_ptr, *spw_ptr;
+	const char *name;
+
+	if ((NULL == shadow) || (NULL == shadow->head)) {
+		return 0;
+	}
+
+	for (pw_ptr = passwd->head; NULL != pw_ptr; pw_ptr = pw_ptr->next) {
+		if (NULL == pw_ptr->eptr) {
+			continue;
+		}
+		name = passwd->ops->getname (pw_ptr->eptr);
+		for (spw_ptr = shadow->head;
+		     NULL != spw_ptr;
+		     spw_ptr = spw_ptr->next) {
+			if (NULL == spw_ptr->eptr) {
+				continue;
+			}
+			if (strcmp (name, shadow->ops->getname (spw_ptr->eptr))
+			    == 0) {
+				break;
+			}
+		}
+		if (NULL == spw_ptr) {
+			continue;
+		}
+		commonio_del_entry (shadow, spw_ptr);
+		spw_ptr->next = head;
+		head = spw_ptr;
+	}
+
+	for (spw_ptr = head; NULL != spw_ptr; spw_ptr = head) {
+		head = head->next;
+
+		if (NULL != shadow->head) {
+			shadow->head->prev = spw_ptr;
+		}
+		spw_ptr->next = shadow->head;
+		shadow->head = spw_ptr;
+	}
+
+	shadow->head->prev = NULL;
+	shadow->changed = true;
+
+	return 0;
+}
+
+/*
+ * write_all - Write the database to its file.
+ *
+ * It returns 0 if all the entries could be written correctly.
+ */
+static int write_all (const struct commonio_db *db)
+	/*@requires notnull db->fp@*/
+{
+	const struct commonio_entry *p;
+	void *eptr;
+
+	for (p = db->head; NULL != p; p = p->next) {
+		if (p->changed) {
+			eptr = p->eptr;
+			assert (NULL != eptr);
+			if (db->ops->put (eptr, db->fp) != 0) {
+				return -1;
+			}
+		} else if (NULL != p->line) {
+			if (db->ops->fputs (p->line, db->fp) == EOF) {
+				return -1;
+			}
+			if (putc ('\n', db->fp) == EOF) {
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+
+int commonio_close (struct commonio_db *db)
+	/*@requires notnull db->fp@*/
+{
+	char buf[1024];
+	int errors = 0;
+	struct stat sb;
+
+	if (!db->isopen) {
+		errno = EINVAL;
+		return 0;
+	}
+	db->isopen = false;
+
+	if (!db->changed || db->readonly) {
+		(void) fclose (db->fp);
+		db->fp = NULL;
+		goto success;
+	}
+
+	if ((NULL != db->ops->close_hook) && (db->ops->close_hook () == 0)) {
+		goto fail;
+	}
+
+	memzero (&sb, sizeof sb);
+	if (NULL != db->fp) {
+		if (fstat (fileno (db->fp), &sb) != 0) {
+			(void) fclose (db->fp);
+			db->fp = NULL;
+			goto fail;
+		}
+
+		/*
+		 * Create backup file.
+		 */
+		snprintf (buf, sizeof buf, "%s-", db->filename);
+
+#ifdef WITH_SELINUX
+		if (set_selinux_file_context (buf) != 0) {
+			errors++;
+		}
+#endif
+		if (create_backup (buf, db->fp) != 0) {
+			errors++;
+		}
+
+		if (fclose (db->fp) != 0) {
+			errors++;
+		}
+
+#ifdef WITH_SELINUX
+		if (reset_selinux_file_context () != 0) {
+			errors++;
+		}
+#endif
+		if (errors != 0) {
+			db->fp = NULL;
+			goto fail;
+		}
+	} else {
+		struct group *grp;
+		/*
+		 * Default permissions for new [g]shadow files.
+		 * (passwd and group always exist...)
+		 */
+		sb.st_mode = 0440;
+		sb.st_uid = 0;
+		/*
+		 * Try to retrieve the shadow's GID, and fall back to GID 0.
+		 */
+		if ((grp = getgrnam("shadow")) != NULL)
+			sb.st_gid = grp->gr_gid;
+		else
+			sb.st_gid = 0;
+	}
+
+	snprintf (buf, sizeof buf, "%s+", db->filename);
+
+#ifdef WITH_SELINUX
+	if (set_selinux_file_context (buf) != 0) {
+		errors++;
+	}
+#endif
+
+	db->fp = fopen_set_perms (buf, "w", &sb);
+	if (NULL == db->fp) {
+		goto fail;
+	}
+
+	if (write_all (db) != 0) {
+		errors++;
+	}
+
+	if (fflush (db->fp) != 0) {
+		errors++;
+	}
+#ifdef HAVE_FSYNC
+	if (fsync (fileno (db->fp)) != 0) {
+		errors++;
+	}
+#else				/* !HAVE_FSYNC */
+	sync ();
+#endif				/* !HAVE_FSYNC */
+	if (fclose (db->fp) != 0) {
+		errors++;
+	}
+
+	db->fp = NULL;
+
+	if (errors != 0) {
+		unlink (buf);
+		goto fail;
+	}
+
+	if (lrename (buf, db->filename) != 0) {
+		goto fail;
+	}
+
+#ifdef WITH_SELINUX
+	if (reset_selinux_file_context () != 0) {
+		goto fail;
+	}
+#endif
+
+	nscd_need_reload = true;
+	goto success;
+      fail:
+	errors++;
+      success:
+
+	free_linked_list (db);
+	return errors == 0;
+}
+
+static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
+	struct commonio_db *db,
+	/*@null@*/struct commonio_entry *pos,
+	const char *name)
+{
+	struct commonio_entry *p;
+	void *ep;
+
+	if (NULL == pos) {
+		return NULL;
+	}
+
+	for (p = pos; NULL != p; p = p->next) {
+		ep = p->eptr;
+		if (   (NULL != ep)
+		    && (strcmp (db->ops->getname (ep), name) == 0)) {
+			break;
+		}
+	}
+	return p;
+}
+
+static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name (
+	struct commonio_db *db,
+	const char *name)
+{
+	return next_entry_by_name (db, db->head, name);
+}
+
+
+int commonio_update (struct commonio_db *db, const void *eptr)
+{
+	struct commonio_entry *p;
+	void *nentry;
+
+	if (!db->isopen || db->readonly) {
+		errno = EINVAL;
+		return 0;
+	}
+	nentry = db->ops->dup (eptr);
+	if (NULL == nentry) {
+		errno = ENOMEM;
+		return 0;
+	}
+	p = find_entry_by_name (db, db->ops->getname (eptr));
+	if (NULL != p) {
+		if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) {
+			fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
+			return 0;
+		}
+		db->ops->free (p->eptr);
+		p->eptr = nentry;
+		p->changed = true;
+		db->cursor = p;
+
+		db->changed = true;
+		return 1;
+	}
+	/* not found, new entry */
+	p = (struct commonio_entry *) malloc (sizeof *p);
+	if (NULL == p) {
+		db->ops->free (nentry);
+		errno = ENOMEM;
+		return 0;
+	}
+
+	p->eptr = nentry;
+	p->line = NULL;
+	p->changed = true;
+
+#if KEEP_NIS_AT_END
+	add_one_entry_nis (db, p);
+#else				/* !KEEP_NIS_AT_END */
+	add_one_entry (db, p);
+#endif				/* !KEEP_NIS_AT_END */
+
+	db->changed = true;
+	return 1;
+}
+
+#ifdef ENABLE_SUBIDS
+int commonio_append (struct commonio_db *db, const void *eptr)
+{
+	struct commonio_entry *p;
+	void *nentry;
+
+	if (!db->isopen || db->readonly) {
+		errno = EINVAL;
+		return 0;
+	}
+	nentry = db->ops->dup (eptr);
+	if (NULL == nentry) {
+		errno = ENOMEM;
+		return 0;
+	}
+	/* new entry */
+	p = (struct commonio_entry *) malloc (sizeof *p);
+	if (NULL == p) {
+		db->ops->free (nentry);
+		errno = ENOMEM;
+		return 0;
+	}
+
+	p->eptr = nentry;
+	p->line = NULL;
+	p->changed = true;
+	add_one_entry (db, p);
+
+	db->changed = true;
+	return 1;
+}
+#endif				/* ENABLE_SUBIDS */
+
+void commonio_del_entry (struct commonio_db *db, const struct commonio_entry *p)
+{
+	if (p == db->cursor) {
+		db->cursor = p->next;
+	}
+
+	if (NULL != p->prev) {
+		p->prev->next = p->next;
+	} else {
+		db->head = p->next;
+	}
+
+	if (NULL != p->next) {
+		p->next->prev = p->prev;
+	} else {
+		db->tail = p->prev;
+	}
+
+	db->changed = true;
+}
+
+/*
+ * commonio_remove - Remove the entry of the given name from the database.
+ */
+int commonio_remove (struct commonio_db *db, const char *name)
+{
+	struct commonio_entry *p;
+
+	if (!db->isopen || db->readonly) {
+		errno = EINVAL;
+		return 0;
+	}
+	p = find_entry_by_name (db, name);
+	if (NULL == p) {
+		errno = ENOENT;
+		return 0;
+	}
+	if (next_entry_by_name (db, p->next, name) != NULL) {
+		fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
+		return 0;
+	}
+
+	commonio_del_entry (db, p);
+
+	if (NULL != p->line) {
+		free (p->line);
+	}
+
+	if (NULL != p->eptr) {
+		db->ops->free (p->eptr);
+	}
+
+	return 1;
+}
+
+/*
+ * commonio_locate - Find the first entry with the specified name in
+ *                   the database.
+ *
+ *	If found, it returns the entry and set the cursor of the database to
+ *	that entry.
+ *
+ *	Otherwise, it returns NULL.
+ */
+/*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *db, const char *name)
+{
+	struct commonio_entry *p;
+
+	if (!db->isopen) {
+		errno = EINVAL;
+		return NULL;
+	}
+	p = find_entry_by_name (db, name);
+	if (NULL == p) {
+		errno = ENOENT;
+		return NULL;
+	}
+	db->cursor = p;
+	return p->eptr;
+}
+
+/*
+ * commonio_rewind - Restore the database cursor to the first entry.
+ *
+ * It returns 0 on error, 1 on success.
+ */
+int commonio_rewind (struct commonio_db *db)
+{
+	if (!db->isopen) {
+		errno = EINVAL;
+		return 0;
+	}
+	db->cursor = NULL;
+	return 1;
+}
+
+/*
+ * commonio_next - Return the next entry of the specified database
+ *
+ * It returns the next entry, or NULL if no other entries could be found.
+ */
+/*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *db)
+{
+	void *eptr;
+
+	if (!db->isopen) {
+		errno = EINVAL;
+		return 0;
+	}
+	if (NULL == db->cursor) {
+		db->cursor = db->head;
+	} else {
+		db->cursor = db->cursor->next;
+	}
+
+	while (NULL != db->cursor) {
+		eptr = db->cursor->eptr;
+		if (NULL != eptr) {
+			return eptr;
+		}
+
+		db->cursor = db->cursor->next;
+	}
+	return NULL;
+}
+
diff -pruN 1:4.2-3.2/.pc/1010_extrausers.patch/lib/defines.h 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/lib/defines.h
--- 1:4.2-3.2/.pc/1010_extrausers.patch/lib/defines.h	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/lib/defines.h	2014-03-01 17:56:04.000000000 +0000
@@ -0,0 +1,385 @@
+/* $Id$ */
+/* some useful defines */
+
+#ifndef _DEFINES_H_
+#define _DEFINES_H_
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# if ! HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+typedef unsigned char _Bool;
+#  endif
+# endif
+# define bool _Bool
+# define false (0)
+# define true  (1)
+# define __bool_true_false_are_defined 1
+#endif
+
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* Take care of NLS matters.  */
+#ifdef S_SPLINT_S
+extern char *setlocale(int categorie, const char *locale);
+# define LC_ALL		(6)
+extern char * bindtextdomain (const char * domainname, const char * dirname);
+extern char * textdomain (const char * domainname);
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#else
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(category, locale)	(NULL)
+# ifndef LC_ALL
+#  define LC_ALL	6
+# endif
+#endif
+
+#define gettext_noop(String) (String)
+/* #define gettext_def(String) "#define String" */
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory)	(NULL)
+# undef textdomain
+# define textdomain(Domain)	(NULL)
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#endif
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else				/* not STDC_HEADERS */
+# ifndef HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr (), *strtok ();
+
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy((s), (d), (n))
+# endif
+#endif				/* not STDC_HEADERS */
+
+#if HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else				/* not TIME_WITH_SYS_TIME */
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif				/* not TIME_WITH_SYS_TIME */
+
+#ifdef HAVE_MEMSET
+# define memzero(ptr, size) memset((void *)(ptr), 0, (size))
+#else
+# define memzero(ptr, size) bzero((char *)(ptr), (size))
+#endif
+#define strzero(s) memzero(s, strlen(s))	/* warning: evaluates twice */
+
+#ifdef HAVE_DIRENT_H		/* DIR_SYSV */
+# include <dirent.h>
+# define DIRECT dirent
+#else
+# ifdef HAVE_SYS_NDIR_H		/* DIR_XENIX */
+#  include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H		/* DIR_??? */
+#  include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H		/* DIR_BSD */
+#  include <ndir.h>
+# endif
+# define DIRECT direct
+#endif
+
+/*
+ * Possible cases:
+ * - /usr/include/shadow.h exists and includes the shadow group stuff.
+ * - /usr/include/shadow.h exists, but we use our own gshadow.h.
+ */
+#include <shadow.h>
+#if defined(SHADOWGRP) && !defined(GSHADOW)
+#include "gshadow_.h"
+#endif
+
+#include <limits.h>
+
+#ifndef	NGROUPS_MAX
+#ifdef	NGROUPS
+#define	NGROUPS_MAX	NGROUPS
+#else
+#define	NGROUPS_MAX	64
+#endif
+#endif
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+
+#ifndef LOG_WARN
+#define LOG_WARN LOG_WARNING
+#endif
+
+/* LOG_NOWAIT is deprecated */
+#ifndef LOG_NOWAIT
+#define LOG_NOWAIT 0
+#endif
+
+/* LOG_AUTH is deprecated, use LOG_AUTHPRIV instead */
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+/* cleaner than lots of #ifdefs everywhere - use this as follows:
+   SYSLOG((LOG_CRIT, "user %s cracked root", user)); */
+#ifdef ENABLE_NLS
+/* Temporarily set LC_TIME to "C" to avoid strange dates in syslog.
+   This is a workaround for a more general syslog(d) design problem -
+   syslogd should log the current system time for each event, and not
+   trust the formatted time received from the unix domain (or worse,
+   UDP) socket.  -MM */
+/* Avoid translated PAM error messages: Set LC_ALL to "C".
+ * --Nekral */
+#define SYSLOG(x)							\
+	do {								\
+		char *old_locale = setlocale (LC_ALL, NULL);		\
+		char *saved_locale = NULL;				\
+		if (NULL != old_locale) {				\
+			saved_locale = strdup (old_locale);		\
+		}							\
+		if (NULL != saved_locale) {				\
+			(void) setlocale (LC_ALL, "C");			\
+		}							\
+		syslog x ;						\
+		if (NULL != saved_locale) {				\
+			(void) setlocale (LC_ALL, saved_locale);	\
+			free (saved_locale);				\
+		}							\
+	} while (false)
+#else				/* !ENABLE_NLS */
+#define SYSLOG(x) syslog x
+#endif				/* !ENABLE_NLS */
+
+#else				/* !USE_SYSLOG */
+
+#define SYSLOG(x)		/* empty */
+#define openlog(a,b,c)		/* empty */
+#define closelog()		/* empty */
+
+#endif				/* !USE_SYSLOG */
+
+/* The default syslog settings can now be changed here,
+   in just one place.  */
+
+#ifndef SYSLOG_OPTIONS
+/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS | LOG_NOWAIT) */
+#define SYSLOG_OPTIONS (LOG_PID)
+#endif
+
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_AUTHPRIV
+#endif
+
+#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY)
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+# define S_ISDIR(x) ((x) & S_IFMT) == S_IFDIR)
+# define S_ISREG(x) ((x) & S_IFMT) == S_IFREG)
+# ifdef S_IFLNK
+#  define S_ISLNK(x) ((x) & S_IFMT) == S_IFLNK)
+# endif
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(x) (0)
+#endif
+
+#if HAVE_LCHOWN
+#define LCHOWN lchown
+#else
+#define LCHOWN chown
+#endif
+
+#if HAVE_LSTAT
+#define LSTAT lstat
+#else
+#define LSTAT stat
+#endif
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
+# define GTTY(fd, termio) tcgetattr(fd, termio)
+# define TERMIO struct termios
+# define USE_TERMIOS
+#else				/* assumed HAVE_TERMIO_H */
+# include <sys/ioctl.h>
+# include <termio.h>
+# define STTY(fd, termio) ioctl(fd, TCSETA, termio)
+# define GTTY(fd, termio) ioctl(fd, TCGETA, termio)
+# define TEMRIO struct termio
+# define USE_TERMIO
+#endif
+
+/*
+ * Password aging constants
+ *
+ * DAY - seconds / day
+ * WEEK - seconds / week
+ * SCALE - seconds / aging unit
+ */
+
+/* Solaris defines this in shadow.h */
+#ifndef DAY
+#define DAY (24L*3600L)
+#endif
+
+#define WEEK (7*DAY)
+
+#ifdef ITI_AGING
+#define SCALE 1
+#else
+#define SCALE DAY
+#endif
+
+/* Copy string pointed by B to array A with size checking.  It was originally
+   in lmain.c but is _very_ useful elsewhere.  Some setuid root programs with
+   very sloppy coding used to assume that BUFSIZ will always be enough...  */
+
+					/* danger - side effects */
+#define STRFCPY(A,B) \
+	(strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')
+
+#ifndef PASSWD_FILE
+#define PASSWD_FILE "/etc/passwd"
+#endif
+
+#ifndef GROUP_FILE
+#define GROUP_FILE "/etc/group"
+#endif
+
+#ifndef SHADOW_FILE
+#define SHADOW_FILE "/etc/shadow"
+#endif
+
+#ifdef SHADOWGRP
+#ifndef SGROUP_FILE
+#define SGROUP_FILE "/etc/gshadow"
+#endif
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifdef sun			/* hacks for compiling on SunOS */
+# ifndef SOLARIS
+extern int fputs ();
+extern char *strdup ();
+extern char *strerror ();
+# endif
+#endif
+
+/*
+ * string to use for the pw_passwd field in /etc/passwd when using
+ * shadow passwords - most systems use "x" but there are a few
+ * exceptions, so it can be changed here if necessary.  --marekm
+ */
+#ifndef SHADOW_PASSWD_STRING
+#define SHADOW_PASSWD_STRING "x"
+#endif
+
+#define SHADOW_SP_FLAG_UNSET ((unsigned long int)-1)
+
+#ifdef WITH_AUDIT
+#ifdef __u8			/* in case we use pam < 0.80 */
+#undef __u8
+#endif
+#ifdef __u32
+#undef __u32
+#endif
+
+#include <libaudit.h>
+#endif
+
+/* To be used for verified unused parameters */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define unused __attribute__((unused))
+#else
+# define unused
+#endif
+
+/* ! Arguments evaluated twice ! */
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+#endif
+
+/* Maximum length of usernames */
+#ifdef HAVE_UTMPX_H
+# include <utmpx.h>
+# define USER_NAME_MAX_LENGTH (sizeof (((struct utmpx *)NULL)->ut_user))
+#else
+# include <utmp.h>
+# ifdef HAVE_STRUCT_UTMP_UT_USER
+#  define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_user))
+# else
+#  ifdef HAVE_STRUCT_UTMP_UT_NAME
+#   define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_name))
+#  else
+#   define USER_NAME_MAX_LENGTH 32
+#  endif
+# endif
+#endif
+
+#endif				/* _DEFINES_H_ */
diff -pruN 1:4.2-3.2/.pc/1010_extrausers.patch/src/passwd.c 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/src/passwd.c
--- 1:4.2-3.2/.pc/1010_extrausers.patch/src/passwd.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/src/passwd.c	2014-03-01 17:56:04.000000000 +0000
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include <selinux/context.h>
+#endif				/* WITH_SELINUX */
+#include <time.h>
+#include "defines.h"
+#include "getdef.h"
+#include "nscd.h"
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+#include "shadowio.h"
+
+/*
+ * exit status values
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_NOPERM	1	/* permission denied */
+#define E_USAGE		2	/* invalid combination of options */
+#define E_FAILURE	3	/* unexpected failure, nothing done */
+#define E_MISSING	4	/* unexpected failure, passwd file missing */
+#define E_PWDBUSY	5	/* passwd file busy, try again later */
+#define E_BAD_ARG	6	/* invalid argument to option */
+/*
+ * Global variables
+ */
+const char *Prog;		/* Program name */
+
+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 bool
+    aflg = false,			/* -a - show status for all users */
+    dflg = false,			/* -d - delete password */
+    eflg = false,			/* -e - force password change */
+    iflg = false,			/* -i - set inactive days */
+    kflg = false,			/* -k - change only if expired */
+    lflg = false,			/* -l - lock the user's password */
+    nflg = false,			/* -n - set minimum days */
+    qflg = false,			/* -q - quiet mode */
+    Sflg = false,			/* -S - show password status */
+    uflg = false,			/* -u - unlock the user's password */
+    wflg = false,			/* -w - set warning days */
+    xflg = false;			/* -x - set maximum days */
+
+/*
+ * set to 1 if there are any flags which require root privileges,
+ * and require username to be specified
+ */
+static bool anyflag = false;
+
+static long age_min = 0;	/* Minimum days before change   */
+static long age_max = 0;	/* Maximum days until change     */
+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
+ *   rounds=	7
+ *   999999999	9
+ *   $		1
+ *   salt	16
+ *   $		1
+ *   SHA512	123
+ *   nul	1
+ *
+ *   total	161
+ */
+static char crypt_passwd[256];
+static bool do_update_pwd = false;
+#endif				/* !USE_PAM */
+
+/*
+ * External identifiers
+ */
+
+/* local function prototypes */
+static /*@noreturn@*/void usage (int);
+
+#ifndef USE_PAM
+static bool reuse (const char *, const struct passwd *);
+static int new_password (const struct passwd *);
+
+static void check_password (const struct passwd *, const struct spwd *);
+#endif				/* !USE_PAM */
+static /*@observer@*/const char *date_to_str (time_t);
+static /*@observer@*/const char *pw_status (const char *);
+static void print_status (const struct passwd *);
+static /*@noreturn@*/void fail_exit (int);
+static /*@noreturn@*/void oom (void);
+static char *update_crypt_pw (char *);
+static void update_noshadow (void);
+
+static void update_shadow (void);
+#ifdef WITH_SELINUX
+static int check_selinux_access (const char *changed_user,
+                                 uid_t changed_uid,
+                                 access_vector_t requested_access);
+#endif				/* WITH_SELINUX */
+
+/*
+ * usage - print command usage and exit
+ */
+static /*@noreturn@*/void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] [LOGIN]\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog);
+	(void) fputs (_("  -a, --all                     report password status on all accounts\n"), usageout);
+	(void) fputs (_("  -d, --delete                  delete the password for the named account\n"), usageout);
+	(void) fputs (_("  -e, --expire                  force expire the password for the named account\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -k, --keep-tokens             change password only if expired\n"), usageout);
+	(void) fputs (_("  -i, --inactive INACTIVE       set password inactive after expiration\n"
+	                "                                to INACTIVE\n"), usageout);
+	(void) fputs (_("  -l, --lock                    lock the password of the named account\n"), usageout);
+	(void) fputs (_("  -n, --mindays MIN_DAYS        set minimum number of days before password\n"
+	                "                                change to MIN_DAYS\n"), usageout);
+	(void) fputs (_("  -q, --quiet                   quiet mode\n"), usageout);
+	(void) fputs (_("  -r, --repository REPOSITORY   change password in REPOSITORY repository\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -S, --status                  report password status on the named account\n"), usageout);
+	(void) fputs (_("  -u, --unlock                  unlock the password of the named account\n"), usageout);
+	(void) fputs (_("  -w, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"), usageout);
+	(void) fputs (_("  -x, --maxdays MAX_DAYS        set maximum number of days before password\n"
+	                "                                change to MAX_DAYS\n"), usageout);
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+#ifndef USE_PAM
+static bool reuse (const char *pass, const struct passwd *pw)
+{
+#ifdef HAVE_LIBCRACK_HIST
+	const char *reason;
+
+#ifdef HAVE_LIBCRACK_PW
+	const char *FascistHistoryPw (const char *, const struct passwd *);
+
+	reason = FascistHistory (pass, pw);
+#else				/* !HAVE_LIBCRACK_PW */
+	const char *FascistHistory (const char *, int);
+
+	reason = FascistHistory (pass, pw->pw_uid);
+#endif				/* !HAVE_LIBCRACK_PW */
+	if (NULL != reason) {
+		(void) printf (_("Bad password: %s.  "), reason);
+		return true;
+	}
+#endif				/* HAVE_LIBCRACK_HIST */
+	return false;
+}
+
+/*
+ * new_password - validate old password and replace with new (both old and
+ * new in global "char crypt_passwd[128]")
+ */
+static int new_password (const struct passwd *pw)
+{
+	char *clear;		/* Pointer to clear text */
+	char *cipher;		/* Pointer to cipher text */
+	const char *salt;	/* Pointer to new salt */
+	char *cp;		/* Pointer to getpass() response */
+	char orig[200];		/* Original password */
+	char pass[200];		/* New password */
+	int i;			/* Counter for retries */
+	bool warned;
+	int pass_max_len = -1;
+	const char *method;
+
+#ifdef HAVE_LIBCRACK_HIST
+	int HistUpdate (const char *, const char *);
+#endif				/* HAVE_LIBCRACK_HIST */
+
+	/*
+	 * Authenticate the user. The user will be prompted for their own
+	 * password.
+	 */
+
+	if (!amroot && ('\0' != crypt_passwd[0])) {
+		clear = getpass (_("Old password: "));
+		if (NULL == clear) {
+			return -1;
+		}
+
+		cipher = pw_encrypt (clear, crypt_passwd);
+
+		if (NULL == cipher) {
+			strzero (clear);
+			fprintf (stderr,
+			         _("%s: failed to crypt password with previous salt: %s\n"),
+			         Prog, strerror (errno));
+			SYSLOG ((LOG_INFO,
+			         "Failed to crypt password with previous salt of user '%s'",
+			         pw->pw_name));
+			return -1;
+		}
+
+		if (strcmp (cipher, crypt_passwd) != 0) {
+			strzero (clear);
+			strzero (cipher);
+			SYSLOG ((LOG_WARN, "incorrect password for %s",
+			         pw->pw_name));
+			(void) sleep (1);
+			(void) fprintf (stderr,
+			                _("Incorrect password for %s.\n"),
+			                pw->pw_name);
+			return -1;
+		}
+		STRFCPY (orig, clear);
+		strzero (clear);
+		strzero (cipher);
+	} else {
+		orig[0] = '\0';
+	}
+
+	/*
+	 * Get the new password. The user is prompted for the new password
+	 * and has five tries to get it right. The password will be tested
+	 * for strength, unless it is the root user. This provides an escape
+	 * for initial login passwords.
+	 */
+	method = getdef_str ("ENCRYPT_METHOD");
+	if (NULL == method) {
+		if (!getdef_bool ("MD5_CRYPT_ENAB")) {
+			pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
+		}
+	} else {
+		if (   (strcmp (method, "MD5")    == 0)
+#ifdef USE_SHA_CRYPT
+		    || (strcmp (method, "SHA256") == 0)
+		    || (strcmp (method, "SHA512") == 0)
+#endif				/* USE_SHA_CRYPT */
+		    ) {
+			pass_max_len = -1;
+		} else {
+			pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
+		}
+	}
+	if (!qflg) {
+		if (pass_max_len == -1) {
+			(void) printf (_(
+"Enter the new password (minimum of %d characters)\n"
+"Please use a combination of upper and lower case letters and numbers.\n"),
+				getdef_num ("PASS_MIN_LEN", 5));
+		} else {
+			(void) printf (_(
+"Enter the new password (minimum of %d, maximum of %d characters)\n"
+"Please use a combination of upper and lower case letters and numbers.\n"),
+				getdef_num ("PASS_MIN_LEN", 5), pass_max_len);
+		}
+	}
+
+	warned = false;
+	for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
+		cp = getpass (_("New password: "));
+		if (NULL == cp) {
+			memzero (orig, sizeof orig);
+			return -1;
+		}
+		if (warned && (strcmp (pass, cp) != 0)) {
+			warned = false;
+		}
+		STRFCPY (pass, cp);
+		strzero (cp);
+
+		if (!amroot && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
+			(void) puts (_("Try again."));
+			continue;
+		}
+
+		/*
+		 * If enabled, warn about weak passwords even if you are
+		 * root (enter this password again to use it anyway). 
+		 * --marekm
+		 */
+		if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN")
+		    && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
+			(void) puts (_("\nWarning: weak password (enter it again to use it anyway)."));
+			warned = true;
+			continue;
+		}
+		cp = getpass (_("Re-enter new password: "));
+		if (NULL == cp) {
+			memzero (orig, sizeof orig);
+			return -1;
+		}
+		if (strcmp (cp, pass) != 0) {
+			(void) fputs (_("They don't match; try again.\n"), stderr);
+		} else {
+			strzero (cp);
+			break;
+		}
+	}
+	memzero (orig, sizeof orig);
+
+	if (i == 0) {
+		memzero (pass, sizeof pass);
+		return -1;
+	}
+
+	/*
+	 * Encrypt the password, then wipe the cleartext password.
+	 */
+	salt = crypt_make_salt (NULL, NULL);
+	cp = pw_encrypt (pass, salt);
+	memzero (pass, sizeof pass);
+
+	if (NULL == cp) {
+		fprintf (stderr,
+		         _("%s: failed to crypt password with salt '%s': %s\n"),
+		         Prog, salt, strerror (errno));
+		return -1;
+	}
+
+#ifdef HAVE_LIBCRACK_HIST
+	HistUpdate (pw->pw_name, crypt_passwd);
+#endif				/* HAVE_LIBCRACK_HIST */
+	STRFCPY (crypt_passwd, cp);
+	return 0;
+}
+
+/*
+ * check_password - test a password to see if it can be changed
+ *
+ *	check_password() sees if the invoker has permission to change the
+ *	password for the given user.
+ */
+static void check_password (const struct passwd *pw, const struct spwd *sp)
+{
+	time_t now;
+	int exp_status;
+
+	exp_status = isexpired (pw, sp);
+
+	/*
+	 * If not expired and the "change only if expired" option (idea from
+	 * PAM) was specified, do nothing. --marekm
+	 */
+	if (kflg && (0 == exp_status)) {
+		exit (E_SUCCESS);
+	}
+
+	/*
+	 * Root can change any password any time.
+	 */
+	if (amroot) {
+		return;
+	}
+
+	(void) time (&now);
+
+	/*
+	 * Expired accounts cannot be changed ever. Passwords which are
+	 * locked may not be changed. Passwords where min > max may not be
+	 * changed. Passwords which have been inactive too long cannot be
+	 * changed.
+	 */
+	if (   (sp->sp_pwdp[0] == '!')
+	    || (exp_status > 1)
+	    || (   (sp->sp_max >= 0)
+	        && (sp->sp_min > sp->sp_max))) {
+		(void) fprintf (stderr,
+		                _("The password for %s cannot be changed.\n"),
+		                sp->sp_namp);
+		SYSLOG ((LOG_WARN, "password locked for '%s'", sp->sp_namp));
+		closelog ();
+		exit (E_NOPERM);
+	}
+
+	/*
+	 * Passwords may only be changed after sp_min time is up.
+	 */
+	if (sp->sp_lstchg > 0) {
+		time_t ok;
+		ok = (time_t) sp->sp_lstchg * SCALE;
+		if (sp->sp_min > 0) {
+			ok += (time_t) sp->sp_min * SCALE;
+		}
+
+		if (now < ok) {
+			(void) fprintf (stderr,
+			                _("The password for %s cannot be changed yet.\n"),
+			                pw->pw_name);
+			SYSLOG ((LOG_WARN, "now < minimum age for '%s'", pw->pw_name));
+			closelog ();
+			exit (E_NOPERM);
+		}
+	}
+}
+#endif				/* !USE_PAM */
+
+static /*@observer@*/const char *date_to_str (time_t t)
+{
+	static char buf[80];
+	struct tm *tm;
+
+	tm = gmtime (&t);
+#ifdef HAVE_STRFTIME
+	(void) strftime (buf, sizeof buf, "%m/%d/%Y", tm);
+#else				/* !HAVE_STRFTIME */
+	(void) snprintf (buf, sizeof buf, "%02d/%02d/%04d",
+	                 tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+#endif				/* !HAVE_STRFTIME */
+	return buf;
+}
+
+static /*@observer@*/const char *pw_status (const char *pass)
+{
+	if (*pass == '*' || *pass == '!') {
+		return "L";
+	}
+	if (*pass == '\0') {
+		return "NP";
+	}
+	return "P";
+}
+
+/*
+ * print_status - print current password status
+ */
+static void print_status (const struct passwd *pw)
+{
+	struct spwd *sp;
+
+	sp = getspnam (pw->pw_name); /* local, no need for xgetspnam */
+	if (NULL != sp) {
+		(void) printf ("%s %s %s %lld %lld %lld %lld\n",
+		               pw->pw_name,
+		               pw_status (sp->sp_pwdp),
+		               date_to_str (sp->sp_lstchg * SCALE),
+		               ((long long)sp->sp_min * SCALE) / DAY,
+		               ((long long)sp->sp_max * SCALE) / DAY,
+		               ((long long)sp->sp_warn * SCALE) / DAY,
+		               ((long long)sp->sp_inact * SCALE) / DAY);
+	} else {
+		(void) printf ("%s %s\n",
+		               pw->pw_name, pw_status (pw->pw_passwd));
+	}
+}
+
+
+static /*@noreturn@*/void fail_exit (int status)
+{
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			(void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+			/* continue */
+		}
+	}
+
+	if (spw_locked) {
+		if (spw_unlock () == 0) {
+			(void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+			/* continue */
+		}
+	}
+
+	exit (status);
+}
+
+static /*@noreturn@*/void oom (void)
+{
+	(void) fprintf (stderr, _("%s: out of memory\n"), Prog);
+	fail_exit (E_FAILURE);
+}
+
+static char *update_crypt_pw (char *cp)
+{
+#ifndef USE_PAM
+	if (do_update_pwd) {
+		cp = xstrdup (crypt_passwd);
+	}
+#endif				/* !USE_PAM */
+
+	if (dflg) {
+		*cp = '\0';
+	}
+
+	if (uflg && *cp == '!') {
+		if (cp[1] == '\0') {
+			(void) fprintf (stderr,
+			                _("%s: unlocking the password would result in a passwordless account.\n"
+			                  "You should set a password with usermod -p to unlock the password of this account.\n"),
+			                Prog);
+			fail_exit (E_FAILURE);
+		} else {
+			cp++;
+		}
+	}
+
+	if (lflg && *cp != '!') {
+		char *newpw = xmalloc (strlen (cp) + 2);
+
+		strcpy (newpw, "!");
+		strcat (newpw, cp);
+		cp = newpw;
+	}
+	return cp;
+}
+
+
+static void update_noshadow (void)
+{
+	const struct passwd *pw;
+	struct passwd *npw;
+
+	if (pw_lock () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: cannot lock %s; try again later.\n"),
+		                Prog, pw_dbname ());
+		exit (E_PWDBUSY);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		(void) fprintf (stderr,
+		                _("%s: cannot open %s\n"),
+		                Prog, pw_dbname ());
+		SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
+		fail_exit (E_MISSING);
+	}
+	pw = pw_locate (name);
+	if (NULL == pw) {
+		(void) fprintf (stderr,
+		                _("%s: user '%s' does not exist in %s\n"),
+		                Prog, name, pw_dbname ());
+		fail_exit (E_NOPERM);
+	}
+	npw = __pw_dup (pw);
+	if (NULL == npw) {
+		oom ();
+	}
+	npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
+	if (pw_update (npw) == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failed to prepare the new %s entry '%s'\n"),
+		                Prog, pw_dbname (), npw->pw_name);
+		fail_exit (E_FAILURE);
+	}
+	if (pw_close () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failure while writing changes to %s\n"),
+		                Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_FAILURE);
+	}
+	if (pw_unlock () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failed to unlock %s\n"),
+		                Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+		/* continue */
+	}
+	pw_locked = false;
+}
+
+static void update_shadow (void)
+{
+	const struct spwd *sp;
+	struct spwd *nsp;
+
+	if (spw_lock () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: cannot lock %s; try again later.\n"),
+		                Prog, spw_dbname ());
+		exit (E_PWDBUSY);
+	}
+	spw_locked = true;
+	if (spw_open (O_RDWR) == 0) {
+		(void) fprintf (stderr,
+		                _("%s: cannot open %s\n"),
+		                Prog, spw_dbname ());
+		SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ()));
+		fail_exit (E_FAILURE);
+	}
+	sp = spw_locate (name);
+	if (NULL == sp) {
+		/* Try to update the password in /etc/passwd instead. */
+		(void) spw_close ();
+		update_noshadow ();
+		if (spw_unlock () == 0) {
+			(void) fprintf (stderr,
+			                _("%s: failed to unlock %s\n"),
+			                Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+			/* continue */
+		}
+		spw_locked = false;
+		return;
+	}
+	nsp = __spw_dup (sp);
+	if (NULL == nsp) {
+		oom ();
+	}
+	nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
+	if (xflg) {
+		nsp->sp_max = (age_max * DAY) / SCALE;
+	}
+	if (nflg) {
+		nsp->sp_min = (age_min * DAY) / SCALE;
+	}
+	if (wflg) {
+		nsp->sp_warn = (warn * DAY) / SCALE;
+	}
+	if (iflg) {
+		nsp->sp_inact = (inact * DAY) / SCALE;
+	}
+#ifndef USE_PAM
+	if (do_update_age) {
+		nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+		if (0 == nsp->sp_lstchg) {
+			/* Better disable aging than requiring a password
+			 * change */
+			nsp->sp_lstchg = -1;
+		}
+	}
+#endif				/* !USE_PAM */
+
+	/*
+	 * Force change on next login, like SunOS 4.x passwd -e or Solaris
+	 * 2.x passwd -f. Solaris 2.x seems to do the same thing (set
+	 * sp_lstchg to 0).
+	 */
+	if (eflg) {
+		nsp->sp_lstchg = 0;
+	}
+
+	if (spw_update (nsp) == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failed to prepare the new %s entry '%s'\n"),
+		                Prog, spw_dbname (), nsp->sp_namp);
+		fail_exit (E_FAILURE);
+	}
+	if (spw_close () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failure while writing changes to %s\n"),
+		                Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
+		fail_exit (E_FAILURE);
+	}
+	if (spw_unlock () == 0) {
+		(void) fprintf (stderr,
+		                _("%s: failed to unlock %s\n"),
+		                Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+		/* continue */
+	}
+	spw_locked = false;
+}
+
+#ifdef WITH_SELINUX
+static int check_selinux_access (const char *changed_user,
+                                 uid_t changed_uid,
+                                 access_vector_t requested_access)
+{
+	int status = -1;
+	security_context_t user_context;
+	context_t c;
+	const char *user;
+
+	/* if in permissive mode then allow the operation */
+	if (security_getenforce() == 0) {
+		return 0;
+	}
+
+	/* get the context of the process which executed passwd */
+	if (getprevcon(&user_context) != 0) {
+		return -1;
+	}
+
+	/* get the "user" portion of the context (the part before the first
+	   colon) */
+	c = context_new(user_context);
+	user = context_user_get(c);
+
+	/* if changing a password for an account with UID==0 or for an account
+	   where the identity matches then return success */
+	if (changed_uid != 0 && strcmp(changed_user, user) == 0) {
+		status = 0;
+	} else {
+		struct av_decision avd;
+		int retval;
+		retval = security_compute_av(user_context,
+		                             user_context,
+		                             SECCLASS_PASSWD,
+		                             requested_access,
+		                             &avd);
+		if ((retval == 0) &&
+		    ((requested_access & avd.allowed) == requested_access)) {
+			status = 0;
+		}
+	}
+	context_free(c);
+	freecon(user_context);
+	return status;
+}
+
+#endif				/* WITH_SELINUX */
+
+/*
+ * passwd - change a user's password file information
+ *
+ *	This command controls the password file and commands which are used
+ * 	to modify it.
+ *
+ *	The valid options are
+ *
+ *	-d	delete the password for the named account (*)
+ *	-e	expire the password for the named account (*)
+ *	-f	execute chfn command to interpret flags
+ *	-g	execute gpasswd command to interpret flags
+ *	-i #	set sp_inact to # days (*)
+ *	-k	change password only if expired
+ *	-l	lock the password of the named account (*)
+ *	-n #	set sp_min to # days (*)
+ *	-r #	change password in # repository
+ *	-s	execute chsh command to interpret flags
+ *	-S	show password status of named account
+ *	-u	unlock the password of the named account (*)
+ *	-w #	set sp_warn to # days (*)
+ *	-x #	set sp_max to # days (*)
+ *
+ *	(*) requires root permission to execute.
+ *
+ *	All of the time fields are entered in days and converted to the
+ * 	appropriate internal format. For finer resolute the chage
+ *	command must be used.
+ */
+int main (int argc, char **argv)
+{
+	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 */
+
+	sanitize_env ();
+
+	/*
+	 * Get the program name. The program name is used as a prefix to
+	 * most error messages.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	/*
+	 * The program behaves differently when executed by root than when
+	 * executed by a normal user.
+	 */
+	amroot = (getuid () == 0);
+
+	OPENLOG ("passwd");
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"all",         no_argument,       NULL, 'a'},
+			{"delete",      no_argument,       NULL, 'd'},
+			{"expire",      no_argument,       NULL, 'e'},
+			{"help",        no_argument,       NULL, 'h'},
+			{"inactive",    required_argument, NULL, 'i'},
+			{"keep-tokens", no_argument,       NULL, 'k'},
+			{"lock",        no_argument,       NULL, 'l'},
+			{"mindays",     required_argument, NULL, 'n'},
+			{"quiet",       no_argument,       NULL, 'q'},
+			{"repository",  required_argument, NULL, 'r'},
+			{"root",        required_argument, NULL, 'R'},
+			{"status",      no_argument,       NULL, 'S'},
+			{"unlock",      no_argument,       NULL, 'u'},
+			{"warndays",    required_argument, NULL, 'w'},
+			{"maxdays",     required_argument, NULL, 'x'},
+			{NULL, 0, NULL, '\0'}
+		};
+
+		while ((c = getopt_long (argc, argv, "adehi:kln:qr:R:Suw:x:",
+		                         long_options, NULL)) != -1) {
+			switch (c) {
+			case 'a':
+				aflg = true;
+				break;
+			case 'd':
+				dflg = true;
+				anyflag = true;
+				break;
+			case 'e':
+				eflg = true;
+				anyflag = true;
+				break;
+			case 'h':
+				usage (E_SUCCESS);
+				/*@notreached@*/break;
+			case 'i':
+				if (   (getlong (optarg, &inact) == 0)
+				    || (inact < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					usage (E_BAD_ARG);
+				}
+				iflg = true;
+				anyflag = true;
+				break;
+			case 'k':
+				/* change only if expired, like Linux-PAM passwd -k. */
+				kflg = true;	/* ok for users */
+				break;
+			case 'l':
+				lflg = true;
+				anyflag = true;
+				break;
+			case 'n':
+				if (   (getlong (optarg, &age_min) == 0)
+				    || (age_min < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					usage (E_BAD_ARG);
+				}
+				nflg = true;
+				anyflag = true;
+				break;
+			case 'q':
+				qflg = true;	/* ok for users */
+				break;
+			case 'r':
+				/* -r repository (files|nis|nisplus) */
+				/* only "files" supported for now */
+				if (strcmp (optarg, "files") != 0) {
+					fprintf (stderr,
+					         _("%s: repository %s not supported\n"),
+						 Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				break;
+			case 'R': /* no-op, handled in process_root_flag () */
+				break;
+			case 'S':
+				Sflg = true;	/* ok for users */
+				break;
+			case 'u':
+				uflg = true;
+				anyflag = true;
+				break;
+			case 'w':
+				if (   (getlong (optarg, &warn) == 0)
+				    || (warn < -1)) {
+					(void) fprintf (stderr,
+					                _("%s: invalid numeric argument '%s'\n"),
+					                Prog, optarg);
+					usage (E_BAD_ARG);
+				}
+				wflg = true;
+				anyflag = true;
+				break;
+			case 'x':
+				if (   (getlong (optarg, &age_max) == 0)
+				    || (age_max < -1)) {
+					(void) fprintf (stderr,
+					                _("%s: invalid numeric argument '%s'\n"),
+					                Prog, optarg);
+					usage (E_BAD_ARG);
+				}
+				xflg = true;
+				anyflag = true;
+				break;
+			default:
+				usage (E_BAD_ARG);
+			}
+		}
+	}
+
+	/*
+	 * Now I have to get the user name. The name will be gotten from the
+	 * command line if possible. Otherwise it is figured out from the
+	 * environment.
+	 */
+	pw = get_my_pwent ();
+	if (NULL == pw) {
+		(void) fprintf (stderr,
+		                _("%s: Cannot determine your user name.\n"),
+		                Prog);
+		SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
+		         (unsigned long) getuid ()));
+		exit (E_NOPERM);
+	}
+	myname = xstrdup (pw->pw_name);
+	if (optind < argc) {
+		name = argv[optind];
+	} else {
+		name = myname;
+	}
+
+	/*
+	 * Make sure that at most one username was specified.
+	 */
+	if (argc > (optind+1)) {
+		usage (E_USAGE);
+	}
+
+	/*
+	 * The -a flag requires -S, no other flags, no username, and
+	 * you must be root.  --marekm
+	 */
+	if (aflg) {
+		if (anyflag || !Sflg || (optind < argc)) {
+			usage (E_USAGE);
+		}
+		if (!amroot) {
+			(void) fprintf (stderr,
+			                _("%s: Permission denied.\n"),
+			                Prog);
+			exit (E_NOPERM);
+		}
+		setpwent ();
+		while ( (pw = getpwent ()) != NULL ) {
+			print_status (pw);
+		}
+		endpwent ();
+		exit (E_SUCCESS);
+	}
+#if 0
+	/*
+	 * Allow certain users (administrators) to change passwords of
+	 * certain users. Not implemented yet. --marekm
+	 */
+	if (may_change_passwd (myname, name))
+		amroot = 1;
+#endif
+
+	/*
+	 * If any of the flags were given, a user name must be supplied on
+	 * the command line. Only an unadorned command line doesn't require
+	 * the user's name be given. Also, -x, -n, -w, -i, -e, -d,
+	 * -l, -u may appear with each other. -S, -k must appear alone.
+	 */
+
+	/*
+	 * -S now ok for normal users (check status of my own account), and
+	 * doesn't require username.  --marekm
+	 */
+	if (anyflag && optind >= argc) {
+		usage (E_USAGE);
+	}
+
+	if (   (Sflg && kflg)
+	    || (anyflag && (Sflg || kflg))) {
+		usage (E_USAGE);
+	}
+
+	if (anyflag && !amroot) {
+		(void) fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+		exit (E_NOPERM);
+	}
+
+	pw = xgetpwnam (name);
+	if (NULL == pw) {
+		(void) fprintf (stderr,
+		                _("%s: user '%s' does not exist\n"),
+		                Prog, name);
+		exit (E_NOPERM);
+	}
+#ifdef WITH_SELINUX
+	/* only do this check when getuid()==0 because it's a pre-condition for
+	   changing a password without entering the old one */
+	if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
+	    (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
+		security_context_t user_context = NULL;
+		const char *user = "Unknown user context";
+		if (getprevcon (&user_context) == 0) {
+			user = user_context; /* FIXME: use context_user_get? */
+		}
+		SYSLOG ((LOG_ALERT,
+		         "%s is not authorized to change the password of %s",
+		         user, name));
+		(void) fprintf(stderr,
+		               _("%s: %s is not authorized to change the password of %s\n"),
+		               Prog, user, name);
+		if (NULL != user_context) {
+			freecon (user_context);
+		}
+		exit (E_NOPERM);
+	}
+#endif				/* WITH_SELINUX */
+
+	/*
+	 * If the UID of the user does not match the current real UID,
+	 * check if I'm root.
+	 */
+	if (!amroot && (pw->pw_uid != getuid ())) {
+		(void) fprintf (stderr,
+		                _("%s: You may not view or modify password information for %s.\n"),
+		                Prog, name);
+		SYSLOG ((LOG_WARN,
+		         "%s: can't view or modify password information for %s",
+		         Prog, name));
+		closelog ();
+		exit (E_NOPERM);
+	}
+
+	if (Sflg) {
+		print_status (pw);
+		exit (E_SUCCESS);
+	}
+#ifndef USE_PAM
+	/*
+	 * The user name is valid, so let's get the shadow file entry.
+	 */
+	sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */
+	if (NULL == sp) {
+		if (errno == EACCES) {
+			(void) fprintf (stderr,
+			                _("%s: Permission denied.\n"),
+			                Prog);
+			exit (E_NOPERM);
+		}
+		sp = pwd_to_spwd (pw);
+	}
+
+	cp = sp->sp_pwdp;
+
+	/*
+	 * If there are no other flags, just change the password.
+	 */
+	if (!anyflag) {
+		STRFCPY (crypt_passwd, cp);
+
+		/*
+		 * See if the user is permitted to change the password. 
+		 * Otherwise, go ahead and set a new password.
+		 */
+		check_password (pw, sp);
+
+		/*
+		 * Let the user know whose password is being changed.
+		 */
+		if (!qflg) {
+			(void) printf (_("Changing password for %s\n"), name);
+		}
+
+		if (new_password (pw) != 0) {
+			(void) fprintf (stderr,
+			                _("The password for %s is unchanged.\n"),
+			                name);
+			closelog ();
+			exit (E_NOPERM);
+		}
+		do_update_pwd = true;
+		do_update_age = true;
+	}
+#endif				/* !USE_PAM */
+	/*
+	 * Before going any further, raise the ulimit to prevent colliding
+	 * into a lowered ulimit, and set the real UID to root to protect
+	 * against unexpected signals. Any keyboard signals are set to be
+	 * ignored.
+	 */
+	pwd_init ();
+
+#ifdef USE_PAM
+	/*
+	 * Don't set the real UID for PAM...
+	 */
+	if (!anyflag) {
+		do_pam_passwd (name, qflg, kflg);
+		exit (E_SUCCESS);
+	}
+#endif				/* USE_PAM */
+	if (setuid (0) != 0) {
+		(void) fputs (_("Cannot change ID to root.\n"), stderr);
+		SYSLOG ((LOG_ERR, "can't setuid(0)"));
+		closelog ();
+		exit (E_NOPERM);
+	}
+	if (spw_file_present ()) {
+		update_shadow ();
+	} else {
+		update_noshadow ();
+	}
+
+	nscd_flush_cache ("passwd");
+	nscd_flush_cache ("group");
+
+	SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname));
+	closelog ();
+	if (!qflg) {
+		if (!anyflag) {
+#ifndef USE_PAM
+			(void) printf (_("%s: password changed.\n"), Prog);
+#endif				/* USE_PAM */
+		} else {
+			(void) printf (_("%s: password expiry information changed.\n"), Prog);
+		}
+	}
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/.pc/1010_extrausers.patch/src/usermod.c 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/src/usermod.c
--- 1:4.2-3.2/.pc/1010_extrausers.patch/src/usermod.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1010_extrausers.patch/src/usermod.c	2014-03-01 17:56:04.000000000 +0000
@@ -0,0 +1,2272 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <lastlog.h>
+#include <pwd.h>
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+#include "pam_defs.h"
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include "chkname.h"
+#include "defines.h"
+#include "faillog.h"
+#include "getdef.h"
+#include "groupio.h"
+#include "nscd.h"
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+#ifdef	SHADOWGRP
+#include "sgroupio.h"
+#endif
+#include "shadowio.h"
+#ifdef ENABLE_SUBIDS
+#include "subordinateio.h"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
+
+/*
+ * exit status values
+ * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
+ * will be implemented (as documented in the Solaris 2.x man page).
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_PW_UPDATE	1	/* can't update password file */
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define E_UID_IN_USE	4	/* UID already in use (and no -o) */
+/* #define E_BAD_PWFILE	5	   passwd file contains errors */
+#define E_NOTFOUND	6	/* specified user/group doesn't exist */
+#define E_USER_BUSY	8	/* user to modify is logged in */
+#define E_NAME_IN_USE	9	/* username already in use */
+#define E_GRP_UPDATE	10	/* can't update group file */
+/* #define E_NOSPACE	11	   insufficient space to move home dir */
+#define E_HOMEDIR	12	/* unable to complete home dir move */
+#define E_SE_UPDATE	13	/* can't update SELinux user mapping */
+#ifdef ENABLE_SUBIDS
+#define E_SUB_UID_UPDATE 16	/* can't update the subordinate uid file */
+#define E_SUB_GID_UPDATE 18	/* can't update the subordinate gid file */
+#endif				/* ENABLE_SUBIDS */
+
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
+/*
+ * Global variables
+ */
+const char *Prog;
+
+static char *user_name;
+static char *user_newname;
+static char *user_pass;
+static uid_t user_id;
+static uid_t user_newid;
+static gid_t user_gid;
+static gid_t user_newgid;
+static char *user_comment;
+static char *user_newcomment;
+static char *user_home;
+static char *user_newhome;
+static char *user_shell;
+#ifdef WITH_SELINUX
+static const char *user_selinux = "";
+#endif				/* WITH_SELINUX */
+static char *user_newshell;
+static long user_expire;
+static long user_newexpire;
+static long user_inactive;
+static long user_newinactive;
+static long sys_ngroups;
+static char **user_groups;	/* NULL-terminated list */
+
+static bool
+    aflg = false,		/* append to existing secondary group set */
+    cflg = false,		/* new comment (GECOS) field */
+    dflg = false,		/* new home directory */
+    eflg = false,		/* days since 1970-01-01 when account becomes expired */
+    fflg = false,		/* days until account with expired password is locked */
+    gflg = false,		/* new primary group ID */
+    Gflg = false,		/* new secondary group set */
+    Lflg = false,		/* lock the password */
+    lflg = false,		/* new user name */
+    mflg = false,		/* create user's home directory if it doesn't exist */
+    oflg = false,		/* permit non-unique user ID to be specified with -u */
+    pflg = false,		/* new encrypted password */
+    sflg = false,		/* new shell program */
+#ifdef WITH_SELINUX
+    Zflg = false,		/* new selinux user */
+#endif
+#ifdef ENABLE_SUBIDS
+    vflg = false,		/*    add subordinate uids */
+    Vflg = false,		/* delete subordinate uids */
+    wflg = false,		/*    add subordinate gids */
+    Wflg = false,		/* delete subordinate gids */
+#endif				/* ENABLE_SUBIDS */
+    uflg = false,		/* specify new user ID */
+    Uflg = false;		/* unlock the password */
+
+static bool is_shadow_pwd;
+
+#ifdef SHADOWGRP
+static bool is_shadow_grp;
+#endif
+
+#ifdef ENABLE_SUBIDS
+static bool is_sub_uid = false;
+static bool is_sub_gid = false;
+#endif				/* ENABLE_SUBIDS */
+
+static bool pw_locked  = false;
+static bool spw_locked = false;
+static bool gr_locked  = false;
+#ifdef SHADOWGRP
+static bool sgr_locked = false;
+#endif
+#ifdef ENABLE_SUBIDS
+static bool sub_uid_locked = false;
+static bool sub_gid_locked = false;
+#endif				/* ENABLE_SUBIDS */
+
+
+/* local function prototypes */
+static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
+                         long int date);
+static int get_groups (char *);
+static /*@noreturn@*/void usage (int status);
+static void new_pwent (struct passwd *);
+static void new_spent (struct spwd *);
+static /*@noreturn@*/void fail_exit (int);
+static void update_group (void);
+
+#ifdef SHADOWGRP
+static void update_gshadow (void);
+#endif
+static void grp_update (void);
+
+static void process_flags (int, char **);
+static void close_files (void);
+static void open_files (void);
+static void usr_update (void);
+static void move_home (void);
+static void update_lastlog (void);
+static void update_faillog (void);
+
+#ifndef NO_MOVE_MAILBOX
+static void move_mailbox (void);
+#endif
+
+static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
+                         long int date)
+{
+	struct tm *tp;
+
+	if (date < 0) {
+		strncpy (buf, "never", maxsize);
+	} else {
+		time_t t = (time_t) date;
+		tp = gmtime (&t);
+#ifdef HAVE_STRFTIME
+		strftime (buf, maxsize, "%Y-%m-%d", tp);
+#else
+		(void) snprintf (buf, maxsize, "%04d-%02d-%02d",
+		                 tp->tm_year + 1900,
+		                 tp->tm_mon + 1,
+		                 tp->tm_mday);
+#endif				/* HAVE_STRFTIME */
+	}
+	buf[maxsize - 1] = '\0';
+}
+
+/*
+ * get_groups - convert a list of group names to an array of group IDs
+ *
+ *	get_groups() takes a comma-separated list of group names and
+ *	converts it to a NULL-terminated array. Any unknown group names are
+ *	reported as errors.
+ */
+static int get_groups (char *list)
+{
+	char *cp;
+	const struct group *grp;
+	int errors = 0;
+	int ngroups = 0;
+
+	/*
+	 * Initialize the list to be empty
+	 */
+	user_groups[0] = (char *) 0;
+
+	if ('\0' == *list) {
+		return 0;
+	}
+
+	/*
+	 * So long as there is some data to be converted, strip off each
+	 * name and look it up. A mix of numerical and string values for
+	 * group identifiers is permitted.
+	 */
+	do {
+		/*
+		 * Strip off a single name from the list
+		 */
+		cp = strchr (list, ',');
+		if (NULL != cp) {
+			*cp = '\0';
+			cp++;
+		}
+
+		/*
+		 * Names starting with digits are treated as numerical GID
+		 * values, otherwise the string is looked up as is.
+		 */
+		grp = getgr_nam_gid (list);
+
+		/*
+		 * There must be a match, either by GID value or by
+		 * string name.
+		 */
+		if (NULL == grp) {
+			fprintf (stderr, _("%s: group '%s' does not exist\n"),
+			         Prog, list);
+			errors++;
+		}
+		list = cp;
+
+		/*
+		 * If the group doesn't exist, don't dump core. Instead,
+		 * try the next one.  --marekm
+		 */
+		if (NULL == grp) {
+			continue;
+		}
+
+#ifdef	USE_NIS
+		/*
+		 * Don't add this group if they are an NIS group. Tell the
+		 * user to go to the server for this group.
+		 */
+		if (__isgrNIS ()) {
+			fprintf (stderr,
+			         _("%s: group '%s' is a NIS group.\n"),
+			         Prog, grp->gr_name);
+			gr_free ((struct group *)grp);
+			continue;
+		}
+#endif
+
+		if (ngroups == sys_ngroups) {
+			fprintf (stderr,
+			         _("%s: too many groups specified (max %d).\n"),
+			         Prog, ngroups);
+			gr_free ((struct group *)grp);
+			break;
+		}
+
+		/*
+		 * Add the group name to the user's list of groups.
+		 */
+		user_groups[ngroups++] = xstrdup (grp->gr_name);
+		gr_free ((struct group *)grp);
+	} while (NULL != list);
+
+	user_groups[ngroups] = (char *) 0;
+
+	/*
+	 * Any errors in finding group names are fatal
+	 */
+	if (0 != errors) {
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef ENABLE_SUBIDS
+struct ulong_range
+{
+	unsigned long first;
+	unsigned long last;
+};
+
+static struct ulong_range getulong_range(const char *str)
+{
+	struct ulong_range result = { .first = ULONG_MAX, .last = 0 };
+	long long first, last;
+	char *pos;
+
+	errno = 0;
+	first = strtoll(str, &pos, 10);
+	if (('\0' == *str) || ('-' != *pos ) || (ERANGE == errno) ||
+	    (first != (unsigned long int)first))
+		goto out;
+
+	errno = 0;
+	last = strtoll(pos + 1, &pos, 10);
+	if (('\0' != *pos ) || (ERANGE == errno) ||
+	    (last != (unsigned long int)last))
+		goto out;
+
+	if (first > last)
+		goto out;
+
+	result.first = (unsigned long int)first;
+	result.last = (unsigned long int)last;
+out:
+	return result;
+	
+}
+
+struct ulong_range_list_entry {
+	struct ulong_range_list_entry *next;
+	struct ulong_range range;
+};
+
+static struct ulong_range_list_entry *add_sub_uids = NULL, *del_sub_uids = NULL;
+static struct ulong_range_list_entry *add_sub_gids = NULL, *del_sub_gids = NULL;
+
+static int prepend_range(const char *str, struct ulong_range_list_entry **head)
+{
+	struct ulong_range range;
+	struct ulong_range_list_entry *entry;
+	range = getulong_range(str);
+	if (range.first > range.last)
+		return 0;
+
+	entry = malloc(sizeof(*entry));
+	if (!entry) {
+		fprintf (stderr,
+			_("%s: failed to allocate memory: %s\n"),
+			Prog, strerror (errno));
+		return 0;
+	}
+	entry->next = *head;
+	entry->range = range;
+	*head = entry;
+	return 1;
+}
+#endif				/* ENABLE_SUBIDS */
+
+/*
+ * usage - display usage message and exit
+ */
+static /*@noreturn@*/void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] LOGIN\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog);
+	(void) fputs (_("  -c, --comment COMMENT         new value of the GECOS field\n"), usageout);
+	(void) fputs (_("  -d, --home HOME_DIR           new home directory for the user account\n"), usageout);
+	(void) fputs (_("  -e, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"), usageout);
+	(void) fputs (_("  -f, --inactive INACTIVE       set password inactive after expiration\n"
+	                "                                to INACTIVE\n"), usageout);
+	(void) fputs (_("  -g, --gid GROUP               force use GROUP as new primary group\n"), usageout);
+	(void) fputs (_("  -G, --groups GROUPS           new list of supplementary GROUPS\n"), usageout);
+	(void) fputs (_("  -a, --append                  append the user to the supplemental GROUPS\n"
+	                "                                mentioned by the -G option without removing\n"
+	                "                                him/her from other groups\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -l, --login NEW_LOGIN         new value of the login name\n"), usageout);
+	(void) fputs (_("  -L, --lock                    lock the user account\n"), usageout);
+	(void) fputs (_("  -m, --move-home               move contents of the home directory to the\n"
+	                "                                new location (use only with -d)\n"), usageout);
+	(void) fputs (_("  -o, --non-unique              allow using duplicate (non-unique) UID\n"), usageout);
+	(void) fputs (_("  -p, --password PASSWORD       use encrypted password for the new password\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -s, --shell SHELL             new login shell for the user account\n"), usageout);
+	(void) fputs (_("  -u, --uid UID                 new UID for the user account\n"), usageout);
+	(void) fputs (_("  -U, --unlock                  unlock the user account\n"), usageout);
+#ifdef ENABLE_SUBIDS
+	(void) fputs (_("  -v, --add-subuids FIRST-LAST  add range of subordinate uids\n"), usageout);
+	(void) fputs (_("  -V, --del-subuids FIRST-LAST  remove range of subordinate uids\n"), usageout);
+	(void) fputs (_("  -w, --add-subgids FIRST-LAST  add range of subordinate gids\n"), usageout);
+	(void) fputs (_("  -W, --del-subgids FIRST-LAST  remove range of subordinate gids\n"), usageout);
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+	(void) fputs (_("  -Z, --selinux-user SEUSER     new SELinux user mapping for the user account\n"), usageout);
+#endif				/* WITH_SELINUX */
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * update encrypted password string (for both shadow and non-shadow
+ * passwords)
+ */
+static char *new_pw_passwd (char *pw_pass)
+{
+	if (Lflg && ('!' != pw_pass[0])) {
+		char *buf = xmalloc (strlen (pw_pass) + 2);
+
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "updating passwd",
+		              user_newname, (unsigned int) user_newid, 0);
+#endif
+		SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
+		strcpy (buf, "!");
+		strcat (buf, pw_pass);
+		pw_pass = buf;
+	} else if (Uflg && pw_pass[0] == '!') {
+		char *s;
+
+		if (pw_pass[1] == '\0') {
+			fprintf (stderr,
+			         _("%s: unlocking the user's password would result in a passwordless account.\n"
+			           "You should set a password with usermod -p to unlock this user's password.\n"),
+			         Prog);
+			return pw_pass;
+		}
+
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "updating password",
+		              user_newname, (unsigned int) user_newid, 0);
+#endif
+		SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
+		s = pw_pass;
+		while ('\0' != *s) {
+			*s = *(s + 1);
+			s++;
+		}
+	} else if (pflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing password",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
+		pw_pass = xstrdup (user_pass);
+	}
+	return pw_pass;
+}
+
+/*
+ * new_pwent - initialize the values in a password file entry
+ *
+ *	new_pwent() takes all of the values that have been entered and fills
+ *	in a (struct passwd) with them.
+ */
+static void new_pwent (struct passwd *pwent)
+{
+	if (lflg) {
+		if (pw_locate (user_newname) != NULL) {
+			/* This should never happen.
+			 * It was already checked that the user doesn't
+			 * exist on the system.
+			 */
+			fprintf (stderr,
+			         _("%s: user '%s' already exists in %s\n"),
+			         Prog, user_newname, pw_dbname ());
+			fail_exit (E_NAME_IN_USE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing name",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user name '%s' to '%s'",
+		         pwent->pw_name, user_newname));
+		pwent->pw_name = xstrdup (user_newname);
+	}
+	/* Update the password in passwd if there is no shadow file or if
+	 * the password is currently in passwd (pw_passwd != "x").
+	 * We do not force the usage of shadow passwords if they are not
+	 * used for this account.
+	 */
+	if (   (!is_shadow_pwd)
+	    || (strcmp (pwent->pw_passwd, SHADOW_PASSWD_STRING) != 0)) {
+		pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd);
+	}
+
+	if (uflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing uid",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' UID from '%d' to '%d'",
+		         pwent->pw_name, pwent->pw_uid, user_newid));
+		pwent->pw_uid = user_newid;
+	}
+	if (gflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing primary group",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' GID from '%d' to '%d'",
+		         pwent->pw_name, pwent->pw_gid, user_newgid));
+		pwent->pw_gid = user_newgid;
+	}
+	if (cflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing comment",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		pwent->pw_gecos = user_newcomment;
+	}
+
+	if (dflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing home directory",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' home from '%s' to '%s'",
+		         pwent->pw_name, pwent->pw_dir, user_newhome));
+		pwent->pw_dir = user_newhome;
+	}
+	if (sflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing user shell",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' shell from '%s' to '%s'",
+		         pwent->pw_name, pwent->pw_shell, user_newshell));
+		pwent->pw_shell = user_newshell;
+	}
+}
+
+/*
+ * new_spent - initialize the values in a shadow password file entry
+ *
+ *	new_spent() takes all of the values that have been entered and fills
+ *	in a (struct spwd) with them.
+ */
+static void new_spent (struct spwd *spent)
+{
+	if (lflg) {
+		if (spw_locate (user_newname) != NULL) {
+			fprintf (stderr,
+			         _("%s: user '%s' already exists in %s\n"),
+			         Prog, user_newname, spw_dbname ());
+			fail_exit (E_NAME_IN_USE);
+		}
+		spent->sp_namp = xstrdup (user_newname);
+	}
+
+	if (fflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing inactive days",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' inactive from '%ld' to '%ld'",
+		         spent->sp_namp, spent->sp_inact, user_newinactive));
+		spent->sp_inact = user_newinactive;
+	}
+	if (eflg) {
+		/* log dates rather than numbers of days. */
+		char new_exp[16], old_exp[16];
+		date_to_str (new_exp, sizeof(new_exp),
+		             user_newexpire * DAY);
+		date_to_str (old_exp, sizeof(old_exp),
+		             user_expire * DAY);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing expiration date",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' expiration from '%s' to '%s'",
+		         spent->sp_namp, old_exp, new_exp));
+		spent->sp_expire = user_newexpire;
+	}
+
+	/* Always update the shadowed password if there is a shadow entry
+	 * (even if shadowed passwords might not be enabled for this
+	 * account (pw_passwd != "x")).
+	 * It seems better to update the password in both places in case a
+	 * shadow and a non shadow entry exist.
+	 * This might occur if:
+	 *  + there were already both entries
+	 *  + aging has been requested
+	 */
+	spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
+
+	if (pflg) {
+		spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+		if (0 == spent->sp_lstchg) {
+			/* Better disable aging than requiring a password
+			 * change. */
+			spent->sp_lstchg = -1;
+		}
+	}
+}
+
+/*
+ * fail_exit - exit with an error code after unlocking files
+ */
+static /*@noreturn@*/void fail_exit (int code)
+{
+	if (gr_locked) {
+		if (gr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+			/* continue */
+		}
+	}
+#ifdef	SHADOWGRP
+	if (sgr_locked) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+			/* continue */
+		}
+	}
+#endif
+	if (spw_locked) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+			/* continue */
+		}
+	}
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+			/* continue */
+		}
+	}
+#ifdef ENABLE_SUBIDS
+	if (sub_uid_locked) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+			/* continue */
+		}
+	}
+	if (sub_gid_locked) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+			/* continue */
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+	              "modifying account",
+	              user_name, AUDIT_NO_ID, 0);
+#endif
+	exit (code);
+}
+
+
+static void update_group (void)
+{
+	bool is_member;
+	bool was_member;
+	bool changed;
+	const struct group *grp;
+	struct group *ngrp;
+
+	changed = false;
+
+	/*
+	 * Scan through the entire group file looking for the groups that
+	 * the user is a member of.
+	 */
+	while ((grp = gr_next ()) != NULL) {
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		was_member = is_on_list (grp->gr_mem, user_name);
+		is_member = Gflg && (   (was_member && aflg)
+		                     || is_on_list (user_groups, grp->gr_name));
+
+		if (!was_member && !is_member) {
+			continue;
+		}
+
+		ngrp = __gr_dup (grp);
+		if (NULL == ngrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+
+		if (was_member) {
+			if ((!Gflg) || is_member) {
+				/* User was a member and is still a member
+				 * of this group.
+				 * But the user might have been renamed.
+				 */
+				if (lflg) {
+					ngrp->gr_mem = del_list (ngrp->gr_mem,
+					                         user_name);
+					ngrp->gr_mem = add_list (ngrp->gr_mem,
+					                         user_newname);
+					changed = true;
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+					              "changing group member",
+					              user_newname, AUDIT_NO_ID, 1);
+#endif
+					SYSLOG ((LOG_INFO,
+					         "change '%s' to '%s' in group '%s'",
+					         user_name, user_newname,
+					         ngrp->gr_name));
+				}
+			} else {
+				/* User was a member but is no more a
+				 * member of this group.
+				 */
+				ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
+				changed = true;
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "removing group member",
+				              user_name, AUDIT_NO_ID, 1);
+#endif
+				SYSLOG ((LOG_INFO,
+				         "delete '%s' from group '%s'",
+				         user_name, ngrp->gr_name));
+			}
+		} else {
+			/* User was not a member but is now a member this
+			 * group.
+			 */
+			ngrp->gr_mem = add_list (ngrp->gr_mem, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
+			         user_newname, ngrp->gr_name));
+		}
+		if (!changed) {
+			continue;
+		}
+
+		changed = false;
+		if (gr_update (ngrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, gr_dbname (), ngrp->gr_name);
+			SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name));
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+}
+
+#ifdef SHADOWGRP
+static void update_gshadow (void)
+{
+	bool is_member;
+	bool was_member;
+	bool was_admin;
+	bool changed;
+	const struct sgrp *sgrp;
+	struct sgrp *nsgrp;
+
+	changed = false;
+
+	/*
+	 * Scan through the entire shadow group file looking for the groups
+	 * that the user is a member of.
+	 */
+	while ((sgrp = sgr_next ()) != NULL) {
+
+		/*
+		 * See if the user was a member of this group
+		 */
+		was_member = is_on_list (sgrp->sg_mem, user_name);
+
+		/*
+		 * See if the user was an administrator of this group
+		 */
+		was_admin = is_on_list (sgrp->sg_adm, user_name);
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		is_member = Gflg && (   (was_member && aflg)
+		                     || is_on_list (user_groups, sgrp->sg_name));
+
+		if (!was_member && !was_admin && !is_member) {
+			continue;
+		}
+
+		nsgrp = __sgr_dup (sgrp);
+		if (NULL == nsgrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+
+		if (was_admin && lflg) {
+			/* User was an admin of this group but the user
+			 * has been renamed.
+			 */
+			nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
+			nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing admin name in shadow group",
+			              user_name, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO,
+			         "change admin '%s' to '%s' in shadow group '%s'",
+			         user_name, user_newname, nsgrp->sg_name));
+		}
+
+		if (was_member) {
+			if ((!Gflg) || is_member) {
+				/* User was a member and is still a member
+				 * of this group.
+				 * But the user might have been renamed.
+				 */
+				if (lflg) {
+					nsgrp->sg_mem = del_list (nsgrp->sg_mem,
+					                          user_name);
+					nsgrp->sg_mem = add_list (nsgrp->sg_mem,
+					                          user_newname);
+					changed = true;
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+					              "changing member in shadow group",
+					              user_name, AUDIT_NO_ID, 1);
+#endif
+					SYSLOG ((LOG_INFO,
+					         "change '%s' to '%s' in shadow group '%s'",
+					         user_name, user_newname,
+					         nsgrp->sg_name));
+				}
+			} else {
+				/* User was a member but is no more a
+				 * member of this group.
+				 */
+				nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
+				changed = true;
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "removing user from shadow group",
+				              user_name, AUDIT_NO_ID, 1);
+#endif
+				SYSLOG ((LOG_INFO,
+				         "delete '%s' from shadow group '%s'",
+				         user_name, nsgrp->sg_name));
+			}
+		} else if (is_member) {
+			/* User was not a member but is now a member this
+			 * group.
+			 */
+			nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "adding user to shadow group",
+			              user_newname, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
+			         user_newname, nsgrp->sg_name));
+		}
+		if (!changed) {
+			continue;
+		}
+
+		changed = false;
+
+		/* 
+		 * Update the group entry to reflect the changes.
+		 */
+		if (sgr_update (nsgrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, sgr_dbname (), nsgrp->sg_name);
+			SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'",
+			         sgr_dbname (), nsgrp->sg_name));
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+}
+#endif				/* SHADOWGRP */
+
+/*
+ * grp_update - add user to secondary group set
+ *
+ *	grp_update() takes the secondary group set given in user_groups and
+ *	adds the user to each group given by that set.
+ */
+static void grp_update (void)
+{
+	update_group ();
+#ifdef SHADOWGRP
+	if (is_shadow_grp) {
+		update_gshadow ();
+	}
+#endif
+}
+
+/*
+ * 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)
+{
+	const struct group *grp;
+
+	bool anyflag = false;
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"append",       no_argument,       NULL, 'a'},
+			{"comment",      required_argument, NULL, 'c'},
+			{"home",         required_argument, NULL, 'd'},
+			{"expiredate",   required_argument, NULL, 'e'},
+			{"inactive",     required_argument, NULL, 'f'},
+			{"gid",          required_argument, NULL, 'g'},
+			{"groups",       required_argument, NULL, 'G'},
+			{"help",         no_argument,       NULL, 'h'},
+			{"login",        required_argument, NULL, 'l'},
+			{"lock",         no_argument,       NULL, 'L'},
+			{"move-home",    no_argument,       NULL, 'm'},
+			{"non-unique",   no_argument,       NULL, 'o'},
+			{"password",     required_argument, NULL, 'p'},
+			{"root",         required_argument, NULL, 'R'},
+			{"shell",        required_argument, NULL, 's'},
+			{"uid",          required_argument, NULL, 'u'},
+			{"unlock",       no_argument,       NULL, 'U'},
+#ifdef ENABLE_SUBIDS
+			{"add-subuids",  required_argument, NULL, 'v'},
+			{"del-subuids",  required_argument, NULL, 'V'},
+ 			{"add-subgids",  required_argument, NULL, 'w'},
+ 			{"del-subgids",  required_argument, NULL, 'W'},
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			{"selinux-user", required_argument, NULL, 'Z'},
+#endif				/* WITH_SELINUX */
+			{NULL, 0, NULL, '\0'}
+		};
+		while ((c = getopt_long (argc, argv,
+		                         "ac:d:e:f:g:G:hl:Lmop:R:s:u:U"
+#ifdef ENABLE_SUBIDS
+		                         "v:w:V:W:"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			                 "Z:"
+#endif				/* WITH_SELINUX */
+			                 , long_options, NULL)) != -1) {
+			switch (c) {
+			case 'a':
+				aflg = true;
+				break;
+			case 'c':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_newcomment = optarg;
+				cflg = true;
+				break;
+			case 'd':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				dflg = true;
+				user_newhome = optarg;
+				break;
+			case 'e':
+				if ('\0' != *optarg) {
+					user_newexpire = strtoday (optarg);
+					if (user_newexpire < -1) {
+						fprintf (stderr,
+						         _("%s: invalid date '%s'\n"),
+						         Prog, optarg);
+						exit (E_BAD_ARG);
+					}
+					user_newexpire *= DAY / SCALE;
+				} else {
+					user_newexpire = -1;
+				}
+				eflg = true;
+				break;
+			case 'f':
+				if (   (getlong (optarg, &user_newinactive) == 0)
+				    || (user_newinactive < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				fflg = true;
+				break;
+			case 'g':
+				grp = getgr_nam_gid (optarg);
+				if (NULL == grp) {
+					fprintf (stderr,
+					         _("%s: group '%s' does not exist\n"),
+					         Prog, optarg);
+					exit (E_NOTFOUND);
+				}
+				user_newgid = grp->gr_gid;
+				gflg = true;
+				break;
+			case 'G':
+				if (get_groups (optarg) != 0) {
+					exit (E_NOTFOUND);
+				}
+				Gflg = true;
+				break;
+			case 'h':
+				usage (E_SUCCESS);
+				/*@notreached@*/break;
+			case 'l':
+				if (!is_valid_user_name (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid user name '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				lflg = true;
+				user_newname = optarg;
+				break;
+			case 'L':
+				Lflg = true;
+				break;
+			case 'm':
+				mflg = true;
+				break;
+			case 'o':
+				oflg = true;
+				break;
+			case 'p':
+				user_pass = optarg;
+				pflg = true;
+				break;
+			case 'R': /* no-op, handled in process_root_flag () */
+				break;
+			case 's':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_newshell = optarg;
+				sflg = true;
+				break;
+			case 'u':
+				if (   (get_uid (optarg, &user_newid) ==0)
+				    || (user_newid == (uid_t)-1)) {
+					fprintf (stderr,
+					         _("%s: invalid user ID '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				uflg = true;
+				break;
+			case 'U':
+				Uflg = true;
+				break;
+#ifdef ENABLE_SUBIDS
+			case 'v':
+				if (prepend_range (optarg, &add_sub_uids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate uid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				vflg = true;
+				break;
+			case 'V':
+				if (prepend_range (optarg, &del_sub_uids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate uid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				Vflg = true;
+				break;
+			case 'w':
+				if (prepend_range (optarg, &add_sub_gids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate gid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				wflg = true;
+                break;
+			case 'W':
+				if (prepend_range (optarg, &del_sub_gids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate gid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				Wflg = true;
+				break;
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			case 'Z':
+				if (is_selinux_enabled () > 0) {
+					user_selinux = optarg;
+					Zflg = true;
+				} else {
+					fprintf (stderr,
+					         _("%s: -Z requires SELinux enabled kernel\n"),
+					         Prog);
+					exit (E_BAD_ARG);
+				}
+				break;
+#endif				/* WITH_SELINUX */
+			default:
+				usage (E_USAGE);
+			}
+			anyflag = true;
+		}
+	}
+
+	if (optind != argc - 1) {
+		usage (E_USAGE);
+	}
+
+	user_name = argv[argc - 1];
+
+	{
+		const struct passwd *pwd;
+		/* local, no need for xgetpwnam */
+		pwd = getpwnam (user_name);
+		if (NULL == pwd) {
+			fprintf (stderr,
+			         _("%s: user '%s' does not exist\n"),
+			         Prog, user_name);
+			exit (E_NOTFOUND);
+		}
+
+		user_id = pwd->pw_uid;
+		user_gid = pwd->pw_gid;
+		user_comment = xstrdup (pwd->pw_gecos);
+		user_home = xstrdup (pwd->pw_dir);
+		user_shell = xstrdup (pwd->pw_shell);
+	}
+
+	/* user_newname, user_newid, user_newgid can be used even when the
+	 * options where not specified. */
+	if (!lflg) {
+		user_newname = user_name;
+	}
+	if (!uflg) {
+		user_newid = user_id;
+	}
+	if (!gflg) {
+		user_newgid = user_gid;
+	}
+
+#ifdef	USE_NIS
+	/*
+	 * Now make sure it isn't an NIS user.
+	 */
+	if (__ispwNIS ()) {
+		char *nis_domain;
+		char *nis_master;
+
+		fprintf (stderr,
+		         _("%s: user %s is a NIS user\n"),
+		         Prog, user_name);
+
+		if (   !yp_get_default_domain (&nis_domain)
+		    && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
+			fprintf (stderr,
+			         _("%s: %s is the NIS master\n"),
+			         Prog, nis_master);
+		}
+		exit (E_NOTFOUND);
+	}
+#endif
+
+	{
+		const struct spwd *spwd = NULL;
+		/* local, no need for xgetspnam */
+		if (is_shadow_pwd && ((spwd = getspnam (user_name)) != NULL)) {
+			user_expire = spwd->sp_expire;
+			user_inactive = spwd->sp_inact;
+		}
+	}
+
+	if (!anyflag) {
+		fprintf (stderr, _("%s: no options\n"), Prog);
+		usage (E_USAGE);
+	}
+
+	if (aflg && (!Gflg)) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-a", "-G");
+		usage (E_USAGE);
+	}
+
+	if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
+		fprintf (stderr,
+		         _("%s: the -L, -p, and -U flags are exclusive\n"),
+		         Prog);
+		usage (E_USAGE);
+	}
+
+	if (oflg && !uflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-o", "-u");
+		usage (E_USAGE);
+	}
+
+	if (mflg && !dflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-m", "-d");
+		usage (E_USAGE);
+	}
+
+	if (user_newid == user_id) {
+		uflg = false;
+		oflg = false;
+	}
+	if (user_newgid == user_gid) {
+		gflg = false;
+	}
+	if (   (NULL != user_newshell)
+	    && (strcmp (user_newshell, user_shell) == 0)) {
+		sflg = false;
+	}
+	if (strcmp (user_newname, user_name) == 0) {
+		lflg = false;
+	}
+	if (user_newinactive == user_inactive) {
+		fflg = false;
+	}
+	if (user_newexpire == user_expire) {
+		eflg = false;
+	}
+	if (   (NULL != user_newhome)
+	    && (strcmp (user_newhome, user_home) == 0)) {
+		dflg = false;
+		mflg = false;
+	}
+	if (   (NULL != user_newcomment)
+	    && (strcmp (user_newcomment, user_comment) == 0)) {
+		cflg = false;
+	}
+
+	if (!(Uflg || uflg || sflg || pflg || mflg || Lflg ||
+	      lflg || Gflg || gflg || fflg || eflg || dflg || cflg
+#ifdef ENABLE_SUBIDS
+	      || vflg || Vflg || wflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+	      || Zflg
+#endif				/* WITH_SELINUX */
+	)) {
+		fprintf (stderr, _("%s: no changes\n"), Prog);
+		exit (E_SUCCESS);
+	}
+
+	if (!is_shadow_pwd && (eflg || fflg)) {
+		fprintf (stderr,
+		         _("%s: shadow passwords required for -e and -f\n"),
+		         Prog);
+		exit (E_USAGE);
+	}
+
+	/* local, no need for xgetpwnam */
+	if (lflg && (getpwnam (user_newname) != NULL)) {
+		fprintf (stderr,
+		         _("%s: user '%s' already exists\n"),
+		         Prog, user_newname);
+		exit (E_NAME_IN_USE);
+	}
+
+	/* local, no need for xgetpwuid */
+	if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
+		fprintf (stderr,
+		         _("%s: UID '%lu' already exists\n"),
+		         Prog, (unsigned long) user_newid);
+		exit (E_UID_IN_USE);
+	}
+
+	if (   (vflg || Vflg)
+	    && !is_sub_uid) {
+		fprintf (stderr,
+		         _("%s: %s does not exist, you cannot use the flags %s or %s\n"),
+		         Prog, sub_uid_dbname (), "-v", "-V");
+		exit (E_USAGE);
+	}
+
+	if (   (wflg || Wflg)
+	    && !is_sub_gid) {
+		fprintf (stderr,
+		         _("%s: %s does not exist, you cannot use the flags %s or %s\n"),
+		         Prog, sub_gid_dbname (), "-w", "-W");
+		exit (E_USAGE);
+	}
+}
+
+/*
+ * close_files - close all of the files that were opened
+ *
+ *	close_files() closes all of the files that were opened for this new
+ *	user. This causes any modified entries to be written out.
+ */
+static void close_files (void)
+{
+	if (pw_close () == 0) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"),
+		         Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"),
+		         Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR,
+		         "failure while writing changes to %s",
+		         spw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+
+	if (Gflg || lflg) {
+		if (gr_close () == 0) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failure while writing changes to %s",
+			         gr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef SHADOWGRP
+		if (is_shadow_grp) {
+			if (sgr_close () == 0) {
+				fprintf (stderr,
+				         _("%s: failure while writing changes to %s\n"),
+				         Prog, sgr_dbname ());
+				SYSLOG ((LOG_ERR,
+				         "failure while writing changes to %s",
+				         sgr_dbname ()));
+				fail_exit (E_GRP_UPDATE);
+			}
+		}
+#endif
+#ifdef SHADOWGRP
+		if (is_shadow_grp) {
+			if (sgr_unlock () == 0) {
+				fprintf (stderr,
+				         _("%s: failed to unlock %s\n"),
+				         Prog, sgr_dbname ());
+				SYSLOG ((LOG_ERR,
+				         "failed to unlock %s",
+				         sgr_dbname ()));
+				/* continue */
+			}
+		}
+#endif
+		if (gr_unlock () == 0) {
+			fprintf (stderr,
+			         _("%s: failed to unlock %s\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failed to unlock %s",
+			         gr_dbname ()));
+			/* continue */
+		}
+	}
+
+	if (is_shadow_pwd) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr,
+			         _("%s: failed to unlock %s\n"),
+			         Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failed to unlock %s",
+			         spw_dbname ()));
+			/* continue */
+		}
+	}
+	if (pw_unlock () == 0) {
+		fprintf (stderr,
+		         _("%s: failed to unlock %s\n"),
+		         Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+		/* continue */
+	}
+
+	pw_locked = false;
+	spw_locked = false;
+	gr_locked = false;
+#ifdef	SHADOWGRP
+	sgr_locked = false;
+#endif
+
+#ifdef ENABLE_SUBIDS
+	if (vflg || Vflg) {
+		if (sub_uid_close () == 0) {
+			fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ()));
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+			/* continue */
+		}
+		sub_uid_locked = false;
+	}
+	if (wflg || Wflg) {
+		if (sub_gid_close () == 0) {
+			fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ()));
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+			/* continue */
+		}
+		sub_gid_locked = false;
+	}
+#endif				/* ENABLE_SUBIDS */
+
+	/*
+	 * Close the DBM and/or flat files
+	 */
+	endpwent ();
+	endspent ();
+	endgrent ();
+#ifdef	SHADOWGRP
+	endsgent ();
+#endif
+}
+
+/*
+ * open_files - lock and open the password files
+ *
+ *	open_files() opens the two password files.
+ */
+static void open_files (void)
+{
+	if (pw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_lock () == 0)) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	spw_locked = true;
+	if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+
+	if (Gflg || lflg) {
+		/*
+		 * Lock and open the group file. This will load all of the
+		 * group entries.
+		 */
+		if (gr_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		gr_locked = true;
+		if (gr_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef SHADOWGRP
+		if (is_shadow_grp && (sgr_lock () == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		sgr_locked = true;
+		if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+#endif
+	}
+#ifdef ENABLE_SUBIDS
+	if (vflg || Vflg) {
+		if (sub_uid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		sub_uid_locked = true;
+		if (sub_uid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+	}
+	if (wflg || Wflg) {
+		if (sub_gid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		sub_gid_locked = true;
+		if (sub_gid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * usr_update - create the user entries
+ *
+ *	usr_update() creates the password file entries for this user and
+ *	will update the group entries if required.
+ */
+static void usr_update (void)
+{
+	struct passwd pwent;
+	const struct passwd *pwd;
+
+	struct spwd spent;
+	const struct spwd *spwd = NULL;
+
+	/*
+	 * Locate the entry in /etc/passwd, which MUST exist.
+	 */
+	pwd = pw_locate (user_name);
+	if (NULL == pwd) {
+		fprintf (stderr,
+		         _("%s: user '%s' does not exist in %s\n"),
+		         Prog, user_name, pw_dbname ());
+		fail_exit (E_NOTFOUND);
+	}
+	pwent = *pwd;
+	new_pwent (&pwent);
+
+
+	/* If the shadow file does not exist, it won't be created */
+	if (is_shadow_pwd) {
+		spwd = spw_locate (user_name);
+		if (NULL != spwd) {
+			/* Update the shadow entry if it exists */
+			spent = *spwd;
+			new_spent (&spent);
+		} else if (   (    pflg
+		               && (strcmp (pwent.pw_passwd, SHADOW_PASSWD_STRING) == 0))
+		           || eflg || fflg) {
+			/* In some cases, we force the creation of a
+			 * shadow entry:
+			 *  + new password requested and passwd indicates
+			 *    a shadowed password
+			 *  + aging information is requested
+			 */
+			memset (&spent, 0, sizeof spent);
+			spent.sp_namp   = user_name;
+
+			/* The user explicitly asked for a shadow feature.
+			 * Enable shadowed passwords for this new account.
+			 */
+			spent.sp_pwdp   = xstrdup (pwent.pw_passwd);
+			pwent.pw_passwd = xstrdup (SHADOW_PASSWD_STRING);
+
+			spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+			if (0 == spent.sp_lstchg) {
+				/* Better disable aging than
+				 * requiring a password change */
+				spent.sp_lstchg = -1;
+			}
+			spent.sp_min    = getdef_num ("PASS_MIN_DAYS", -1);
+			spent.sp_max    = getdef_num ("PASS_MAX_DAYS", -1);
+			spent.sp_warn   = getdef_num ("PASS_WARN_AGE", -1);
+			spent.sp_inact  = -1;
+			spent.sp_expire = -1;
+			spent.sp_flag   = SHADOW_SP_FLAG_UNSET;
+			new_spent (&spent);
+			spwd = &spent; /* entry needs to be committed */
+		}
+	}
+
+	if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
+	    || Lflg || Uflg) {
+		if (pw_update (&pwent) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, pw_dbname (), pwent.pw_name);
+			fail_exit (E_PW_UPDATE);
+		}
+		if (lflg && (pw_remove (user_name) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot remove entry '%s' from %s\n"),
+			         Prog, user_name, pw_dbname ());
+			fail_exit (E_PW_UPDATE);
+		}
+	}
+	if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
+		if (spw_update (&spent) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, spw_dbname (), spent.sp_namp);
+			fail_exit (E_PW_UPDATE);
+		}
+		if (lflg && (spw_remove (user_name) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot remove entry '%s' from %s\n"),
+			         Prog, user_name, spw_dbname ());
+			fail_exit (E_PW_UPDATE);
+		}
+	}
+#ifdef ENABLE_SUBIDS
+	if (Vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (Wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+	if (wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * move_home - move the user's home directory
+ *
+ *	move_home() moves the user's home directory to a new location. The
+ *	files will be copied if the directory cannot simply be renamed.
+ */
+static void move_home (void)
+{
+	struct stat sb;
+
+	if (access (user_newhome, F_OK) == 0) {
+		/*
+		 * If the new home directory already exist, the user
+		 * should not use -m.
+		 */
+		fprintf (stderr,
+		         _("%s: directory %s exists\n"),
+		         Prog, user_newhome);
+		fail_exit (E_HOMEDIR);
+	}
+
+	if (stat (user_home, &sb) == 0) {
+		/*
+		 * Don't try to move it if it is not a directory
+		 * (but /dev/null for example).  --marekm
+		 */
+		if (!S_ISDIR (sb.st_mode)) {
+			fprintf (stderr,
+			         _("%s: The previous home directory (%s) was "
+			           "not a directory. It is not removed and no "
+			           "home directories are created.\n"),
+			         Prog, user_home);
+			fail_exit (E_HOMEDIR);
+		}
+
+		if (rename (user_home, user_newhome) == 0) {
+			/* FIXME: rename above may have broken symlinks
+			 *        pointing to the user's home directory
+			 *        with an absolute path. */
+			if (chown_tree (user_newhome,
+			                user_id,  uflg ? user_newid  : (uid_t)-1,
+			                user_gid, gflg ? user_newgid : (gid_t)-1) != 0) {
+				fprintf (stderr,
+				         _("%s: Failed to change ownership of the home directory"),
+				         Prog);
+				fail_exit (E_HOMEDIR);
+			}
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "moving home directory",
+			              user_newname, (unsigned int) user_newid,
+			              1);
+#endif
+			return;
+		} else {
+			if (EXDEV == errno) {
+				if (copy_tree (user_home, user_newhome, true,
+				               true,
+				               user_id,
+				               uflg ? user_newid : (uid_t)-1,
+				               user_gid,
+				               gflg ? user_newgid : (gid_t)-1) == 0) {
+					if (remove_tree (user_home, true) != 0) {
+						fprintf (stderr,
+						         _("%s: warning: failed to completely remove old home directory %s"),
+						         Prog, user_home);
+					}
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK,
+					              Prog,
+					              "moving home directory",
+					              user_newname,
+					              (unsigned int) user_newid,
+					              1);
+#endif
+					return;
+				}
+
+				(void) remove_tree (user_newhome, true);
+			}
+			fprintf (stderr,
+			         _("%s: cannot rename directory %s to %s\n"),
+			         Prog, user_home, user_newhome);
+			fail_exit (E_HOMEDIR);
+		}
+	}
+}
+
+/*
+ * update_lastlog - update the lastlog file
+ *
+ * Relocate the "lastlog" entries for the user. The old entry is
+ * left alone in case the UID was shared. It doesn't hurt anything
+ * to just leave it be.
+ */
+static void update_lastlog (void)
+{
+	struct lastlog ll;
+	int fd;
+	off_t off_uid = (off_t) user_id * sizeof ll;
+	off_t off_newuid = (off_t) user_newid * sizeof ll;
+
+	if (access (LASTLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	fd = open (LASTLOG_FILE, O_RDWR);
+
+	if (-1 == fd) {
+		fprintf (stderr,
+		         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+		         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		return;
+	}
+
+	if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
+	    && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
+		/* Copy the old entry to its new location */
+		if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+		    || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
+		    || (fsync (fd) != 0)
+		    || (close (fd) != 0)) {
+			fprintf (stderr,
+			         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+			         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		}
+	} else {
+		/* Assume lseek or read failed because there is
+		 * no entry for the old UID */
+
+		/* Check if the new UID already has an entry */
+		if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
+		    && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
+			/* Reset the new uid's lastlog entry */
+			memzero (&ll, sizeof (ll));
+			if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+			    || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
+			    || (fsync (fd) != 0)
+			    || (close (fd) != 0)) {
+				fprintf (stderr,
+				         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+				         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+			}
+		} else {
+			(void) close (fd);
+		}
+	}
+}
+
+/*
+ * update_faillog - update the faillog file
+ *
+ * Relocate the "faillog" entries for the user. The old entry is
+ * left alone in case the UID was shared. It doesn't hurt anything
+ * to just leave it be.
+ */
+static void update_faillog (void)
+{
+	struct faillog fl;
+	int fd;
+	off_t off_uid = (off_t) user_id * sizeof fl;
+	off_t off_newuid = (off_t) user_newid * sizeof fl;
+
+	if (access (FAILLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	fd = open (FAILLOG_FILE, O_RDWR);
+
+	if (-1 == fd) {
+		fprintf (stderr,
+		         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+		         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		return;
+	}
+
+	if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
+	    && (read (fd, (char *) &fl, sizeof fl) == (ssize_t) sizeof fl)) {
+		/* Copy the old entry to its new location */
+		if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+		    || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
+		    || (fsync (fd) != 0)
+		    || (close (fd) != 0)) {
+			fprintf (stderr,
+			         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+			         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		}
+	} else {
+		/* Assume lseek or read failed because there is
+		 * no entry for the old UID */
+
+		/* Check if the new UID already has an entry */
+		if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
+		    && (read (fd, &fl, sizeof fl) == (ssize_t) sizeof fl)) {
+			/* Reset the new uid's faillog entry */
+			memzero (&fl, sizeof (fl));
+			if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+			    || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
+			    || (close (fd) != 0)) {
+				fprintf (stderr,
+				         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+				         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+			}
+		} else {
+			(void) close (fd);
+		}
+	}
+}
+
+#ifndef NO_MOVE_MAILBOX
+/*
+ * This is the new and improved code to carefully chown/rename the user's
+ * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
+ * happens to be mode 1777 (this makes mail user agents work without
+ * being setgid mail, but is NOT recommended; they all should be fixed
+ * to use movemail).  --marekm
+ */
+static void move_mailbox (void)
+{
+	const char *maildir;
+	char mailfile[1024], newmailfile[1024];
+	int fd;
+	struct stat st;
+
+	maildir = getdef_str ("MAIL_DIR");
+#ifdef MAIL_SPOOL_DIR
+	if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
+		maildir = MAIL_SPOOL_DIR;
+	}
+#endif
+	if (NULL == maildir) {
+		return;
+	}
+
+	/*
+	 * O_NONBLOCK is to make sure open won't hang on mandatory locks.
+	 * We do fstat/fchown to make sure there are no races (someone
+	 * replacing /var/spool/mail/luser with a hard link to /etc/passwd
+	 * between stat and chown).  --marekm
+	 */
+	(void) snprintf (mailfile, sizeof mailfile, "%s/%s",
+	                 maildir, user_name);
+	mailfile[(sizeof mailfile) - 1] = '\0';
+	fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
+	if (fd < 0) {
+		/* no need for warnings if the mailbox doesn't exist */
+		if (errno != ENOENT) {
+			perror (mailfile);
+		}
+		return;
+	}
+	if (fstat (fd, &st) < 0) {
+		perror ("fstat");
+		(void) close (fd);
+		return;
+	}
+	if (st.st_uid != user_id) {
+		/* better leave it alone */
+		fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
+		         Prog, mailfile, user_name);
+		(void) close (fd);
+		return;
+	}
+	if (uflg) {
+		if (fchown (fd, user_newid, (gid_t) -1) < 0) {
+			perror (_("failed to change mailbox owner"));
+		}
+#ifdef WITH_AUDIT
+		else {
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing mail file owner",
+			              user_newname, (unsigned int) user_newid, 1);
+		}
+#endif
+	}
+
+	(void) close (fd);
+
+	if (lflg) {
+		(void) snprintf (newmailfile, sizeof newmailfile, "%s/%s",
+		                 maildir, user_newname);
+		newmailfile[(sizeof newmailfile) - 1] = '\0';
+		if (   (link (mailfile, newmailfile) != 0)
+		    || (unlink (mailfile) != 0)) {
+			perror (_("failed to rename mailbox"));
+		}
+#ifdef WITH_AUDIT
+		else {
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing mail file name",
+			              user_newname, (unsigned int) user_newid, 1);
+		}
+#endif
+	}
+}
+#endif
+
+/*
+ * main - usermod command
+ */
+int main (int argc, char **argv)
+{
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/*
+	 * Get my name so that I can use it to report errors.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	OPENLOG ("usermod");
+#ifdef WITH_AUDIT
+	audit_help_open ();
+#endif
+
+	sys_ngroups = sysconf (_SC_NGROUPS_MAX);
+	user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
+	user_groups[0] = (char *) 0;
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+#ifdef ENABLE_SUBIDS
+	is_sub_uid = sub_uid_file_present ();
+	is_sub_gid = sub_gid_file_present ();
+#endif				/* ENABLE_SUBIDS */
+
+	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
+#ifdef ENABLE_SUBIDS
+	        || Vflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+	       )
+	    && (user_busy (user_name, user_id) != 0)) {
+		exit (E_USER_BUSY);
+	}
+
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	{
+		struct passwd *pampw;
+		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+		if (pampw == NULL) {
+			fprintf (stderr,
+			         _("%s: Cannot determine your user name.\n"),
+			         Prog);
+			exit (1);
+		}
+
+		retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		exit (1);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+#ifdef WITH_TCB
+	if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
+		exit (E_PW_UPDATE);
+	}
+#endif
+
+	/*
+	 * Do the hard stuff - open the files, change the user entries,
+	 * change the home directory, then close and update the files.
+	 */
+	open_files ();
+	if (   cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
+	    || sflg || uflg || Uflg
+#ifdef ENABLE_SUBIDS
+	    || vflg || Vflg || wflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+	    ) {
+		usr_update ();
+	}
+	if (Gflg || lflg) {
+		grp_update ();
+	}
+	close_files ();
+
+#ifdef WITH_TCB
+	if (   (lflg || uflg)
+	    && (shadowtcb_move (user_newname, user_newid) == SHADOWTCB_FAILURE) ) {
+		exit (E_PW_UPDATE);
+	}
+#endif
+
+	nscd_flush_cache ("passwd");
+	nscd_flush_cache ("group");
+
+#ifdef WITH_SELINUX
+	if (Zflg) {
+		if ('\0' != *user_selinux) {
+			if (set_seuser (user_name, user_selinux) != 0) {
+				fprintf (stderr,
+				         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+				         Prog, user_name, user_selinux);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "modifying User mapping ",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif				/* WITH_AUDIT */
+				fail_exit (E_SE_UPDATE);
+			}
+		} else {
+			if (del_seuser (user_name) != 0) {
+				fprintf (stderr,
+				         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
+				         Prog, user_name);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_ADD_USER, Prog,
+				              "removing SELinux user mapping",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif				/* WITH_AUDIT */
+				fail_exit (E_SE_UPDATE);
+			}
+		}
+	}
+#endif				/* WITH_SELINUX */
+
+	if (mflg) {
+		move_home ();
+	}
+
+#ifndef NO_MOVE_MAILBOX
+	if (lflg || uflg) {
+		move_mailbox ();
+	}
+#endif				/* NO_MOVE_MAILBOX */
+
+	if (uflg) {
+		update_lastlog ();
+		update_faillog ();
+	}
+
+	if (!mflg && (uflg || gflg)) {
+		if (access (dflg ? user_newhome : user_home, F_OK) == 0) {
+			/*
+			 * Change the UID on all of the files owned by
+			 * `user_id' to `user_newid' in the user's home
+			 * directory.
+			 *
+			 * move_home() already takes care of changing the
+			 * ownership.
+			 *
+			 */
+			if (chown_tree (dflg ? user_newhome : user_home,
+			                user_id,
+			                uflg ? user_newid  : (uid_t)-1,
+			                user_gid,
+			                gflg ? user_newgid : (gid_t)-1) != 0) {
+				fprintf (stderr,
+				         _("%s: Failed to change ownership of the home directory"),
+				         Prog);
+				fail_exit (E_HOMEDIR);
+			}
+		}
+	}
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/lib/defines.h 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/lib/defines.h
--- 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/lib/defines.h	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/lib/defines.h	2016-09-20 17:11:18.000000000 +0000
@@ -0,0 +1,393 @@
+/* $Id$ */
+/* some useful defines */
+
+#ifndef _DEFINES_H_
+#define _DEFINES_H_
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# if ! HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+typedef unsigned char _Bool;
+#  endif
+# endif
+# define bool _Bool
+# define false (0)
+# define true  (1)
+# define __bool_true_false_are_defined 1
+#endif
+
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* Take care of NLS matters.  */
+#ifdef S_SPLINT_S
+extern char *setlocale(int categorie, const char *locale);
+# define LC_ALL		(6)
+extern char * bindtextdomain (const char * domainname, const char * dirname);
+extern char * textdomain (const char * domainname);
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#else
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(category, locale)	(NULL)
+# ifndef LC_ALL
+#  define LC_ALL	6
+# endif
+#endif
+
+#define gettext_noop(String) (String)
+/* #define gettext_def(String) "#define String" */
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory)	(NULL)
+# undef textdomain
+# define textdomain(Domain)	(NULL)
+# define _(Text) Text
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+#endif
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else				/* not STDC_HEADERS */
+# ifndef HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr (), *strtok ();
+
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy((s), (d), (n))
+# endif
+#endif				/* not STDC_HEADERS */
+
+#if HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else				/* not TIME_WITH_SYS_TIME */
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif				/* not TIME_WITH_SYS_TIME */
+
+#ifdef HAVE_MEMSET
+# define memzero(ptr, size) memset((void *)(ptr), 0, (size))
+#else
+# define memzero(ptr, size) bzero((char *)(ptr), (size))
+#endif
+#define strzero(s) memzero(s, strlen(s))	/* warning: evaluates twice */
+
+#ifdef HAVE_DIRENT_H		/* DIR_SYSV */
+# include <dirent.h>
+# define DIRECT dirent
+#else
+# ifdef HAVE_SYS_NDIR_H		/* DIR_XENIX */
+#  include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H		/* DIR_??? */
+#  include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H		/* DIR_BSD */
+#  include <ndir.h>
+# endif
+# define DIRECT direct
+#endif
+
+/*
+ * Possible cases:
+ * - /usr/include/shadow.h exists and includes the shadow group stuff.
+ * - /usr/include/shadow.h exists, but we use our own gshadow.h.
+ */
+#include <shadow.h>
+#if defined(SHADOWGRP) && !defined(GSHADOW)
+#include "gshadow_.h"
+#endif
+
+#include <limits.h>
+
+#ifndef	NGROUPS_MAX
+#ifdef	NGROUPS
+#define	NGROUPS_MAX	NGROUPS
+#else
+#define	NGROUPS_MAX	64
+#endif
+#endif
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+
+#ifndef LOG_WARN
+#define LOG_WARN LOG_WARNING
+#endif
+
+/* LOG_NOWAIT is deprecated */
+#ifndef LOG_NOWAIT
+#define LOG_NOWAIT 0
+#endif
+
+/* LOG_AUTH is deprecated, use LOG_AUTHPRIV instead */
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+/* cleaner than lots of #ifdefs everywhere - use this as follows:
+   SYSLOG((LOG_CRIT, "user %s cracked root", user)); */
+#ifdef ENABLE_NLS
+/* Temporarily set LC_TIME to "C" to avoid strange dates in syslog.
+   This is a workaround for a more general syslog(d) design problem -
+   syslogd should log the current system time for each event, and not
+   trust the formatted time received from the unix domain (or worse,
+   UDP) socket.  -MM */
+/* Avoid translated PAM error messages: Set LC_ALL to "C".
+ * --Nekral */
+#define SYSLOG(x)							\
+	do {								\
+		char *old_locale = setlocale (LC_ALL, NULL);		\
+		char *saved_locale = NULL;				\
+		if (NULL != old_locale) {				\
+			saved_locale = strdup (old_locale);		\
+		}							\
+		if (NULL != saved_locale) {				\
+			(void) setlocale (LC_ALL, "C");			\
+		}							\
+		syslog x ;						\
+		if (NULL != saved_locale) {				\
+			(void) setlocale (LC_ALL, saved_locale);	\
+			free (saved_locale);				\
+		}							\
+	} while (false)
+#else				/* !ENABLE_NLS */
+#define SYSLOG(x) syslog x
+#endif				/* !ENABLE_NLS */
+
+#else				/* !USE_SYSLOG */
+
+#define SYSLOG(x)		/* empty */
+#define openlog(a,b,c)		/* empty */
+#define closelog()		/* empty */
+
+#endif				/* !USE_SYSLOG */
+
+/* The default syslog settings can now be changed here,
+   in just one place.  */
+
+#ifndef SYSLOG_OPTIONS
+/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS | LOG_NOWAIT) */
+#define SYSLOG_OPTIONS (LOG_PID)
+#endif
+
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_AUTHPRIV
+#endif
+
+#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY)
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+# define S_ISDIR(x) ((x) & S_IFMT) == S_IFDIR)
+# define S_ISREG(x) ((x) & S_IFMT) == S_IFREG)
+# ifdef S_IFLNK
+#  define S_ISLNK(x) ((x) & S_IFMT) == S_IFLNK)
+# endif
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(x) (0)
+#endif
+
+#if HAVE_LCHOWN
+#define LCHOWN lchown
+#else
+#define LCHOWN chown
+#endif
+
+#if HAVE_LSTAT
+#define LSTAT lstat
+#else
+#define LSTAT stat
+#endif
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
+# define GTTY(fd, termio) tcgetattr(fd, termio)
+# define TERMIO struct termios
+# define USE_TERMIOS
+#else				/* assumed HAVE_TERMIO_H */
+# include <sys/ioctl.h>
+# include <termio.h>
+# define STTY(fd, termio) ioctl(fd, TCSETA, termio)
+# define GTTY(fd, termio) ioctl(fd, TCGETA, termio)
+# define TEMRIO struct termio
+# define USE_TERMIO
+#endif
+
+/*
+ * Password aging constants
+ *
+ * DAY - seconds / day
+ * WEEK - seconds / week
+ * SCALE - seconds / aging unit
+ */
+
+/* Solaris defines this in shadow.h */
+#ifndef DAY
+#define DAY (24L*3600L)
+#endif
+
+#define WEEK (7*DAY)
+
+#ifdef ITI_AGING
+#define SCALE 1
+#else
+#define SCALE DAY
+#endif
+
+/* Copy string pointed by B to array A with size checking.  It was originally
+   in lmain.c but is _very_ useful elsewhere.  Some setuid root programs with
+   very sloppy coding used to assume that BUFSIZ will always be enough...  */
+
+					/* danger - side effects */
+#define STRFCPY(A,B) \
+	(strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')
+
+#ifndef PASSWD_FILE
+#define PASSWD_FILE "/etc/passwd"
+#endif
+
+#ifndef GROUP_FILE
+#define GROUP_FILE "/etc/group"
+#endif
+
+#ifndef SHADOW_FILE
+#define SHADOW_FILE "/etc/shadow"
+#endif
+
+#ifdef SHADOWGRP
+#ifndef SGROUP_FILE
+#define SGROUP_FILE "/etc/gshadow"
+#endif
+#endif
+
+#ifndef EXTRAUSERS_PASSWD_FILE
+#define EXTRAUSERS_PASSWD_FILE "/var/lib/extrausers/passwd"
+#endif
+
+#ifndef EXTRAUSERS_SHADOW_FILE
+#define EXTRAUSERS_SHADOW_FILE "/var/lib/extrausers/shadow"
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifdef sun			/* hacks for compiling on SunOS */
+# ifndef SOLARIS
+extern int fputs ();
+extern char *strdup ();
+extern char *strerror ();
+# endif
+#endif
+
+/*
+ * string to use for the pw_passwd field in /etc/passwd when using
+ * shadow passwords - most systems use "x" but there are a few
+ * exceptions, so it can be changed here if necessary.  --marekm
+ */
+#ifndef SHADOW_PASSWD_STRING
+#define SHADOW_PASSWD_STRING "x"
+#endif
+
+#define SHADOW_SP_FLAG_UNSET ((unsigned long int)-1)
+
+#ifdef WITH_AUDIT
+#ifdef __u8			/* in case we use pam < 0.80 */
+#undef __u8
+#endif
+#ifdef __u32
+#undef __u32
+#endif
+
+#include <libaudit.h>
+#endif
+
+/* To be used for verified unused parameters */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define unused __attribute__((unused))
+#else
+# define unused
+#endif
+
+/* ! Arguments evaluated twice ! */
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+#endif
+
+/* Maximum length of usernames */
+#ifdef HAVE_UTMPX_H
+# include <utmpx.h>
+# define USER_NAME_MAX_LENGTH (sizeof (((struct utmpx *)NULL)->ut_user))
+#else
+# include <utmp.h>
+# ifdef HAVE_STRUCT_UTMP_UT_USER
+#  define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_user))
+# else
+#  ifdef HAVE_STRUCT_UTMP_UT_NAME
+#   define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_name))
+#  else
+#   define USER_NAME_MAX_LENGTH 32
+#  endif
+# endif
+#endif
+
+#endif				/* _DEFINES_H_ */
diff -pruN 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/src/groupadd.c 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/src/groupadd.c
--- 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/src/groupadd.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/src/groupadd.c	2014-03-01 17:56:04.000000000 +0000
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 1991 - 1993, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+#include "pam_defs.h"
+#include <pwd.h>
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+#include "chkname.h"
+#include "defines.h"
+#include "getdef.h"
+#include "groupio.h"
+#include "nscd.h"
+#include "prototypes.h"
+#ifdef	SHADOWGRP
+#include "sgroupio.h"
+#endif
+
+/*
+ * exit status values
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define E_GID_IN_USE	4	/* gid not unique (when -o not used) */
+#define E_NAME_IN_USE	9	/* group name not unique */
+#define E_GRP_UPDATE	10	/* can't update group file */
+
+/*
+ * Global variables
+ */
+const char *Prog;
+
+static /*@null@*/char *group_name;
+static gid_t group_id;
+static /*@null@*/char *group_passwd;
+static /*@null@*/char *empty_list = NULL;
+
+static bool oflg = false;	/* permit non-unique group ID to be specified with -g */
+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
+static bool is_shadow_grp;
+#endif
+
+/* local function prototypes */
+static /*@noreturn@*/void usage (int status);
+static void new_grent (struct group *grent);
+
+#ifdef SHADOWGRP
+static void new_sgent (struct sgrp *sgent);
+#endif
+static void grp_update (void);
+static void check_new_name (void);
+static void close_files (void);
+static void open_files (void);
+static void process_flags (int argc, char **argv);
+static void check_flags (void);
+static void check_perms (void);
+
+/*
+ * usage - display usage message and exit
+ */
+static /*@noreturn@*/void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] GROUP\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog);
+	(void) fputs (_("  -f, --force                   exit successfully if the group already exists,\n"
+	                "                                and cancel -g if the GID is already used\n"), usageout);
+	(void) fputs (_("  -g, --gid GID                 use GID for the new group\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -K, --key KEY=VALUE           override /etc/login.defs defaults\n"), usageout);
+	(void) fputs (_("  -o, --non-unique              allow to create groups with duplicate\n"
+	                "                                (non-unique) GID\n"), usageout);
+	(void) fputs (_("  -p, --password PASSWORD       use this encrypted password for the new group\n"), usageout);
+	(void) fputs (_("  -r, --system                  create a system account\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * new_grent - initialize the values in a group file entry
+ *
+ *	new_grent() takes all of the values that have been entered and fills
+ *	in a (struct group) with them.
+ */
+static void new_grent (struct group *grent)
+{
+	memzero (grent, sizeof *grent);
+	grent->gr_name = group_name;
+	if (pflg) {
+		grent->gr_passwd = group_passwd;
+	} else {
+		grent->gr_passwd = SHADOW_PASSWD_STRING;	/* XXX warning: const */
+	}
+	grent->gr_gid = group_id;
+	grent->gr_mem = &empty_list;
+}
+
+#ifdef	SHADOWGRP
+/*
+ * new_sgent - initialize the values in a shadow group file entry
+ *
+ *	new_sgent() takes all of the values that have been entered and fills
+ *	in a (struct sgrp) with them.
+ */
+static void new_sgent (struct sgrp *sgent)
+{
+	memzero (sgent, sizeof *sgent);
+	sgent->sg_name = group_name;
+	if (pflg) {
+		sgent->sg_passwd = group_passwd;
+	} else {
+		sgent->sg_passwd = "!";	/* XXX warning: const */
+	}
+	sgent->sg_adm = &empty_list;
+	sgent->sg_mem = &empty_list;
+}
+#endif				/* SHADOWGRP */
+
+/*
+ * grp_update - add new group file entries
+ *
+ *	grp_update() writes the new records to the group files.
+ */
+static void grp_update (void)
+{
+	struct group grp;
+
+#ifdef	SHADOWGRP
+	struct sgrp sgrp;
+#endif				/* SHADOWGRP */
+
+	/*
+	 * To add the group, we need to update /etc/group.
+	 * Make sure failures will be reported.
+	 */
+	add_cleanup (cleanup_report_add_group_group, group_name);
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		/* We also need to update /etc/gshadow */
+		add_cleanup (cleanup_report_add_group_gshadow, group_name);
+	}
+#endif
+
+	/*
+	 * Create the initial entries for this new group.
+	 */
+	new_grent (&grp);
+#ifdef	SHADOWGRP
+	new_sgent (&sgrp);
+	if (is_shadow_grp && pflg) {
+		grp.gr_passwd = SHADOW_PASSWD_STRING;	/* XXX warning: const */
+	}
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Write out the new group file entry.
+	 */
+	if (gr_update (&grp) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, gr_dbname (), grp.gr_name);
+		exit (E_GRP_UPDATE);
+	}
+#ifdef	SHADOWGRP
+	/*
+	 * Write out the new shadow group entries as well.
+	 */
+	if (is_shadow_grp && (sgr_update (&sgrp) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, sgr_dbname (), sgrp.sg_name);
+		exit (E_GRP_UPDATE);
+	}
+#endif				/* SHADOWGRP */
+}
+
+/*
+ * check_new_name - check the new name for validity
+ *
+ *	check_new_name() insures that the new name doesn't contain any
+ *	illegal characters.
+ */
+static void check_new_name (void)
+{
+	if (is_valid_group_name (group_name)) {
+		return;
+	}
+
+	/*
+	 * All invalid group names land here.
+	 */
+
+	fprintf (stderr, _("%s: '%s' is not a valid group name\n"),
+	         Prog, group_name);
+
+	exit (E_BAD_ARG);
+}
+
+/*
+ * close_files - close all of the files that were opened
+ *
+ *	close_files() closes all of the files that were opened for this new
+ *	group. This causes any modified entries to be written out.
+ */
+static void close_files (void)
+{
+	/* First, write the changes in the regular group database */
+	if (gr_close () == 0) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"),
+		         Prog, gr_dbname ());
+		exit (E_GRP_UPDATE);
+	}
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_GROUP, Prog,
+	              "adding group to /etc/group",
+	              group_name, (unsigned int) group_id,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	SYSLOG ((LOG_INFO, "group added to %s: name=%s, GID=%u",
+	         gr_dbname (), group_name, (unsigned int) group_id));
+	del_cleanup (cleanup_report_add_group_group);
+
+	cleanup_unlock_group (NULL);
+	del_cleanup (cleanup_unlock_group);
+
+	/* Now, write the changes in the shadow database */
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_close () == 0) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"),
+			         Prog, sgr_dbname ());
+			exit (E_GRP_UPDATE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_GROUP, Prog,
+		              "adding group to /etc/gshadow",
+		              group_name, (unsigned int) group_id,
+		              SHADOW_AUDIT_SUCCESS);
+#endif
+		SYSLOG ((LOG_INFO, "group added to %s: name=%s",
+		         sgr_dbname (), group_name));
+		del_cleanup (cleanup_report_add_group_gshadow);
+
+		cleanup_unlock_gshadow (NULL);
+		del_cleanup (cleanup_unlock_gshadow);
+	}
+#endif				/* SHADOWGRP */
+
+	/* Report success at the system level */
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_GROUP, Prog,
+	              "",
+	              group_name, (unsigned int) group_id,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
+	         group_name, (unsigned int) group_id));
+	del_cleanup (cleanup_report_add_group);
+}
+
+/*
+ * open_files - lock and open the group files
+ *
+ *	open_files() opens the two group files.
+ */
+static void open_files (void)
+{
+	/* First, lock the databases */
+	if (gr_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, gr_dbname ());
+		exit (E_GRP_UPDATE);
+	}
+	add_cleanup (cleanup_unlock_group, NULL);
+
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sgr_dbname ());
+			exit (E_GRP_UPDATE);
+		}
+		add_cleanup (cleanup_unlock_gshadow, NULL);
+	}
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Now if the group is not added, it's our fault.
+	 * Make sure failures will be reported.
+	 */
+	add_cleanup (cleanup_report_add_group, group_name);
+
+	/* And now open the databases */
+	if (gr_open (O_RDWR) == 0) {
+		fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+		SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
+		exit (E_GRP_UPDATE);
+	}
+
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sgr_dbname ());
+			SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
+			exit (E_GRP_UPDATE);
+		}
+	}
+#endif				/* SHADOWGRP */
+}
+
+/*
+ * process_flags - parse the command line options
+ *
+ *	It will not return if an error is encountered.
+ */
+static void process_flags (int argc, char **argv)
+{
+	/*
+	 * Parse the command line options.
+	 */
+	char *cp;
+	int c;
+	static struct option long_options[] = {
+		{"force",      no_argument,       NULL, 'f'},
+		{"gid",        required_argument, NULL, 'g'},
+		{"help",       no_argument,       NULL, 'h'},
+		{"key",        required_argument, NULL, 'K'},
+		{"non-unique", no_argument,       NULL, 'o'},
+		{"password",   required_argument, NULL, 'p'},
+		{"system",     no_argument,       NULL, 'r'},
+		{"root",       required_argument, NULL, 'R'},
+		{NULL, 0, NULL, '\0'}
+	};
+
+	while ((c = getopt_long (argc, argv, "fg:hK:op:rR:",
+		                 long_options, NULL)) != -1) {
+		switch (c) {
+		case 'f':
+			/*
+			 * "force" - do nothing, just exit(0), if the
+			 * specified group already exists. With -g, if
+			 * specified gid already exists, choose another
+			 * (unique) gid (turn off -g). Based on the RedHat's
+			 * patch from shadow-utils-970616-9.
+			 */
+			fflg = true;
+			break;
+		case 'g':
+			gflg = true;
+			if (   (get_gid (optarg, &group_id) == 0)
+			    || (group_id == (gid_t)-1)) {
+				fprintf (stderr,
+				         _("%s: invalid group ID '%s'\n"),
+				         Prog, optarg);
+				exit (E_BAD_ARG);
+			}
+			break;
+		case 'h':
+			usage (E_SUCCESS);
+			/*@notreached@*/break;
+		case 'K':
+			/*
+			 * override login.defs defaults (-K name=value)
+			 * example: -K GID_MIN=100 -K GID_MAX=499
+			 * note: -K GID_MIN=10,GID_MAX=499 doesn't work yet
+			 */
+			cp = strchr (optarg, '=');
+			if (NULL == cp) {
+				fprintf (stderr,
+				         _("%s: -K requires KEY=VALUE\n"),
+				         Prog);
+				exit (E_BAD_ARG);
+			}
+			/* terminate name, point to value */
+			*cp++ = '\0';
+			if (putdef_str (optarg, cp) < 0) {
+				exit (E_BAD_ARG);
+			}
+			break;
+		case 'o':
+			oflg = true;
+			break;
+		case 'p':
+			pflg = true;
+			group_passwd = optarg;
+			break;
+		case 'r':
+			rflg = true;
+			break;
+		case 'R': /* no-op, handled in process_root_flag () */
+			break;
+		default:
+			usage (E_USAGE);
+		}
+	}
+
+	/*
+	 * Check the flags consistency
+	 */
+	if (optind != argc - 1) {
+		usage (E_USAGE);
+	}
+	group_name = argv[optind];
+
+	check_flags ();
+}
+
+/*
+ * check_flags - check flags and parameters consistency
+ *
+ *	It will not return if an error is encountered.
+ */
+static void check_flags (void)
+{
+	/* -o does not make sense without -g */
+	if (oflg && !gflg) {
+		usage (E_USAGE);
+	}
+
+	check_new_name ();
+
+	/*
+	 * Check if the group already exist.
+	 */
+	/* local, no need for xgetgrnam */
+	if (getgrnam (group_name) != NULL) {
+		/* The group already exist */
+		if (fflg) {
+			/* OK, no need to do anything */
+			exit (E_SUCCESS);
+		}
+		fprintf (stderr,
+		         _("%s: group '%s' already exists\n"),
+		         Prog, group_name);
+		exit (E_NAME_IN_USE);
+	}
+
+	if (gflg && (getgrgid (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
+		 *    we will use another one (-f)
+		 *  - either it is a failure
+		 */
+		if (oflg) {
+			/* Continue with this GID */
+		} else if (fflg) {
+			/* Turn off -g, we can use any GID */
+			gflg = false;
+		} else {
+			fprintf (stderr,
+			         _("%s: GID '%lu' already exists\n"),
+			         Prog, (unsigned long int) group_id);
+			exit (E_GID_IN_USE);
+		}
+	}
+}
+
+/*
+ * check_perms - check if the caller is allowed to add a group
+ *
+ *	With PAM support, the setuid bit can be set on groupadd to allow
+ *	non-root users to groups.
+ *	Without PAM support, only users who can write in the group databases
+ *	can add groups.
+ *
+ *	It will not return if the user is not allowed.
+ */
+static void check_perms (void)
+{
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+	struct passwd *pampw;
+
+	pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+	if (NULL == pampw) {
+		fprintf (stderr,
+		         _("%s: Cannot determine your user name.\n"),
+		         Prog);
+		exit (1);
+	}
+
+	retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		exit (1);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+}
+
+/*
+ * main - groupadd command
+ */
+int main (int argc, char **argv)
+{
+	/*
+	 * Get my name so that I can use it to report errors.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	OPENLOG ("groupadd");
+#ifdef WITH_AUDIT
+	audit_help_open ();
+#endif
+
+	if (atexit (do_cleanups) != 0) {
+		fprintf (stderr,
+		         _("%s: Cannot setup cleanup service.\n"),
+		         Prog);
+		exit (1);
+	}
+
+	/*
+	 * Parse the command line options.
+	 */
+	process_flags (argc, argv);
+
+	check_perms ();
+
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+
+	/*
+	 * Do the hard stuff - open the files, create the group entries,
+	 * then close and update the files.
+	 */
+	open_files ();
+
+	if (!gflg) {
+		if (find_new_gid (rflg, &group_id, NULL) < 0) {
+			exit (E_GID_IN_USE);
+		}
+	}
+
+	grp_update ();
+	close_files ();
+
+	nscd_flush_cache ("group");
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/src/useradd.c 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/src/useradd.c
--- 1:4.2-3.2/.pc/1011_extrausers_toggle.patch/src/useradd.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1011_extrausers_toggle.patch/src/useradd.c	2016-09-20 17:11:18.000000000 +0000
@@ -0,0 +1,2248 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2012, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <lastlog.h>
+#include <pwd.h>
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+#include "pam_defs.h"
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include "chkname.h"
+#include "defines.h"
+#include "faillog.h"
+#include "getdef.h"
+#include "groupio.h"
+#include "nscd.h"
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+#ifdef	SHADOWGRP
+#include "sgroupio.h"
+#endif
+#include "shadowio.h"
+#ifdef ENABLE_SUBIDS
+#include "subordinateio.h"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
+
+#ifndef SKEL_DIR
+#define SKEL_DIR "/etc/skel"
+#endif
+#ifndef USER_DEFAULTS_FILE
+#define USER_DEFAULTS_FILE "/etc/default/useradd"
+#define NEW_USER_FILE "/etc/default/nuaddXXXXXX"
+#endif
+/*
+ * Needed for MkLinux DR1/2/2.1 - J.
+ */
+#ifndef LASTLOG_FILE
+#define LASTLOG_FILE "/var/log/lastlog"
+#endif
+/*
+ * Global variables
+ */
+const char *Prog;
+
+/*
+ * These defaults are used if there is no defaults file.
+ */
+static gid_t def_group = 100;
+static const char *def_gname = "other";
+static const char *def_home = "/home";
+static const char *def_shell = "";
+static const char *def_template = SKEL_DIR;
+static const char *def_create_mail_spool = "no";
+
+static long def_inactive = -1;
+static const char *def_expire = "";
+
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
+static const char *user_name = "";
+static const char *user_pass = "!";
+static uid_t user_id;
+static gid_t user_gid;
+static const char *user_comment = "";
+static const char *user_home = "";
+static const char *user_shell = "";
+static const char *create_mail_spool = "";
+#ifdef WITH_SELINUX
+static /*@notnull@*/const char *user_selinux = "";
+#endif				/* WITH_SELINUX */
+
+static long user_expire = -1;
+static bool is_shadow_pwd;
+
+#ifdef SHADOWGRP
+static bool is_shadow_grp;
+static bool sgr_locked = false;
+#endif
+#ifdef ENABLE_SUBIDS
+static bool is_sub_uid = false;
+static bool is_sub_gid = false;
+static bool sub_uid_locked = false;
+static bool sub_gid_locked = false;
+static uid_t sub_uid_start;	/* New subordinate uid range */
+static unsigned long sub_uid_count;
+static gid_t sub_gid_start;	/* New subordinate gid range */
+static unsigned long sub_gid_count;
+#endif				/* ENABLE_SUBIDS */
+static bool pw_locked = false;
+static bool gr_locked = false;
+static bool spw_locked = false;
+static char **user_groups;	/* NULL-terminated list */
+static long sys_ngroups;
+static bool do_grp_update = false;	/* group files need to be updated */
+
+static bool
+    bflg = false,		/* new default root of home directory */
+    cflg = false,		/* comment (GECOS) field for new account */
+    dflg = false,		/* home directory for new account */
+    Dflg = false,		/* set/show new user default values */
+    eflg = false,		/* days since 1970-01-01 when account is locked */
+    fflg = false,		/* days until account with expired password is locked */
+    gflg = false,		/* primary group ID for new account */
+    Gflg = false,		/* secondary group set for new account */
+    kflg = false,		/* specify a directory to fill new user directory */
+    lflg = false,		/* do not add user to lastlog/faillog databases */
+    mflg = false,		/* create user's home directory if it doesn't exist */
+    Mflg = false,		/* do not create user's home directory even if CREATE_HOME is set */
+    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 */
+    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 */
+
+#ifdef WITH_SELINUX
+#define Zflg ('\0' != *user_selinux)
+#endif				/* WITH_SELINUX */
+
+static bool home_added = false;
+
+/*
+ * exit status values
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_PW_UPDATE	1	/* can't update password file */
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define E_UID_IN_USE	4	/* UID already in use (and no -o) */
+#define E_NOTFOUND	6	/* specified group doesn't exist */
+#define E_NAME_IN_USE	9	/* username already in use */
+#define E_GRP_UPDATE	10	/* can't update group file */
+#define E_HOMEDIR	12	/* can't create home directory */
+#define E_SE_UPDATE	14	/* can't update SELinux user mapping */
+#ifdef ENABLE_SUBIDS
+#define E_SUB_UID_UPDATE 16	/* can't update the subordinate uid file */
+#define E_SUB_GID_UPDATE 18	/* can't update the subordinate gid file */
+#endif				/* ENABLE_SUBIDS */
+
+#define DGROUP			"GROUP="
+#define DHOME			"HOME="
+#define DSHELL			"SHELL="
+#define DINACT			"INACTIVE="
+#define DEXPIRE			"EXPIRE="
+#define DSKEL			"SKEL="
+#define DCREATE_MAIL_SPOOL	"CREATE_MAIL_SPOOL="
+
+/* local function prototypes */
+static void fail_exit (int);
+static void get_defaults (void);
+static void show_defaults (void);
+static int set_defaults (void);
+static int get_groups (char *);
+static void usage (int status);
+static void new_pwent (struct passwd *);
+
+static long scale_age (long);
+static void new_spent (struct spwd *);
+static void grp_update (void);
+
+static void process_flags (int argc, char **argv);
+static void close_files (void);
+static void open_files (void);
+static void open_shadow (void);
+static void faillog_reset (uid_t);
+static void lastlog_reset (uid_t);
+static void usr_update (void);
+static void create_home (void);
+static void create_mail (void);
+
+/*
+ * fail_exit - undo as much as possible
+ */
+static void fail_exit (int code)
+{
+	if (home_added) {
+		if (rmdir (user_home) != 0) {
+			fprintf (stderr,
+			         _("%s: %s was created, but could not be removed\n"),
+			         Prog, user_home);
+			SYSLOG ((LOG_ERR, "failed to remove %s", user_home));
+		}
+	}
+
+	if (spw_locked) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking shadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking passwd file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (gr_locked) {
+		if (gr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking group file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#ifdef	SHADOWGRP
+	if (sgr_locked) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking gshadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (sub_uid_locked) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking subordinate user file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (sub_gid_locked) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking subordinate group file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_USER, Prog,
+	              "adding user",
+	              user_name, AUDIT_NO_ID,
+	              SHADOW_AUDIT_FAILURE);
+#endif
+	SYSLOG ((LOG_INFO, "failed adding user '%s', data deleted", user_name));
+	exit (code);
+}
+
+#define MATCH(x,y) (strncmp((x),(y),strlen(y)) == 0)
+
+/*
+ * get_defaults - read the defaults file
+ *
+ *	get_defaults() reads the defaults file for this command. It sets the
+ *	various values from the file, or uses built-in default values if the
+ *	file does not exist.
+ */
+static void get_defaults (void)
+{
+	FILE *fp;
+	char buf[1024];
+	char *cp;
+
+	/*
+	 * Open the defaults file for reading.
+	 */
+
+	fp = fopen (USER_DEFAULTS_FILE, "r");
+	if (NULL == fp) {
+		return;
+	}
+
+	/*
+	 * Read the file a line at a time. Only the lines that have relevant
+	 * values are used, everything else can be ignored.
+	 */
+	while (fgets (buf, (int) sizeof buf, fp) == buf) {
+		cp = strrchr (buf, '\n');
+		if (NULL != cp) {
+			*cp = '\0';
+		}
+
+		cp = strchr (buf, '=');
+		if (NULL == cp) {
+			continue;
+		}
+
+		cp++;
+
+		/*
+		 * Primary GROUP identifier
+		 */
+		if (MATCH (buf, DGROUP)) {
+			const struct group *grp = getgr_nam_gid (cp);
+			if (NULL == grp) {
+				fprintf (stderr,
+				         _("%s: group '%s' does not exist\n"),
+				         Prog, cp);
+				fprintf (stderr,
+				         _("%s: the %s configuration in %s will be ignored\n"),
+				         Prog, DGROUP, USER_DEFAULTS_FILE);
+			} else {
+				def_group = grp->gr_gid;
+				def_gname = xstrdup (grp->gr_name);
+			}
+		}
+
+		/*
+		 * Default HOME filesystem
+		 */
+		else if (MATCH (buf, DHOME)) {
+			def_home = xstrdup (cp);
+		}
+
+		/*
+		 * Default Login Shell command
+		 */
+		else if (MATCH (buf, DSHELL)) {
+			def_shell = xstrdup (cp);
+		}
+
+		/*
+		 * Default Password Inactive value
+		 */
+		else if (MATCH (buf, DINACT)) {
+			if (   (getlong (cp, &def_inactive) == 0)
+			    || (def_inactive < -1)) {
+				fprintf (stderr,
+				         _("%s: invalid numeric argument '%s'\n"),
+				         Prog, cp);
+				fprintf (stderr,
+				         _("%s: the %s configuration in %s will be ignored\n"),
+				         Prog, DINACT, USER_DEFAULTS_FILE);
+				def_inactive = -1;
+			}
+		}
+
+		/*
+		 * Default account expiration date
+		 */
+		else if (MATCH (buf, DEXPIRE)) {
+			def_expire = xstrdup (cp);
+		}
+
+		/*
+		 * Default Skeleton information
+		 */
+		else if (MATCH (buf, DSKEL)) {
+			if ('\0' == *cp) {
+				cp = SKEL_DIR;	/* XXX warning: const */
+			}
+
+			def_template = xstrdup (cp);
+		}
+
+		/*
+		 * Create by default user mail spool or not ?
+		 */
+		else if (MATCH (buf, DCREATE_MAIL_SPOOL)) {
+			if (*cp == '\0') {
+				cp = "no";	/* XXX warning: const */
+			}
+
+			def_create_mail_spool = xstrdup (cp);
+		}
+	}
+	(void) fclose (fp);
+}
+
+/*
+ * show_defaults - show the contents of the defaults file
+ *
+ *	show_defaults() displays the values that are used from the default
+ *	file and the built-in values.
+ */
+static void show_defaults (void)
+{
+	printf ("GROUP=%u\n", (unsigned int) def_group);
+	printf ("HOME=%s\n", def_home);
+	printf ("INACTIVE=%ld\n", def_inactive);
+	printf ("EXPIRE=%s\n", def_expire);
+	printf ("SHELL=%s\n", def_shell);
+	printf ("SKEL=%s\n", def_template);
+	printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool);
+}
+
+/*
+ * set_defaults - write new defaults file
+ *
+ *	set_defaults() re-writes the defaults file using the values that
+ *	are currently set. Duplicated lines are pruned, missing lines are
+ *	added, and unrecognized lines are copied as is.
+ */
+static int set_defaults (void)
+{
+	FILE *ifp;
+	FILE *ofp;
+	char buf[1024];
+	static char new_file[] = NEW_USER_FILE;
+	char *cp;
+	int ofd;
+	int wlen;
+	bool out_group = false;
+	bool out_home = false;
+	bool out_inactive = false;
+	bool out_expire = false;
+	bool out_shell = false;
+	bool out_skel = false;
+	bool out_create_mail_spool = false;
+
+	/*
+	 * Create a temporary file to copy the new output to.
+	 */
+	ofd = mkstemp (new_file);
+	if (-1 == ofd) {
+		fprintf (stderr,
+		         _("%s: cannot create new defaults file\n"),
+		         Prog);
+		return -1;
+	}
+
+	ofp = fdopen (ofd, "w");
+	if (NULL == ofp) {
+		fprintf (stderr,
+		         _("%s: cannot open new defaults file\n"),
+		         Prog);
+		return -1;
+	}
+
+	/*
+	 * Open the existing defaults file and copy the lines to the
+	 * temporary file, using any new values. Each line is checked
+	 * to insure that it is not output more than once.
+	 */
+	ifp = fopen (USER_DEFAULTS_FILE, "r");
+	if (NULL == ifp) {
+		fprintf (ofp, "# useradd defaults file\n");
+		goto skip;
+	}
+
+	while (fgets (buf, (int) sizeof buf, ifp) == buf) {
+		cp = strrchr (buf, '\n');
+		if (NULL != cp) {
+			*cp = '\0';
+		} else {
+			/* A line which does not end with \n is only valid
+			 * at the end of the file.
+			 */
+			if (feof (ifp) == 0) {
+				fprintf (stderr,
+				         _("%s: line too long in %s: %s..."),
+				         Prog, USER_DEFAULTS_FILE, buf);
+				(void) fclose (ifp);
+				return -1;
+			}
+		}
+
+		if (!out_group && MATCH (buf, DGROUP)) {
+			fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
+			out_group = true;
+		} else if (!out_home && MATCH (buf, DHOME)) {
+			fprintf (ofp, DHOME "%s\n", def_home);
+			out_home = true;
+		} else if (!out_inactive && MATCH (buf, DINACT)) {
+			fprintf (ofp, DINACT "%ld\n", def_inactive);
+			out_inactive = true;
+		} else if (!out_expire && MATCH (buf, DEXPIRE)) {
+			fprintf (ofp, DEXPIRE "%s\n", def_expire);
+			out_expire = true;
+		} else if (!out_shell && MATCH (buf, DSHELL)) {
+			fprintf (ofp, DSHELL "%s\n", def_shell);
+			out_shell = true;
+		} else if (!out_skel && MATCH (buf, DSKEL)) {
+			fprintf (ofp, DSKEL "%s\n", def_template);
+			out_skel = true;
+		} else if (!out_create_mail_spool
+			   && MATCH (buf, DCREATE_MAIL_SPOOL)) {
+			fprintf (ofp,
+			         DCREATE_MAIL_SPOOL "%s\n",
+			         def_create_mail_spool);
+			out_create_mail_spool = true;
+		} else
+			fprintf (ofp, "%s\n", buf);
+	}
+	(void) fclose (ifp);
+
+      skip:
+	/*
+	 * Check each line to insure that every line was output. This
+	 * causes new values to be added to a file which did not previously
+	 * have an entry for that value.
+	 */
+	if (!out_group)
+		fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
+	if (!out_home)
+		fprintf (ofp, DHOME "%s\n", def_home);
+	if (!out_inactive)
+		fprintf (ofp, DINACT "%ld\n", def_inactive);
+	if (!out_expire)
+		fprintf (ofp, DEXPIRE "%s\n", def_expire);
+	if (!out_shell)
+		fprintf (ofp, DSHELL "%s\n", def_shell);
+	if (!out_skel)
+		fprintf (ofp, DSKEL "%s\n", def_template);
+
+	if (!out_create_mail_spool)
+		fprintf (ofp, DCREATE_MAIL_SPOOL "%s\n", def_create_mail_spool);
+
+	/*
+	 * Flush and close the file. Check for errors to make certain
+	 * the new file is intact.
+	 */
+	(void) fflush (ofp);
+	if (   (ferror (ofp) != 0)
+	    || (fsync (fileno (ofp)) != 0)
+	    || (fclose (ofp) != 0)) {
+		unlink (new_file);
+		return -1;
+	}
+
+	/*
+	 * Rename the current default file to its backup name.
+	 */
+	wlen = snprintf (buf, sizeof buf, "%s-", USER_DEFAULTS_FILE);
+	assert (wlen < (int) sizeof buf);
+	unlink (buf);
+	if ((link (USER_DEFAULTS_FILE, buf) != 0) && (ENOENT != errno)) {
+		int err = errno;
+		fprintf (stderr,
+		         _("%s: Cannot create backup file (%s): %s\n"),
+		         Prog, buf, strerror (err));
+		unlink (new_file);
+		return -1;
+	}
+
+	/*
+	 * Rename the new default file to its correct name.
+	 */
+	if (rename (new_file, USER_DEFAULTS_FILE) != 0) {
+		int err = errno;
+		fprintf (stderr,
+		         _("%s: rename: %s: %s\n"),
+		         Prog, new_file, strerror (err));
+		return -1;
+	}
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_USYS_CONFIG, Prog,
+	              "changing useradd defaults",
+	              NULL, AUDIT_NO_ID,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	SYSLOG ((LOG_INFO,
+	         "useradd defaults: GROUP=%u, HOME=%s, SHELL=%s, INACTIVE=%ld, "
+	         "EXPIRE=%s, SKEL=%s, CREATE_MAIL_SPOOL=%s",
+	         (unsigned int) def_group, def_home, def_shell,
+	         def_inactive, def_expire, def_template,
+	         def_create_mail_spool));
+	return 0;
+}
+
+/*
+ * get_groups - convert a list of group names to an array of group IDs
+ *
+ *	get_groups() takes a comma-separated list of group names and
+ *	converts it to a NULL-terminated array. Any unknown group
+ *	names are reported as errors.
+ */
+static int get_groups (char *list)
+{
+	char *cp;
+	const struct group *grp;
+	int errors = 0;
+	int ngroups = 0;
+
+	if ('\0' == *list) {
+		return 0;
+	}
+
+	/*
+	 * So long as there is some data to be converted, strip off
+	 * each name and look it up. A mix of numerical and string
+	 * values for group identifiers is permitted.
+	 */
+	do {
+		/*
+		 * Strip off a single name from the list
+		 */
+		cp = strchr (list, ',');
+		if (NULL != cp) {
+			*cp++ = '\0';
+		}
+
+		/*
+		 * Names starting with digits are treated as numerical
+		 * GID values, otherwise the string is looked up as is.
+		 */
+		grp = getgr_nam_gid (list);
+
+		/*
+		 * There must be a match, either by GID value or by
+		 * string name.
+		 * FIXME: It should exist according to gr_locate,
+		 *        otherwise, we can't change its members
+		 */
+		if (NULL == grp) {
+			fprintf (stderr,
+			         _("%s: group '%s' does not exist\n"),
+			         Prog, list);
+			errors++;
+		}
+		list = cp;
+
+		/*
+		 * If the group doesn't exist, don't dump core...
+		 * Instead, try the next one.  --marekm
+		 */
+		if (NULL == grp) {
+			continue;
+		}
+
+#ifdef	USE_NIS
+		/*
+		 * Don't add this group if they are an NIS group. Tell
+		 * the user to go to the server for this group.
+		 */
+		if (__isgrNIS ()) {
+			fprintf (stderr,
+			         _("%s: group '%s' is a NIS group.\n"),
+			         Prog, grp->gr_name);
+			continue;
+		}
+#endif
+
+		if (ngroups == sys_ngroups) {
+			fprintf (stderr,
+			         _("%s: too many groups specified (max %d).\n"),
+			         Prog, ngroups);
+			break;
+		}
+
+		/*
+		 * Add the group name to the user's list of groups.
+		 */
+		user_groups[ngroups++] = xstrdup (grp->gr_name);
+	} while (NULL != list);
+
+	user_groups[ngroups] = (char *) 0;
+
+	/*
+	 * Any errors in finding group names are fatal
+	 */
+	if (0 != errors) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * usage - display usage message and exit
+ */
+static void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] LOGIN\n"
+	                  "       %s -D\n"
+	                  "       %s -D [options]\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog, Prog, Prog);
+	(void) fputs (_("  -b, --base-dir BASE_DIR       base directory for the home directory of the\n"
+	                "                                new account\n"), usageout);
+	(void) fputs (_("  -c, --comment COMMENT         GECOS field of the new account\n"), usageout);
+	(void) fputs (_("  -d, --home-dir HOME_DIR       home directory of the new account\n"), usageout);
+	(void) fputs (_("  -D, --defaults                print or change default useradd configuration\n"), usageout);
+	(void) fputs (_("  -e, --expiredate EXPIRE_DATE  expiration date of the new account\n"), usageout);
+	(void) fputs (_("  -f, --inactive INACTIVE       password inactivity period of the new account\n"), usageout);
+	(void) fputs (_("  -g, --gid GROUP               name or ID of the primary group of the new\n"
+	                "                                account\n"), usageout);
+	(void) fputs (_("  -G, --groups GROUPS           list of supplementary groups of the new\n"
+	                "                                account\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -k, --skel SKEL_DIR           use this alternative skeleton directory\n"), usageout);
+	(void) fputs (_("  -K, --key KEY=VALUE           override /etc/login.defs defaults\n"), usageout);
+	(void) fputs (_("  -l, --no-log-init             do not add the user to the lastlog and\n"
+	                "                                faillog databases\n"), usageout);
+	(void) fputs (_("  -m, --create-home             create the user's home directory\n"), usageout);
+	(void) fputs (_("  -M, --no-create-home          do not create the user's home directory\n"), usageout);
+	(void) fputs (_("  -N, --no-user-group           do not create a group with the same name as\n"
+	                "                                the user\n"), usageout);
+	(void) fputs (_("  -o, --non-unique              allow to create users with duplicate\n"
+	                "                                (non-unique) UID\n"), usageout);
+	(void) fputs (_("  -p, --password PASSWORD       encrypted password of the new account\n"), usageout);
+	(void) fputs (_("  -r, --system                  create a system account\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -s, --shell SHELL             login shell of the new account\n"), usageout);
+	(void) fputs (_("  -u, --uid UID                 user ID of the new account\n"), usageout);
+	(void) fputs (_("  -U, --user-group              create a group with the same name as the user\n"), usageout);
+#ifdef WITH_SELINUX
+	(void) fputs (_("  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping\n"), usageout);
+#endif				/* WITH_SELINUX */
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * new_pwent - initialize the values in a password file entry
+ *
+ *	new_pwent() takes all of the values that have been entered and
+ *	fills in a (struct passwd) with them.
+ */
+static void new_pwent (struct passwd *pwent)
+{
+	memzero (pwent, sizeof *pwent);
+	pwent->pw_name = (char *) user_name;
+	if (is_shadow_pwd) {
+		pwent->pw_passwd = (char *) SHADOW_PASSWD_STRING;
+	} else {
+		pwent->pw_passwd = (char *) user_pass;
+	}
+
+	pwent->pw_uid = user_id;
+	pwent->pw_gid = user_gid;
+	pwent->pw_gecos = (char *) user_comment;
+	pwent->pw_dir = (char *) user_home;
+	pwent->pw_shell = (char *) user_shell;
+}
+
+static long scale_age (long x)
+{
+	if (x <= 0) {
+		return x;
+	}
+
+	return x * (DAY / SCALE);
+}
+
+/*
+ * new_spent - initialize the values in a shadow password file entry
+ *
+ *	new_spent() takes all of the values that have been entered and
+ *	fills in a (struct spwd) with them.
+ */
+static void new_spent (struct spwd *spent)
+{
+	memzero (spent, sizeof *spent);
+	spent->sp_namp = (char *) user_name;
+	spent->sp_pwdp = (char *) user_pass;
+	spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+	if (0 == spent->sp_lstchg) {
+		/* Better disable aging than requiring a password change */
+		spent->sp_lstchg = -1;
+	}
+	if (!rflg) {
+		spent->sp_min = scale_age (getdef_num ("PASS_MIN_DAYS", -1));
+		spent->sp_max = scale_age (getdef_num ("PASS_MAX_DAYS", -1));
+		spent->sp_warn = scale_age (getdef_num ("PASS_WARN_AGE", -1));
+		spent->sp_inact = scale_age (def_inactive);
+		spent->sp_expire = scale_age (user_expire);
+	} else {
+		spent->sp_min = -1;
+		spent->sp_max = -1;
+		spent->sp_warn = -1;
+		spent->sp_inact = -1;
+		spent->sp_expire = -1;
+	}
+	spent->sp_flag = SHADOW_SP_FLAG_UNSET;
+}
+
+/*
+ * grp_update - add user to secondary group set
+ *
+ *	grp_update() takes the secondary group set given in user_groups
+ *	and adds the user to each group given by that set.
+ *
+ *	The group files are opened and locked in open_files().
+ *
+ *	close_files() should be called afterwards to commit the changes
+ *	and unlocking the group files.
+ */
+static void grp_update (void)
+{
+	const struct group *grp;
+	struct group *ngrp;
+
+#ifdef	SHADOWGRP
+	const struct sgrp *sgrp;
+	struct sgrp *nsgrp;
+#endif
+
+	/*
+	 * Scan through the entire group file looking for the groups that
+	 * the user is a member of.
+	 * FIXME: we currently do not check that all groups of user_groups
+	 *        were completed with the new user.
+	 */
+	for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) {
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		if (!is_on_list (user_groups, grp->gr_name)) {
+			continue;
+		}
+
+		/*
+		 * Make a copy - gr_update() will free() everything
+		 * from the old entry, and we need it later.
+		 */
+		ngrp = __gr_dup (grp);
+		if (NULL == ngrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);	/* XXX */
+		}
+
+		/* 
+		 * Add the username to the list of group members and
+		 * update the group entry to reflect the change.
+		 */
+		ngrp->gr_mem = add_list (ngrp->gr_mem, user_name);
+		if (gr_update (ngrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, gr_dbname (), ngrp->gr_name);
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user to group",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_SUCCESS);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "add '%s' to group '%s'",
+		         user_name, ngrp->gr_name));
+	}
+
+#ifdef	SHADOWGRP
+	if (!is_shadow_grp)
+		return;
+
+	/*
+	 * Scan through the entire shadow group file looking for the groups
+	 * that the user is a member of. The administrative list isn't
+	 * modified.
+	 */
+	for (sgr_rewind (), sgrp = sgr_next (); NULL != sgrp; sgrp = sgr_next ()) {
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 * FIXME: is it really needed?
+		 *        This would be important only if the group is in
+		 *        user_groups. All these groups should be checked
+		 *        for existence with gr_locate already.
+		 */
+		if (gr_locate (sgrp->sg_name) == NULL) {
+			continue;
+		}
+
+		if (!is_on_list (user_groups, sgrp->sg_name)) {
+			continue;
+		}
+
+		/*
+		 * Make a copy - sgr_update() will free() everything
+		 * from the old entry, and we need it later.
+		 */
+		nsgrp = __sgr_dup (sgrp);
+		if (NULL == nsgrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to shadow group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);	/* XXX */
+		}
+
+		/* 
+		 * Add the username to the list of group members and
+		 * update the group entry to reflect the change.
+		 */
+		nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_name);
+		if (sgr_update (nsgrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, sgr_dbname (), nsgrp->sg_name);
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to shadow group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user to shadow group",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_SUCCESS);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "add '%s' to shadow group '%s'",
+		         user_name, nsgrp->sg_name));
+	}
+#endif				/* SHADOWGRP */
+}
+
+/*
+ * 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)
+{
+	const struct group *grp;
+	bool anyflag = false;
+	char *cp;
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"base-dir",       required_argument, NULL, 'b'},
+			{"comment",        required_argument, NULL, 'c'},
+			{"home-dir",       required_argument, NULL, 'd'},
+			{"defaults",       no_argument,       NULL, 'D'},
+			{"expiredate",     required_argument, NULL, 'e'},
+			{"inactive",       required_argument, NULL, 'f'},
+			{"gid",            required_argument, NULL, 'g'},
+			{"groups",         required_argument, NULL, 'G'},
+			{"help",           no_argument,       NULL, 'h'},
+			{"skel",           required_argument, NULL, 'k'},
+			{"key",            required_argument, NULL, 'K'},
+			{"no-log-init",    no_argument,       NULL, 'l'},
+			{"create-home",    no_argument,       NULL, 'm'},
+			{"no-create-home", no_argument,       NULL, 'M'},
+			{"no-user-group",  no_argument,       NULL, 'N'},
+			{"non-unique",     no_argument,       NULL, 'o'},
+			{"password",       required_argument, NULL, 'p'},
+			{"system",         no_argument,       NULL, 'r'},
+			{"root",           required_argument, NULL, 'R'},
+			{"shell",          required_argument, NULL, 's'},
+			{"uid",            required_argument, NULL, 'u'},
+			{"user-group",     no_argument,       NULL, 'U'},
+#ifdef WITH_SELINUX
+			{"selinux-user",   required_argument, NULL, 'Z'},
+#endif				/* WITH_SELINUX */
+			{NULL, 0, NULL, '\0'}
+		};
+		while ((c = getopt_long (argc, argv,
+#ifdef WITH_SELINUX
+		                         "b:c:d:De:f:g:G:hk:O:K:lmMNop:rR:s:u:UZ:",
+#else				/* !WITH_SELINUX */
+		                         "b:c:d:De:f:g:G:hk:O:K:lmMNop:rR:s:u:U",
+#endif				/* !WITH_SELINUX */
+		                         long_options, NULL)) != -1) {
+			switch (c) {
+			case 'b':
+				if (   ( !VALID (optarg) )
+				    || ( optarg[0] != '/' )) {
+					fprintf (stderr,
+					         _("%s: invalid base directory '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				def_home = optarg;
+				bflg = true;
+				break;
+			case 'c':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid comment '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_comment = optarg;
+				cflg = true;
+				break;
+			case 'd':
+				if (   ( !VALID (optarg) )
+				    || ( optarg[0] != '/' )) {
+					fprintf (stderr,
+					         _("%s: invalid home directory '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_home = optarg;
+				dflg = true;
+				break;
+			case 'D':
+				if (anyflag) {
+					usage (E_USAGE);
+				}
+				Dflg = true;
+				break;
+			case 'e':
+				if ('\0' != *optarg) {
+					user_expire = strtoday (optarg);
+					if (user_expire < -1) {
+						fprintf (stderr,
+						         _("%s: invalid date '%s'\n"),
+						         Prog, optarg);
+						exit (E_BAD_ARG);
+					}
+				} else {
+					user_expire = -1;
+				}
+
+				/*
+				 * -e "" is allowed without /etc/shadow
+				 * (it's a no-op in such case)
+				 */
+				if ((-1 != user_expire) && !is_shadow_pwd) {
+					fprintf (stderr,
+					         _("%s: shadow passwords required for -e\n"),
+					         Prog);
+					exit (E_USAGE);
+				}
+				if (Dflg) {
+					def_expire = optarg;
+				}
+				eflg = true;
+				break;
+			case 'f':
+				if (   (getlong (optarg, &def_inactive) == 0)
+				    || (def_inactive < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				/*
+				 * -f -1 is allowed
+				 * it's a no-op without /etc/shadow
+				 */
+				if ((-1 != def_inactive) && !is_shadow_pwd) {
+					fprintf (stderr,
+					         _("%s: shadow passwords required for -f\n"),
+					         Prog);
+					exit (E_USAGE);
+				}
+				fflg = true;
+				break;
+			case 'g':
+				grp = getgr_nam_gid (optarg);
+				if (NULL == grp) {
+					fprintf (stderr,
+					         _("%s: group '%s' does not exist\n"),
+					         Prog, optarg);
+					exit (E_NOTFOUND);
+				}
+				if (Dflg) {
+					def_group = grp->gr_gid;
+					def_gname = optarg;
+				} else {
+					user_gid = grp->gr_gid;
+				}
+				gflg = true;
+				break;
+			case 'G':
+				if (get_groups (optarg) != 0) {
+					exit (E_NOTFOUND);
+				}
+				if (NULL != user_groups[0]) {
+					do_grp_update = true;
+				}
+				Gflg = true;
+				break;
+			case 'h':
+				usage (E_SUCCESS);
+				break;
+			case 'k':
+				def_template = optarg;
+				kflg = true;
+				break;
+			case 'K':
+			case 'O': /* compatibility with previous Debian useradd */
+				/*
+				 * override login.defs defaults (-K name=value)
+				 * example: -K UID_MIN=100 -K UID_MAX=499
+				 * note: -K UID_MIN=10,UID_MAX=499 doesn't work yet
+				 */
+				cp = strchr (optarg, '=');
+				if (NULL == cp) {
+					fprintf (stderr,
+					         _("%s: -K requires KEY=VALUE\n"),
+					         Prog);
+					exit (E_BAD_ARG);
+				}
+				/* terminate name, point to value */
+				*cp = '\0';
+				cp++;
+				if (putdef_str (optarg, cp) < 0) {
+					exit (E_BAD_ARG);
+				}
+				break;
+			case 'l':
+				lflg = true;
+				break;
+			case 'm':
+				mflg = true;
+				break;
+			case 'M':
+				Mflg = true;
+				break;
+			case 'N':
+				Nflg = true;
+				break;
+			case 'o':
+				oflg = true;
+				break;
+			case 'p':	/* set encrypted password */
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_pass = optarg;
+				break;
+			case 'r':
+				rflg = true;
+				break;
+			case 'R': /* no-op, handled in process_root_flag () */
+				break;
+			case 's':
+				if (   ( !VALID (optarg) )
+				    || (   ('\0' != optarg[0])
+				        && ('/'  != optarg[0])
+				        && ('*'  != optarg[0]) )) {
+					fprintf (stderr,
+					         _("%s: invalid shell '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_shell = optarg;
+				def_shell = optarg;
+				sflg = true;
+				break;
+			case 'u':
+				if (   (get_uid (optarg, &user_id) == 0)
+				    || (user_id == (gid_t)-1)) {
+					fprintf (stderr,
+					         _("%s: invalid user ID '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				uflg = true;
+				break;
+			case 'U':
+				Uflg = true;
+				break;
+#ifdef WITH_SELINUX
+			case 'Z':
+				if (is_selinux_enabled () > 0) {
+					user_selinux = optarg;
+				} else {
+					fprintf (stderr,
+					         _("%s: -Z requires SELinux enabled kernel\n"),
+					         Prog);
+
+					exit (E_BAD_ARG);
+				}
+				break;
+#endif				/* WITH_SELINUX */
+			default:
+				usage (E_USAGE);
+			}
+			anyflag = true;
+		}
+	}
+
+	if (!gflg && !Nflg && !Uflg) {
+		/* Get the settings from login.defs */
+		Uflg = getdef_bool ("USERGROUPS_ENAB");
+	}
+
+	/*
+	 * Certain options are only valid in combination with others.
+	 * Check it here so that they can be specified in any order.
+	 */
+	if (oflg && !uflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-o", "-u");
+		usage (E_USAGE);
+	}
+	if (kflg && !mflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-k", "-m");
+		usage (E_USAGE);
+	}
+	if (Uflg && gflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-U", "-g");
+		usage (E_USAGE);
+	}
+	if (Uflg && Nflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-U", "-N");
+		usage (E_USAGE);
+	}
+	if (mflg && Mflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-m", "-M");
+		usage (E_USAGE);
+	}
+
+	/*
+	 * Either -D or username is required. Defaults can be set with -D
+	 * for the -b, -e, -f, -g, -s options only.
+	 */
+	if (Dflg) {
+		if (optind != argc) {
+			usage (E_USAGE);
+		}
+
+		if (uflg || Gflg || dflg || cflg || mflg) {
+			usage (E_USAGE);
+		}
+	} else {
+		if (optind != argc - 1) {
+			usage (E_USAGE);
+		}
+
+		user_name = argv[optind];
+		if (!is_valid_user_name (user_name)) {
+			fprintf (stderr,
+			         _("%s: invalid user name '%s'\n"),
+			         Prog, user_name);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			exit (E_BAD_ARG);
+		}
+		if (!dflg) {
+			char *uh;
+			size_t len = strlen (def_home) + strlen (user_name) + 2;
+			int wlen;
+
+			uh = xmalloc (len);
+			wlen = snprintf (uh, len, "%s/%s", def_home, user_name);
+			assert (wlen == (int) len -1);
+
+			user_home = uh;
+		}
+	}
+
+	if (!eflg) {
+		user_expire = strtoday (def_expire);
+	}
+
+	if (!gflg) {
+		user_gid = def_group;
+	}
+
+	if (!sflg) {
+		user_shell = def_shell;
+	}
+
+	create_mail_spool = def_create_mail_spool;
+
+	if (!rflg) {
+		/* for system accounts defaults are ignored and we
+		 * do not create a home dir */
+		if (getdef_bool ("CREATE_HOME")) {
+			mflg = true;
+		}
+	}
+
+	if (Mflg) {
+		/* absolutely sure that we do not create home dirs */
+		mflg = false;
+	}
+}
+
+/*
+ * close_files - close all of the files that were opened
+ *
+ *	close_files() closes all of the files that were opened for this
+ *	new user. This causes any modified entries to be written out.
+ */
+static void close_files (void)
+{
+	if (pw_close () == 0) {
+		fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (do_grp_update) {
+		if (gr_close () == 0) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef	SHADOWGRP
+		if (is_shadow_grp && (sgr_close () == 0)) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"),
+			         Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#endif
+	}
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid  && (sub_uid_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ()));
+		fail_exit (E_SUB_UID_UPDATE);
+	}
+	if (is_sub_gid  && (sub_gid_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ()));
+		fail_exit (E_SUB_GID_UPDATE);
+	}
+#endif				/* ENABLE_SUBIDS */
+	if (is_shadow_pwd) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking shadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		spw_locked = false;
+	}
+	if (pw_unlock () == 0) {
+		fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "unlocking passwd file",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		/* continue */
+	}
+	pw_locked = false;
+	if (gr_unlock () == 0) {
+		fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "unlocking group file",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		/* continue */
+	}
+	gr_locked = false;
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking gshadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sgr_locked = false;
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+				"unlocking subordinate user file",
+				user_name, AUDIT_NO_ID,
+				SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sub_uid_locked = false;
+	}
+	if (is_sub_gid) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+				"unlocking subordinate group file",
+				user_name, AUDIT_NO_ID,
+				SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sub_gid_locked = false;
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * open_files - lock and open the password files
+ *
+ *	open_files() opens the two password files.
+ */
+static void open_files (void)
+{
+	if (pw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, pw_dbname ());
+		exit (E_PW_UPDATE);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+
+	/* shadow file will be opened by open_shadow(); */
+
+	/*
+	 * Lock and open the group file.
+	 */
+	if (gr_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, gr_dbname ());
+		fail_exit (E_GRP_UPDATE);
+	}
+	gr_locked = true;
+	if (gr_open (O_RDWR) == 0) {
+		fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+		fail_exit (E_GRP_UPDATE);
+	}
+#ifdef  SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		sgr_locked = true;
+		if (sgr_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (sub_uid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		sub_uid_locked = true;
+		if (sub_uid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+	}
+	if (is_sub_gid) {
+		if (sub_gid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		sub_gid_locked = true;
+		if (sub_gid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+static void open_shadow (void)
+{
+	if (!is_shadow_pwd) {
+		return;
+	}
+	if (spw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	spw_locked = true;
+	if (spw_open (O_RDWR) == 0) {
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+}
+
+static char *empty_list = NULL;
+
+/*
+ * new_grent - initialize the values in a group file entry
+ *
+ *      new_grent() takes all of the values that have been entered and fills
+ *      in a (struct group) with them.
+ */
+
+static void new_grent (struct group *grent)
+{
+	memzero (grent, sizeof *grent);
+	grent->gr_name = (char *) user_name;
+#ifdef  SHADOWGRP
+	if (is_shadow_grp) {
+		grent->gr_passwd = SHADOW_PASSWD_STRING;	/* XXX warning: const */
+	} else
+#endif				/* SHADOWGRP */
+	{
+		grent->gr_passwd = "!";	/* XXX warning: const */
+	}
+	grent->gr_gid = user_gid;
+	grent->gr_mem = &empty_list;
+}
+
+#ifdef  SHADOWGRP
+/*
+ * new_sgent - initialize the values in a shadow group file entry
+ *
+ *      new_sgent() takes all of the values that have been entered and fills
+ *      in a (struct sgrp) with them.
+ */
+
+static void new_sgent (struct sgrp *sgent)
+{
+	memzero (sgent, sizeof *sgent);
+	sgent->sg_name = (char *) user_name;
+	sgent->sg_passwd = "!";	/* XXX warning: const */
+	sgent->sg_adm = &empty_list;
+	sgent->sg_mem = &empty_list;
+}
+#endif				/* SHADOWGRP */
+
+
+/*
+ * grp_add - add new group file entries
+ *
+ *      grp_add() writes the new records to the group files.
+ */
+
+static void grp_add (void)
+{
+	struct group grp;
+
+#ifdef  SHADOWGRP
+	struct sgrp sgrp;
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Create the initial entries for this new group.
+	 */
+	new_grent (&grp);
+#ifdef  SHADOWGRP
+	new_sgent (&sgrp);
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Write out the new group file entry.
+	 */
+	if (gr_update (&grp) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, gr_dbname (), grp.gr_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_GROUP, Prog,
+		              "adding group",
+		              grp.gr_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_GRP_UPDATE);
+	}
+#ifdef  SHADOWGRP
+	/*
+	 * Write out the new shadow group entries as well.
+	 */
+	if (is_shadow_grp && (sgr_update (&sgrp) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, sgr_dbname (), sgrp.sg_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_GROUP, Prog,
+		              "adding group",
+		              grp.gr_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_GRP_UPDATE);
+	}
+#endif				/* SHADOWGRP */
+	SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid));
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_GROUP, Prog,
+	              "adding group",
+	              grp.gr_name, AUDIT_NO_ID,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	do_grp_update = true;
+}
+
+static void faillog_reset (uid_t uid)
+{
+	struct faillog fl;
+	int fd;
+	off_t offset_uid = (off_t) (sizeof fl) * uid;
+
+	if (access (FAILLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	memzero (&fl, sizeof (fl));
+
+	fd = open (FAILLOG_FILE, O_RDWR);
+	if (   (-1 == fd)
+	    || (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
+	    || (write (fd, &fl, sizeof (fl)) != (ssize_t) sizeof (fl))
+	    || (fsync (fd) != 0)
+	    || (close (fd) != 0)) {
+		fprintf (stderr,
+		         _("%s: failed to reset the faillog entry of UID %lu: %s\n"),
+		         Prog, (unsigned long) uid, strerror (errno));
+		SYSLOG ((LOG_WARN, "failed to reset the faillog entry of UID %lu", (unsigned long) uid));
+		/* continue */
+	}
+}
+
+static void lastlog_reset (uid_t uid)
+{
+	struct lastlog ll;
+	int fd;
+	off_t offset_uid = (off_t) (sizeof ll) * uid;
+
+	if (access (LASTLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	memzero (&ll, sizeof (ll));
+
+	fd = open (LASTLOG_FILE, O_RDWR);
+	if (   (-1 == fd)
+	    || (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
+	    || (write (fd, &ll, sizeof (ll)) != (ssize_t) sizeof (ll))
+	    || (fsync (fd) != 0)
+	    || (close (fd) != 0)) {
+		fprintf (stderr,
+		         _("%s: failed to reset the lastlog entry of UID %lu: %s\n"),
+		         Prog, (unsigned long) uid, strerror (errno));
+		SYSLOG ((LOG_WARN, "failed to reset the lastlog entry of UID %lu", (unsigned long) uid));
+		/* continue */
+	}
+}
+
+/*
+ * usr_update - create the user entries
+ *
+ *	usr_update() creates the password file entries for this user
+ *	and will update the group entries if required.
+ */
+static void usr_update (void)
+{
+	struct passwd pwent;
+	struct spwd spent;
+
+	/*
+	 * Fill in the password structure with any new fields, making
+	 * copies of strings.
+	 */
+	new_pwent (&pwent);
+	new_spent (&spent);
+
+	/*
+	 * Create a syslog entry. We need to do this now in case anything
+	 * happens so we know what we were trying to accomplish.
+	 */
+	SYSLOG ((LOG_INFO,
+	         "new user: name=%s, UID=%u, GID=%u, home=%s, shell=%s",
+	         user_name, (unsigned int) user_id,
+	         (unsigned int) user_gid, user_home, user_shell));
+
+	/*
+	 * Initialize faillog and lastlog entries for this UID in case
+	 * it belongs to a previously deleted user. We do it only if
+	 * 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)) {
+		faillog_reset (user_id);
+		lastlog_reset (user_id);
+	}
+
+	/*
+	 * Put the new (struct passwd) in the table.
+	 */
+	if (pw_update (&pwent) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, pw_dbname (), pwent.pw_name);
+		fail_exit (E_PW_UPDATE);
+	}
+
+	/*
+	 * Put the new (struct spwd) in the table.
+	 */
+	if (is_shadow_pwd && (spw_update (&spent) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, spw_dbname (), spent.sp_namp);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding shadow password",
+		              user_name, (unsigned int) user_id,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_PW_UPDATE);
+	}
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid &&
+	    (sub_uid_add(user_name, sub_uid_start, sub_uid_count) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry\n"),
+		         Prog, sub_uid_dbname ());
+		fail_exit (E_SUB_UID_UPDATE);
+	}
+	if (is_sub_gid &&
+	    (sub_gid_add(user_name, sub_gid_start, sub_gid_count) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry\n"),
+		         Prog, sub_uid_dbname ());
+		fail_exit (E_SUB_GID_UPDATE);
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_USER, Prog,
+	              "adding user",
+	              user_name, (unsigned int) user_id,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	/*
+	 * Do any group file updates for this user.
+	 */
+	if (do_grp_update) {
+		grp_update ();
+	}
+}
+
+/*
+ * create_home - create the user's home directory
+ *
+ *	create_home() creates the user's home directory if it does not
+ *	already exist. It will be created mode 755 owned by the user
+ *	with the user's default group.
+ */
+static void create_home (void)
+{
+	if (access (user_home, F_OK) != 0) {
+#ifdef WITH_SELINUX
+		if (set_selinux_file_context (user_home) != 0) {
+			fail_exit (E_HOMEDIR);
+		}
+#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;
+#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 */
+		if (reset_selinux_file_context () != 0) {
+			fail_exit (E_HOMEDIR);
+		}
+#endif
+	}
+}
+
+/*
+ * create_mail - create the user's mail spool
+ *
+ *	create_mail() creates the user's mail spool if it does not already
+ *	exist. It will be created mode 660 owned by the user and group
+ *	'mail'
+ */
+static void create_mail (void)
+{
+	if (strcasecmp (create_mail_spool, "yes") == 0) {
+		const char *spool;
+		char *file;
+		int fd;
+		struct group *gr;
+		gid_t gid;
+		mode_t mode;
+
+		spool = getdef_str ("MAIL_DIR");
+		if (NULL == spool) {
+			spool = "/var/mail";
+		}
+		file = alloca (strlen (spool) + strlen (user_name) + 2);
+		sprintf (file, "%s/%s", spool, user_name);
+		fd = open (file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0);
+		if (fd < 0) {
+			perror (_("Creating mailbox file"));
+			return;
+		}
+
+		gr = getgrnam ("mail"); /* local, no need for xgetgrnam */
+		if (NULL == gr) {
+			fputs (_("Group 'mail' not found. Creating the user mailbox file with 0600 mode.\n"),
+			       stderr);
+			gid = user_gid;
+			mode = 0600;
+		} else {
+			gid = gr->gr_gid;
+			mode = 0660;
+		}
+
+		if (   (fchown (fd, user_id, gid) != 0)
+		    || (fchmod (fd, mode) != 0)) {
+			perror (_("Setting mailbox file permissions"));
+		}
+
+		fsync (fd);
+		close (fd);
+	}
+}
+
+/*
+ * main - useradd command
+ */
+int main (int argc, char **argv)
+{
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/* Needed for userns check */
+	uid_t uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL);
+	uid_t uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL);
+
+	/*
+	 * Get my name so that I can use it to report errors.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	OPENLOG ("useradd");
+#ifdef WITH_AUDIT
+	audit_help_open ();
+#endif
+
+	sys_ngroups = sysconf (_SC_NGROUPS_MAX);
+	user_groups = (char **) xmalloc ((1 + sys_ngroups) * sizeof (char *));
+	/*
+	 * Initialize the list to be empty
+	 */
+	user_groups[0] = (char *) 0;
+
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+#ifdef ENABLE_SUBIDS
+	is_sub_uid = sub_uid_file_present () && !rflg &&
+	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
+	is_sub_gid = sub_gid_file_present () && !rflg &&
+	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
+#endif				/* ENABLE_SUBIDS */
+
+	get_defaults ();
+
+	process_flags (argc, argv);
+
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	{
+		struct passwd *pampw;
+		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+		if (pampw == NULL) {
+			fprintf (stderr,
+			         _("%s: Cannot determine your user name.\n"),
+			         Prog);
+			fail_exit (1);
+		}
+
+		retval = pam_start ("useradd", pampw->pw_name, &conv, &pamh);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		fail_exit (1);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/*
+	 * See if we are messing with the defaults file, or creating
+	 * a new user.
+	 */
+	if (Dflg) {
+		if (gflg || bflg || fflg || eflg || sflg) {
+			exit ((set_defaults () != 0) ? 1 : 0);
+		}
+
+		show_defaults ();
+		exit (E_SUCCESS);
+	}
+
+	/*
+	 * Start with a quick check to see if the user exists.
+	 */
+	if (getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */
+		fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_NAME_IN_USE);
+	}
+
+	/*
+	 * Don't blindly overwrite a group when a user is added...
+	 * If you already have a group username, and want to add the user
+	 * to that group, use useradd -g username username.
+	 * --bero
+	 */
+	if (Uflg) {
+		/* local, no need for xgetgrnam */
+		if (getgrnam (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);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_NAME_IN_USE);
+		}
+	}
+
+	/*
+	 * Do the hard stuff:
+	 * - open the files,
+	 * - create the user entries,
+	 * - create the home directory,
+	 * - create user mail spool,
+	 * - flush nscd caches for passwd and group services,
+	 * - then close and update the files.
+	 */
+	open_files ();
+
+	if (!oflg) {
+		/* first, seek for a valid uid to use for this user.
+		 * We do this because later we can use the uid we found as
+		 * gid too ... --gafton */
+		if (!uflg) {
+			if (find_new_uid (rflg, &user_id, NULL) < 0) {
+				fprintf (stderr, _("%s: can't create user\n"), Prog);
+				fail_exit (E_UID_IN_USE);
+			}
+		} else {
+			if (getpwuid (user_id) != NULL) {
+				fprintf (stderr,
+				         _("%s: UID %lu is not unique\n"),
+				         Prog, (unsigned long) user_id);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_ADD_USER, Prog,
+				              "adding user",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif
+				fail_exit (E_UID_IN_USE);
+			}
+		}
+	}
+
+#ifdef WITH_TCB
+	if (getdef_bool ("USE_TCB")) {
+		if (shadowtcb_create (user_name, user_id) == SHADOWTCB_FAILURE) {
+			fprintf (stderr,
+			         _("%s: Failed to create tcb directory for %s\n"),
+			         Prog, user_name);
+			fail_exit (E_UID_IN_USE);
+		}
+	}
+#endif
+	open_shadow ();
+
+	/* do we have to add a group for that user? This is why we need to
+	 * open the group files in the open_files() function  --gafton */
+	if (Uflg) {
+		if (find_new_gid (rflg, &user_gid, &user_id) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create group\n"),
+			         Prog);
+			fail_exit (4);
+		}
+		grp_add ();
+	}
+
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (find_new_sub_uids(user_name, &sub_uid_start, &sub_uid_count) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create subordinate user IDs\n"),
+			         Prog);
+			fail_exit(E_SUB_UID_UPDATE);
+		}
+	}
+	if (is_sub_gid) {
+		if (find_new_sub_gids(user_name, &sub_gid_start, &sub_gid_count) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create subordinate group IDs\n"),
+			         Prog);
+			fail_exit(E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+	usr_update ();
+
+	if (mflg) {
+		create_home ();
+		if (home_added) {
+			copy_tree (def_template, user_home, false, false,
+			           (uid_t)-1, user_id, (gid_t)-1, user_gid);
+		} else {
+			fprintf (stderr,
+			         _("%s: warning: the home directory already exists.\n"
+			           "Not copying any file from skel directory into it.\n"),
+			         Prog);
+		}
+
+	}
+
+	/* Do not create mail directory for system accounts */
+	if (!rflg) {
+		create_mail ();
+	}
+
+	close_files ();
+
+#ifdef WITH_SELINUX
+	if (Zflg) {
+		if (set_seuser (user_name, user_selinux) != 0) {
+			fprintf (stderr,
+			         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+			         Prog, user_name, user_selinux);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding SELinux user mapping",
+			              user_name, (unsigned int) user_id, 0);
+#endif				/* WITH_AUDIT */
+			fail_exit (E_SE_UPDATE);
+		}
+	}
+#endif				/* WITH_SELINUX */
+
+	nscd_flush_cache ("passwd");
+	nscd_flush_cache ("group");
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/.pc/1012_extrausers_chfn.patch/src/chfn.c 1:4.2-3.2ubuntu1/.pc/1012_extrausers_chfn.patch/src/chfn.c
--- 1:4.2-3.2/.pc/1012_extrausers_chfn.patch/src/chfn.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1012_extrausers_chfn.patch/src/chfn.c	2014-03-01 17:56:04.000000000 +0000
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <getopt.h>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/av_permissions.h>
+#endif
+#include "defines.h"
+#include "getdef.h"
+#include "nscd.h"
+#ifdef USE_PAM
+#include "pam_defs.h"
+#endif
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+/*@-exitarg@*/
+#include "exitcodes.h"
+
+/*
+ * Global variables.
+ */
+const char *Prog;
+static char fullnm[BUFSIZ];
+static char roomno[BUFSIZ];
+static char workph[BUFSIZ];
+static char homeph[BUFSIZ];
+static char slop[BUFSIZ];
+static bool amroot;
+/* Flags */
+static bool fflg = false;		/* -f - set full name                */
+static bool rflg = false;		/* -r - set room number              */
+static bool wflg = false;		/* -w - set work phone number        */
+static bool hflg = false;		/* -h - set home phone number        */
+static bool oflg = false;		/* -o - set other information        */
+static bool pw_locked = false;
+
+/*
+ * External identifiers
+ */
+
+/* local function prototypes */
+static void fail_exit (int code);
+static /*@noreturn@*/void usage (int status);
+static bool may_change_field (int);
+static void new_fields (void);
+static char *copy_field (char *, char *, char *);
+static void process_flags (int argc, char **argv);
+static void check_perms (const struct passwd *pw);
+static void update_gecos (const char *user, char *gecos);
+static void get_old_fields (const char *gecos);
+
+/*
+ * fail_exit - exit with an error and do some cleanup
+ */
+static void fail_exit (int code)
+{
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+			/* continue */
+		}
+	}
+	pw_locked = false;
+
+	closelog ();
+
+	exit (code);
+}
+
+/*
+ * usage - print command line syntax and exit
+ */
+static /*@noreturn@*/void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] [LOGIN]\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog);
+	(void) fputs (_("  -f, --full-name FULL_NAME     change user's full name\n"), usageout);
+	(void) fputs (_("  -h, --home-phone HOME_PHONE   change user's home phone number\n"), usageout);
+	(void) fputs (_("  -o, --other OTHER_INFO        change user's other GECOS information\n"), usageout);
+	(void) fputs (_("  -r, --room ROOM_NUMBER        change user's room number\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -u, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -w, --work-phone WORK_PHONE   change user's office phone number\n"), usageout);
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * may_change_field - indicate if the user is allowed to change a given field
+ *                    of her gecos information
+ *
+ *	root can change any field.
+ *
+ *	field should be one of 'f', 'r', 'w', 'h'
+ *
+ *	Return true if the user can change the field and false otherwise.
+ */
+static bool may_change_field (int field)
+{
+	const char *cp;
+
+	/*
+	 * CHFN_RESTRICT can now specify exactly which fields may be changed
+	 * by regular users, by using any combination of the following
+	 * letters:
+	 *  f - full name
+	 *  r - room number
+	 *  w - work phone
+	 *  h - home phone
+	 *
+	 * This makes it possible to disallow changing the room number
+	 * information, for example - this feature was suggested by Maciej
+	 * 'Tycoon' Majchrowski.
+	 *
+	 * For backward compatibility, "yes" is equivalent to "rwh",
+	 * "no" is equivalent to "frwh". Only root can change anything
+	 * if the string is empty or not defined at all.
+	 */
+	if (amroot) {
+		return true;
+	}
+
+	cp = getdef_str ("CHFN_RESTRICT");
+	if (NULL == cp) {
+		cp = "";
+	} else if (strcmp (cp, "yes") == 0) {
+		cp = "rwh";
+	} else if (strcmp (cp, "no") == 0) {
+		cp = "frwh";
+	}
+
+	if (strchr (cp, field) != NULL) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * new_fields - change the user's GECOS information interactively
+ *
+ * prompt the user for each of the four fields and fill in the fields from
+ * the user's response, or leave alone if nothing was entered.
+ */
+static void new_fields (void)
+{
+	puts (_("Enter the new value, or press ENTER for the default"));
+
+	if (may_change_field ('f')) {
+		change_field (fullnm, sizeof fullnm, _("Full Name"));
+	} else {
+		printf (_("\t%s: %s\n"), _("Full Name"), fullnm);
+	}
+
+	if (may_change_field ('r')) {
+		change_field (roomno, sizeof roomno, _("Room Number"));
+	} else {
+		printf (_("\t%s: %s\n"), _("Room Number"), fullnm);
+	}
+
+	if (may_change_field ('w')) {
+		change_field (workph, sizeof workph, _("Work Phone"));
+	} else {
+		printf (_("\t%s: %s\n"), _("Work Phone"), fullnm);
+	}
+
+	if (may_change_field ('h')) {
+		change_field (homeph, sizeof homeph, _("Home Phone"));
+	} else {
+		printf (_("\t%s: %s\n"), _("Home Phone"), fullnm);
+	}
+
+	if (amroot) {
+		change_field (slop, sizeof slop, _("Other"));
+	}
+}
+
+/*
+ * copy_field - get the next field from the gecos field
+ *
+ * copy_field copies the next field from the gecos field, returning a
+ * pointer to the field which follows, or NULL if there are no more fields.
+ *
+ *	in - the current GECOS field
+ *	out - where to copy the field to
+ *	extra - fields with '=' get copied here
+ */
+static char *copy_field (char *in, char *out, char *extra)
+{
+	char *cp = NULL;
+
+	while (NULL != in) {
+		cp = strchr (in, ',');
+		if (NULL != cp) {
+			*cp++ = '\0';
+		}
+
+		if (strchr (in, '=') == NULL) {
+			break;
+		}
+
+		if (NULL != extra) {
+			if ('\0' != extra[0]) {
+				strcat (extra, ",");
+			}
+
+			strcat (extra, in);
+		}
+		in = cp;
+	}
+	if ((NULL != in) && (NULL != out)) {
+		strcpy (out, in);
+	}
+
+	return cp;
+}
+
+/*
+ * process_flags - parse the command line options
+ *
+ *	It will not return if an error is encountered.
+ */
+static void process_flags (int argc, char **argv)
+{
+	int c;		/* flag currently being processed    */
+	static struct option long_options[] = {
+		{"full-name",  required_argument, NULL, 'f'},
+		{"home-phone", required_argument, NULL, 'h'},
+		{"other",      required_argument, NULL, 'o'},
+		{"room",       required_argument, NULL, 'r'},
+		{"root",       required_argument, NULL, 'R'},
+		{"help",       no_argument,       NULL, 'u'},
+		{"work-phone", required_argument, NULL, 'w'},
+		{NULL, 0, NULL, '\0'}
+	};
+
+	/* 
+	 * The remaining arguments will be processed one by one and executed
+	 * by this command. The name is the last argument if it does not
+	 * begin with a "-", otherwise the name is determined from the
+	 * environment and must agree with the real UID. Also, the UID will
+	 * be checked for any commands which are restricted to root only.
+	 */
+	while ((c = getopt_long (argc, argv, "f:h:o:r:R:uw:",
+	                         long_options, NULL)) != -1) {
+		switch (c) {
+		case 'f':
+			if (!may_change_field ('f')) {
+				fprintf (stderr,
+				         _("%s: Permission denied.\n"), Prog);
+				exit (E_NOPERM);
+			}
+			fflg = true;
+			STRFCPY (fullnm, optarg);
+			break;
+		case 'h':
+			if (!may_change_field ('h')) {
+				fprintf (stderr,
+				         _("%s: Permission denied.\n"), Prog);
+				exit (E_NOPERM);
+			}
+			hflg = true;
+			STRFCPY (homeph, optarg);
+			break;
+		case 'o':
+			if (!amroot) {
+				fprintf (stderr,
+				         _("%s: Permission denied.\n"), Prog);
+				exit (E_NOPERM);
+			}
+			oflg = true;
+			STRFCPY (slop, optarg);
+			break;
+		case 'r':
+			if (!may_change_field ('r')) {
+				fprintf (stderr,
+				         _("%s: Permission denied.\n"), Prog);
+				exit (E_NOPERM);
+			}
+			rflg = true;
+			STRFCPY (roomno, optarg);
+			break;
+		case 'R': /* no-op, handled in process_root_flag () */
+			break;
+		case 'u':
+			usage (E_SUCCESS);
+			/*@notreached@*/break;
+		case 'w':
+			if (!may_change_field ('w')) {
+				fprintf (stderr,
+				         _("%s: Permission denied.\n"), Prog);
+				exit (E_NOPERM);
+			}
+			wflg = true;
+			STRFCPY (workph, optarg);
+			break;
+		default:
+			usage (E_USAGE);
+		}
+	}
+}
+
+/*
+ * check_perms - check if the caller is allowed to add a group
+ *
+ *	Non-root users are only allowed to change their gecos field.
+ *	(see also may_change_field())
+ *
+ *	Non-root users must be authenticated.
+ *
+ *	It will not return if the user is not allowed.
+ */
+static void check_perms (const struct passwd *pw)
+{
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+	struct passwd *pampw;
+#endif
+
+	/*
+	 * Non-privileged users are only allowed to change the gecos field
+	 * if the UID of the user matches the current real UID.
+	 */
+	if (!amroot && pw->pw_uid != getuid ()) {
+		fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+		closelog ();
+		exit (E_NOPERM);
+	}
+#ifdef WITH_SELINUX
+	/*
+	 * If the UID of the user does not match the current real UID,
+	 * check if the change is allowed by SELinux policy.
+	 */
+	if ((pw->pw_uid != getuid ())
+	    && (is_selinux_enabled () > 0)
+	    && (selinux_check_passwd_access (PASSWD__CHFN) != 0)) {
+		fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+		closelog ();
+		exit (E_NOPERM);
+	}
+#endif
+
+#ifndef USE_PAM
+	/*
+	 * Non-privileged users are optionally authenticated (must enter the
+	 * password of the user whose information is being changed) before
+	 * any changes can be made. Idea from util-linux chfn/chsh. 
+	 * --marekm
+	 */
+	if (!amroot && getdef_bool ("CHFN_AUTH")) {
+		passwd_check (pw->pw_name, pw->pw_passwd, "chfn");
+	}
+
+#else				/* !USE_PAM */
+	pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+	if (NULL == pampw) {
+		fprintf (stderr,
+		         _("%s: Cannot determine your user name.\n"),
+		         Prog);
+		exit (E_NOPERM);
+	}
+
+	retval = pam_start ("chfn", pampw->pw_name, &conv, &pamh);
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		exit (E_NOPERM);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+}
+
+/*
+ * update_gecos - update the gecos fields in the password database
+ *
+ *	Commit the user's entry after changing her gecos field.
+ */
+static void update_gecos (const char *user, char *gecos)
+{
+	const struct passwd *pw;	/* The user's password file entry */
+	struct passwd pwent;		/* modified password file entry */
+
+	/*
+	 * Before going any further, raise the ulimit to prevent colliding
+	 * into a lowered ulimit, and set the real UID to root to protect
+	 * against unexpected signals. Any keyboard signals are set to be
+	 * ignored.
+	 */
+	if (setuid (0) != 0) {
+		fputs (_("Cannot change ID to root.\n"), stderr);
+		SYSLOG ((LOG_ERR, "can't setuid(0)"));
+		fail_exit (E_NOPERM);
+	}
+	pwd_init ();
+
+	/*
+	 * The passwd entry is now ready to be committed back to the
+	 * password file. Get a lock on the file and open it.
+	 */
+	if (pw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, pw_dbname ());
+		fail_exit (E_NOPERM);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"), Prog, pw_dbname ());
+		fail_exit (E_NOPERM);
+	}
+
+	/*
+	 * Get the entry to update using pw_locate() - we want the real one
+	 * from /etc/passwd, not the one from getpwnam() which could contain
+	 * the shadow password if (despite the warnings) someone enables
+	 * AUTOSHADOW (or SHADOW_COMPAT in libc).  --marekm
+	 */
+	pw = pw_locate (user);
+	if (NULL == pw) {
+		fprintf (stderr,
+		         _("%s: user '%s' does not exist in %s\n"),
+		         Prog, user, pw_dbname ());
+		fail_exit (E_NOPERM);
+	}
+
+	/*
+	 * Make a copy of the entry, then change the gecos field. The other
+	 * fields remain unchanged.
+	 */
+	pwent = *pw;
+	pwent.pw_gecos = gecos;
+
+	/*
+	 * Update the passwd file entry. If there is a DBM file, update that
+	 * entry as well.
+	 */
+	if (pw_update (&pwent) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, pw_dbname (), pwent.pw_name);
+		fail_exit (E_NOPERM);
+	}
+
+	/*
+	 * Changes have all been made, so commit them and unlock the file.
+	 */
+	if (pw_close () == 0) {
+		fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_NOPERM);
+	}
+	if (pw_unlock () == 0) {
+		fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+		/* continue */
+	}
+	pw_locked = false;
+}
+
+/*
+ * get_old_fields - parse the old gecos and use the old value for the fields
+ *                  which are not set on the command line
+ */
+static void get_old_fields (const char *gecos)
+{
+	char *cp;		/* temporary character pointer       */
+	char old_gecos[BUFSIZ];	/* buffer for old GECOS fields       */
+	STRFCPY (old_gecos, gecos);
+
+	/*
+	 * Now get the full name. It is the first comma separated field in
+	 * the GECOS field.
+	 */
+	cp = copy_field (old_gecos, fflg ? (char *) 0 : fullnm, slop);
+
+	/*
+	 * Now get the room number. It is the next comma separated field,
+	 * if there is indeed one.
+	 */
+	if (NULL != cp) {
+		cp = copy_field (cp, rflg ? (char *) 0 : roomno, slop);
+	}
+
+	/*
+	 * Now get the work phone number. It is the third field.
+	 */
+	if (NULL != cp) {
+		cp = copy_field (cp, wflg ? (char *) 0 : workph, slop);
+	}
+
+	/*
+	 * Now get the home phone number. It is the fourth field.
+	 */
+	if (NULL != cp) {
+		cp = copy_field (cp, hflg ? (char *) 0 : homeph, slop);
+	}
+
+	/*
+	 * Anything left over is "slop".
+	 */
+	if ((NULL != cp) && !oflg) {
+		if ('\0' != slop[0]) {
+			strcat (slop, ",");
+		}
+
+		strcat (slop, cp);
+	}
+}
+
+/*
+ * check_fields - check all of the fields for valid information
+ *
+ *	It will not return if a field is not valid.
+ */
+static void check_fields (void)
+{
+	int err;
+	err = valid_field (fullnm, ":,=\n");
+	if (err > 0) {
+		fprintf (stderr, _("%s: name with non-ASCII characters: '%s'\n"), Prog, fullnm);
+	} else if (err < 0) {
+		fprintf (stderr, _("%s: invalid name: '%s'\n"), Prog, fullnm);
+		fail_exit (E_NOPERM);
+	}
+	err = valid_field (roomno, ":,=\n");
+	if (err > 0) {
+		fprintf (stderr, _("%s: room number with non-ASCII characters: '%s'\n"), Prog, roomno);
+	} else if (err < 0) {
+		fprintf (stderr, _("%s: invalid room number: '%s'\n"),
+		         Prog, roomno);
+		fail_exit (E_NOPERM);
+	}
+	if (valid_field (workph, ":,=\n") != 0) {
+		fprintf (stderr, _("%s: invalid work phone: '%s'\n"),
+		         Prog, workph);
+		fail_exit (E_NOPERM);
+	}
+	if (valid_field (homeph, ":,=\n") != 0) {
+		fprintf (stderr, _("%s: invalid home phone: '%s'\n"),
+		         Prog, homeph);
+		fail_exit (E_NOPERM);
+	}
+	err = valid_field (slop, ":\n");
+	if (err > 0) {
+		fprintf (stderr, _("%s: '%s' contains non-ASCII characters\n"), Prog, slop);
+	} else if (err < 0) {
+		fprintf (stderr,
+		         _("%s: '%s' contains illegal characters\n"),
+		         Prog, slop);
+		fail_exit (E_NOPERM);
+	}
+}
+
+/*
+ * chfn - change a user's password file information
+ *
+ *	This command controls the GECOS field information in the password
+ *	file entry.
+ *
+ *	The valid options are
+ *
+ *	-f	full name
+ *	-r	room number
+ *	-w	work phone number
+ *	-h	home phone number
+ *	-o	other information (*)
+ *
+ *	(*) requires root permission to execute.
+ */
+int main (int argc, char **argv)
+{
+	const struct passwd *pw;	/* password file entry               */
+	char new_gecos[BUFSIZ];	/* buffer for new GECOS fields       */
+	char *user;
+
+	/*
+	 * Get the program name. The program name is used as a
+	 * prefix to most error messages.
+	 */
+	Prog = Basename (argv[0]);
+
+	sanitize_env ();
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	/*
+	 * This command behaves different for root and non-root
+	 * users.
+	 */
+	amroot = (getuid () == 0);
+
+	OPENLOG ("chfn");
+
+	/* parse the command line options */
+	process_flags (argc, argv);
+
+	/*
+	 * Get the name of the user to check. It is either the command line
+	 * name, or the name getlogin() returns.
+	 */
+	if (optind < argc) {
+		user = argv[optind];
+		pw = xgetpwnam (user);
+		if (NULL == pw) {
+			fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog,
+			         user);
+			fail_exit (E_NOPERM);
+		}
+	} else {
+		pw = get_my_pwent ();
+		if (NULL == pw) {
+			fprintf (stderr,
+			         _("%s: Cannot determine your user name.\n"),
+			         Prog);
+			SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
+			         (unsigned long) getuid ()));
+			fail_exit (E_NOPERM);
+		}
+		user = xstrdup (pw->pw_name);
+	}
+
+#ifdef	USE_NIS
+	/*
+	 * Now we make sure this is a LOCAL password entry for this user ...
+	 */
+	if (__ispwNIS ()) {
+		char *nis_domain;
+		char *nis_master;
+
+		fprintf (stderr,
+		         _("%s: cannot change user '%s' on NIS client.\n"),
+		         Prog, user);
+
+		if (!yp_get_default_domain (&nis_domain) &&
+		    !yp_master (nis_domain, "passwd.byname", &nis_master)) {
+			fprintf (stderr,
+			         _
+			         ("%s: '%s' is the NIS master for this client.\n"),
+			         Prog, nis_master);
+		}
+		fail_exit (E_NOPERM);
+	}
+#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);
+
+	/*
+	 * If none of the fields were changed from the command line, let the
+	 * user interactively change them.
+	 */
+	if (!fflg && !rflg && !wflg && !hflg && !oflg) {
+		printf (_("Changing the user information for %s\n"), user);
+		new_fields ();
+	}
+
+	/*
+	 * Check all of the fields for valid information
+	 */
+	check_fields ();
+
+	/*
+	 * Build the new GECOS field by plastering all the pieces together,
+	 * if they will fit ...
+	 */
+	if ((strlen (fullnm) + strlen (roomno) + strlen (workph) +
+	     strlen (homeph) + strlen (slop)) > (unsigned int) 80) {
+		fprintf (stderr, _("%s: fields too long\n"), Prog);
+		fail_exit (E_NOPERM);
+	}
+	snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s",
+	          fullnm, roomno, workph, homeph,
+	          ('\0' != slop[0]) ? "," : "", slop);
+
+	/* Rewrite the user's gecos in the passwd file */
+	update_gecos (user, new_gecos);
+
+	SYSLOG ((LOG_INFO, "changed user '%s' information", user));
+
+	nscd_flush_cache ("passwd");
+
+	closelog ();
+	exit (E_SUCCESS);
+}
+
diff -pruN 1:4.2-3.2/.pc/1021_no_subuids_for_system_users.patch/src/useradd.c 1:4.2-3.2ubuntu1/.pc/1021_no_subuids_for_system_users.patch/src/useradd.c
--- 1:4.2-3.2/.pc/1021_no_subuids_for_system_users.patch/src/useradd.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/1021_no_subuids_for_system_users.patch/src/useradd.c	2016-09-20 17:11:18.000000000 +0000
@@ -0,0 +1,2271 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2012, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <lastlog.h>
+#include <pwd.h>
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+#include "pam_defs.h"
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include "chkname.h"
+#include "defines.h"
+#include "faillog.h"
+#include "getdef.h"
+#include "groupio.h"
+#include "nscd.h"
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+#ifdef	SHADOWGRP
+#include "sgroupio.h"
+#endif
+#include "shadowio.h"
+#ifdef ENABLE_SUBIDS
+#include "subordinateio.h"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
+
+#ifndef SKEL_DIR
+#define SKEL_DIR "/etc/skel"
+#endif
+#ifndef USER_DEFAULTS_FILE
+#define USER_DEFAULTS_FILE "/etc/default/useradd"
+#define NEW_USER_FILE "/etc/default/nuaddXXXXXX"
+#endif
+/*
+ * Needed for MkLinux DR1/2/2.1 - J.
+ */
+#ifndef LASTLOG_FILE
+#define LASTLOG_FILE "/var/log/lastlog"
+#endif
+/*
+ * Global variables
+ */
+const char *Prog;
+
+/*
+ * These defaults are used if there is no defaults file.
+ */
+static gid_t def_group = 100;
+static const char *def_gname = "other";
+static const char *def_home = "/home";
+static const char *def_shell = "";
+static const char *def_template = SKEL_DIR;
+static const char *def_create_mail_spool = "no";
+
+static long def_inactive = -1;
+static const char *def_expire = "";
+
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
+static const char *user_name = "";
+static const char *user_pass = "!";
+static uid_t user_id;
+static gid_t user_gid;
+static const char *user_comment = "";
+static const char *user_home = "";
+static const char *user_shell = "";
+static const char *create_mail_spool = "";
+#ifdef WITH_SELINUX
+static /*@notnull@*/const char *user_selinux = "";
+#endif				/* WITH_SELINUX */
+
+static long user_expire = -1;
+static bool is_shadow_pwd;
+
+#ifdef SHADOWGRP
+static bool is_shadow_grp;
+static bool sgr_locked = false;
+#endif
+#ifdef ENABLE_SUBIDS
+static bool is_sub_uid = false;
+static bool is_sub_gid = false;
+static bool sub_uid_locked = false;
+static bool sub_gid_locked = false;
+static uid_t sub_uid_start;	/* New subordinate uid range */
+static unsigned long sub_uid_count;
+static gid_t sub_gid_start;	/* New subordinate gid range */
+static unsigned long sub_gid_count;
+#endif				/* ENABLE_SUBIDS */
+static bool pw_locked = false;
+static bool gr_locked = false;
+static bool spw_locked = false;
+static char **user_groups;	/* NULL-terminated list */
+static long sys_ngroups;
+static bool do_grp_update = false;	/* group files need to be updated */
+
+#ifndef EXTRAUSERS_OPT
+#define EXTRAUSERS_OPT 100000
+#endif
+
+static bool use_extrausers = false;
+
+static bool
+    bflg = false,		/* new default root of home directory */
+    cflg = false,		/* comment (GECOS) field for new account */
+    dflg = false,		/* home directory for new account */
+    Dflg = false,		/* set/show new user default values */
+    eflg = false,		/* days since 1970-01-01 when account is locked */
+    fflg = false,		/* days until account with expired password is locked */
+    gflg = false,		/* primary group ID for new account */
+    Gflg = false,		/* secondary group set for new account */
+    kflg = false,		/* specify a directory to fill new user directory */
+    lflg = false,		/* do not add user to lastlog/faillog databases */
+    mflg = false,		/* create user's home directory if it doesn't exist */
+    Mflg = false,		/* do not create user's home directory even if CREATE_HOME is set */
+    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 */
+    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 */
+
+#ifdef WITH_SELINUX
+#define Zflg ('\0' != *user_selinux)
+#endif				/* WITH_SELINUX */
+
+static bool home_added = false;
+
+/*
+ * exit status values
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_PW_UPDATE	1	/* can't update password file */
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define E_UID_IN_USE	4	/* UID already in use (and no -o) */
+#define E_NOTFOUND	6	/* specified group doesn't exist */
+#define E_NAME_IN_USE	9	/* username already in use */
+#define E_GRP_UPDATE	10	/* can't update group file */
+#define E_HOMEDIR	12	/* can't create home directory */
+#define E_SE_UPDATE	14	/* can't update SELinux user mapping */
+#ifdef ENABLE_SUBIDS
+#define E_SUB_UID_UPDATE 16	/* can't update the subordinate uid file */
+#define E_SUB_GID_UPDATE 18	/* can't update the subordinate gid file */
+#endif				/* ENABLE_SUBIDS */
+
+#define DGROUP			"GROUP="
+#define DHOME			"HOME="
+#define DSHELL			"SHELL="
+#define DINACT			"INACTIVE="
+#define DEXPIRE			"EXPIRE="
+#define DSKEL			"SKEL="
+#define DCREATE_MAIL_SPOOL	"CREATE_MAIL_SPOOL="
+
+/* local function prototypes */
+static void fail_exit (int);
+static void get_defaults (void);
+static void show_defaults (void);
+static int set_defaults (void);
+static int get_groups (char *);
+static void usage (int status);
+static void new_pwent (struct passwd *);
+
+static long scale_age (long);
+static void new_spent (struct spwd *);
+static void grp_update (void);
+
+static void process_flags (int argc, char **argv);
+static void close_files (void);
+static void open_files (void);
+static void open_shadow (void);
+static void faillog_reset (uid_t);
+static void lastlog_reset (uid_t);
+static void usr_update (void);
+static void create_home (void);
+static void create_mail (void);
+
+/*
+ * fail_exit - undo as much as possible
+ */
+static void fail_exit (int code)
+{
+	if (home_added) {
+		if (rmdir (user_home) != 0) {
+			fprintf (stderr,
+			         _("%s: %s was created, but could not be removed\n"),
+			         Prog, user_home);
+			SYSLOG ((LOG_ERR, "failed to remove %s", user_home));
+		}
+	}
+
+	if (spw_locked) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking shadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking passwd file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (gr_locked) {
+		if (gr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking group file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#ifdef	SHADOWGRP
+	if (sgr_locked) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking gshadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (sub_uid_locked) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking subordinate user file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+	if (sub_gid_locked) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking subordinate group file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_USER, Prog,
+	              "adding user",
+	              user_name, AUDIT_NO_ID,
+	              SHADOW_AUDIT_FAILURE);
+#endif
+	SYSLOG ((LOG_INFO, "failed adding user '%s', data deleted", user_name));
+	exit (code);
+}
+
+#define MATCH(x,y) (strncmp((x),(y),strlen(y)) == 0)
+
+/*
+ * get_defaults - read the defaults file
+ *
+ *	get_defaults() reads the defaults file for this command. It sets the
+ *	various values from the file, or uses built-in default values if the
+ *	file does not exist.
+ */
+static void get_defaults (void)
+{
+	FILE *fp;
+	char buf[1024];
+	char *cp;
+
+	/*
+	 * Open the defaults file for reading.
+	 */
+
+	fp = fopen (USER_DEFAULTS_FILE, "r");
+	if (NULL == fp) {
+		return;
+	}
+
+	/*
+	 * Read the file a line at a time. Only the lines that have relevant
+	 * values are used, everything else can be ignored.
+	 */
+	while (fgets (buf, (int) sizeof buf, fp) == buf) {
+		cp = strrchr (buf, '\n');
+		if (NULL != cp) {
+			*cp = '\0';
+		}
+
+		cp = strchr (buf, '=');
+		if (NULL == cp) {
+			continue;
+		}
+
+		cp++;
+
+		/*
+		 * Primary GROUP identifier
+		 */
+		if (MATCH (buf, DGROUP)) {
+			const struct group *grp = getgr_nam_gid (cp);
+			if (NULL == grp) {
+				fprintf (stderr,
+				         _("%s: group '%s' does not exist\n"),
+				         Prog, cp);
+				fprintf (stderr,
+				         _("%s: the %s configuration in %s will be ignored\n"),
+				         Prog, DGROUP, USER_DEFAULTS_FILE);
+			} else {
+				def_group = grp->gr_gid;
+				def_gname = xstrdup (grp->gr_name);
+			}
+		}
+
+		/*
+		 * Default HOME filesystem
+		 */
+		else if (MATCH (buf, DHOME)) {
+			def_home = xstrdup (cp);
+		}
+
+		/*
+		 * Default Login Shell command
+		 */
+		else if (MATCH (buf, DSHELL)) {
+			def_shell = xstrdup (cp);
+		}
+
+		/*
+		 * Default Password Inactive value
+		 */
+		else if (MATCH (buf, DINACT)) {
+			if (   (getlong (cp, &def_inactive) == 0)
+			    || (def_inactive < -1)) {
+				fprintf (stderr,
+				         _("%s: invalid numeric argument '%s'\n"),
+				         Prog, cp);
+				fprintf (stderr,
+				         _("%s: the %s configuration in %s will be ignored\n"),
+				         Prog, DINACT, USER_DEFAULTS_FILE);
+				def_inactive = -1;
+			}
+		}
+
+		/*
+		 * Default account expiration date
+		 */
+		else if (MATCH (buf, DEXPIRE)) {
+			def_expire = xstrdup (cp);
+		}
+
+		/*
+		 * Default Skeleton information
+		 */
+		else if (MATCH (buf, DSKEL)) {
+			if ('\0' == *cp) {
+				cp = SKEL_DIR;	/* XXX warning: const */
+			}
+
+			def_template = xstrdup (cp);
+		}
+
+		/*
+		 * Create by default user mail spool or not ?
+		 */
+		else if (MATCH (buf, DCREATE_MAIL_SPOOL)) {
+			if (*cp == '\0') {
+				cp = "no";	/* XXX warning: const */
+			}
+
+			def_create_mail_spool = xstrdup (cp);
+		}
+	}
+	(void) fclose (fp);
+}
+
+/*
+ * show_defaults - show the contents of the defaults file
+ *
+ *	show_defaults() displays the values that are used from the default
+ *	file and the built-in values.
+ */
+static void show_defaults (void)
+{
+	printf ("GROUP=%u\n", (unsigned int) def_group);
+	printf ("HOME=%s\n", def_home);
+	printf ("INACTIVE=%ld\n", def_inactive);
+	printf ("EXPIRE=%s\n", def_expire);
+	printf ("SHELL=%s\n", def_shell);
+	printf ("SKEL=%s\n", def_template);
+	printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool);
+}
+
+/*
+ * set_defaults - write new defaults file
+ *
+ *	set_defaults() re-writes the defaults file using the values that
+ *	are currently set. Duplicated lines are pruned, missing lines are
+ *	added, and unrecognized lines are copied as is.
+ */
+static int set_defaults (void)
+{
+	FILE *ifp;
+	FILE *ofp;
+	char buf[1024];
+	static char new_file[] = NEW_USER_FILE;
+	char *cp;
+	int ofd;
+	int wlen;
+	bool out_group = false;
+	bool out_home = false;
+	bool out_inactive = false;
+	bool out_expire = false;
+	bool out_shell = false;
+	bool out_skel = false;
+	bool out_create_mail_spool = false;
+
+	/*
+	 * Create a temporary file to copy the new output to.
+	 */
+	ofd = mkstemp (new_file);
+	if (-1 == ofd) {
+		fprintf (stderr,
+		         _("%s: cannot create new defaults file\n"),
+		         Prog);
+		return -1;
+	}
+
+	ofp = fdopen (ofd, "w");
+	if (NULL == ofp) {
+		fprintf (stderr,
+		         _("%s: cannot open new defaults file\n"),
+		         Prog);
+		return -1;
+	}
+
+	/*
+	 * Open the existing defaults file and copy the lines to the
+	 * temporary file, using any new values. Each line is checked
+	 * to insure that it is not output more than once.
+	 */
+	ifp = fopen (USER_DEFAULTS_FILE, "r");
+	if (NULL == ifp) {
+		fprintf (ofp, "# useradd defaults file\n");
+		goto skip;
+	}
+
+	while (fgets (buf, (int) sizeof buf, ifp) == buf) {
+		cp = strrchr (buf, '\n');
+		if (NULL != cp) {
+			*cp = '\0';
+		} else {
+			/* A line which does not end with \n is only valid
+			 * at the end of the file.
+			 */
+			if (feof (ifp) == 0) {
+				fprintf (stderr,
+				         _("%s: line too long in %s: %s..."),
+				         Prog, USER_DEFAULTS_FILE, buf);
+				(void) fclose (ifp);
+				return -1;
+			}
+		}
+
+		if (!out_group && MATCH (buf, DGROUP)) {
+			fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
+			out_group = true;
+		} else if (!out_home && MATCH (buf, DHOME)) {
+			fprintf (ofp, DHOME "%s\n", def_home);
+			out_home = true;
+		} else if (!out_inactive && MATCH (buf, DINACT)) {
+			fprintf (ofp, DINACT "%ld\n", def_inactive);
+			out_inactive = true;
+		} else if (!out_expire && MATCH (buf, DEXPIRE)) {
+			fprintf (ofp, DEXPIRE "%s\n", def_expire);
+			out_expire = true;
+		} else if (!out_shell && MATCH (buf, DSHELL)) {
+			fprintf (ofp, DSHELL "%s\n", def_shell);
+			out_shell = true;
+		} else if (!out_skel && MATCH (buf, DSKEL)) {
+			fprintf (ofp, DSKEL "%s\n", def_template);
+			out_skel = true;
+		} else if (!out_create_mail_spool
+			   && MATCH (buf, DCREATE_MAIL_SPOOL)) {
+			fprintf (ofp,
+			         DCREATE_MAIL_SPOOL "%s\n",
+			         def_create_mail_spool);
+			out_create_mail_spool = true;
+		} else
+			fprintf (ofp, "%s\n", buf);
+	}
+	(void) fclose (ifp);
+
+      skip:
+	/*
+	 * Check each line to insure that every line was output. This
+	 * causes new values to be added to a file which did not previously
+	 * have an entry for that value.
+	 */
+	if (!out_group)
+		fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
+	if (!out_home)
+		fprintf (ofp, DHOME "%s\n", def_home);
+	if (!out_inactive)
+		fprintf (ofp, DINACT "%ld\n", def_inactive);
+	if (!out_expire)
+		fprintf (ofp, DEXPIRE "%s\n", def_expire);
+	if (!out_shell)
+		fprintf (ofp, DSHELL "%s\n", def_shell);
+	if (!out_skel)
+		fprintf (ofp, DSKEL "%s\n", def_template);
+
+	if (!out_create_mail_spool)
+		fprintf (ofp, DCREATE_MAIL_SPOOL "%s\n", def_create_mail_spool);
+
+	/*
+	 * Flush and close the file. Check for errors to make certain
+	 * the new file is intact.
+	 */
+	(void) fflush (ofp);
+	if (   (ferror (ofp) != 0)
+	    || (fsync (fileno (ofp)) != 0)
+	    || (fclose (ofp) != 0)) {
+		unlink (new_file);
+		return -1;
+	}
+
+	/*
+	 * Rename the current default file to its backup name.
+	 */
+	wlen = snprintf (buf, sizeof buf, "%s-", USER_DEFAULTS_FILE);
+	assert (wlen < (int) sizeof buf);
+	unlink (buf);
+	if ((link (USER_DEFAULTS_FILE, buf) != 0) && (ENOENT != errno)) {
+		int err = errno;
+		fprintf (stderr,
+		         _("%s: Cannot create backup file (%s): %s\n"),
+		         Prog, buf, strerror (err));
+		unlink (new_file);
+		return -1;
+	}
+
+	/*
+	 * Rename the new default file to its correct name.
+	 */
+	if (rename (new_file, USER_DEFAULTS_FILE) != 0) {
+		int err = errno;
+		fprintf (stderr,
+		         _("%s: rename: %s: %s\n"),
+		         Prog, new_file, strerror (err));
+		return -1;
+	}
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_USYS_CONFIG, Prog,
+	              "changing useradd defaults",
+	              NULL, AUDIT_NO_ID,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	SYSLOG ((LOG_INFO,
+	         "useradd defaults: GROUP=%u, HOME=%s, SHELL=%s, INACTIVE=%ld, "
+	         "EXPIRE=%s, SKEL=%s, CREATE_MAIL_SPOOL=%s",
+	         (unsigned int) def_group, def_home, def_shell,
+	         def_inactive, def_expire, def_template,
+	         def_create_mail_spool));
+	return 0;
+}
+
+/*
+ * get_groups - convert a list of group names to an array of group IDs
+ *
+ *	get_groups() takes a comma-separated list of group names and
+ *	converts it to a NULL-terminated array. Any unknown group
+ *	names are reported as errors.
+ */
+static int get_groups (char *list)
+{
+	char *cp;
+	const struct group *grp;
+	int errors = 0;
+	int ngroups = 0;
+
+	if ('\0' == *list) {
+		return 0;
+	}
+
+	/*
+	 * So long as there is some data to be converted, strip off
+	 * each name and look it up. A mix of numerical and string
+	 * values for group identifiers is permitted.
+	 */
+	do {
+		/*
+		 * Strip off a single name from the list
+		 */
+		cp = strchr (list, ',');
+		if (NULL != cp) {
+			*cp++ = '\0';
+		}
+
+		/*
+		 * Names starting with digits are treated as numerical
+		 * GID values, otherwise the string is looked up as is.
+		 */
+		grp = getgr_nam_gid (list);
+
+		/*
+		 * There must be a match, either by GID value or by
+		 * string name.
+		 * FIXME: It should exist according to gr_locate,
+		 *        otherwise, we can't change its members
+		 */
+		if (NULL == grp) {
+			fprintf (stderr,
+			         _("%s: group '%s' does not exist\n"),
+			         Prog, list);
+			errors++;
+		}
+		list = cp;
+
+		/*
+		 * If the group doesn't exist, don't dump core...
+		 * Instead, try the next one.  --marekm
+		 */
+		if (NULL == grp) {
+			continue;
+		}
+
+#ifdef	USE_NIS
+		/*
+		 * Don't add this group if they are an NIS group. Tell
+		 * the user to go to the server for this group.
+		 */
+		if (__isgrNIS ()) {
+			fprintf (stderr,
+			         _("%s: group '%s' is a NIS group.\n"),
+			         Prog, grp->gr_name);
+			continue;
+		}
+#endif
+
+		if (ngroups == sys_ngroups) {
+			fprintf (stderr,
+			         _("%s: too many groups specified (max %d).\n"),
+			         Prog, ngroups);
+			break;
+		}
+
+		/*
+		 * Add the group name to the user's list of groups.
+		 */
+		user_groups[ngroups++] = xstrdup (grp->gr_name);
+	} while (NULL != list);
+
+	user_groups[ngroups] = (char *) 0;
+
+	/*
+	 * Any errors in finding group names are fatal
+	 */
+	if (0 != errors) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * usage - display usage message and exit
+ */
+static void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] LOGIN\n"
+	                  "       %s -D\n"
+	                  "       %s -D [options]\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog, Prog, Prog);
+	(void) fputs (_("  -b, --base-dir BASE_DIR       base directory for the home directory of the\n"
+	                "                                new account\n"), usageout);
+	(void) fputs (_("  -c, --comment COMMENT         GECOS field of the new account\n"), usageout);
+	(void) fputs (_("  -d, --home-dir HOME_DIR       home directory of the new account\n"), usageout);
+	(void) fputs (_("  -D, --defaults                print or change default useradd configuration\n"), usageout);
+	(void) fputs (_("  -e, --expiredate EXPIRE_DATE  expiration date of the new account\n"), usageout);
+	(void) fputs (_("  -f, --inactive INACTIVE       password inactivity period of the new account\n"), usageout);
+	(void) fputs (_("  -g, --gid GROUP               name or ID of the primary group of the new\n"
+	                "                                account\n"), usageout);
+	(void) fputs (_("  -G, --groups GROUPS           list of supplementary groups of the new\n"
+	                "                                account\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -k, --skel SKEL_DIR           use this alternative skeleton directory\n"), usageout);
+	(void) fputs (_("  -K, --key KEY=VALUE           override /etc/login.defs defaults\n"), usageout);
+	(void) fputs (_("  -l, --no-log-init             do not add the user to the lastlog and\n"
+	                "                                faillog databases\n"), usageout);
+	(void) fputs (_("  -m, --create-home             create the user's home directory\n"), usageout);
+	(void) fputs (_("  -M, --no-create-home          do not create the user's home directory\n"), usageout);
+	(void) fputs (_("  -N, --no-user-group           do not create a group with the same name as\n"
+	                "                                the user\n"), usageout);
+	(void) fputs (_("  -o, --non-unique              allow to create users with duplicate\n"
+	                "                                (non-unique) UID\n"), usageout);
+	(void) fputs (_("  -p, --password PASSWORD       encrypted password of the new account\n"), usageout);
+	(void) fputs (_("  -r, --system                  create a system account\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -s, --shell SHELL             login shell of the new account\n"), usageout);
+	(void) fputs (_("  -u, --uid UID                 user ID of the new account\n"), usageout);
+	(void) fputs (_("  -U, --user-group              create a group with the same name as the user\n"), usageout);
+#ifdef WITH_SELINUX
+	(void) fputs (_("  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping\n"), usageout);
+#endif				/* WITH_SELINUX */
+	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * new_pwent - initialize the values in a password file entry
+ *
+ *	new_pwent() takes all of the values that have been entered and
+ *	fills in a (struct passwd) with them.
+ */
+static void new_pwent (struct passwd *pwent)
+{
+	memzero (pwent, sizeof *pwent);
+	pwent->pw_name = (char *) user_name;
+	if (is_shadow_pwd) {
+		pwent->pw_passwd = (char *) SHADOW_PASSWD_STRING;
+	} else {
+		pwent->pw_passwd = (char *) user_pass;
+	}
+
+	pwent->pw_uid = user_id;
+	pwent->pw_gid = user_gid;
+	pwent->pw_gecos = (char *) user_comment;
+	pwent->pw_dir = (char *) user_home;
+	pwent->pw_shell = (char *) user_shell;
+}
+
+static long scale_age (long x)
+{
+	if (x <= 0) {
+		return x;
+	}
+
+	return x * (DAY / SCALE);
+}
+
+/*
+ * new_spent - initialize the values in a shadow password file entry
+ *
+ *	new_spent() takes all of the values that have been entered and
+ *	fills in a (struct spwd) with them.
+ */
+static void new_spent (struct spwd *spent)
+{
+	memzero (spent, sizeof *spent);
+	spent->sp_namp = (char *) user_name;
+	spent->sp_pwdp = (char *) user_pass;
+	spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+	if (0 == spent->sp_lstchg) {
+		/* Better disable aging than requiring a password change */
+		spent->sp_lstchg = -1;
+	}
+	if (!rflg) {
+		spent->sp_min = scale_age (getdef_num ("PASS_MIN_DAYS", -1));
+		spent->sp_max = scale_age (getdef_num ("PASS_MAX_DAYS", -1));
+		spent->sp_warn = scale_age (getdef_num ("PASS_WARN_AGE", -1));
+		spent->sp_inact = scale_age (def_inactive);
+		spent->sp_expire = scale_age (user_expire);
+	} else {
+		spent->sp_min = -1;
+		spent->sp_max = -1;
+		spent->sp_warn = -1;
+		spent->sp_inact = -1;
+		spent->sp_expire = -1;
+	}
+	spent->sp_flag = SHADOW_SP_FLAG_UNSET;
+}
+
+/*
+ * grp_update - add user to secondary group set
+ *
+ *	grp_update() takes the secondary group set given in user_groups
+ *	and adds the user to each group given by that set.
+ *
+ *	The group files are opened and locked in open_files().
+ *
+ *	close_files() should be called afterwards to commit the changes
+ *	and unlocking the group files.
+ */
+static void grp_update (void)
+{
+	const struct group *grp;
+	struct group *ngrp;
+
+#ifdef	SHADOWGRP
+	const struct sgrp *sgrp;
+	struct sgrp *nsgrp;
+#endif
+
+	/*
+	 * Scan through the entire group file looking for the groups that
+	 * the user is a member of.
+	 * FIXME: we currently do not check that all groups of user_groups
+	 *        were completed with the new user.
+	 */
+	for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) {
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		if (!is_on_list (user_groups, grp->gr_name)) {
+			continue;
+		}
+
+		/*
+		 * Make a copy - gr_update() will free() everything
+		 * from the old entry, and we need it later.
+		 */
+		ngrp = __gr_dup (grp);
+		if (NULL == ngrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);	/* XXX */
+		}
+
+		/* 
+		 * Add the username to the list of group members and
+		 * update the group entry to reflect the change.
+		 */
+		ngrp->gr_mem = add_list (ngrp->gr_mem, user_name);
+		if (gr_update (ngrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, gr_dbname (), ngrp->gr_name);
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user to group",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_SUCCESS);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "add '%s' to group '%s'",
+		         user_name, ngrp->gr_name));
+	}
+
+#ifdef	SHADOWGRP
+	if (!is_shadow_grp)
+		return;
+
+	/*
+	 * Scan through the entire shadow group file looking for the groups
+	 * that the user is a member of. The administrative list isn't
+	 * modified.
+	 */
+	for (sgr_rewind (), sgrp = sgr_next (); NULL != sgrp; sgrp = sgr_next ()) {
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 * FIXME: is it really needed?
+		 *        This would be important only if the group is in
+		 *        user_groups. All these groups should be checked
+		 *        for existence with gr_locate already.
+		 */
+		if (gr_locate (sgrp->sg_name) == NULL) {
+			continue;
+		}
+
+		if (!is_on_list (user_groups, sgrp->sg_name)) {
+			continue;
+		}
+
+		/*
+		 * Make a copy - sgr_update() will free() everything
+		 * from the old entry, and we need it later.
+		 */
+		nsgrp = __sgr_dup (sgrp);
+		if (NULL == nsgrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to shadow group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);	/* XXX */
+		}
+
+		/* 
+		 * Add the username to the list of group members and
+		 * update the group entry to reflect the change.
+		 */
+		nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_name);
+		if (sgr_update (nsgrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, sgr_dbname (), nsgrp->sg_name);
+			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user to shadow group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user to shadow group",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_SUCCESS);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "add '%s' to shadow group '%s'",
+		         user_name, nsgrp->sg_name));
+	}
+#endif				/* SHADOWGRP */
+}
+
+/*
+ * 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)
+{
+	const struct group *grp;
+	bool anyflag = false;
+	char *cp;
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"base-dir",       required_argument, NULL, 'b'},
+			{"comment",        required_argument, NULL, 'c'},
+			{"home-dir",       required_argument, NULL, 'd'},
+			{"defaults",       no_argument,       NULL, 'D'},
+			{"expiredate",     required_argument, NULL, 'e'},
+			{"inactive",       required_argument, NULL, 'f'},
+			{"gid",            required_argument, NULL, 'g'},
+			{"groups",         required_argument, NULL, 'G'},
+			{"help",           no_argument,       NULL, 'h'},
+			{"skel",           required_argument, NULL, 'k'},
+			{"key",            required_argument, NULL, 'K'},
+			{"no-log-init",    no_argument,       NULL, 'l'},
+			{"create-home",    no_argument,       NULL, 'm'},
+			{"no-create-home", no_argument,       NULL, 'M'},
+			{"no-user-group",  no_argument,       NULL, 'N'},
+			{"non-unique",     no_argument,       NULL, 'o'},
+			{"password",       required_argument, NULL, 'p'},
+			{"system",         no_argument,       NULL, 'r'},
+			{"root",           required_argument, NULL, 'R'},
+			{"shell",          required_argument, NULL, 's'},
+			{"uid",            required_argument, NULL, 'u'},
+			{"user-group",     no_argument,       NULL, 'U'},
+#ifdef WITH_SELINUX
+			{"selinux-user",   required_argument, NULL, 'Z'},
+#endif				/* WITH_SELINUX */
+			{"extrausers",     no_argument,       NULL, EXTRAUSERS_OPT},
+			{NULL, 0, NULL, '\0'}
+		};
+		while ((c = getopt_long (argc, argv,
+#ifdef WITH_SELINUX
+		                         "b:c:d:De:f:g:G:hk:O:K:lmMNop:rR:s:u:UZ:",
+#else				/* !WITH_SELINUX */
+		                         "b:c:d:De:f:g:G:hk:O:K:lmMNop:rR:s:u:U",
+#endif				/* !WITH_SELINUX */
+		                         long_options, NULL)) != -1) {
+			switch (c) {
+			case EXTRAUSERS_OPT:
+                use_extrausers = true;
+                break;
+			case 'b':
+				if (   ( !VALID (optarg) )
+				    || ( optarg[0] != '/' )) {
+					fprintf (stderr,
+					         _("%s: invalid base directory '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				def_home = optarg;
+				bflg = true;
+				break;
+			case 'c':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid comment '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_comment = optarg;
+				cflg = true;
+				break;
+			case 'd':
+				if (   ( !VALID (optarg) )
+				    || ( optarg[0] != '/' )) {
+					fprintf (stderr,
+					         _("%s: invalid home directory '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_home = optarg;
+				dflg = true;
+				break;
+			case 'D':
+				if (anyflag) {
+					usage (E_USAGE);
+				}
+				Dflg = true;
+				break;
+			case 'e':
+				if ('\0' != *optarg) {
+					user_expire = strtoday (optarg);
+					if (user_expire < -1) {
+						fprintf (stderr,
+						         _("%s: invalid date '%s'\n"),
+						         Prog, optarg);
+						exit (E_BAD_ARG);
+					}
+				} else {
+					user_expire = -1;
+				}
+
+				/*
+				 * -e "" is allowed without /etc/shadow
+				 * (it's a no-op in such case)
+				 */
+				if ((-1 != user_expire) && !is_shadow_pwd) {
+					fprintf (stderr,
+					         _("%s: shadow passwords required for -e\n"),
+					         Prog);
+					exit (E_USAGE);
+				}
+				if (Dflg) {
+					def_expire = optarg;
+				}
+				eflg = true;
+				break;
+			case 'f':
+				if (   (getlong (optarg, &def_inactive) == 0)
+				    || (def_inactive < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				/*
+				 * -f -1 is allowed
+				 * it's a no-op without /etc/shadow
+				 */
+				if ((-1 != def_inactive) && !is_shadow_pwd) {
+					fprintf (stderr,
+					         _("%s: shadow passwords required for -f\n"),
+					         Prog);
+					exit (E_USAGE);
+				}
+				fflg = true;
+				break;
+			case 'g':
+				grp = getgr_nam_gid (optarg);
+				if (NULL == grp) {
+					fprintf (stderr,
+					         _("%s: group '%s' does not exist\n"),
+					         Prog, optarg);
+					exit (E_NOTFOUND);
+				}
+				if (Dflg) {
+					def_group = grp->gr_gid;
+					def_gname = optarg;
+				} else {
+					user_gid = grp->gr_gid;
+				}
+				gflg = true;
+				break;
+			case 'G':
+				if (get_groups (optarg) != 0) {
+					exit (E_NOTFOUND);
+				}
+				if (NULL != user_groups[0]) {
+					do_grp_update = true;
+				}
+				Gflg = true;
+				break;
+			case 'h':
+				usage (E_SUCCESS);
+				break;
+			case 'k':
+				def_template = optarg;
+				kflg = true;
+				break;
+			case 'K':
+			case 'O': /* compatibility with previous Debian useradd */
+				/*
+				 * override login.defs defaults (-K name=value)
+				 * example: -K UID_MIN=100 -K UID_MAX=499
+				 * note: -K UID_MIN=10,UID_MAX=499 doesn't work yet
+				 */
+				cp = strchr (optarg, '=');
+				if (NULL == cp) {
+					fprintf (stderr,
+					         _("%s: -K requires KEY=VALUE\n"),
+					         Prog);
+					exit (E_BAD_ARG);
+				}
+				/* terminate name, point to value */
+				*cp = '\0';
+				cp++;
+				if (putdef_str (optarg, cp) < 0) {
+					exit (E_BAD_ARG);
+				}
+				break;
+			case 'l':
+				lflg = true;
+				break;
+			case 'm':
+				mflg = true;
+				break;
+			case 'M':
+				Mflg = true;
+				break;
+			case 'N':
+				Nflg = true;
+				break;
+			case 'o':
+				oflg = true;
+				break;
+			case 'p':	/* set encrypted password */
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_pass = optarg;
+				break;
+			case 'r':
+				rflg = true;
+				break;
+			case 'R': /* no-op, handled in process_root_flag () */
+				break;
+			case 's':
+				if (   ( !VALID (optarg) )
+				    || (   ('\0' != optarg[0])
+				        && ('/'  != optarg[0])
+				        && ('*'  != optarg[0]) )) {
+					fprintf (stderr,
+					         _("%s: invalid shell '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_shell = optarg;
+				def_shell = optarg;
+				sflg = true;
+				break;
+			case 'u':
+				if (   (get_uid (optarg, &user_id) == 0)
+				    || (user_id == (gid_t)-1)) {
+					fprintf (stderr,
+					         _("%s: invalid user ID '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				uflg = true;
+				break;
+			case 'U':
+				Uflg = true;
+				break;
+#ifdef WITH_SELINUX
+			case 'Z':
+				if (is_selinux_enabled () > 0) {
+					user_selinux = optarg;
+				} else {
+					fprintf (stderr,
+					         _("%s: -Z requires SELinux enabled kernel\n"),
+					         Prog);
+
+					exit (E_BAD_ARG);
+				}
+				break;
+#endif				/* WITH_SELINUX */
+			default:
+				usage (E_USAGE);
+			}
+			anyflag = true;
+		}
+	}
+
+	if (!gflg && !Nflg && !Uflg) {
+		/* Get the settings from login.defs */
+		Uflg = getdef_bool ("USERGROUPS_ENAB");
+	}
+
+	/*
+	 * Certain options are only valid in combination with others.
+	 * Check it here so that they can be specified in any order.
+	 */
+	if (oflg && !uflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-o", "-u");
+		usage (E_USAGE);
+	}
+	if (kflg && !mflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-k", "-m");
+		usage (E_USAGE);
+	}
+	if (Uflg && gflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-U", "-g");
+		usage (E_USAGE);
+	}
+	if (Uflg && Nflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-U", "-N");
+		usage (E_USAGE);
+	}
+	if (mflg && Mflg) {
+		fprintf (stderr,
+		         _("%s: options %s and %s conflict\n"),
+		         Prog, "-m", "-M");
+		usage (E_USAGE);
+	}
+
+	/*
+	 * Either -D or username is required. Defaults can be set with -D
+	 * for the -b, -e, -f, -g, -s options only.
+	 */
+	if (Dflg) {
+		if (optind != argc) {
+			usage (E_USAGE);
+		}
+
+		if (uflg || Gflg || dflg || cflg || mflg) {
+			usage (E_USAGE);
+		}
+	} else {
+		if (optind != argc - 1) {
+			usage (E_USAGE);
+		}
+
+		user_name = argv[optind];
+		if (!is_valid_user_name (user_name)) {
+			fprintf (stderr,
+			         _("%s: invalid user name '%s'\n"),
+			         Prog, user_name);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding user",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			exit (E_BAD_ARG);
+		}
+		if (!dflg) {
+			char *uh;
+			size_t len = strlen (def_home) + strlen (user_name) + 2;
+			int wlen;
+
+			uh = xmalloc (len);
+			wlen = snprintf (uh, len, "%s/%s", def_home, user_name);
+			assert (wlen == (int) len -1);
+
+			user_home = uh;
+		}
+	}
+
+	if (!eflg) {
+		user_expire = strtoday (def_expire);
+	}
+
+	if (!gflg) {
+		user_gid = def_group;
+	}
+
+	if (!sflg) {
+		user_shell = def_shell;
+	}
+
+	create_mail_spool = def_create_mail_spool;
+
+	if (!rflg) {
+		/* for system accounts defaults are ignored and we
+		 * do not create a home dir */
+		if (getdef_bool ("CREATE_HOME")) {
+			mflg = true;
+		}
+	}
+
+	if (Mflg) {
+		/* absolutely sure that we do not create home dirs */
+		mflg = false;
+	}
+}
+
+/*
+ * close_files - close all of the files that were opened
+ *
+ *	close_files() closes all of the files that were opened for this
+ *	new user. This causes any modified entries to be written out.
+ */
+static void close_files (void)
+{
+	if (pw_close () == 0) {
+		fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (do_grp_update) {
+		if (gr_close () == 0) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef	SHADOWGRP
+		if (is_shadow_grp && (sgr_close () == 0)) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"),
+			         Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#endif
+	}
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid  && (sub_uid_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ()));
+		fail_exit (E_SUB_UID_UPDATE);
+	}
+	if (is_sub_gid  && (sub_gid_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ()));
+		fail_exit (E_SUB_GID_UPDATE);
+	}
+#endif				/* ENABLE_SUBIDS */
+	if (is_shadow_pwd) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking shadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		spw_locked = false;
+	}
+	if (pw_unlock () == 0) {
+		fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "unlocking passwd file",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		/* continue */
+	}
+	pw_locked = false;
+	if (gr_unlock () == 0) {
+		fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "unlocking group file",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		/* continue */
+	}
+	gr_locked = false;
+#ifdef	SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "unlocking gshadow file",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sgr_locked = false;
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+				"unlocking subordinate user file",
+				user_name, AUDIT_NO_ID,
+				SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sub_uid_locked = false;
+	}
+	if (is_sub_gid) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+				"unlocking subordinate group file",
+				user_name, AUDIT_NO_ID,
+				SHADOW_AUDIT_FAILURE);
+#endif
+			/* continue */
+		}
+		sub_gid_locked = false;
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * open_files - lock and open the password files
+ *
+ *	open_files() opens the two password files.
+ */
+static void open_files (void)
+{
+	if (pw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, pw_dbname ());
+		exit (E_PW_UPDATE);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+
+	/* shadow file will be opened by open_shadow(); */
+
+	/*
+	 * Lock and open the group file.
+	 */
+	if (gr_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, gr_dbname ());
+		fail_exit (E_GRP_UPDATE);
+	}
+	gr_locked = true;
+	if (gr_open (O_RDWR) == 0) {
+		fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+		fail_exit (E_GRP_UPDATE);
+	}
+#ifdef  SHADOWGRP
+	if (is_shadow_grp) {
+		if (sgr_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		sgr_locked = true;
+		if (sgr_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+#endif
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (sub_uid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		sub_uid_locked = true;
+		if (sub_uid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+	}
+	if (is_sub_gid) {
+		if (sub_gid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		sub_gid_locked = true;
+		if (sub_gid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+static void open_shadow (void)
+{
+	if (!is_shadow_pwd) {
+		return;
+	}
+	if (spw_lock () == 0) {
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	spw_locked = true;
+	if (spw_open (O_RDWR) == 0) {
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+}
+
+static char *empty_list = NULL;
+
+/*
+ * new_grent - initialize the values in a group file entry
+ *
+ *      new_grent() takes all of the values that have been entered and fills
+ *      in a (struct group) with them.
+ */
+
+static void new_grent (struct group *grent)
+{
+	memzero (grent, sizeof *grent);
+	grent->gr_name = (char *) user_name;
+#ifdef  SHADOWGRP
+	if (is_shadow_grp) {
+		grent->gr_passwd = SHADOW_PASSWD_STRING;	/* XXX warning: const */
+	} else
+#endif				/* SHADOWGRP */
+	{
+		grent->gr_passwd = "!";	/* XXX warning: const */
+	}
+	grent->gr_gid = user_gid;
+	grent->gr_mem = &empty_list;
+}
+
+#ifdef  SHADOWGRP
+/*
+ * new_sgent - initialize the values in a shadow group file entry
+ *
+ *      new_sgent() takes all of the values that have been entered and fills
+ *      in a (struct sgrp) with them.
+ */
+
+static void new_sgent (struct sgrp *sgent)
+{
+	memzero (sgent, sizeof *sgent);
+	sgent->sg_name = (char *) user_name;
+	sgent->sg_passwd = "!";	/* XXX warning: const */
+	sgent->sg_adm = &empty_list;
+	sgent->sg_mem = &empty_list;
+}
+#endif				/* SHADOWGRP */
+
+
+/*
+ * grp_add - add new group file entries
+ *
+ *      grp_add() writes the new records to the group files.
+ */
+
+static void grp_add (void)
+{
+	struct group grp;
+
+#ifdef  SHADOWGRP
+	struct sgrp sgrp;
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Create the initial entries for this new group.
+	 */
+	new_grent (&grp);
+#ifdef  SHADOWGRP
+	new_sgent (&sgrp);
+#endif				/* SHADOWGRP */
+
+	/*
+	 * Write out the new group file entry.
+	 */
+	if (gr_update (&grp) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, gr_dbname (), grp.gr_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_GROUP, Prog,
+		              "adding group",
+		              grp.gr_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_GRP_UPDATE);
+	}
+#ifdef  SHADOWGRP
+	/*
+	 * Write out the new shadow group entries as well.
+	 */
+	if (is_shadow_grp && (sgr_update (&sgrp) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, sgr_dbname (), sgrp.sg_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_GROUP, Prog,
+		              "adding group",
+		              grp.gr_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_GRP_UPDATE);
+	}
+#endif				/* SHADOWGRP */
+	SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid));
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_GROUP, Prog,
+	              "adding group",
+	              grp.gr_name, AUDIT_NO_ID,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	do_grp_update = true;
+}
+
+static void faillog_reset (uid_t uid)
+{
+	struct faillog fl;
+	int fd;
+	off_t offset_uid = (off_t) (sizeof fl) * uid;
+
+	if (access (FAILLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	memzero (&fl, sizeof (fl));
+
+	fd = open (FAILLOG_FILE, O_RDWR);
+	if (   (-1 == fd)
+	    || (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
+	    || (write (fd, &fl, sizeof (fl)) != (ssize_t) sizeof (fl))
+	    || (fsync (fd) != 0)
+	    || (close (fd) != 0)) {
+		fprintf (stderr,
+		         _("%s: failed to reset the faillog entry of UID %lu: %s\n"),
+		         Prog, (unsigned long) uid, strerror (errno));
+		SYSLOG ((LOG_WARN, "failed to reset the faillog entry of UID %lu", (unsigned long) uid));
+		/* continue */
+	}
+}
+
+static void lastlog_reset (uid_t uid)
+{
+	struct lastlog ll;
+	int fd;
+	off_t offset_uid = (off_t) (sizeof ll) * uid;
+
+	if (access (LASTLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	memzero (&ll, sizeof (ll));
+
+	fd = open (LASTLOG_FILE, O_RDWR);
+	if (   (-1 == fd)
+	    || (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
+	    || (write (fd, &ll, sizeof (ll)) != (ssize_t) sizeof (ll))
+	    || (fsync (fd) != 0)
+	    || (close (fd) != 0)) {
+		fprintf (stderr,
+		         _("%s: failed to reset the lastlog entry of UID %lu: %s\n"),
+		         Prog, (unsigned long) uid, strerror (errno));
+		SYSLOG ((LOG_WARN, "failed to reset the lastlog entry of UID %lu", (unsigned long) uid));
+		/* continue */
+	}
+}
+
+/*
+ * usr_update - create the user entries
+ *
+ *	usr_update() creates the password file entries for this user
+ *	and will update the group entries if required.
+ */
+static void usr_update (void)
+{
+	struct passwd pwent;
+	struct spwd spent;
+
+	/*
+	 * Fill in the password structure with any new fields, making
+	 * copies of strings.
+	 */
+	new_pwent (&pwent);
+	new_spent (&spent);
+
+	/*
+	 * Create a syslog entry. We need to do this now in case anything
+	 * happens so we know what we were trying to accomplish.
+	 */
+	SYSLOG ((LOG_INFO,
+	         "new user: name=%s, UID=%u, GID=%u, home=%s, shell=%s",
+	         user_name, (unsigned int) user_id,
+	         (unsigned int) user_gid, user_home, user_shell));
+
+	/*
+	 * Initialize faillog and lastlog entries for this UID in case
+	 * it belongs to a previously deleted user. We do it only if
+	 * 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)) {
+		faillog_reset (user_id);
+		lastlog_reset (user_id);
+	}
+
+	/*
+	 * Put the new (struct passwd) in the table.
+	 */
+	if (pw_update (&pwent) == 0) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, pw_dbname (), pwent.pw_name);
+		fail_exit (E_PW_UPDATE);
+	}
+
+	/*
+	 * Put the new (struct spwd) in the table.
+	 */
+	if (is_shadow_pwd && (spw_update (&spent) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry '%s'\n"),
+		         Prog, spw_dbname (), spent.sp_namp);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding shadow password",
+		              user_name, (unsigned int) user_id,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_PW_UPDATE);
+	}
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid &&
+	    (sub_uid_add(user_name, sub_uid_start, sub_uid_count) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry\n"),
+		         Prog, sub_uid_dbname ());
+		fail_exit (E_SUB_UID_UPDATE);
+	}
+	if (is_sub_gid &&
+	    (sub_gid_add(user_name, sub_gid_start, sub_gid_count) == 0)) {
+		fprintf (stderr,
+		         _("%s: failed to prepare the new %s entry\n"),
+		         Prog, sub_uid_dbname ());
+		fail_exit (E_SUB_GID_UPDATE);
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_ADD_USER, Prog,
+	              "adding user",
+	              user_name, (unsigned int) user_id,
+	              SHADOW_AUDIT_SUCCESS);
+#endif
+	/*
+	 * Do any group file updates for this user.
+	 */
+	if (do_grp_update) {
+		grp_update ();
+	}
+}
+
+/*
+ * create_home - create the user's home directory
+ *
+ *	create_home() creates the user's home directory if it does not
+ *	already exist. It will be created mode 755 owned by the user
+ *	with the user's default group.
+ */
+static void create_home (void)
+{
+	if (access (user_home, F_OK) != 0) {
+#ifdef WITH_SELINUX
+		if (set_selinux_file_context (user_home) != 0) {
+			fail_exit (E_HOMEDIR);
+		}
+#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;
+#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 */
+		if (reset_selinux_file_context () != 0) {
+			fail_exit (E_HOMEDIR);
+		}
+#endif
+	}
+}
+
+/*
+ * create_mail - create the user's mail spool
+ *
+ *	create_mail() creates the user's mail spool if it does not already
+ *	exist. It will be created mode 660 owned by the user and group
+ *	'mail'
+ */
+static void create_mail (void)
+{
+	if (strcasecmp (create_mail_spool, "yes") == 0) {
+		const char *spool;
+		char *file;
+		int fd;
+		struct group *gr;
+		gid_t gid;
+		mode_t mode;
+
+		spool = getdef_str ("MAIL_DIR");
+		if (NULL == spool) {
+			spool = "/var/mail";
+		}
+		file = alloca (strlen (spool) + strlen (user_name) + 2);
+		sprintf (file, "%s/%s", spool, user_name);
+		fd = open (file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0);
+		if (fd < 0) {
+			perror (_("Creating mailbox file"));
+			return;
+		}
+
+		gr = getgrnam ("mail"); /* local, no need for xgetgrnam */
+		if (NULL == gr) {
+			fputs (_("Group 'mail' not found. Creating the user mailbox file with 0600 mode.\n"),
+			       stderr);
+			gid = user_gid;
+			mode = 0600;
+		} else {
+			gid = gr->gr_gid;
+			mode = 0660;
+		}
+
+		if (   (fchown (fd, user_id, gid) != 0)
+		    || (fchmod (fd, mode) != 0)) {
+			perror (_("Setting mailbox file permissions"));
+		}
+
+		fsync (fd);
+		close (fd);
+	}
+}
+
+/*
+ * main - useradd command
+ */
+int main (int argc, char **argv)
+{
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/* Needed for userns check */
+	uid_t uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL);
+	uid_t uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL);
+
+	/*
+	 * Get my name so that I can use it to report errors.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	OPENLOG ("useradd");
+#ifdef WITH_AUDIT
+	audit_help_open ();
+#endif
+
+	sys_ngroups = sysconf (_SC_NGROUPS_MAX);
+	user_groups = (char **) xmalloc ((1 + sys_ngroups) * sizeof (char *));
+	/*
+	 * Initialize the list to be empty
+	 */
+	user_groups[0] = (char *) 0;
+
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+#ifdef ENABLE_SUBIDS
+	is_sub_uid = sub_uid_file_present () && !rflg &&
+	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
+	is_sub_gid = sub_gid_file_present () && !rflg &&
+	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
+#endif				/* ENABLE_SUBIDS */
+
+	get_defaults ();
+
+	process_flags (argc, argv);
+
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	{
+		struct passwd *pampw;
+		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+		if (pampw == NULL) {
+			fprintf (stderr,
+			         _("%s: Cannot determine your user name.\n"),
+			         Prog);
+			fail_exit (1);
+		}
+
+		retval = pam_start ("useradd", pampw->pw_name, &conv, &pamh);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		fail_exit (1);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/*
+	 * See if we are messing with the defaults file, or creating
+	 * a new user.
+	 */
+	if (Dflg) {
+		if (gflg || bflg || fflg || eflg || sflg) {
+			exit ((set_defaults () != 0) ? 1 : 0);
+		}
+
+		show_defaults ();
+		exit (E_SUCCESS);
+	}
+
+	/*
+	 * Start with a quick check to see if the user exists.
+	 */
+	if (getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */
+		fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_ADD_USER, Prog,
+		              "adding user",
+		              user_name, AUDIT_NO_ID,
+		              SHADOW_AUDIT_FAILURE);
+#endif
+		fail_exit (E_NAME_IN_USE);
+	}
+
+	/*
+	 * Don't blindly overwrite a group when a user is added...
+	 * If you already have a group username, and want to add the user
+	 * to that group, use useradd -g username username.
+	 * --bero
+	 */
+	if (Uflg) {
+		/* local, no need for xgetgrnam */
+		if (getgrnam (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);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding group",
+			              user_name, AUDIT_NO_ID,
+			              SHADOW_AUDIT_FAILURE);
+#endif
+			fail_exit (E_NAME_IN_USE);
+		}
+	}
+
+    if (use_extrausers) {
+        pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+        spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+        gr_setdbname (EXTRAUSERS_GROUP_FILE);
+        /* TODO expose this information in other tools */
+        sub_uid_setdbname(EXTRAUSERS_SUBUID_FILE);
+        sub_gid_setdbname(EXTRAUSERS_SUBGID_FILE);
+#ifdef SHADOWGRP
+        sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
+#endif
+    }
+
+	/*
+	 * Do the hard stuff:
+	 * - open the files,
+	 * - create the user entries,
+	 * - create the home directory,
+	 * - create user mail spool,
+	 * - flush nscd caches for passwd and group services,
+	 * - then close and update the files.
+	 */
+	open_files ();
+
+	if (!oflg) {
+		/* first, seek for a valid uid to use for this user.
+		 * We do this because later we can use the uid we found as
+		 * gid too ... --gafton */
+		if (!uflg) {
+			if (find_new_uid (rflg, &user_id, NULL) < 0) {
+				fprintf (stderr, _("%s: can't create user\n"), Prog);
+				fail_exit (E_UID_IN_USE);
+			}
+		} else {
+			if (getpwuid (user_id) != NULL) {
+				fprintf (stderr,
+				         _("%s: UID %lu is not unique\n"),
+				         Prog, (unsigned long) user_id);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_ADD_USER, Prog,
+				              "adding user",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif
+				fail_exit (E_UID_IN_USE);
+			}
+		}
+	}
+
+#ifdef WITH_TCB
+	if (getdef_bool ("USE_TCB")) {
+		if (shadowtcb_create (user_name, user_id) == SHADOWTCB_FAILURE) {
+			fprintf (stderr,
+			         _("%s: Failed to create tcb directory for %s\n"),
+			         Prog, user_name);
+			fail_exit (E_UID_IN_USE);
+		}
+	}
+#endif
+	open_shadow ();
+
+	/* do we have to add a group for that user? This is why we need to
+	 * open the group files in the open_files() function  --gafton */
+	if (Uflg) {
+		if (find_new_gid (rflg, &user_gid, &user_id) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create group\n"),
+			         Prog);
+			fail_exit (4);
+		}
+		grp_add ();
+	}
+
+#ifdef ENABLE_SUBIDS
+	if (is_sub_uid) {
+		if (find_new_sub_uids(user_name, &sub_uid_start, &sub_uid_count) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create subordinate user IDs\n"),
+			         Prog);
+			fail_exit(E_SUB_UID_UPDATE);
+		}
+	}
+	if (is_sub_gid) {
+		if (find_new_sub_gids(user_name, &sub_gid_start, &sub_gid_count) < 0) {
+			fprintf (stderr,
+			         _("%s: can't create subordinate group IDs\n"),
+			         Prog);
+			fail_exit(E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+	usr_update ();
+
+	if (mflg) {
+		create_home ();
+		if (home_added) {
+			copy_tree (def_template, user_home, false, false,
+			           (uid_t)-1, user_id, (gid_t)-1, user_gid);
+		} else {
+			fprintf (stderr,
+			         _("%s: warning: the home directory already exists.\n"
+			           "Not copying any file from skel directory into it.\n"),
+			         Prog);
+		}
+
+	}
+
+	/* Do not create mail directory for system accounts */
+	if (!rflg) {
+		create_mail ();
+	}
+
+	close_files ();
+
+#ifdef WITH_SELINUX
+	if (Zflg) {
+		if (set_seuser (user_name, user_selinux) != 0) {
+			fprintf (stderr,
+			         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+			         Prog, user_name, user_selinux);
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_ADD_USER, Prog,
+			              "adding SELinux user mapping",
+			              user_name, (unsigned int) user_id, 0);
+#endif				/* WITH_AUDIT */
+			fail_exit (E_SE_UPDATE);
+		}
+	}
+#endif				/* WITH_SELINUX */
+
+	nscd_flush_cache ("passwd");
+	nscd_flush_cache ("group");
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/.pc/applied-patches 1:4.2-3.2ubuntu1/.pc/applied-patches
--- 1:4.2-3.2/.pc/applied-patches	2016-09-20 17:11:18.253062282 +0000
+++ 1:4.2-3.2ubuntu1/.pc/applied-patches	2016-09-20 17:11:18.777076640 +0000
@@ -12,5 +12,10 @@
 508_nologin_in_usr_sbin
 505_useradd_recommend_adduser
 1000_configure_userns
+1010_extrausers.patch
 1010_vietnamese_translation
 1020_fix_user_busy_errors
+userns/subuids-nonlocal-users
+1011_extrausers_toggle.patch
+1021_no_subuids_for_system_users.patch
+1012_extrausers_chfn.patch
diff -pruN 1:4.2-3.2/.pc/userns/subuids-nonlocal-users/src/usermod.c 1:4.2-3.2ubuntu1/.pc/userns/subuids-nonlocal-users/src/usermod.c
--- 1:4.2-3.2/.pc/userns/subuids-nonlocal-users/src/usermod.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.2-3.2ubuntu1/.pc/userns/subuids-nonlocal-users/src/usermod.c	2016-09-20 17:11:18.000000000 +0000
@@ -0,0 +1,2320 @@
+/*
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz KÅ‚oczko
+ * Copyright (c) 2007 - 2011, 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.
+ */
+
+#include <config.h>
+
+#ident "$Id$"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <lastlog.h>
+#include <pwd.h>
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+#include "pam_defs.h"
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include "chkname.h"
+#include "defines.h"
+#include "faillog.h"
+#include "getdef.h"
+#include "groupio.h"
+#include "nscd.h"
+#include "prototypes.h"
+#include "pwauth.h"
+#include "pwio.h"
+#ifdef	SHADOWGRP
+#include "sgroupio.h"
+#endif
+#include "shadowio.h"
+#ifdef ENABLE_SUBIDS
+#include "subordinateio.h"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
+
+/*
+ * exit status values
+ * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
+ * will be implemented (as documented in the Solaris 2.x man page).
+ */
+/*@-exitarg@*/
+#define E_SUCCESS	0	/* success */
+#define E_PW_UPDATE	1	/* can't update password file */
+#define E_USAGE		2	/* invalid command syntax */
+#define E_BAD_ARG	3	/* invalid argument to option */
+#define E_UID_IN_USE	4	/* UID already in use (and no -o) */
+/* #define E_BAD_PWFILE	5	   passwd file contains errors */
+#define E_NOTFOUND	6	/* specified user/group doesn't exist */
+#define E_USER_BUSY	8	/* user to modify is logged in */
+#define E_NAME_IN_USE	9	/* username already in use */
+#define E_GRP_UPDATE	10	/* can't update group file */
+/* #define E_NOSPACE	11	   insufficient space to move home dir */
+#define E_HOMEDIR	12	/* unable to complete home dir move */
+#define E_SE_UPDATE	13	/* can't update SELinux user mapping */
+#ifdef ENABLE_SUBIDS
+#define E_SUB_UID_UPDATE 16	/* can't update the subordinate uid file */
+#define E_SUB_GID_UPDATE 18	/* can't update the subordinate gid file */
+#endif				/* ENABLE_SUBIDS */
+
+#define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
+
+/*
+ * Global variables
+ */
+const char *Prog;
+
+static char *user_name;
+static char *user_newname;
+static char *user_pass;
+static uid_t user_id;
+static uid_t user_newid;
+static gid_t user_gid;
+static gid_t user_newgid;
+static char *user_comment;
+static char *user_newcomment;
+static char *user_home;
+static char *user_newhome;
+static char *user_shell;
+#ifdef WITH_SELINUX
+static const char *user_selinux = "";
+#endif				/* WITH_SELINUX */
+static char *user_newshell;
+static long user_expire;
+static long user_newexpire;
+static long user_inactive;
+static long user_newinactive;
+static long sys_ngroups;
+static char **user_groups;	/* NULL-terminated list */
+
+static bool
+    aflg = false,		/* append to existing secondary group set */
+    cflg = false,		/* new comment (GECOS) field */
+    dflg = false,		/* new home directory */
+    eflg = false,		/* days since 1970-01-01 when account becomes expired */
+    fflg = false,		/* days until account with expired password is locked */
+    gflg = false,		/* new primary group ID */
+    Gflg = false,		/* new secondary group set */
+    Lflg = false,		/* lock the password */
+    lflg = false,		/* new user name */
+    mflg = false,		/* create user's home directory if it doesn't exist */
+    oflg = false,		/* permit non-unique user ID to be specified with -u */
+    pflg = false,		/* new encrypted password */
+    sflg = false,		/* new shell program */
+#ifdef WITH_SELINUX
+    Zflg = false,		/* new selinux user */
+#endif
+#ifdef ENABLE_SUBIDS
+    vflg = false,		/*    add subordinate uids */
+    Vflg = false,		/* delete subordinate uids */
+    wflg = false,		/*    add subordinate gids */
+    Wflg = false,		/* delete subordinate gids */
+#endif				/* ENABLE_SUBIDS */
+    uflg = false,		/* specify new user ID */
+    Uflg = false;		/* unlock the password */
+
+static bool is_shadow_pwd;
+
+#ifdef SHADOWGRP
+static bool is_shadow_grp;
+#endif
+
+#ifdef ENABLE_SUBIDS
+static bool is_sub_uid = false;
+static bool is_sub_gid = false;
+#endif				/* ENABLE_SUBIDS */
+
+static bool pw_locked  = false;
+static bool spw_locked = false;
+static bool gr_locked  = false;
+#ifdef SHADOWGRP
+static bool sgr_locked = false;
+#endif
+#ifdef ENABLE_SUBIDS
+static bool sub_uid_locked = false;
+static bool sub_gid_locked = false;
+#endif				/* ENABLE_SUBIDS */
+
+
+/* local function prototypes */
+static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
+                         long int date);
+static int get_groups (char *);
+static /*@noreturn@*/void usage (int status);
+static void new_pwent (struct passwd *);
+static void new_spent (struct spwd *);
+static /*@noreturn@*/void fail_exit (int);
+static void update_group (void);
+
+#ifdef SHADOWGRP
+static void update_gshadow (void);
+#endif
+static void grp_update (void);
+
+static void process_flags (int, char **);
+static void close_files (void);
+static void open_files (void);
+static void usr_update (void);
+static void move_home (void);
+static void update_lastlog (void);
+static void update_faillog (void);
+
+#ifndef NO_MOVE_MAILBOX
+static void move_mailbox (void);
+#endif
+
+static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
+                         long int date)
+{
+	struct tm *tp;
+
+	if (date < 0) {
+		strncpy (buf, "never", maxsize);
+	} else {
+		time_t t = (time_t) date;
+		tp = gmtime (&t);
+#ifdef HAVE_STRFTIME
+		strftime (buf, maxsize, "%Y-%m-%d", tp);
+#else
+		(void) snprintf (buf, maxsize, "%04d-%02d-%02d",
+		                 tp->tm_year + 1900,
+		                 tp->tm_mon + 1,
+		                 tp->tm_mday);
+#endif				/* HAVE_STRFTIME */
+	}
+	buf[maxsize - 1] = '\0';
+}
+
+/*
+ * get_groups - convert a list of group names to an array of group IDs
+ *
+ *	get_groups() takes a comma-separated list of group names and
+ *	converts it to a NULL-terminated array. Any unknown group names are
+ *	reported as errors.
+ */
+static int get_groups (char *list)
+{
+	char *cp;
+	const struct group *grp;
+	int errors = 0;
+	int ngroups = 0;
+
+	/*
+	 * Initialize the list to be empty
+	 */
+	user_groups[0] = (char *) 0;
+
+	if ('\0' == *list) {
+		return 0;
+	}
+
+	/*
+	 * So long as there is some data to be converted, strip off each
+	 * name and look it up. A mix of numerical and string values for
+	 * group identifiers is permitted.
+	 */
+	do {
+		/*
+		 * Strip off a single name from the list
+		 */
+		cp = strchr (list, ',');
+		if (NULL != cp) {
+			*cp = '\0';
+			cp++;
+		}
+
+		/*
+		 * Names starting with digits are treated as numerical GID
+		 * values, otherwise the string is looked up as is.
+		 */
+		grp = getgr_nam_gid (list);
+
+		/*
+		 * There must be a match, either by GID value or by
+		 * string name.
+		 */
+		if (NULL == grp) {
+			fprintf (stderr, _("%s: group '%s' does not exist\n"),
+			         Prog, list);
+			errors++;
+		}
+		list = cp;
+
+		/*
+		 * If the group doesn't exist, don't dump core. Instead,
+		 * try the next one.  --marekm
+		 */
+		if (NULL == grp) {
+			continue;
+		}
+
+#ifdef	USE_NIS
+		/*
+		 * Don't add this group if they are an NIS group. Tell the
+		 * user to go to the server for this group.
+		 */
+		if (__isgrNIS ()) {
+			fprintf (stderr,
+			         _("%s: group '%s' is a NIS group.\n"),
+			         Prog, grp->gr_name);
+			gr_free ((struct group *)grp);
+			continue;
+		}
+#endif
+
+		if (ngroups == sys_ngroups) {
+			fprintf (stderr,
+			         _("%s: too many groups specified (max %d).\n"),
+			         Prog, ngroups);
+			gr_free ((struct group *)grp);
+			break;
+		}
+
+		/*
+		 * Add the group name to the user's list of groups.
+		 */
+		user_groups[ngroups++] = xstrdup (grp->gr_name);
+		gr_free ((struct group *)grp);
+	} while (NULL != list);
+
+	user_groups[ngroups] = (char *) 0;
+
+	/*
+	 * Any errors in finding group names are fatal
+	 */
+	if (0 != errors) {
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef ENABLE_SUBIDS
+struct ulong_range
+{
+	unsigned long first;
+	unsigned long last;
+};
+
+static struct ulong_range getulong_range(const char *str)
+{
+	struct ulong_range result = { .first = ULONG_MAX, .last = 0 };
+	long long first, last;
+	char *pos;
+
+	errno = 0;
+	first = strtoll(str, &pos, 10);
+	if (('\0' == *str) || ('-' != *pos ) || (ERANGE == errno) ||
+	    (first != (unsigned long int)first))
+		goto out;
+
+	errno = 0;
+	last = strtoll(pos + 1, &pos, 10);
+	if (('\0' != *pos ) || (ERANGE == errno) ||
+	    (last != (unsigned long int)last))
+		goto out;
+
+	if (first > last)
+		goto out;
+
+	result.first = (unsigned long int)first;
+	result.last = (unsigned long int)last;
+out:
+	return result;
+	
+}
+
+struct ulong_range_list_entry {
+	struct ulong_range_list_entry *next;
+	struct ulong_range range;
+};
+
+static struct ulong_range_list_entry *add_sub_uids = NULL, *del_sub_uids = NULL;
+static struct ulong_range_list_entry *add_sub_gids = NULL, *del_sub_gids = NULL;
+
+static int prepend_range(const char *str, struct ulong_range_list_entry **head)
+{
+	struct ulong_range range;
+	struct ulong_range_list_entry *entry;
+	range = getulong_range(str);
+	if (range.first > range.last)
+		return 0;
+
+	entry = malloc(sizeof(*entry));
+	if (!entry) {
+		fprintf (stderr,
+			_("%s: failed to allocate memory: %s\n"),
+			Prog, strerror (errno));
+		return 0;
+	}
+	entry->next = *head;
+	entry->range = range;
+	*head = entry;
+	return 1;
+}
+#endif				/* ENABLE_SUBIDS */
+
+/*
+ * usage - display usage message and exit
+ */
+static /*@noreturn@*/void usage (int status)
+{
+	FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+	(void) fprintf (usageout,
+	                _("Usage: %s [options] LOGIN\n"
+	                  "\n"
+	                  "Options:\n"),
+	                Prog);
+	(void) fputs (_("  -c, --comment COMMENT         new value of the GECOS field\n"), usageout);
+	(void) fputs (_("  -d, --home HOME_DIR           new home directory for the user account\n"), usageout);
+	(void) fputs (_("  -e, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"), usageout);
+	(void) fputs (_("  -f, --inactive INACTIVE       set password inactive after expiration\n"
+	                "                                to INACTIVE\n"), usageout);
+	(void) fputs (_("  -g, --gid GROUP               force use GROUP as new primary group\n"), usageout);
+	(void) fputs (_("  -G, --groups GROUPS           new list of supplementary GROUPS\n"), usageout);
+	(void) fputs (_("  -a, --append                  append the user to the supplemental GROUPS\n"
+	                "                                mentioned by the -G option without removing\n"
+	                "                                him/her from other groups\n"), usageout);
+	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+	(void) fputs (_("  -l, --login NEW_LOGIN         new value of the login name\n"), usageout);
+	(void) fputs (_("  -L, --lock                    lock the user account\n"), usageout);
+	(void) fputs (_("  -m, --move-home               move contents of the home directory to the\n"
+	                "                                new location (use only with -d)\n"), usageout);
+	(void) fputs (_("  -o, --non-unique              allow using duplicate (non-unique) UID\n"), usageout);
+	(void) fputs (_("  -p, --password PASSWORD       use encrypted password for the new password\n"), usageout);
+	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("  -s, --shell SHELL             new login shell for the user account\n"), usageout);
+	(void) fputs (_("  -u, --uid UID                 new UID for the user account\n"), usageout);
+	(void) fputs (_("  -U, --unlock                  unlock the user account\n"), usageout);
+#ifdef ENABLE_SUBIDS
+	(void) fputs (_("  -v, --add-subuids FIRST-LAST  add range of subordinate uids\n"), usageout);
+	(void) fputs (_("  -V, --del-subuids FIRST-LAST  remove range of subordinate uids\n"), usageout);
+	(void) fputs (_("  -w, --add-subgids FIRST-LAST  add range of subordinate gids\n"), usageout);
+	(void) fputs (_("  -W, --del-subgids FIRST-LAST  remove range of subordinate gids\n"), usageout);
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+	(void) fputs (_("  -Z, --selinux-user SEUSER     new SELinux user mapping for the user account\n"), usageout);
+#endif				/* WITH_SELINUX */
+	(void) fputs ("\n", usageout);
+	exit (status);
+}
+
+/*
+ * update encrypted password string (for both shadow and non-shadow
+ * passwords)
+ */
+static char *new_pw_passwd (char *pw_pass)
+{
+	if (Lflg && ('!' != pw_pass[0])) {
+		char *buf = xmalloc (strlen (pw_pass) + 2);
+
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "updating passwd",
+		              user_newname, (unsigned int) user_newid, 0);
+#endif
+		SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
+		strcpy (buf, "!");
+		strcat (buf, pw_pass);
+		pw_pass = buf;
+	} else if (Uflg && pw_pass[0] == '!') {
+		char *s;
+
+		if (pw_pass[1] == '\0') {
+			fprintf (stderr,
+			         _("%s: unlocking the user's password would result in a passwordless account.\n"
+			           "You should set a password with usermod -p to unlock this user's password.\n"),
+			         Prog);
+			return pw_pass;
+		}
+
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "updating password",
+		              user_newname, (unsigned int) user_newid, 0);
+#endif
+		SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
+		s = pw_pass;
+		while ('\0' != *s) {
+			*s = *(s + 1);
+			s++;
+		}
+	} else if (pflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing password",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
+		pw_pass = xstrdup (user_pass);
+	}
+	return pw_pass;
+}
+
+/*
+ * new_pwent - initialize the values in a password file entry
+ *
+ *	new_pwent() takes all of the values that have been entered and fills
+ *	in a (struct passwd) with them.
+ */
+static void new_pwent (struct passwd *pwent)
+{
+	if (lflg) {
+		if (pw_locate (user_newname) != NULL) {
+			/* This should never happen.
+			 * It was already checked that the user doesn't
+			 * exist on the system.
+			 */
+			fprintf (stderr,
+			         _("%s: user '%s' already exists in %s\n"),
+			         Prog, user_newname, pw_dbname ());
+			fail_exit (E_NAME_IN_USE);
+		}
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing name",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user name '%s' to '%s'",
+		         pwent->pw_name, user_newname));
+		pwent->pw_name = xstrdup (user_newname);
+	}
+	/* Update the password in passwd if there is no shadow file or if
+	 * the password is currently in passwd (pw_passwd != "x").
+	 * We do not force the usage of shadow passwords if they are not
+	 * used for this account.
+	 */
+	if (   (!is_shadow_pwd)
+	    || (strcmp (pwent->pw_passwd, SHADOW_PASSWD_STRING) != 0)) {
+		pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd);
+	}
+
+	if (uflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing uid",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' UID from '%d' to '%d'",
+		         pwent->pw_name, pwent->pw_uid, user_newid));
+		pwent->pw_uid = user_newid;
+	}
+	if (gflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing primary group",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' GID from '%d' to '%d'",
+		         pwent->pw_name, pwent->pw_gid, user_newgid));
+		pwent->pw_gid = user_newgid;
+	}
+	if (cflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing comment",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		pwent->pw_gecos = user_newcomment;
+	}
+
+	if (dflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing home directory",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' home from '%s' to '%s'",
+		         pwent->pw_name, pwent->pw_dir, user_newhome));
+		pwent->pw_dir = user_newhome;
+	}
+	if (sflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing user shell",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' shell from '%s' to '%s'",
+		         pwent->pw_name, pwent->pw_shell, user_newshell));
+		pwent->pw_shell = user_newshell;
+	}
+}
+
+/*
+ * new_spent - initialize the values in a shadow password file entry
+ *
+ *	new_spent() takes all of the values that have been entered and fills
+ *	in a (struct spwd) with them.
+ */
+static void new_spent (struct spwd *spent)
+{
+	if (lflg) {
+		if (spw_locate (user_newname) != NULL) {
+			fprintf (stderr,
+			         _("%s: user '%s' already exists in %s\n"),
+			         Prog, user_newname, spw_dbname ());
+			fail_exit (E_NAME_IN_USE);
+		}
+		spent->sp_namp = xstrdup (user_newname);
+	}
+
+	if (fflg) {
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing inactive days",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' inactive from '%ld' to '%ld'",
+		         spent->sp_namp, spent->sp_inact, user_newinactive));
+		spent->sp_inact = user_newinactive;
+	}
+	if (eflg) {
+		/* log dates rather than numbers of days. */
+		char new_exp[16], old_exp[16];
+		date_to_str (new_exp, sizeof(new_exp),
+		             user_newexpire * DAY);
+		date_to_str (old_exp, sizeof(old_exp),
+		             user_expire * DAY);
+#ifdef WITH_AUDIT
+		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+		              "changing expiration date",
+		              user_newname, (unsigned int) user_newid, 1);
+#endif
+		SYSLOG ((LOG_INFO,
+		         "change user '%s' expiration from '%s' to '%s'",
+		         spent->sp_namp, old_exp, new_exp));
+		spent->sp_expire = user_newexpire;
+	}
+
+	/* Always update the shadowed password if there is a shadow entry
+	 * (even if shadowed passwords might not be enabled for this
+	 * account (pw_passwd != "x")).
+	 * It seems better to update the password in both places in case a
+	 * shadow and a non shadow entry exist.
+	 * This might occur if:
+	 *  + there were already both entries
+	 *  + aging has been requested
+	 */
+	spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
+
+	if (pflg) {
+		spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+		if (0 == spent->sp_lstchg) {
+			/* Better disable aging than requiring a password
+			 * change. */
+			spent->sp_lstchg = -1;
+		}
+	}
+}
+
+/*
+ * fail_exit - exit with an error code after unlocking files
+ */
+static /*@noreturn@*/void fail_exit (int code)
+{
+	if (gr_locked) {
+		if (gr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+			/* continue */
+		}
+	}
+#ifdef	SHADOWGRP
+	if (sgr_locked) {
+		if (sgr_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+			/* continue */
+		}
+	}
+#endif
+	if (spw_locked) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+			/* continue */
+		}
+	}
+	if (pw_locked) {
+		if (pw_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+			/* continue */
+		}
+	}
+#ifdef ENABLE_SUBIDS
+	if (sub_uid_locked) {
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+			/* continue */
+		}
+	}
+	if (sub_gid_locked) {
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+			/* continue */
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+
+#ifdef WITH_AUDIT
+	audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+	              "modifying account",
+	              user_name, AUDIT_NO_ID, 0);
+#endif
+	exit (code);
+}
+
+
+static void update_group (void)
+{
+	bool is_member;
+	bool was_member;
+	bool changed;
+	const struct group *grp;
+	struct group *ngrp;
+
+	changed = false;
+
+	/*
+	 * Scan through the entire group file looking for the groups that
+	 * the user is a member of.
+	 */
+	while ((grp = gr_next ()) != NULL) {
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		was_member = is_on_list (grp->gr_mem, user_name);
+		is_member = Gflg && (   (was_member && aflg)
+		                     || is_on_list (user_groups, grp->gr_name));
+
+		if (!was_member && !is_member) {
+			continue;
+		}
+
+		ngrp = __gr_dup (grp);
+		if (NULL == ngrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+
+		if (was_member) {
+			if ((!Gflg) || is_member) {
+				/* User was a member and is still a member
+				 * of this group.
+				 * But the user might have been renamed.
+				 */
+				if (lflg) {
+					ngrp->gr_mem = del_list (ngrp->gr_mem,
+					                         user_name);
+					ngrp->gr_mem = add_list (ngrp->gr_mem,
+					                         user_newname);
+					changed = true;
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+					              "changing group member",
+					              user_newname, AUDIT_NO_ID, 1);
+#endif
+					SYSLOG ((LOG_INFO,
+					         "change '%s' to '%s' in group '%s'",
+					         user_name, user_newname,
+					         ngrp->gr_name));
+				}
+			} else {
+				/* User was a member but is no more a
+				 * member of this group.
+				 */
+				ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
+				changed = true;
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "removing group member",
+				              user_name, AUDIT_NO_ID, 1);
+#endif
+				SYSLOG ((LOG_INFO,
+				         "delete '%s' from group '%s'",
+				         user_name, ngrp->gr_name));
+			}
+		} else {
+			/* User was not a member but is now a member this
+			 * group.
+			 */
+			ngrp->gr_mem = add_list (ngrp->gr_mem, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "adding user to group",
+			              user_name, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
+			         user_newname, ngrp->gr_name));
+		}
+		if (!changed) {
+			continue;
+		}
+
+		changed = false;
+		if (gr_update (ngrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, gr_dbname (), ngrp->gr_name);
+			SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name));
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+}
+
+#ifdef SHADOWGRP
+static void update_gshadow (void)
+{
+	bool is_member;
+	bool was_member;
+	bool was_admin;
+	bool changed;
+	const struct sgrp *sgrp;
+	struct sgrp *nsgrp;
+
+	changed = false;
+
+	/*
+	 * Scan through the entire shadow group file looking for the groups
+	 * that the user is a member of.
+	 */
+	while ((sgrp = sgr_next ()) != NULL) {
+
+		/*
+		 * See if the user was a member of this group
+		 */
+		was_member = is_on_list (sgrp->sg_mem, user_name);
+
+		/*
+		 * See if the user was an administrator of this group
+		 */
+		was_admin = is_on_list (sgrp->sg_adm, user_name);
+
+		/*
+		 * See if the user specified this group as one of their
+		 * concurrent groups.
+		 */
+		is_member = Gflg && (   (was_member && aflg)
+		                     || is_on_list (user_groups, sgrp->sg_name));
+
+		if (!was_member && !was_admin && !is_member) {
+			continue;
+		}
+
+		nsgrp = __sgr_dup (sgrp);
+		if (NULL == nsgrp) {
+			fprintf (stderr,
+			         _("%s: Out of memory. Cannot update %s.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+
+		if (was_admin && lflg) {
+			/* User was an admin of this group but the user
+			 * has been renamed.
+			 */
+			nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
+			nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing admin name in shadow group",
+			              user_name, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO,
+			         "change admin '%s' to '%s' in shadow group '%s'",
+			         user_name, user_newname, nsgrp->sg_name));
+		}
+
+		if (was_member) {
+			if ((!Gflg) || is_member) {
+				/* User was a member and is still a member
+				 * of this group.
+				 * But the user might have been renamed.
+				 */
+				if (lflg) {
+					nsgrp->sg_mem = del_list (nsgrp->sg_mem,
+					                          user_name);
+					nsgrp->sg_mem = add_list (nsgrp->sg_mem,
+					                          user_newname);
+					changed = true;
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+					              "changing member in shadow group",
+					              user_name, AUDIT_NO_ID, 1);
+#endif
+					SYSLOG ((LOG_INFO,
+					         "change '%s' to '%s' in shadow group '%s'",
+					         user_name, user_newname,
+					         nsgrp->sg_name));
+				}
+			} else {
+				/* User was a member but is no more a
+				 * member of this group.
+				 */
+				nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
+				changed = true;
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "removing user from shadow group",
+				              user_name, AUDIT_NO_ID, 1);
+#endif
+				SYSLOG ((LOG_INFO,
+				         "delete '%s' from shadow group '%s'",
+				         user_name, nsgrp->sg_name));
+			}
+		} else if (is_member) {
+			/* User was not a member but is now a member this
+			 * group.
+			 */
+			nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_newname);
+			changed = true;
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "adding user to shadow group",
+			              user_newname, AUDIT_NO_ID, 1);
+#endif
+			SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
+			         user_newname, nsgrp->sg_name));
+		}
+		if (!changed) {
+			continue;
+		}
+
+		changed = false;
+
+		/* 
+		 * Update the group entry to reflect the changes.
+		 */
+		if (sgr_update (nsgrp) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, sgr_dbname (), nsgrp->sg_name);
+			SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'",
+			         sgr_dbname (), nsgrp->sg_name));
+			fail_exit (E_GRP_UPDATE);
+		}
+	}
+}
+#endif				/* SHADOWGRP */
+
+/*
+ * grp_update - add user to secondary group set
+ *
+ *	grp_update() takes the secondary group set given in user_groups and
+ *	adds the user to each group given by that set.
+ */
+static void grp_update (void)
+{
+	update_group ();
+#ifdef SHADOWGRP
+	if (is_shadow_grp) {
+		update_gshadow ();
+	}
+#endif
+}
+
+/*
+ * 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)
+{
+	const struct group *grp;
+
+	bool anyflag = false;
+
+	{
+		/*
+		 * Parse the command line options.
+		 */
+		int c;
+		static struct option long_options[] = {
+			{"append",       no_argument,       NULL, 'a'},
+			{"comment",      required_argument, NULL, 'c'},
+			{"home",         required_argument, NULL, 'd'},
+			{"expiredate",   required_argument, NULL, 'e'},
+			{"inactive",     required_argument, NULL, 'f'},
+			{"gid",          required_argument, NULL, 'g'},
+			{"groups",       required_argument, NULL, 'G'},
+			{"help",         no_argument,       NULL, 'h'},
+			{"login",        required_argument, NULL, 'l'},
+			{"lock",         no_argument,       NULL, 'L'},
+			{"move-home",    no_argument,       NULL, 'm'},
+			{"non-unique",   no_argument,       NULL, 'o'},
+			{"password",     required_argument, NULL, 'p'},
+			{"root",         required_argument, NULL, 'R'},
+			{"shell",        required_argument, NULL, 's'},
+			{"uid",          required_argument, NULL, 'u'},
+			{"unlock",       no_argument,       NULL, 'U'},
+#ifdef ENABLE_SUBIDS
+			{"add-subuids",  required_argument, NULL, 'v'},
+			{"del-subuids",  required_argument, NULL, 'V'},
+ 			{"add-subgids",  required_argument, NULL, 'w'},
+ 			{"del-subgids",  required_argument, NULL, 'W'},
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			{"selinux-user", required_argument, NULL, 'Z'},
+#endif				/* WITH_SELINUX */
+			{NULL, 0, NULL, '\0'}
+		};
+		while ((c = getopt_long (argc, argv,
+		                         "ac:d:e:f:g:G:hl:Lmop:R:s:u:U"
+#ifdef ENABLE_SUBIDS
+		                         "v:w:V:W:"
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			                 "Z:"
+#endif				/* WITH_SELINUX */
+			                 , long_options, NULL)) != -1) {
+			switch (c) {
+			case 'a':
+				aflg = true;
+				break;
+			case 'c':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_newcomment = optarg;
+				cflg = true;
+				break;
+			case 'd':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				dflg = true;
+				user_newhome = optarg;
+				break;
+			case 'e':
+				if ('\0' != *optarg) {
+					user_newexpire = strtoday (optarg);
+					if (user_newexpire < -1) {
+						fprintf (stderr,
+						         _("%s: invalid date '%s'\n"),
+						         Prog, optarg);
+						exit (E_BAD_ARG);
+					}
+					user_newexpire *= DAY / SCALE;
+				} else {
+					user_newexpire = -1;
+				}
+				eflg = true;
+				break;
+			case 'f':
+				if (   (getlong (optarg, &user_newinactive) == 0)
+				    || (user_newinactive < -1)) {
+					fprintf (stderr,
+					         _("%s: invalid numeric argument '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				fflg = true;
+				break;
+			case 'g':
+				grp = getgr_nam_gid (optarg);
+				if (NULL == grp) {
+					fprintf (stderr,
+					         _("%s: group '%s' does not exist\n"),
+					         Prog, optarg);
+					exit (E_NOTFOUND);
+				}
+				user_newgid = grp->gr_gid;
+				gflg = true;
+				break;
+			case 'G':
+				if (get_groups (optarg) != 0) {
+					exit (E_NOTFOUND);
+				}
+				Gflg = true;
+				break;
+			case 'h':
+				usage (E_SUCCESS);
+				/*@notreached@*/break;
+			case 'l':
+				if (!is_valid_user_name (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid user name '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				lflg = true;
+				user_newname = optarg;
+				break;
+			case 'L':
+				Lflg = true;
+				break;
+			case 'm':
+				mflg = true;
+				break;
+			case 'o':
+				oflg = true;
+				break;
+			case 'p':
+				user_pass = optarg;
+				pflg = true;
+				break;
+			case 'R': /* no-op, handled in process_root_flag () */
+				break;
+			case 's':
+				if (!VALID (optarg)) {
+					fprintf (stderr,
+					         _("%s: invalid field '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				user_newshell = optarg;
+				sflg = true;
+				break;
+			case 'u':
+				if (   (get_uid (optarg, &user_newid) ==0)
+				    || (user_newid == (uid_t)-1)) {
+					fprintf (stderr,
+					         _("%s: invalid user ID '%s'\n"),
+					         Prog, optarg);
+					exit (E_BAD_ARG);
+				}
+				uflg = true;
+				break;
+			case 'U':
+				Uflg = true;
+				break;
+#ifdef ENABLE_SUBIDS
+			case 'v':
+				if (prepend_range (optarg, &add_sub_uids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate uid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				vflg = true;
+				break;
+			case 'V':
+				if (prepend_range (optarg, &del_sub_uids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate uid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				Vflg = true;
+				break;
+			case 'w':
+				if (prepend_range (optarg, &add_sub_gids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate gid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				wflg = true;
+                break;
+			case 'W':
+				if (prepend_range (optarg, &del_sub_gids) == 0) {
+					fprintf (stderr,
+						_("%s: invalid subordinate gid range '%s'\n"),
+						Prog, optarg);
+					exit(E_BAD_ARG);
+				}
+				Wflg = true;
+				break;
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+			case 'Z':
+				if (is_selinux_enabled () > 0) {
+					user_selinux = optarg;
+					Zflg = true;
+				} else {
+					fprintf (stderr,
+					         _("%s: -Z requires SELinux enabled kernel\n"),
+					         Prog);
+					exit (E_BAD_ARG);
+				}
+				break;
+#endif				/* WITH_SELINUX */
+			default:
+				usage (E_USAGE);
+			}
+			anyflag = true;
+		}
+	}
+
+	if (optind != argc - 1) {
+		usage (E_USAGE);
+	}
+
+	user_name = argv[argc - 1];
+
+	{
+		const struct passwd *pwd;
+		/* local, no need for xgetpwnam */
+		pwd = getpwnam (user_name);
+		if (NULL == pwd) {
+			fprintf (stderr,
+			         _("%s: user '%s' does not exist\n"),
+			         Prog, user_name);
+			exit (E_NOTFOUND);
+		}
+
+		user_id = pwd->pw_uid;
+		user_gid = pwd->pw_gid;
+		user_comment = xstrdup (pwd->pw_gecos);
+		user_home = xstrdup (pwd->pw_dir);
+		user_shell = xstrdup (pwd->pw_shell);
+	}
+
+	/* user_newname, user_newid, user_newgid can be used even when the
+	 * options where not specified. */
+	if (!lflg) {
+		user_newname = user_name;
+	}
+	if (!uflg) {
+		user_newid = user_id;
+	}
+	if (!gflg) {
+		user_newgid = user_gid;
+	}
+
+#ifdef	USE_NIS
+	/*
+	 * Now make sure it isn't an NIS user.
+	 */
+	if (__ispwNIS ()) {
+		char *nis_domain;
+		char *nis_master;
+
+		fprintf (stderr,
+		         _("%s: user %s is a NIS user\n"),
+		         Prog, user_name);
+
+		if (   !yp_get_default_domain (&nis_domain)
+		    && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
+			fprintf (stderr,
+			         _("%s: %s is the NIS master\n"),
+			         Prog, nis_master);
+		}
+		exit (E_NOTFOUND);
+	}
+#endif
+
+	{
+		const struct spwd *spwd = NULL;
+		/* local, no need for xgetspnam */
+		if (is_shadow_pwd && ((spwd = getspnam (user_name)) != NULL)) {
+			user_expire = spwd->sp_expire;
+			user_inactive = spwd->sp_inact;
+		}
+	}
+
+	if (!anyflag) {
+		fprintf (stderr, _("%s: no options\n"), Prog);
+		usage (E_USAGE);
+	}
+
+	if (aflg && (!Gflg)) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-a", "-G");
+		usage (E_USAGE);
+	}
+
+	if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
+		fprintf (stderr,
+		         _("%s: the -L, -p, and -U flags are exclusive\n"),
+		         Prog);
+		usage (E_USAGE);
+	}
+
+	if (oflg && !uflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-o", "-u");
+		usage (E_USAGE);
+	}
+
+	if (mflg && !dflg) {
+		fprintf (stderr,
+		         _("%s: %s flag is only allowed with the %s flag\n"),
+		         Prog, "-m", "-d");
+		usage (E_USAGE);
+	}
+
+	if (user_newid == user_id) {
+		uflg = false;
+		oflg = false;
+	}
+	if (user_newgid == user_gid) {
+		gflg = false;
+	}
+	if (   (NULL != user_newshell)
+	    && (strcmp (user_newshell, user_shell) == 0)) {
+		sflg = false;
+	}
+	if (strcmp (user_newname, user_name) == 0) {
+		lflg = false;
+	}
+	if (user_newinactive == user_inactive) {
+		fflg = false;
+	}
+	if (user_newexpire == user_expire) {
+		eflg = false;
+	}
+	if (   (NULL != user_newhome)
+	    && (strcmp (user_newhome, user_home) == 0)) {
+		dflg = false;
+		mflg = false;
+	}
+	if (   (NULL != user_newcomment)
+	    && (strcmp (user_newcomment, user_comment) == 0)) {
+		cflg = false;
+	}
+
+	if (!(Uflg || uflg || sflg || pflg || mflg || Lflg ||
+	      lflg || Gflg || gflg || fflg || eflg || dflg || cflg
+#ifdef ENABLE_SUBIDS
+	      || vflg || Vflg || wflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+#ifdef WITH_SELINUX
+	      || Zflg
+#endif				/* WITH_SELINUX */
+	)) {
+		fprintf (stderr, _("%s: no changes\n"), Prog);
+		exit (E_SUCCESS);
+	}
+
+	if (!is_shadow_pwd && (eflg || fflg)) {
+		fprintf (stderr,
+		         _("%s: shadow passwords required for -e and -f\n"),
+		         Prog);
+		exit (E_USAGE);
+	}
+
+	/* local, no need for xgetpwnam */
+	if (lflg && (getpwnam (user_newname) != NULL)) {
+		fprintf (stderr,
+		         _("%s: user '%s' already exists\n"),
+		         Prog, user_newname);
+		exit (E_NAME_IN_USE);
+	}
+
+	/* local, no need for xgetpwuid */
+	if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
+		fprintf (stderr,
+		         _("%s: UID '%lu' already exists\n"),
+		         Prog, (unsigned long) user_newid);
+		exit (E_UID_IN_USE);
+	}
+
+	if (   (vflg || Vflg)
+	    && !is_sub_uid) {
+		fprintf (stderr,
+		         _("%s: %s does not exist, you cannot use the flags %s or %s\n"),
+		         Prog, sub_uid_dbname (), "-v", "-V");
+		exit (E_USAGE);
+	}
+
+	if (   (wflg || Wflg)
+	    && !is_sub_gid) {
+		fprintf (stderr,
+		         _("%s: %s does not exist, you cannot use the flags %s or %s\n"),
+		         Prog, sub_gid_dbname (), "-w", "-W");
+		exit (E_USAGE);
+	}
+}
+
+/*
+ * close_files - close all of the files that were opened
+ *
+ *	close_files() closes all of the files that were opened for this new
+ *	user. This causes any modified entries to be written out.
+ */
+static void close_files (void)
+{
+	if (pw_close () == 0) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"),
+		         Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_close () == 0)) {
+		fprintf (stderr,
+		         _("%s: failure while writing changes to %s\n"),
+		         Prog, spw_dbname ());
+		SYSLOG ((LOG_ERR,
+		         "failure while writing changes to %s",
+		         spw_dbname ()));
+		fail_exit (E_PW_UPDATE);
+	}
+
+	if (Gflg || lflg) {
+		if (gr_close () == 0) {
+			fprintf (stderr,
+			         _("%s: failure while writing changes to %s\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failure while writing changes to %s",
+			         gr_dbname ()));
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef SHADOWGRP
+		if (is_shadow_grp) {
+			if (sgr_close () == 0) {
+				fprintf (stderr,
+				         _("%s: failure while writing changes to %s\n"),
+				         Prog, sgr_dbname ());
+				SYSLOG ((LOG_ERR,
+				         "failure while writing changes to %s",
+				         sgr_dbname ()));
+				fail_exit (E_GRP_UPDATE);
+			}
+		}
+#endif
+#ifdef SHADOWGRP
+		if (is_shadow_grp) {
+			if (sgr_unlock () == 0) {
+				fprintf (stderr,
+				         _("%s: failed to unlock %s\n"),
+				         Prog, sgr_dbname ());
+				SYSLOG ((LOG_ERR,
+				         "failed to unlock %s",
+				         sgr_dbname ()));
+				/* continue */
+			}
+		}
+#endif
+		if (gr_unlock () == 0) {
+			fprintf (stderr,
+			         _("%s: failed to unlock %s\n"),
+			         Prog, gr_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failed to unlock %s",
+			         gr_dbname ()));
+			/* continue */
+		}
+	}
+
+	if (is_shadow_pwd) {
+		if (spw_unlock () == 0) {
+			fprintf (stderr,
+			         _("%s: failed to unlock %s\n"),
+			         Prog, spw_dbname ());
+			SYSLOG ((LOG_ERR,
+			         "failed to unlock %s",
+			         spw_dbname ()));
+			/* continue */
+		}
+	}
+	if (pw_unlock () == 0) {
+		fprintf (stderr,
+		         _("%s: failed to unlock %s\n"),
+		         Prog, pw_dbname ());
+		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+		/* continue */
+	}
+
+	pw_locked = false;
+	spw_locked = false;
+	gr_locked = false;
+#ifdef	SHADOWGRP
+	sgr_locked = false;
+#endif
+
+#ifdef ENABLE_SUBIDS
+	if (vflg || Vflg) {
+		if (sub_uid_close () == 0) {
+			fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ()));
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		if (sub_uid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ()));
+			/* continue */
+		}
+		sub_uid_locked = false;
+	}
+	if (wflg || Wflg) {
+		if (sub_gid_close () == 0) {
+			fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ()));
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		if (sub_gid_unlock () == 0) {
+			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ());
+			SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ()));
+			/* continue */
+		}
+		sub_gid_locked = false;
+	}
+#endif				/* ENABLE_SUBIDS */
+
+	/*
+	 * Close the DBM and/or flat files
+	 */
+	endpwent ();
+	endspent ();
+	endgrent ();
+#ifdef	SHADOWGRP
+	endsgent ();
+#endif
+}
+
+/*
+ * open_files - lock and open the password files
+ *
+ *	open_files() opens the two password files.
+ */
+static void open_files (void)
+{
+	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
+	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
+
+	if (pw_lock () == 0) {
+		if (try_extrausers) {
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	pw_locked = true;
+	if (pw_open (O_RDWR) == 0) {
+		if (try_extrausers) {
+			pw_unlock ();
+			pw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, pw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	if (is_shadow_pwd && (spw_lock () == 0)) {
+		if (try_extrausers) {
+			pw_close ();
+			pw_unlock ();
+			pw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
+		fprintf (stderr,
+		         _("%s: cannot lock %s; try again later.\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+	spw_locked = true;
+	if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
+		if (try_extrausers) {
+			pw_close ();
+			pw_unlock ();
+			spw_unlock ();
+			pw_locked = false;
+			spw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
+		fprintf (stderr,
+		         _("%s: cannot open %s\n"),
+		         Prog, spw_dbname ());
+		fail_exit (E_PW_UPDATE);
+	}
+
+	if (Gflg || lflg) {
+		/*
+		 * Lock and open the group file. This will load all of the
+		 * group entries.
+		 */
+		if (gr_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		gr_locked = true;
+		if (gr_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, gr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+#ifdef SHADOWGRP
+		if (is_shadow_grp && (sgr_lock () == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+		sgr_locked = true;
+		if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sgr_dbname ());
+			fail_exit (E_GRP_UPDATE);
+		}
+#endif
+	}
+#ifdef ENABLE_SUBIDS
+	if (vflg || Vflg) {
+		if (sub_uid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+		sub_uid_locked = true;
+		if (sub_uid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_uid_dbname ());
+			fail_exit (E_SUB_UID_UPDATE);
+		}
+	}
+	if (wflg || Wflg) {
+		if (sub_gid_lock () == 0) {
+			fprintf (stderr,
+			         _("%s: cannot lock %s; try again later.\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+		sub_gid_locked = true;
+		if (sub_gid_open (O_RDWR) == 0) {
+			fprintf (stderr,
+			         _("%s: cannot open %s\n"),
+			         Prog, sub_gid_dbname ());
+			fail_exit (E_SUB_GID_UPDATE);
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * usr_update - create the user entries
+ *
+ *	usr_update() creates the password file entries for this user and
+ *	will update the group entries if required.
+ */
+static void usr_update (void)
+{
+	struct passwd pwent;
+	const struct passwd *pwd;
+
+	struct spwd spent;
+	const struct spwd *spwd = NULL;
+
+	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
+	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
+
+	/*
+	 * Locate the entry in /etc/passwd, which MUST exist.
+	 */
+	pwd = pw_locate (user_name);
+	if (NULL == pwd) {
+		if (try_extrausers) {
+			close_files ();
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			usr_update ();
+			return;
+		}
+		fprintf (stderr,
+		         _("%s: user '%s' does not exist in %s\n"),
+		         Prog, user_name, pw_dbname ());
+		fail_exit (E_NOTFOUND);
+	}
+	pwent = *pwd;
+	new_pwent (&pwent);
+
+
+	/* If the shadow file does not exist, it won't be created */
+	if (is_shadow_pwd) {
+		spwd = spw_locate (user_name);
+		if (NULL != spwd) {
+			/* Update the shadow entry if it exists */
+			spent = *spwd;
+			new_spent (&spent);
+		} else if (   (    pflg
+		               && (strcmp (pwent.pw_passwd, SHADOW_PASSWD_STRING) == 0))
+		           || eflg || fflg) {
+			/* In some cases, we force the creation of a
+			 * shadow entry:
+			 *  + new password requested and passwd indicates
+			 *    a shadowed password
+			 *  + aging information is requested
+			 */
+			memset (&spent, 0, sizeof spent);
+			spent.sp_namp   = user_name;
+
+			/* The user explicitly asked for a shadow feature.
+			 * Enable shadowed passwords for this new account.
+			 */
+			spent.sp_pwdp   = xstrdup (pwent.pw_passwd);
+			pwent.pw_passwd = xstrdup (SHADOW_PASSWD_STRING);
+
+			spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
+			if (0 == spent.sp_lstchg) {
+				/* Better disable aging than
+				 * requiring a password change */
+				spent.sp_lstchg = -1;
+			}
+			spent.sp_min    = getdef_num ("PASS_MIN_DAYS", -1);
+			spent.sp_max    = getdef_num ("PASS_MAX_DAYS", -1);
+			spent.sp_warn   = getdef_num ("PASS_WARN_AGE", -1);
+			spent.sp_inact  = -1;
+			spent.sp_expire = -1;
+			spent.sp_flag   = SHADOW_SP_FLAG_UNSET;
+			new_spent (&spent);
+			spwd = &spent; /* entry needs to be committed */
+		}
+	}
+
+	if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
+	    || Lflg || Uflg) {
+		if (pw_update (&pwent) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, pw_dbname (), pwent.pw_name);
+			fail_exit (E_PW_UPDATE);
+		}
+		if (lflg && (pw_remove (user_name) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot remove entry '%s' from %s\n"),
+			         Prog, user_name, pw_dbname ());
+			fail_exit (E_PW_UPDATE);
+		}
+	}
+	if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
+		if (spw_update (&spent) == 0) {
+			fprintf (stderr,
+			         _("%s: failed to prepare the new %s entry '%s'\n"),
+			         Prog, spw_dbname (), spent.sp_namp);
+			fail_exit (E_PW_UPDATE);
+		}
+		if (lflg && (spw_remove (user_name) == 0)) {
+			fprintf (stderr,
+			         _("%s: cannot remove entry '%s' from %s\n"),
+			         Prog, user_name, spw_dbname ());
+			fail_exit (E_PW_UPDATE);
+		}
+	}
+#ifdef ENABLE_SUBIDS
+	if (Vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (Wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+	if (wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
+}
+
+/*
+ * move_home - move the user's home directory
+ *
+ *	move_home() moves the user's home directory to a new location. The
+ *	files will be copied if the directory cannot simply be renamed.
+ */
+static void move_home (void)
+{
+	struct stat sb;
+
+	if (access (user_newhome, F_OK) == 0) {
+		/*
+		 * If the new home directory already exist, the user
+		 * should not use -m.
+		 */
+		fprintf (stderr,
+		         _("%s: directory %s exists\n"),
+		         Prog, user_newhome);
+		fail_exit (E_HOMEDIR);
+	}
+
+	if (stat (user_home, &sb) == 0) {
+		/*
+		 * Don't try to move it if it is not a directory
+		 * (but /dev/null for example).  --marekm
+		 */
+		if (!S_ISDIR (sb.st_mode)) {
+			fprintf (stderr,
+			         _("%s: The previous home directory (%s) was "
+			           "not a directory. It is not removed and no "
+			           "home directories are created.\n"),
+			         Prog, user_home);
+			fail_exit (E_HOMEDIR);
+		}
+
+		if (rename (user_home, user_newhome) == 0) {
+			/* FIXME: rename above may have broken symlinks
+			 *        pointing to the user's home directory
+			 *        with an absolute path. */
+			if (chown_tree (user_newhome,
+			                user_id,  uflg ? user_newid  : (uid_t)-1,
+			                user_gid, gflg ? user_newgid : (gid_t)-1) != 0) {
+				fprintf (stderr,
+				         _("%s: Failed to change ownership of the home directory"),
+				         Prog);
+				fail_exit (E_HOMEDIR);
+			}
+#ifdef WITH_AUDIT
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "moving home directory",
+			              user_newname, (unsigned int) user_newid,
+			              1);
+#endif
+			return;
+		} else {
+			if (EXDEV == errno) {
+				if (copy_tree (user_home, user_newhome, true,
+				               true,
+				               user_id,
+				               uflg ? user_newid : (uid_t)-1,
+				               user_gid,
+				               gflg ? user_newgid : (gid_t)-1) == 0) {
+					if (remove_tree (user_home, true) != 0) {
+						fprintf (stderr,
+						         _("%s: warning: failed to completely remove old home directory %s"),
+						         Prog, user_home);
+					}
+#ifdef WITH_AUDIT
+					audit_logger (AUDIT_USER_CHAUTHTOK,
+					              Prog,
+					              "moving home directory",
+					              user_newname,
+					              (unsigned int) user_newid,
+					              1);
+#endif
+					return;
+				}
+
+				(void) remove_tree (user_newhome, true);
+			}
+			fprintf (stderr,
+			         _("%s: cannot rename directory %s to %s\n"),
+			         Prog, user_home, user_newhome);
+			fail_exit (E_HOMEDIR);
+		}
+	}
+}
+
+/*
+ * update_lastlog - update the lastlog file
+ *
+ * Relocate the "lastlog" entries for the user. The old entry is
+ * left alone in case the UID was shared. It doesn't hurt anything
+ * to just leave it be.
+ */
+static void update_lastlog (void)
+{
+	struct lastlog ll;
+	int fd;
+	off_t off_uid = (off_t) user_id * sizeof ll;
+	off_t off_newuid = (off_t) user_newid * sizeof ll;
+
+	if (access (LASTLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	fd = open (LASTLOG_FILE, O_RDWR);
+
+	if (-1 == fd) {
+		fprintf (stderr,
+		         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+		         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		return;
+	}
+
+	if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
+	    && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
+		/* Copy the old entry to its new location */
+		if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+		    || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
+		    || (fsync (fd) != 0)
+		    || (close (fd) != 0)) {
+			fprintf (stderr,
+			         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+			         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		}
+	} else {
+		/* Assume lseek or read failed because there is
+		 * no entry for the old UID */
+
+		/* Check if the new UID already has an entry */
+		if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
+		    && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
+			/* Reset the new uid's lastlog entry */
+			memzero (&ll, sizeof (ll));
+			if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+			    || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
+			    || (fsync (fd) != 0)
+			    || (close (fd) != 0)) {
+				fprintf (stderr,
+				         _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
+				         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+			}
+		} else {
+			(void) close (fd);
+		}
+	}
+}
+
+/*
+ * update_faillog - update the faillog file
+ *
+ * Relocate the "faillog" entries for the user. The old entry is
+ * left alone in case the UID was shared. It doesn't hurt anything
+ * to just leave it be.
+ */
+static void update_faillog (void)
+{
+	struct faillog fl;
+	int fd;
+	off_t off_uid = (off_t) user_id * sizeof fl;
+	off_t off_newuid = (off_t) user_newid * sizeof fl;
+
+	if (access (FAILLOG_FILE, F_OK) != 0) {
+		return;
+	}
+
+	fd = open (FAILLOG_FILE, O_RDWR);
+
+	if (-1 == fd) {
+		fprintf (stderr,
+		         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+		         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		return;
+	}
+
+	if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
+	    && (read (fd, (char *) &fl, sizeof fl) == (ssize_t) sizeof fl)) {
+		/* Copy the old entry to its new location */
+		if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+		    || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
+		    || (fsync (fd) != 0)
+		    || (close (fd) != 0)) {
+			fprintf (stderr,
+			         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+			         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+		}
+	} else {
+		/* Assume lseek or read failed because there is
+		 * no entry for the old UID */
+
+		/* Check if the new UID already has an entry */
+		if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
+		    && (read (fd, &fl, sizeof fl) == (ssize_t) sizeof fl)) {
+			/* Reset the new uid's faillog entry */
+			memzero (&fl, sizeof (fl));
+			if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
+			    || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
+			    || (close (fd) != 0)) {
+				fprintf (stderr,
+				         _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
+				         Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
+			}
+		} else {
+			(void) close (fd);
+		}
+	}
+}
+
+#ifndef NO_MOVE_MAILBOX
+/*
+ * This is the new and improved code to carefully chown/rename the user's
+ * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
+ * happens to be mode 1777 (this makes mail user agents work without
+ * being setgid mail, but is NOT recommended; they all should be fixed
+ * to use movemail).  --marekm
+ */
+static void move_mailbox (void)
+{
+	const char *maildir;
+	char mailfile[1024], newmailfile[1024];
+	int fd;
+	struct stat st;
+
+	maildir = getdef_str ("MAIL_DIR");
+#ifdef MAIL_SPOOL_DIR
+	if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
+		maildir = MAIL_SPOOL_DIR;
+	}
+#endif
+	if (NULL == maildir) {
+		return;
+	}
+
+	/*
+	 * O_NONBLOCK is to make sure open won't hang on mandatory locks.
+	 * We do fstat/fchown to make sure there are no races (someone
+	 * replacing /var/spool/mail/luser with a hard link to /etc/passwd
+	 * between stat and chown).  --marekm
+	 */
+	(void) snprintf (mailfile, sizeof mailfile, "%s/%s",
+	                 maildir, user_name);
+	mailfile[(sizeof mailfile) - 1] = '\0';
+	fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
+	if (fd < 0) {
+		/* no need for warnings if the mailbox doesn't exist */
+		if (errno != ENOENT) {
+			perror (mailfile);
+		}
+		return;
+	}
+	if (fstat (fd, &st) < 0) {
+		perror ("fstat");
+		(void) close (fd);
+		return;
+	}
+	if (st.st_uid != user_id) {
+		/* better leave it alone */
+		fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
+		         Prog, mailfile, user_name);
+		(void) close (fd);
+		return;
+	}
+	if (uflg) {
+		if (fchown (fd, user_newid, (gid_t) -1) < 0) {
+			perror (_("failed to change mailbox owner"));
+		}
+#ifdef WITH_AUDIT
+		else {
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing mail file owner",
+			              user_newname, (unsigned int) user_newid, 1);
+		}
+#endif
+	}
+
+	(void) close (fd);
+
+	if (lflg) {
+		(void) snprintf (newmailfile, sizeof newmailfile, "%s/%s",
+		                 maildir, user_newname);
+		newmailfile[(sizeof newmailfile) - 1] = '\0';
+		if (   (link (mailfile, newmailfile) != 0)
+		    || (unlink (mailfile) != 0)) {
+			perror (_("failed to rename mailbox"));
+		}
+#ifdef WITH_AUDIT
+		else {
+			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+			              "changing mail file name",
+			              user_newname, (unsigned int) user_newid, 1);
+		}
+#endif
+	}
+}
+#endif
+
+/*
+ * main - usermod command
+ */
+int main (int argc, char **argv)
+{
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	pam_handle_t *pamh = NULL;
+	int retval;
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+	/*
+	 * Get my name so that I can use it to report errors.
+	 */
+	Prog = Basename (argv[0]);
+
+	(void) setlocale (LC_ALL, "");
+	(void) bindtextdomain (PACKAGE, LOCALEDIR);
+	(void) textdomain (PACKAGE);
+
+	process_root_flag ("-R", argc, argv);
+
+	OPENLOG ("usermod");
+#ifdef WITH_AUDIT
+	audit_help_open ();
+#endif
+
+	sys_ngroups = sysconf (_SC_NGROUPS_MAX);
+	user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
+	user_groups[0] = (char *) 0;
+
+	is_shadow_pwd = spw_file_present ();
+#ifdef SHADOWGRP
+	is_shadow_grp = sgr_file_present ();
+#endif
+#ifdef ENABLE_SUBIDS
+	is_sub_uid = sub_uid_file_present ();
+	is_sub_gid = sub_gid_file_present ();
+#endif				/* ENABLE_SUBIDS */
+
+	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
+#ifdef ENABLE_SUBIDS
+	        || Vflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+	       )
+	    && (user_busy (user_name, user_id) != 0)) {
+		exit (E_USER_BUSY);
+	}
+
+#ifdef ACCT_TOOLS_SETUID
+#ifdef USE_PAM
+	{
+		struct passwd *pampw;
+		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
+		if (pampw == NULL) {
+			fprintf (stderr,
+			         _("%s: Cannot determine your user name.\n"),
+			         Prog);
+			exit (1);
+		}
+
+		retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_authenticate (pamh, 0);
+	}
+
+	if (PAM_SUCCESS == retval) {
+		retval = pam_acct_mgmt (pamh, 0);
+	}
+
+	if (PAM_SUCCESS != retval) {
+		fprintf (stderr, _("%s: PAM: %s\n"),
+		         Prog, pam_strerror (pamh, retval));
+		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
+		if (NULL != pamh) {
+			(void) pam_end (pamh, retval);
+		}
+		exit (1);
+	}
+	(void) pam_end (pamh, retval);
+#endif				/* USE_PAM */
+#endif				/* ACCT_TOOLS_SETUID */
+
+#ifdef WITH_TCB
+	if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
+		exit (E_PW_UPDATE);
+	}
+#endif
+
+	/*
+	 * Do the hard stuff - open the files, change the user entries,
+	 * change the home directory, then close and update the files.
+	 */
+	open_files ();
+	if (   cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
+	    || sflg || uflg || Uflg
+#ifdef ENABLE_SUBIDS
+	    || vflg || Vflg || wflg || Wflg
+#endif				/* ENABLE_SUBIDS */
+	    ) {
+		usr_update ();
+	}
+	if (Gflg || lflg) {
+		grp_update ();
+	}
+	close_files ();
+
+#ifdef WITH_TCB
+	if (   (lflg || uflg)
+	    && (shadowtcb_move (user_newname, user_newid) == SHADOWTCB_FAILURE) ) {
+		exit (E_PW_UPDATE);
+	}
+#endif
+
+	nscd_flush_cache ("passwd");
+	nscd_flush_cache ("group");
+
+#ifdef WITH_SELINUX
+	if (Zflg) {
+		if ('\0' != *user_selinux) {
+			if (set_seuser (user_name, user_selinux) != 0) {
+				fprintf (stderr,
+				         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+				         Prog, user_name, user_selinux);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+				              "modifying User mapping ",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif				/* WITH_AUDIT */
+				fail_exit (E_SE_UPDATE);
+			}
+		} else {
+			if (del_seuser (user_name) != 0) {
+				fprintf (stderr,
+				         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
+				         Prog, user_name);
+#ifdef WITH_AUDIT
+				audit_logger (AUDIT_ADD_USER, Prog,
+				              "removing SELinux user mapping",
+				              user_name, (unsigned int) user_id,
+				              SHADOW_AUDIT_FAILURE);
+#endif				/* WITH_AUDIT */
+				fail_exit (E_SE_UPDATE);
+			}
+		}
+	}
+#endif				/* WITH_SELINUX */
+
+	if (mflg) {
+		move_home ();
+	}
+
+#ifndef NO_MOVE_MAILBOX
+	if (lflg || uflg) {
+		move_mailbox ();
+	}
+#endif				/* NO_MOVE_MAILBOX */
+
+	if (uflg) {
+		update_lastlog ();
+		update_faillog ();
+	}
+
+	if (!mflg && (uflg || gflg)) {
+		if (access (dflg ? user_newhome : user_home, F_OK) == 0) {
+			/*
+			 * Change the UID on all of the files owned by
+			 * `user_id' to `user_newid' in the user's home
+			 * directory.
+			 *
+			 * move_home() already takes care of changing the
+			 * ownership.
+			 *
+			 */
+			if (chown_tree (dflg ? user_newhome : user_home,
+			                user_id,
+			                uflg ? user_newid  : (uid_t)-1,
+			                user_gid,
+			                gflg ? user_newgid : (gid_t)-1) != 0) {
+				fprintf (stderr,
+				         _("%s: Failed to change ownership of the home directory"),
+				         Prog);
+				fail_exit (E_HOMEDIR);
+			}
+		}
+	}
+
+	return E_SUCCESS;
+}
+
diff -pruN 1:4.2-3.2/src/chfn.c 1:4.2-3.2ubuntu1/src/chfn.c
--- 1:4.2-3.2/src/chfn.c	2014-03-01 17:56:04.000000000 +0000
+++ 1:4.2-3.2ubuntu1/src/chfn.c	2016-09-20 17:11:18.000000000 +0000
@@ -74,6 +74,11 @@ static bool hflg = false;		/* -h - set h
 static bool oflg = false;		/* -o - set other information        */
 static bool pw_locked = false;
 
+#ifndef EXTRAUSERS_OPT
+#define EXTRAUSERS_OPT 100000
+#endif
+static bool use_extrausers = false;
+
 /*
  * External identifiers
  */
@@ -126,6 +131,7 @@ static /*@noreturn@*/void usage (int sta
 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
 	(void) fputs (_("  -u, --help                    display this help message and exit\n"), usageout);
 	(void) fputs (_("  -w, --work-phone WORK_PHONE   change user's office phone number\n"), usageout);
+	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);        
 	(void) fputs ("\n", usageout);
 	exit (status);
 }
@@ -276,6 +282,7 @@ static void process_flags (int argc, cha
 		{"root",       required_argument, NULL, 'R'},
 		{"help",       no_argument,       NULL, 'u'},
 		{"work-phone", required_argument, NULL, 'w'},
+                {"extrausers", no_argument, NULL, EXTRAUSERS_OPT},
 		{NULL, 0, NULL, '\0'}
 	};
 
@@ -289,6 +296,9 @@ static void process_flags (int argc, cha
 	while ((c = getopt_long (argc, argv, "f:h:o:r:R:uw:",
 	                         long_options, NULL)) != -1) {
 		switch (c) {
+                case EXTRAUSERS_OPT:
+                   use_extrausers = true;
+                   break;
 		case 'f':
 			if (!may_change_field ('f')) {
 				fprintf (stderr,
@@ -657,6 +667,18 @@ int main (int argc, char **argv)
 	/* parse the command line options */
 	process_flags (argc, argv);
 
+        if (use_extrausers) {
+           pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+           spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+           gr_setdbname (EXTRAUSERS_GROUP_FILE);
+           /* TODO expose this information in other tools */
+           sub_uid_setdbname(EXTRAUSERS_SUBUID_FILE);
+           sub_gid_setdbname(EXTRAUSERS_SUBGID_FILE);
+#ifdef SHADOWGRP
+           sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
+#endif
+        }
+        
 	/*
 	 * Get the name of the user to check. It is either the command line
 	 * name, or the name getlogin() returns.
diff -pruN 1:4.2-3.2/src/groupadd.c 1:4.2-3.2ubuntu1/src/groupadd.c
--- 1:4.2-3.2/src/groupadd.c	2014-03-01 17:56:04.000000000 +0000
+++ 1:4.2-3.2ubuntu1/src/groupadd.c	2016-09-20 17:11:18.000000000 +0000
@@ -102,6 +102,12 @@ static void process_flags (int argc, cha
 static void check_flags (void);
 static void check_perms (void);
 
+#ifndef EXTRAUSERS_OPT
+#define EXTRAUSERS_OPT 100000
+#endif
+
+static bool use_extrausers = false;
+
 /*
  * usage - display usage message and exit
  */
@@ -123,6 +129,7 @@ static /*@noreturn@*/void usage (int sta
 	(void) fputs (_("  -p, --password PASSWORD       use this encrypted password for the new group\n"), usageout);
 	(void) fputs (_("  -r, --system                  create a system account\n"), usageout);
 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);
 	(void) fputs ("\n", usageout);
 	exit (status);
 }
@@ -386,12 +393,16 @@ static void process_flags (int argc, cha
 		{"password",   required_argument, NULL, 'p'},
 		{"system",     no_argument,       NULL, 'r'},
 		{"root",       required_argument, NULL, 'R'},
+        {"extrausers", no_argument,       NULL, EXTRAUSERS_OPT},
 		{NULL, 0, NULL, '\0'}
 	};
 
 	while ((c = getopt_long (argc, argv, "fg:hK:op:rR:",
 		                 long_options, NULL)) != -1) {
 		switch (c) {
+        case EXTRAUSERS_OPT:
+            use_extrausers = true;
+            break;
 		case 'f':
 			/*
 			 * "force" - do nothing, just exit(0), if the
@@ -598,7 +609,18 @@ int main (int argc, char **argv)
 
 	check_perms ();
 
+    if (use_extrausers) {
+		fprintf (stderr, "ENTER EXTRAUSERS_GROUP_FILE");
+        gr_setdbname (EXTRAUSERS_GROUP_FILE);
+		fprintf (stderr, "EXIT EXTRAUSERS_GROUP_FILE");
+    }
+
 #ifdef SHADOWGRP
+    if (use_extrausers) {
+		fprintf (stderr, "ENTER EXTRAUSERS_SHADOWGROUP_FILE");
+        sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
+		fprintf (stderr, "EXIT EXTRAUSERS_SHADOWGROUP_FILE");
+    }
 	is_shadow_grp = sgr_file_present ();
 #endif
 
diff -pruN 1:4.2-3.2/src/passwd.c 1:4.2-3.2ubuntu1/src/passwd.c
--- 1:4.2-3.2/src/passwd.c	2014-03-01 17:56:04.000000000 +0000
+++ 1:4.2-3.2ubuntu1/src/passwd.c	2016-09-20 17:11:18.000000000 +0000
@@ -565,8 +565,15 @@ static void update_noshadow (void)
 {
 	const struct passwd *pw;
 	struct passwd *npw;
+	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
+	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
 
 	if (pw_lock () == 0) {
+		if (try_extrausers) {
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			update_noshadow ();
+			return;
+		}
 		(void) fprintf (stderr,
 		                _("%s: cannot lock %s; try again later.\n"),
 		                Prog, pw_dbname ());
@@ -574,6 +581,20 @@ static void update_noshadow (void)
 	}
 	pw_locked = true;
 	if (pw_open (O_RDWR) == 0) {
+		if (try_extrausers) {
+			if (pw_unlock () == 0) {
+				(void) fprintf (stderr,
+				                _("%s: failed to unlock %s\n"),
+				                Prog, pw_dbname ());
+				SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+				/* continue */
+			}
+			pw_locked = false;
+
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			update_noshadow ();
+			return;
+		}
 		(void) fprintf (stderr,
 		                _("%s: cannot open %s\n"),
 		                Prog, pw_dbname ());
@@ -582,6 +603,21 @@ static void update_noshadow (void)
 	}
 	pw = pw_locate (name);
 	if (NULL == pw) {
+		if (try_extrausers) {
+			(void) pw_close ();
+			if (pw_unlock () == 0) {
+				(void) fprintf (stderr,
+				                _("%s: failed to unlock %s\n"),
+				                Prog, pw_dbname ());
+				SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+				/* continue */
+			}
+			pw_locked = false;
+
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			update_noshadow ();
+			return;
+		}
 		(void) fprintf (stderr,
 		                _("%s: user '%s' does not exist in %s\n"),
 		                Prog, name, pw_dbname ());
@@ -619,8 +655,15 @@ static void update_shadow (void)
 {
 	const struct spwd *sp;
 	struct spwd *nsp;
+	bool try_extrausers = strcmp (spw_dbname (), EXTRAUSERS_SHADOW_FILE) != 0 &&
+	                      access (EXTRAUSERS_SHADOW_FILE, F_OK) == 0;
 
 	if (spw_lock () == 0) {
+		if (try_extrausers) {
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			update_shadow ();
+			return;
+		}
 		(void) fprintf (stderr,
 		                _("%s: cannot lock %s; try again later.\n"),
 		                Prog, spw_dbname ());
@@ -628,6 +671,20 @@ static void update_shadow (void)
 	}
 	spw_locked = true;
 	if (spw_open (O_RDWR) == 0) {
+		if (try_extrausers) {
+			if (spw_unlock () == 0) {
+				(void) fprintf (stderr,
+						        _("%s: failed to unlock %s\n"),
+						        Prog, spw_dbname ());
+				SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+				/* continue */
+			}
+			spw_locked = false;
+
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			update_shadow ();
+			return;
+		}
 		(void) fprintf (stderr,
 		                _("%s: cannot open %s\n"),
 		                Prog, spw_dbname ());
@@ -638,7 +695,9 @@ static void update_shadow (void)
 	if (NULL == sp) {
 		/* Try to update the password in /etc/passwd instead. */
 		(void) spw_close ();
-		update_noshadow ();
+		if (!try_extrausers) {
+			update_noshadow ();
+		}
 		if (spw_unlock () == 0) {
 			(void) fprintf (stderr,
 			                _("%s: failed to unlock %s\n"),
@@ -647,6 +706,10 @@ static void update_shadow (void)
 			/* continue */
 		}
 		spw_locked = false;
+		if (try_extrausers) {
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			update_shadow ();
+		}
 		return;
 	}
 	nsp = __spw_dup (sp);
diff -pruN 1:4.2-3.2/src/useradd.c 1:4.2-3.2ubuntu1/src/useradd.c
--- 1:4.2-3.2/src/useradd.c	2016-09-20 17:11:18.000000000 +0000
+++ 1:4.2-3.2ubuntu1/src/useradd.c	2016-09-20 17:11:18.000000000 +0000
@@ -141,6 +141,12 @@ static char **user_groups;	/* NULL-termi
 static long sys_ngroups;
 static bool do_grp_update = false;	/* group files need to be updated */
 
+#ifndef EXTRAUSERS_OPT
+#define EXTRAUSERS_OPT 100000
+#endif
+
+static bool use_extrausers = false;
+
 static bool
     bflg = false,		/* new default root of home directory */
     cflg = false,		/* comment (GECOS) field for new account */
@@ -778,6 +784,7 @@ static void usage (int status)
 #ifdef WITH_SELINUX
 	(void) fputs (_("  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping\n"), usageout);
 #endif				/* WITH_SELINUX */
+	(void) fputs (_("      --extrausers              Use the extra users database\n"), usageout);
 	(void) fputs ("\n", usageout);
 	exit (status);
 }
@@ -1052,6 +1059,7 @@ static void process_flags (int argc, cha
 #ifdef WITH_SELINUX
 			{"selinux-user",   required_argument, NULL, 'Z'},
 #endif				/* WITH_SELINUX */
+			{"extrausers",     no_argument,       NULL, EXTRAUSERS_OPT},
 			{NULL, 0, NULL, '\0'}
 		};
 		while ((c = getopt_long (argc, argv,
@@ -1062,6 +1070,9 @@ static void process_flags (int argc, cha
 #endif				/* !WITH_SELINUX */
 		                         long_options, NULL)) != -1) {
 			switch (c) {
+			case EXTRAUSERS_OPT:
+                use_extrausers = true;
+                break;
 			case 'b':
 				if (   ( !VALID (optarg) )
 				    || ( optarg[0] != '/' )) {
@@ -2026,6 +2037,9 @@ int main (int argc, char **argv)
 #ifdef SHADOWGRP
 	is_shadow_grp = sgr_file_present ();
 #endif
+
+	process_flags (argc, argv);
+
 #ifdef ENABLE_SUBIDS
 	is_sub_uid = sub_uid_file_present () && !rflg &&
 	    (!user_id || (user_id <= uid_max && user_id >= uid_min));
@@ -2035,8 +2049,6 @@ int main (int argc, char **argv)
 
 	get_defaults ();
 
-	process_flags (argc, argv);
-
 #ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 	{
@@ -2122,6 +2134,18 @@ int main (int argc, char **argv)
 		}
 	}
 
+    if (use_extrausers) {
+        pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+        spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+        gr_setdbname (EXTRAUSERS_GROUP_FILE);
+        /* TODO expose this information in other tools */
+        sub_uid_setdbname(EXTRAUSERS_SUBUID_FILE);
+        sub_gid_setdbname(EXTRAUSERS_SUBGID_FILE);
+#ifdef SHADOWGRP
+        sgr_setdbname (EXTRAUSERS_SHADOWGROUP_FILE);
+#endif
+    }
+
 	/*
 	 * Do the hard stuff:
 	 * - open the files,
diff -pruN 1:4.2-3.2/src/usermod.c 1:4.2-3.2ubuntu1/src/usermod.c
--- 1:4.2-3.2/src/usermod.c	2014-03-01 17:56:04.000000000 +0000
+++ 1:4.2-3.2ubuntu1/src/usermod.c	2016-09-20 17:11:18.000000000 +0000
@@ -1523,7 +1523,16 @@ static void close_files (void)
  */
 static void open_files (void)
 {
+	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
+	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
+
 	if (pw_lock () == 0) {
+		if (try_extrausers) {
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
 		fprintf (stderr,
 		         _("%s: cannot lock %s; try again later.\n"),
 		         Prog, pw_dbname ());
@@ -1531,12 +1540,29 @@ static void open_files (void)
 	}
 	pw_locked = true;
 	if (pw_open (O_RDWR) == 0) {
+		if (try_extrausers) {
+			pw_unlock ();
+			pw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
 		fprintf (stderr,
 		         _("%s: cannot open %s\n"),
 		         Prog, pw_dbname ());
 		fail_exit (E_PW_UPDATE);
 	}
 	if (is_shadow_pwd && (spw_lock () == 0)) {
+		if (try_extrausers) {
+			pw_close ();
+			pw_unlock ();
+			pw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
 		fprintf (stderr,
 		         _("%s: cannot lock %s; try again later.\n"),
 		         Prog, spw_dbname ());
@@ -1544,6 +1570,17 @@ static void open_files (void)
 	}
 	spw_locked = true;
 	if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
+		if (try_extrausers) {
+			pw_close ();
+			pw_unlock ();
+			spw_unlock ();
+			pw_locked = false;
+			spw_locked = false;
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			return;
+		}
 		fprintf (stderr,
 		         _("%s: cannot open %s\n"),
 		         Prog, spw_dbname ());
@@ -1632,11 +1669,22 @@ static void usr_update (void)
 	struct spwd spent;
 	const struct spwd *spwd = NULL;
 
+	bool try_extrausers = strcmp (pw_dbname (), EXTRAUSERS_PASSWD_FILE) != 0 &&
+	                      access (EXTRAUSERS_PASSWD_FILE, F_OK) == 0;
+
 	/*
 	 * Locate the entry in /etc/passwd, which MUST exist.
 	 */
 	pwd = pw_locate (user_name);
 	if (NULL == pwd) {
+		if (try_extrausers) {
+			close_files ();
+			pw_setdbname (EXTRAUSERS_PASSWD_FILE);
+			spw_setdbname (EXTRAUSERS_SHADOW_FILE);
+			open_files ();
+			usr_update ();
+			return;
+		}
 		fprintf (stderr,
 		         _("%s: user '%s' does not exist in %s\n"),
 		         Prog, user_name, pw_dbname ());
@@ -1717,60 +1765,6 @@ static void usr_update (void)
 			fail_exit (E_PW_UPDATE);
 		}
 	}
-#ifdef ENABLE_SUBIDS
-	if (Vflg) {
-		struct ulong_range_list_entry *ptr;
-		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
-			unsigned long count = ptr->range.last - ptr->range.first + 1;
-			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
-				fprintf (stderr,
-					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
-					Prog, ptr->range.first, ptr->range.last, 
-					sub_uid_dbname ());
-				fail_exit (E_SUB_UID_UPDATE);
-			}
-		}
-	}
-	if (vflg) {
-		struct ulong_range_list_entry *ptr;
-		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
-			unsigned long count = ptr->range.last - ptr->range.first + 1;
-			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
-				fprintf (stderr,
-					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
-					Prog, ptr->range.first, ptr->range.last, 
-					sub_uid_dbname ());
-				fail_exit (E_SUB_UID_UPDATE);
-			}
-		}
-	}
-	if (Wflg) {
-		struct ulong_range_list_entry *ptr;
-		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
-			unsigned long count = ptr->range.last - ptr->range.first + 1;
-			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
-				fprintf (stderr,
-					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
-					Prog, ptr->range.first, ptr->range.last, 
-					sub_gid_dbname ());
-				fail_exit (E_SUB_GID_UPDATE);
-			}
-		}
-	}
-	if (wflg) {
-		struct ulong_range_list_entry *ptr;
-		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
-			unsigned long count = ptr->range.last - ptr->range.first + 1;
-			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
-				fprintf (stderr,
-					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
-					Prog, ptr->range.first, ptr->range.last, 
-					sub_gid_dbname ());
-				fail_exit (E_SUB_GID_UPDATE);
-			}
-		}
-	}
-#endif				/* ENABLE_SUBIDS */
 }
 
 /*
@@ -2175,15 +2169,66 @@ int main (int argc, char **argv)
 	open_files ();
 	if (   cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
 	    || sflg || uflg || Uflg
-#ifdef ENABLE_SUBIDS
-	    || vflg || Vflg || wflg || Wflg
-#endif				/* ENABLE_SUBIDS */
 	    ) {
 		usr_update ();
 	}
 	if (Gflg || lflg) {
 		grp_update ();
 	}
+#ifdef ENABLE_SUBIDS
+	if (Vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (vflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_uid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add uid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_uid_dbname ());
+				fail_exit (E_SUB_UID_UPDATE);
+			}
+		}
+	}
+	if (Wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_remove(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to remove gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+	if (wflg) {
+		struct ulong_range_list_entry *ptr;
+		for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) {
+			unsigned long count = ptr->range.last - ptr->range.first + 1;
+			if (sub_gid_add(user_name, ptr->range.first, count) == 0) {
+				fprintf (stderr,
+					_("%s: failed to add gid range %lu-%lu from '%s'\n"),
+					Prog, ptr->range.first, ptr->range.last, 
+					sub_gid_dbname ());
+				fail_exit (E_SUB_GID_UPDATE);
+			}
+		}
+	}
+#endif				/* ENABLE_SUBIDS */
 	close_files ();
 
 #ifdef WITH_TCB


More information about the Pkg-shadow-devel mailing list