]> granicus.if.org Git - shadow/blobdiff - src/useradd.c
* src/useradd.c: Avoid redefinition of SHELL. Use DSHELL instead.
[shadow] / src / useradd.c
index 53026af9e6046ad47a3d399d54a388577388541e..7557f889132a84728ec6b00ac2e2b51cdab84386 100644 (file)
@@ -1,5 +1,8 @@
 /*
- * Copyright 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1991 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2009, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
+ * 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 JULIE HAUGH 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 JULIE HAUGH 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.
+ * 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 <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>
@@ -75,6 +82,8 @@
 /*
  * Global variables
  */
+char *Prog;
+
 /*
  * These defaults are used if there is no defaults file.
  */
@@ -100,43 +109,51 @@ 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 const char *user_selinux = "";
+#endif
 
 static long user_expire = -1;
-static int is_shadow_pwd;
+static bool is_shadow_pwd;
 
 #ifdef SHADOWGRP
-static int is_shadow_grp;
+static bool is_shadow_grp;
+static bool sgr_locked = false;
 #endif
+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 int do_grp_update = 0;  /* group files need to be updated */
-
-static char *Prog;
-
-static int
-    bflg = 0,                  /* new default root of home directory */
-    cflg = 0,                  /* comment (GECOS) field for new account */
-    dflg = 0,                  /* home directory for new account */
-    Dflg = 0,                  /* set/show new user default values */
-    eflg = 0,                  /* days since 1970-01-01 when account is locked */
-    fflg = 0,                  /* days until account with expired password is locked */
-    gflg = 0,                  /* primary group ID for new account */
-    Gflg = 0,                  /* secondary group set for new account */
-    kflg = 0,                  /* specify a directory to fill new user directory */
-    mflg = 0,                  /* create user's home directory if it doesn't exist */
-    nflg = 0,                  /* create a group having the same name as the user */
-    oflg = 0,                  /* permit non-unique user ID to be specified with -u */
-    sflg = 0,                  /* shell program for new account */
-    uflg = 0;                  /* specify user ID for new account */
-
-extern char *optarg;
-extern int optind;
-
-static int home_added;
+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 */
+    Zflg = false;              /* new selinux user */
+
+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 */
@@ -149,29 +166,28 @@ static int home_added;
 #define        E_MAIL_SPOOL    13      /* can't create mail spool */
 
 #define DGROUP                 "GROUP="
-#define HOME                   "HOME="
-#define SHELL                  "SHELL="
-#define INACT                  "INACTIVE="
-#define EXPIRE                 "EXPIRE="
-#define SKEL                   "SKEL="
-#define CREATE_MAIL_SPOOL      "CREATE_MAIL_SPOOL="
+#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 struct group *getgr_nam_gid (const char *);
-static long get_number (const char *);
-static uid_t get_uid (const char *);
 static void get_defaults (void);
 static void show_defaults (void);
 static int set_defaults (void);
 static int get_groups (char *);
-static void usage (void);
+static void usage (int status);
 static void new_pwent (struct passwd *);
+#ifdef WITH_SELINUX
+static void selinux_update_mapping (void);
+#endif
 
 static long scale_age (long);
 static void new_spent (struct spwd *);
 static void grp_update (void);
-static void find_new_uid (void);
 
 static void process_flags (int argc, char **argv);
 static void close_files (void);
@@ -187,55 +203,73 @@ static void create_mail (void);
  */
 static void fail_exit (int code)
 {
-       if (home_added)
+       if (home_added) {
                rmdir (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_USER_CHAUTHTOK, Prog, "adding user", user_name, -1,
-                     0);
+                       audit_logger (AUDIT_ADD_USER, Prog,
+                                     "unlocking shadow file",
+                                     user_name, AUDIT_NO_ID,
+                                     SHADOW_AUDIT_FAILURE);
 #endif
-       SYSLOG ((LOG_INFO, "failed adding user `%s', data deleted", user_name));
-       exit (code);
-}
-
-static struct group *getgr_nam_gid (const char *grname)
-{
-       long gid;
-       char *errptr;
-
-       gid = strtol (grname, &errptr, 10);
-       if (*grname != '\0' && *errptr == '\0' && errno != ERANGE && gid >= 0)
-               return getgrgid (gid);
-       return getgrnam (grname);
-}
-
-static long get_number (const char *numstr)
-{
-       long val;
-       char *errptr;
-
-       val = strtol (numstr, &errptr, 10);
-       if (*errptr || errno == ERANGE) {
-               fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
-                        numstr);
-               exit (E_BAD_ARG);
+                       /* continue */
+               }
        }
-       return val;
-}
-
-static uid_t get_uid (const char *uidstr)
-{
-       long val;
-       char *errptr;
-
-       val = strtol (uidstr, &errptr, 10);
-       if (*errptr || errno == ERANGE || val < 0) {
-               fprintf (stderr,
-                        _("%s: invalid numeric argument '%s'\n"), Prog,
-                        uidstr);
-               exit (E_BAD_ARG);
+       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 */
+               }
        }
-       return val;
+       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 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)
@@ -251,26 +285,31 @@ static void get_defaults (void)
 {
        FILE *fp;
        char buf[1024];
-       char *cp, *ep;
-       const struct group *grp;
+       char *cp;
 
        /*
         * Open the defaults file for reading.
         */
 
-       if (!(fp = fopen (def_file, "r")))
+       fp = fopen (def_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, sizeof buf, fp)) {
-               if ((cp = strrchr (buf, '\n')))
+       while (fgets (buf, (int) sizeof buf, fp) == buf) {
+               cp = strrchr (buf, '\n');
+               if (NULL != cp) {
                        *cp = '\0';
+               }
 
-               if (!(cp = strchr (buf, '=')))
+               cp = strchr (buf, '=');
+               if (NULL == cp) {
                        continue;
+               }
 
                cp++;
 
@@ -278,65 +317,64 @@ static void get_defaults (void)
                 * Primary GROUP identifier
                 */
                if (MATCH (buf, DGROUP)) {
-                       unsigned int val = (unsigned int) strtoul (cp, &ep, 10);
-
-                       if (*cp != '\0' && *ep == '\0') {       /* valid number */
-                               def_group = val;
-                               if ((grp = getgrgid (def_group))) {
-                                       def_gname = xstrdup (grp->gr_name);
-                               } else {
-                                       fprintf (stderr,
-                                                _("%s: unknown GID %s\n"),
-                                                Prog, cp);
-                               }
-                       } else if ((grp = getgrnam (cp))) {
-                               def_group = grp->gr_gid;
-                               def_gname = xstrdup (cp);
-                       } else {
+                       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: unknown group %s\n"), Prog, cp);
+                                        _("%s: the %s configuration in %s will be ignored\n"),
+                                        Prog, DGROUP, def_file);
+                       } else {
+                               def_group = grp->gr_gid;
+                               def_gname = xstrdup (grp->gr_name);
                        }
                }
 
                /*
                 * Default HOME filesystem
                 */
-               else if (MATCH (buf, HOME)) {
+               else if (MATCH (buf, DHOME)) {
                        def_home = xstrdup (cp);
                }
 
                /*
                 * Default Login Shell command
                 */
-               else if (MATCH (buf, SHELL)) {
+               else if (MATCH (buf, DSHELL)) {
                        def_shell = xstrdup (cp);
                }
 
                /*
                 * Default Password Inactive value
                 */
-               else if (MATCH (buf, INACT)) {
-                       long val = strtol (cp, &ep, 10);
-
-                       if (*cp || errno == ERANGE)
-                               def_inactive = val;
-                       else
+               else if (MATCH (buf, DINACT)) {
+                       if (   (getlong (cp, &def_inactive) == 0)
+                           || (def_inactive < -1)) {
+                               fprintf (stderr,
+                                        _("%s: invalid numeric argument '%s'\n"),
+                                        Prog, optarg);
+                               fprintf (stderr,
+                                        _("%s: the %s configuration in %s will be ignored\n"),
+                                        Prog, DINACT, def_file);
                                def_inactive = -1;
+                       }
                }
 
                /*
                 * Default account expiration date
                 */
-               else if (MATCH (buf, EXPIRE)) {
+               else if (MATCH (buf, DEXPIRE)) {
                        def_expire = xstrdup (cp);
                }
 
                /*
                 * Default Skeleton information
                 */
-               else if (MATCH (buf, SKEL)) {
-                       if (*cp == '\0')
+               else if (MATCH (buf, DSKEL)) {
+                       if ('\0' == *cp) {
                                cp = SKEL_DIR;  /* XXX warning: const */
+                       }
 
                        def_template = xstrdup (cp);
                }
@@ -344,13 +382,15 @@ static void get_defaults (void)
                /*
                 * Create by default user mail spool or not ?
                 */
-               else if (MATCH (buf, CREATE_MAIL_SPOOL)) {
-                       if (*cp == '\0')
-                               cp = CREATE_MAIL_SPOOL; /* XXX warning: const */
+               else if (MATCH (buf, DCREATE_MAIL_SPOOL)) {
+                       if (*cp == '\0') {
+                               cp = DCREATE_MAIL_SPOOL;        /* XXX warning: const */
+                       }
 
                        def_create_mail_spool = xstrdup (cp);
                }
        }
+       (void) fclose (fp);
 }
 
 /*
@@ -385,26 +425,31 @@ static int set_defaults (void)
        static char new_file[] = NEW_USER_FILE;
        char *cp;
        int ofd;
-       int out_group = 0;
-       int out_home = 0;
-       int out_inactive = 0;
-       int out_expire = 0;
-       int out_shell = 0;
-       int out_skel = 0;
-       int out_create_mail_spool = 0;
+       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.
         */
-       if ((ofd = mkstemp (new_file)) == -1) {
+       ofd = mkstemp (new_file);
+       if (-1 == ofd) {
                fprintf (stderr,
-                        _("%s: cannot create new defaults file\n"), Prog);
+                        _("%s: cannot create new defaults file\n"),
+                        Prog);
                return -1;
        }
 
-       if (!(ofp = fdopen (ofd, "w"))) {
-               fprintf (stderr, _("%s: cannot open new defaults file\n"),
-                        Prog);
+       ofp = fdopen (ofd, "w");
+       if (NULL == ofp) {
+               fprintf (stderr,
+                        _("%s: cannot open new defaults file\n"),
+                        Prog);
                return -1;
        }
 
@@ -413,42 +458,56 @@ static int set_defaults (void)
         * temporary file, using any new values. Each line is checked
         * to insure that it is not output more than once.
         */
-       if (!(ifp = fopen (def_file, "r"))) {
+       ifp = fopen (def_file, "r");
+       if (NULL == ifp) {
                fprintf (ofp, "# useradd defaults file\n");
                goto skip;
        }
 
-       while (fgets (buf, sizeof buf, ifp)) {
-               if ((cp = strrchr (buf, '\n')))
+       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, def_file, buf);
+                               return -1;
+                       }
+               }
 
                if (!out_group && MATCH (buf, DGROUP)) {
                        fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
-                       out_group++;
-               } else if (!out_home && MATCH (buf, HOME)) {
-                       fprintf (ofp, HOME "%s\n", def_home);
-                       out_home++;
-               } else if (!out_inactive && MATCH (buf, INACT)) {
-                       fprintf (ofp, INACT "%ld\n", def_inactive);
-                       out_inactive++;
-               } else if (!out_expire && MATCH (buf, EXPIRE)) {
-                       fprintf (ofp, EXPIRE "%s\n", def_expire);
-                       out_expire++;
-               } else if (!out_shell && MATCH (buf, SHELL)) {
-                       fprintf (ofp, SHELL "%s\n", def_shell);
-                       out_shell++;
-               } else if (!out_skel && MATCH (buf, SKEL)) {
-                       fprintf (ofp, SKEL "%s\n", def_template);
-                       out_skel++;
+                       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, CREATE_MAIL_SPOOL)) {
-                       fprintf (ofp, CREATE_MAIL_SPOOL "%s\n",
-                                def_create_mail_spool);
-                       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);
        }
-       fclose (ifp);
+       (void) fclose (ifp);
 
       skip:
        /*
@@ -459,25 +518,27 @@ static int set_defaults (void)
        if (!out_group)
                fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group);
        if (!out_home)
-               fprintf (ofp, HOME "%s\n", def_home);
+               fprintf (ofp, DHOME "%s\n", def_home);
        if (!out_inactive)
-               fprintf (ofp, INACT "%ld\n", def_inactive);
+               fprintf (ofp, DINACT "%ld\n", def_inactive);
        if (!out_expire)
-               fprintf (ofp, EXPIRE "%s\n", def_expire);
+               fprintf (ofp, DEXPIRE "%s\n", def_expire);
        if (!out_shell)
-               fprintf (ofp, SHELL "%s\n", def_shell);
+               fprintf (ofp, DSHELL "%s\n", def_shell);
        if (!out_skel)
-               fprintf (ofp, SKEL "%s\n", def_template);
+               fprintf (ofp, DSKEL "%s\n", def_template);
 
        if (!out_create_mail_spool)
-               fprintf (ofp, CREATE_MAIL_SPOOL "%s\n", def_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.
         */
-       fflush (ofp);
-       if (ferror (ofp) || fclose (ofp)) {
+       (void) fflush (ofp);
+       if (   (ferror (ofp) != 0)
+           || (fsync (fileno (ofp)) != 0)
+           || (fclose (ofp) != 0)) {
                unlink (new_file);
                return -1;
        }
@@ -485,10 +546,13 @@ static int set_defaults (void)
        /*
         * Rename the current default file to its backup name.
         */
-       snprintf (buf, sizeof buf, "%s-", def_file);
-       if (rename (def_file, buf) && errno != ENOENT) {
-               snprintf (buf, sizeof buf, _("%s: rename: %s"), Prog, def_file);
-               perror (buf);
+       wlen = snprintf (buf, sizeof buf, "%s-", def_file);
+       assert (wlen < (int) sizeof buf);
+       if ((rename (def_file, buf) != 0) && (ENOENT != errno)) {
+               int err = errno;
+               fprintf (stderr,
+                        _("%s: rename: %s: %s"),
+                        Prog, def_file, strerror (err));
                unlink (new_file);
                return -1;
        }
@@ -496,21 +560,25 @@ static int set_defaults (void)
        /*
         * Rename the new default file to its correct name.
         */
-       if (rename (new_file, def_file)) {
-               snprintf (buf, sizeof buf, _("%s: rename: %s"), Prog, new_file);
-               perror (buf);
+       if (rename (new_file, def_file) != 0) {
+               int err = errno;
+               fprintf (stderr,
+                        _("%s: rename: %s: %s"),
+                        Prog, new_file, strerror (err));
                return -1;
        }
 #ifdef WITH_AUDIT
-       audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing user defaults",
-                     NULL, -1, 1);
+       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));
+                "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;
 }
 
@@ -528,8 +596,9 @@ static int get_groups (char *list)
        int errors = 0;
        int ngroups = 0;
 
-       if (!*list)
+       if ('\0' == *list) {
                return 0;
+       }
 
        /*
         * So long as there is some data to be converted, strip off
@@ -540,8 +609,10 @@ static int get_groups (char *list)
                /*
                 * Strip off a single name from the list
                 */
-               if ((cp = strchr (list, ',')))
+               cp = strchr (list, ',');
+               if (NULL != cp) {
                        *cp++ = '\0';
+               }
 
                /*
                 * Names starting with digits are treated as numerical
@@ -553,9 +624,10 @@ static int get_groups (char *list)
                 * There must be a match, either by GID value or by
                 * string name.
                 */
-               if (!grp) {
-                       fprintf (stderr, _("%s: unknown group %s\n"),
-                                Prog, list);
+               if (NULL == grp) {
+                       fprintf (stderr,
+                                _("%s: group '%s' does not exist\n"),
+                                Prog, list);
                        errors++;
                }
                list = cp;
@@ -564,8 +636,9 @@ static int get_groups (char *list)
                 * If the group doesn't exist, don't dump core...
                 * Instead, try the next one.  --marekm
                 */
-               if (!grp)
+               if (NULL == grp) {
                        continue;
+               }
 
 #ifdef USE_NIS
                /*
@@ -574,17 +647,16 @@ static int get_groups (char *list)
                 */
                if (__isgrNIS ()) {
                        fprintf (stderr,
-                                _("%s: group '%s' is a NIS group.\n"),
-                                Prog, grp->gr_name);
+                                _("%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);
+                                _("%s: too many groups specified (max %d).\n"),
+                                Prog, ngroups);
                        break;
                }
 
@@ -592,15 +664,16 @@ static int get_groups (char *list)
                 * Add the group name to the user's list of groups.
                 */
                user_groups[ngroups++] = xstrdup (grp->gr_name);
-       } while (list);
+       } while (NULL != list);
 
        user_groups[ngroups] = (char *) 0;
 
        /*
         * Any errors in finding group names are fatal
         */
-       if (errors)
+       if (0 != errors) {
                return -1;
+       }
 
        return 0;
 }
@@ -608,36 +681,46 @@ static int get_groups (char *list)
 /*
  * usage - display usage message and exit
  */
-static void usage (void)
+static void usage (int status)
 {
-       fprintf (stderr, _("Usage: useradd [options] LOGIN\n"
-                          "\n"
-                          "Options:\n"
-                          "  -b, --base-dir BASE_DIR   base directory for the new user account\n"
-                          "                            home directory\n"
-                          "  -c, --comment COMMENT             set the GECOS field for the new user account\n"
-                          "  -d, --home-dir HOME_DIR   home directory for the new user account\n"
-                          "  -D, --defaults            print or save modified default useradd\n"
-                          "                            configuration\n"
-                          "  -e, --expiredate EXPIRE_DATE      set account expiration date to EXPIRE_DATE\n"
-                          "  -f, --inactive INACTIVE   set password inactive after expiration\n"
-                          "                            to INACTIVE\n"
-                          "  -g, --gid GROUP           force use GROUP for the new user account\n"
-                          "  -G, --groups GROUPS               list of supplementary groups for the new\n"
-                          "                            user account\n"
-                          "  -h, --help                        display this help message and exit\n"
-                          "  -k, --skel SKEL_DIR               specify an alternative skel directory\n"
-                          "  -K, --key KEY=VALUE               overrides /etc/login.defs defaults\n"
-                          "  -m, --create-home         create home directory for the new user\n"
-                          "                            account\n"
-                          "  -o, --non-unique          allow create user with duplicate\n"
-                          "                            (non-unique) UID\n"
-                          "  -p, --password PASSWORD   use encrypted password for the new user\n"
-                          "                            account\n"
-                          "  -s, --shell SHELL         the login shell for the new user account\n"
-                          "  -u, --uid UID                     force use the UID for the new user account\n"
-                          "\n"));
-       exit (E_USAGE);
+       FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+       (void) fprintf (usageout,
+                       _("Usage: %s [options] LOGIN\n"
+                         "\n"
+                         "Options:\n"),
+                       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 (_("  -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
+       (void) fputs ("\n", usageout);
+       exit (status);
 }
 
 /*
@@ -650,10 +733,11 @@ static void new_pwent (struct passwd *pwent)
 {
        memzero (pwent, sizeof *pwent);
        pwent->pw_name = (char *) user_name;
-       if (is_shadow_pwd)
+       if (is_shadow_pwd) {
                pwent->pw_passwd = (char *) SHADOW_PASSWD_STRING;
-       else
+       } else {
                pwent->pw_passwd = (char *) user_pass;
+       }
 
        pwent->pw_uid = user_id;
        pwent->pw_gid = user_gid;
@@ -664,8 +748,9 @@ static void new_pwent (struct passwd *pwent)
 
 static long scale_age (long x)
 {
-       if (x <= 0)
+       if (x <= 0) {
                return x;
+       }
 
        return x * (DAY / SCALE);
 }
@@ -681,13 +766,25 @@ 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 = time ((time_t *) 0) / SCALE;
-       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);
-       spent->sp_flag = -1;
+       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 = scale_age (-1);
+               spent->sp_max = scale_age (-1);
+               spent->sp_warn = scale_age (-1);
+               spent->sp_inact = scale_age (-1);
+               spent->sp_expire = scale_age (-1);
+       }
+       spent->sp_flag = SHADOW_SP_FLAG_UNSET;
 }
 
 /*
@@ -695,6 +792,11 @@ static void new_spent (struct spwd *spent)
  *
  *     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)
 {
@@ -706,51 +808,36 @@ static void grp_update (void)
        struct sgrp *nsgrp;
 #endif
 
-       /*
-        * Test for unique entries of user_groups in /etc/group
-        * pvrabec@redhat.com
-        */
-       char **user_groups_tmp = user_groups;
-
-       while (*user_groups_tmp) {
-               int count = 0;
-
-               for (gr_rewind (), grp = gr_next (); grp && count < 2;
-                    grp = gr_next ()) {
-                       if (strcmp (*user_groups_tmp, grp->gr_name) == 0) {
-                               count++;
-                       }
-               }
-               if (count > 1) {
-                       fprintf (stderr,
-                                "%s: error not unique group names in group file\n",
-                                Prog);
-                       fail_exit (E_GRP_UPDATE);
-               }
-               user_groups_tmp++;
-       }
-
-       /* Locking and opening of the group files moved to open_files() --gafton */
-
        /*
         * Scan through the entire group file looking for the groups that
         * the user is a member of.
         */
-       for (gr_rewind (), grp = gr_next (); grp; grp = gr_next ()) {
+       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))
+               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 (!ngrp) {
+               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 */
                }
 
@@ -759,17 +846,28 @@ static void grp_update (void)
                 * update the group entry to reflect the change.
                 */
                ngrp->gr_mem = add_list (ngrp->gr_mem, user_name);
-               if (!gr_update (ngrp)) {
+               if (gr_update (ngrp) == 0) {
                        fprintf (stderr,
-                                _("%s: error adding new group entry\n"), Prog);
+                                _("%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_USER_CHAUTHTOK, Prog,
-                             "adding user to group", user_name, -1, 1);
+               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));
+               SYSLOG ((LOG_INFO,
+                        "add '%s' to group '%s'",
+                        user_name, ngrp->gr_name));
        }
 
 #ifdef SHADOWGRP
@@ -781,24 +879,36 @@ static void grp_update (void)
         * that the user is a member of. The administrative list isn't
         * modified.
         */
-       for (sgr_rewind (), sgrp = sgr_next (); sgrp; sgrp = sgr_next ()) {
+       for (sgr_rewind (), sgrp = sgr_next (); NULL != sgrp; sgrp = sgr_next ()) {
 
                /*
                 * See if the user specified this group as one of their
                 * concurrent groups.
                 */
-               if (!gr_locate (sgrp->sg_name))
+               if (gr_locate (sgrp->sg_name) == NULL) {
                        continue;
+               }
 
-               if (!is_on_list (user_groups, sgrp->sg_name))
+               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 (!nsgrp) {
+               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 */
                }
 
@@ -807,178 +917,30 @@ static void grp_update (void)
                 * update the group entry to reflect the change.
                 */
                nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_name);
-               if (!sgr_update (nsgrp)) {
+               if (sgr_update (nsgrp) == 0) {
                        fprintf (stderr,
-                                _("%s: error adding new group entry\n"), Prog);
-                       fail_exit (E_GRP_UPDATE);
-               }
+                                _("%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_USER_CHAUTHTOK, Prog,
-                             "adding user to shadow group", user_name, -1, 1);
+                       audit_logger (AUDIT_ADD_USER, Prog,
+                                     "adding user to shadow group",
+                                     user_name, AUDIT_NO_ID,
+                                     SHADOW_AUDIT_FAILURE);
 #endif
-               SYSLOG ((LOG_INFO, "add `%s' to shadow group `%s'",
-                        user_name, nsgrp->sg_name));
-       }
-#endif                         /* SHADOWGRP */
-}
-
-/*
- * find_new_uid - find the next available UID
- *
- *     find_new_uid() locates the next highest unused UID in the password
- *     file, or checks the given user ID against the existing ones for
- *     uniqueness.
- */
-static void find_new_uid (void)
-{
-       const struct passwd *pwd;
-       uid_t uid_min, uid_max;
-
-       uid_min = getdef_unum ("UID_MIN", 1000);
-       uid_max = getdef_unum ("UID_MAX", 60000);
-
-       /*
-        * Start with some UID value if the user didn't provide us with
-        * one already.
-        */
-       if (!uflg)
-               user_id = uid_min;
-
-       /*
-        * Search the entire password file, either looking for this
-        * UID (if the user specified one with -u) or looking for the
-        * largest unused value.
-        */
-#ifdef NO_GETPWENT
-       pw_rewind ();
-       while ((pwd = pw_next ())) {
-#else                          /* using getpwent() we can check against NIS users etc. */
-       setpwent ();
-       while ((pwd = getpwent ())) {
-#endif
-               if (strcmp (user_name, pwd->pw_name) == 0) {
-                       fprintf (stderr, _("%s: name %s is not unique\n"),
-                                Prog, user_name);
-#ifdef WITH_AUDIT
-                       audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding user",
-                                     user_name, user_id, 0);
-#endif
-                       exit (E_NAME_IN_USE);
+                       fail_exit (E_GRP_UPDATE);
                }
-               if (uflg && user_id == pwd->pw_uid) {
-                       fprintf (stderr, _("%s: UID %u is not unique\n"),
-                                Prog, (unsigned int) user_id);
 #ifdef WITH_AUDIT
-                       audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding user",
-                                     user_name, user_id, 0);
+               audit_logger (AUDIT_ADD_USER, Prog,
+                             "adding user to shadow group",
+                             user_name, AUDIT_NO_ID,
+                             SHADOW_AUDIT_SUCCESS);
 #endif
-                       exit (E_UID_IN_USE);
-               }
-               if (!uflg && pwd->pw_uid >= user_id) {
-                       if (pwd->pw_uid > uid_max)
-                               continue;
-                       user_id = pwd->pw_uid + 1;
-               }
-       }
-
-       /*
-        * If a user with UID equal to UID_MAX exists, the above algorithm
-        * will give us UID_MAX+1 even if not unique. Search for the first
-        * free UID starting with UID_MIN (it's O(n*n) but can be avoided
-        * by not having users with UID equal to UID_MAX).  --marekm
-        */
-       if (!uflg && user_id == uid_max + 1) {
-               for (user_id = uid_min; user_id < uid_max; user_id++) {
-#ifdef NO_GETPWENT
-                       pw_rewind ();
-                       while ((pwd = pw_next ())
-                              && pwd->pw_uid != user_id);
-                       if (!pwd)
-                               break;
-#else
-                       if (!getpwuid (user_id))
-                               break;
-#endif
-               }
-               if (user_id == uid_max) {
-                       fprintf (stderr, _("%s: can't get unique UID\n"), Prog);
-                       fail_exit (E_UID_IN_USE);
-               }
-       }
-}
-
- /*
-  * find_new_gid - find the next available GID
-  *
-  *     find_new_gid() locates the next highest unused GID in the group
-  *     file, or checks the given group ID against the existing ones for
-  *     uniqueness.
-  */
-
-static void find_new_gid ()
-{
-       const struct group *grp;
-       gid_t gid_min, gid_max;
-
-       gid_min = getdef_num ("GID_MIN", 500);
-       gid_max = getdef_num ("GID_MAX", 60000);
-
-       /*
-        * Start with some GID value if the user didn't provide us with
-        * one already.
-        */
-       user_gid = gid_min;
-
-       /*
-        * Search the entire group file, either looking for this
-        * GID (if the user specified one with -g) or looking for the
-        * largest unused value.
-        */
-#ifdef NO_GETGRENT
-       gr_rewind ();
-       while ((grp = gr_next ()))
-#else
-       setgrent ();
-       while ((grp = getgrent ()))
-#endif
-       {
-               if (strcmp (user_name, grp->gr_name) == 0) {
-                       user_gid = grp->gr_gid;
-                       return;
-               }
-               if (grp->gr_gid >= user_gid) {
-                       if (grp->gr_gid > gid_max)
-                               continue;
-                       user_gid = grp->gr_gid + 1;
-               }
-       }
-#ifndef NO_GETGRENT            /* glibc does have this, so ... */
-       /* A quick test gets here: if the UID is available
-        * as a GID, go ahead and use it */
-       if (!getgrgid (user_id)) {
-               user_gid = user_id;
-               return;
-       }
-#endif
-       if (user_gid == gid_max + 1) {
-               for (user_gid = gid_min; user_gid < gid_max; user_gid++) {
-#ifdef NO_GETGRENT
-                       gr_rewind ();
-                       while ((grp = gr_next ()) && grp->gr_gid != user_gid);
-                       if (!grp)
-                               break;
-#else
-                       if (!getgrgid (user_gid))
-                               break;
-#endif
-               }
-               if (user_gid == gid_max) {
-                       fprintf (stderr,
-                                "%s: can't get unique gid (run out of GIDs)\n",
-                                Prog);
-                       fail_exit (4);
-               }
+               SYSLOG ((LOG_INFO,
+                        "add '%s' to shadow group '%s'",
+                        user_name, nsgrp->sg_name));
        }
+#endif                         /* SHADOWGRP */
 }
 
 /*
@@ -991,7 +953,7 @@ static void find_new_gid ()
 static void process_flags (int argc, char **argv)
 {
        const struct group *grp;
-       int anyflag = 0;
+       bool anyflag = false;
        char *cp;
 
        {
@@ -1003,7 +965,7 @@ static void process_flags (int argc, char **argv)
                        {"base-dir", required_argument, NULL, 'b'},
                        {"comment", required_argument, NULL, 'c'},
                        {"home-dir", required_argument, NULL, 'd'},
-                       {"defaults", required_argument, NULL, 'D'},
+                       {"defaults", no_argument, NULL, 'D'},
                        {"expiredate", required_argument, NULL, 'e'},
                        {"inactive", required_argument, NULL, 'f'},
                        {"gid", required_argument, NULL, 'g'},
@@ -1012,107 +974,119 @@ static void process_flags (int argc, char **argv)
                        {"skel", required_argument, NULL, 'k'},
                        {"key", required_argument, NULL, 'K'},
                        {"create-home", no_argument, NULL, 'm'},
+                       {"no-create-home", no_argument, NULL, 'M'},
+                       {"no-log-init", no_argument, NULL, 'l'},
+                       {"no-user-group", no_argument, NULL, 'N'},
                        {"non-unique", no_argument, NULL, 'o'},
                        {"password", required_argument, NULL, 'p'},
+                       {"system", no_argument, NULL, 'r'},
                        {"shell", required_argument, NULL, 's'},
+#ifdef WITH_SELINUX
+                       {"selinux-user", required_argument, NULL, 'Z'},
+#endif
                        {"uid", required_argument, NULL, 'u'},
+                       {"user-group", no_argument, NULL, 'U'},
                        {NULL, 0, NULL, '\0'}
                };
-               while ((c =
-                       getopt_long (argc, argv, "b:c:d:De:f:g:G:k:K:mMop:s:u:",
-                                    long_options, NULL)) != -1) {
+               while ((c = getopt_long (argc, argv,
+#ifdef WITH_SELINUX
+                                        "b:c:d:De:f:g:G:hk:K:lmMNop:rs:u:UZ:",
+#else
+                                        "b:c:d:De:f:g:G:hk:K:lmMNop:rs:u:U",
+#endif
+                                        long_options, NULL)) != -1) {
                        switch (c) {
                        case 'b':
-                               if (!Dflg)
-                                       usage ();
-
-                               if (!VALID (optarg)
-                                   || optarg[0] != '/') {
+                               if (   ( !VALID (optarg) )
+                                   || ( optarg[0] != '/' )) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: invalid base directory '%s'\n"),
-                                                Prog, optarg);
+                                                _("%s: invalid base directory '%s'\n"),
+                                                Prog, optarg);
                                        exit (E_BAD_ARG);
                                }
                                def_home = optarg;
-                               bflg++;
+                               bflg = true;
                                break;
                        case 'c':
                                if (!VALID (optarg)) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: invalid comment '%s'\n"),
-                                                Prog, optarg);
+                                                _("%s: invalid comment '%s'\n"),
+                                                Prog, optarg);
                                        exit (E_BAD_ARG);
                                }
                                user_comment = optarg;
-                               cflg++;
+                               cflg = true;
                                break;
                        case 'd':
-                               if (!VALID (optarg)
-                                   || optarg[0] != '/') {
+                               if (   ( !VALID (optarg) )
+                                   || ( optarg[0] != '/' )) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: invalid home directory '%s'\n"),
-                                                Prog, optarg);
+                                                _("%s: invalid home directory '%s'\n"),
+                                                Prog, optarg);
                                        exit (E_BAD_ARG);
                                }
                                user_home = optarg;
-                               dflg++;
+                               dflg = true;
                                break;
                        case 'D':
-                               if (anyflag)
-                                       usage ();
-                               Dflg++;
+                               if (anyflag) {
+                                       usage (E_USAGE);
+                               }
+                               Dflg = true;
                                break;
                        case 'e':
-                               if (*optarg) {
+                               if ('\0' != *optarg) {
                                        user_expire = strtoday (optarg);
                                        if (user_expire == -1) {
                                                fprintf (stderr,
-                                                        _
-                                                        ("%s: invalid date '%s'\n"),
-                                                        Prog, optarg);
+                                                        _("%s: invalid date '%s'\n"),
+                                                        Prog, optarg);
                                                exit (E_BAD_ARG);
                                        }
-                               } else
+                               } else {
                                        user_expire = -1;
+                               }
 
                                /*
                                 * -e "" is allowed - it's a no-op without /etc/shadow
                                 */
-                               if (*optarg && !is_shadow_pwd) {
+                               if (('\0' != *optarg) && !is_shadow_pwd) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: shadow passwords required for -e\n"),
-                                                Prog);
+                                                _("%s: shadow passwords required for -e\n"),
+                                                Prog);
                                        exit (E_USAGE);
                                }
-                               if (Dflg)
+                               if (Dflg) {
                                        def_expire = optarg;
-                               eflg++;
+                               }
+                               eflg = true;
                                break;
                        case 'f':
-                               def_inactive = get_number (optarg);
+                               if (   (getlong (optarg, &def_inactive) == 0)
+                                   || (def_inactive < -1)) {
+                                       fprintf (stderr,
+                                                _("%s: invalid numeric argument '%s'\n"),
+                                                Prog, optarg);
+                                       usage (E_USAGE);
+                               }
                                /*
-                                * -f -1 is allowed - it's a no-op without /etc/shadow
+                                * -f -1 is allowed
+                                * it's a no-op without /etc/shadow
                                 */
-                               if (def_inactive != -1 && !is_shadow_pwd) {
+                               if ((-1 != def_inactive) && !is_shadow_pwd) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: shadow passwords required for -f\n"),
-                                                Prog);
+                                                _("%s: shadow passwords required for -f\n"),
+                                                Prog);
                                        exit (E_USAGE);
                                }
-                               fflg++;
+                               fflg = true;
                                break;
                        case 'g':
                                grp = getgr_nam_gid (optarg);
-                               if (!grp) {
+                               if (NULL == grp) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: unknown group %s\n"),
-                                                Prog, optarg);
+                                                _("%s: group '%s' does not exist\n"),
+                                                Prog, optarg);
                                        exit (E_NOTFOUND);
                                }
                                if (Dflg) {
@@ -1121,21 +1095,23 @@ static void process_flags (int argc, char **argv)
                                } else {
                                        user_gid = grp->gr_gid;
                                }
-                               gflg++;
+                               gflg = true;
                                break;
                        case 'G':
-                               if (get_groups (optarg))
+                               if (get_groups (optarg) != 0) {
                                        exit (E_NOTFOUND);
-                               if (user_groups[0])
-                                       do_grp_update++;
-                               Gflg++;
+                               }
+                               if (NULL != user_groups[0]) {
+                                       do_grp_update = true;
+                               }
+                               Gflg = true;
                                break;
                        case 'h':
-                               usage ();
+                               usage (E_SUCCESS);
                                break;
                        case 'k':
                                def_template = optarg;
-                               kflg++;
+                               kflg = true;
                                break;
                        case 'K':
                                /*
@@ -1144,116 +1120,203 @@ static void process_flags (int argc, char **argv)
                                 * note: -K UID_MIN=10,UID_MAX=499 doesn't work yet
                                 */
                                cp = strchr (optarg, '=');
-                               if (!cp) {
+                               if (NULL == cp) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: -K requires KEY=VALUE\n"),
-                                                Prog);
+                                                _("%s: -K requires KEY=VALUE\n"),
+                                                Prog);
                                        exit (E_BAD_ARG);
                                }
                                /* terminate name, point to value */
-                               *cp++ = '\0';
-                               if (putdef_str (optarg, cp) < 0)
+                               *cp = '\0';
+                               cp++;
+                               if (putdef_str (optarg, cp) < 0) {
                                        exit (E_BAD_ARG);
+                               }
+                               break;
+                       case 'l':
+                               lflg = true;
                                break;
                        case 'm':
-                               mflg++;
+                               mflg = true;
+                               break;
+                       case 'M':
+                               Mflg = true;
+                               break;
+                       case 'N':
+                               Nflg = true;
                                break;
                        case 'o':
-                               oflg++;
+                               oflg = true;
                                break;
                        case 'p':       /* set encrypted password */
                                if (!VALID (optarg)) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: invalid field '%s'\n"),
-                                                Prog, optarg);
+                                                _("%s: invalid field '%s'\n"),
+                                                Prog, optarg);
                                        exit (E_BAD_ARG);
                                }
                                user_pass = optarg;
                                break;
+                       case 'r':
+                               rflg = true;
+                               break;
                        case 's':
-                               if (!VALID (optarg)
-                                   || (optarg[0]
-                                       && (optarg[0] != '/'
-                                           && optarg[0] != '*'))) {
+                               if (   ( !VALID (optarg) )
+                                   || (   ('\0' != optarg[0])
+                                       && ('/'  != optarg[0])
+                                       && ('*'  != optarg[0]) )) {
                                        fprintf (stderr,
-                                                _
-                                                ("%s: invalid shell '%s'\n"),
-                                                Prog, optarg);
+                                                _("%s: invalid shell '%s'\n"),
+                                                Prog, optarg);
                                        exit (E_BAD_ARG);
                                }
                                user_shell = optarg;
                                def_shell = optarg;
-                               sflg++;
+                               sflg = true;
                                break;
                        case 'u':
-                               user_id = get_uid (optarg);
-                               uflg++;
+                               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;
+                                       Zflg = true;
+                               } else {
+                                       fprintf (stderr,
+                                                _("%s: -Z requires SELinux enabled kernel\n"),
+                                                Prog);
+
+                                       exit (E_BAD_ARG);
+                               }
+                               break;
+#endif
                        default:
-                               usage ();
+                               usage (E_USAGE);
                        }
-                       anyflag++;
+                       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) || (kflg && !mflg))
-               usage ();
+       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 ();
+               if (optind != argc) {
+                       usage (E_USAGE);
+               }
 
-               if (uflg || oflg || Gflg || dflg || cflg || mflg)
-                       usage ();
+               if (uflg || oflg || Gflg || dflg || cflg || mflg) {
+                       usage (E_USAGE);
+               }
        } else {
-               if (optind != argc - 1)
-                       usage ();
+               if (optind != argc - 1) {
+                       usage (E_USAGE);
+               }
 
                user_name = argv[optind];
-               if (!check_user_name (user_name)) {
+               if (!is_valid_user_name (user_name)) {
                        fprintf (stderr,
-                                _
-                                ("%s: invalid user name '%s'\n"),
-                                Prog, user_name);
+                                _("%s: invalid user name '%s'\n"),
+                                Prog, user_name);
 #ifdef WITH_AUDIT
-                       audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding user",
-                                     user_name, -1, 0);
+                       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);
 
-                       uh = xmalloc (strlen (def_home) +
-                                     strlen (user_name) + 2);
-                       sprintf (uh, "%s/%s", def_home, user_name);
                        user_home = uh;
                }
        }
 
-       if (!eflg)
+       if (!eflg) {
                user_expire = strtoday (def_expire);
+       }
 
-       if (!gflg)
+       if (!gflg) {
                user_gid = def_group;
+       }
 
-       if (!sflg)
+       if (!sflg) {
                user_shell = def_shell;
+       }
 
-       /* TODO: add handle change default spool mail creation by 
-          -K CREATE_MAIL_SPOOL={yes,no}. It need rewrite internal API for handle
-          shadow tools configuration */
        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;
+       }
 }
 
 /*
@@ -1264,38 +1327,87 @@ static void process_flags (int argc, char **argv)
  */
 static void close_files (void)
 {
-       if (!pw_close ()) {
-               fprintf (stderr, _("%s: cannot rewrite password file\n"), Prog);
+       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 ()) {
+       if (is_shadow_pwd && (spw_close () == 0)) {
                fprintf (stderr,
-                        _("%s: cannot rewrite shadow password file\n"), Prog);
+                        _("%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 ()) {
+               if (gr_close () == 0) {
                        fprintf (stderr,
-                                _("%s: cannot rewrite group file\n"), Prog);
+                                _("%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 ()) {
+               if (is_shadow_grp && (sgr_close () == 0)) {
                        fprintf (stderr,
-                                _
-                                ("%s: cannot rewrite shadow group file\n"),
-                                Prog);
+                                _("%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
        }
-       if (is_shadow_pwd)
-               spw_unlock ();
-       pw_unlock ();
-       gr_unlock ();
+       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)
-               sgr_unlock ();
+       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
 }
 
@@ -1306,68 +1418,62 @@ static void close_files (void)
  */
 static void open_files (void)
 {
-       if (!pw_lock ()) {
-               fprintf (stderr, _("%s: unable to lock password file\n"), Prog);
-#ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "locking password file", user_name, user_id, 0);
-#endif
-               exit (E_PW_UPDATE);
-       }
-       if (!pw_open (O_RDWR)) {
-               fprintf (stderr, _("%s: unable to open password file\n"), Prog);
-#ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "opening password file", user_name, user_id, 0);
-#endif
-               pw_unlock ();
-               exit (E_PW_UPDATE);
-       }
-       if (is_shadow_pwd && !spw_lock ()) {
+       if (pw_lock () == 0) {
                fprintf (stderr,
-                        _("%s: cannot lock shadow password file\n"), Prog);
-#ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "locking shadow password file", user_name,
-                             user_id, 0);
-#endif
-               pw_unlock ();
+                        _("%s: cannot lock %s; try again later.\n"),
+                        Prog, pw_dbname ());
                exit (E_PW_UPDATE);
        }
-       if (is_shadow_pwd && !spw_open (O_RDWR)) {
-               fprintf (stderr,
-                        _("%s: cannot open shadow password file\n"), Prog);
-#ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "opening shadow password file", user_name,
-                             user_id, 0);
-#endif
-               spw_unlock ();
-               pw_unlock ();
-               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) {
+               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);
+               }
        }
+
        /*
         * Lock and open the group file.
         */
-
-       if (!gr_lock ()) {
-               fprintf (stderr, _("%s: error locking group file\n"), Prog);
+       if (gr_lock () == 0) {
+               fprintf (stderr,
+                        _("%s: cannot lock %s; try again later.\n"),
+                        Prog, gr_dbname ());
                fail_exit (E_GRP_UPDATE);
        }
-       if (!gr_open (O_RDWR)) {
-               fprintf (stderr, _("%s: error opening group file\n"), Prog);
+       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 ()) {
-               fprintf (stderr,
-                        _("%s: error locking shadow group file\n"), Prog);
-               fail_exit (E_GRP_UPDATE);
-       }
-       if (is_shadow_grp && !sgr_open (O_RDWR)) {
-               fprintf (stderr,
-                        _("%s: error opening shadow group file\n"), Prog);
-               fail_exit (E_GRP_UPDATE);
+       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
 }
@@ -1434,34 +1540,68 @@ static void grp_add (void)
        /*
         * Write out the new group file entry.
         */
-       if (!gr_update (&grp)) {
-               fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
+       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)) {
-               fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
+       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));
-       do_grp_update++;
+#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 (fd >= 0) {
-               memzero (&fl, sizeof (fl));
-               lseek (fd, (off_t) sizeof (fl) * uid, SEEK_SET);
-               write (fd, &fl, sizeof (fl));
-               close (fd);
+       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 */
        }
 }
 
@@ -1469,13 +1609,25 @@ 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 (fd >= 0) {
-               memzero (&ll, sizeof (ll));
-               lseek (fd, (off_t) sizeof (ll) * uid, SEEK_SET);
-               write (fd, &ll, sizeof (ll));
-               close (fd);
+       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 */
        }
 }
 
@@ -1490,9 +1642,6 @@ static void usr_update (void)
        struct passwd pwent;
        struct spwd spent;
 
-       if (!oflg)
-               find_new_uid ();
-
        /*
         * Fill in the password structure with any new fields, making
         * copies of strings.
@@ -1505,9 +1654,9 @@ static void usr_update (void)
         * 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));
+                "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
@@ -1515,7 +1664,8 @@ static void usr_update (void)
         * no user with this UID exists yet (entries for shared UIDs
         * are left unchanged).  --marekm
         */
-       if (!getpwuid (user_id)) {
+       /* local, no need for xgetpwuid */
+       if ((!lflg) && (getpwuid (user_id) == NULL)) {
                faillog_reset (user_id);
                lastlog_reset (user_id);
        }
@@ -1523,38 +1673,69 @@ static void usr_update (void)
        /*
         * Put the new (struct passwd) in the table.
         */
-       if (!pw_update (&pwent)) {
+       if (pw_update (&pwent) == 0) {
                fprintf (stderr,
-                        _("%s: error adding new password entry\n"), Prog);
-               exit (E_PW_UPDATE);
+                        _("%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)) {
+       if (is_shadow_pwd && (spw_update (&spent) == 0)) {
                fprintf (stderr,
-                        _
-                        ("%s: error adding new shadow password entry\n"),
-                        Prog);
+                        _("%s: failed to prepare the new %s entry '%s'\n"),
+                        Prog, spw_dbname (), spent.sp_namp);
 #ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "adding shadow password", user_name, user_id, 0);
+               audit_logger (AUDIT_ADD_USER, Prog,
+                             "adding shadow password",
+                             user_name, (unsigned int) user_id,
+                             SHADOW_AUDIT_FAILURE);
 #endif
-               exit (E_PW_UPDATE);
+               fail_exit (E_PW_UPDATE);
        }
 #ifdef WITH_AUDIT
-       audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding user", user_name,
-                     user_id, 1);
+       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)
+       if (do_grp_update) {
                grp_update ();
+       }
 }
 
+#ifdef WITH_SELINUX
+static void selinux_update_mapping (void) {
+       if (is_selinux_enabled () <= 0) return;
+
+       if (*user_selinux) { /* must be done after passwd write() */
+               const char *argv[7];
+               argv[0] = "/usr/sbin/semanage";
+               argv[1] = "login";
+               argv[2] = "-a";
+               argv[3] = "-s";
+               argv[4] = user_selinux;
+               argv[5] = user_name;
+               argv[6] = NULL;
+               if (safe_system (argv[0], argv, NULL, 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
+               }
+       }
+}
+#endif
 /*
  * create_home - create the user's home directory
  *
@@ -1564,27 +1745,36 @@ static void usr_update (void)
  */
 static void create_home (void)
 {
-       if (access (user_home, F_OK)) {
+       if (access (user_home, F_OK) != 0) {
+#ifdef WITH_SELINUX
+               selinux_file_context (user_home);
+#endif
                /* XXX - create missing parent directories.  --marekm */
-               if (mkdir (user_home, 0)) {
+               if (mkdir (user_home, 0) != 0) {
                        fprintf (stderr,
-                                _
-                                ("%s: cannot create directory %s\n"),
-                                Prog, user_home);
+                                _("%s: cannot create directory %s\n"),
+                                Prog, user_home);
 #ifdef WITH_AUDIT
-                       audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                                     "adding home directory", user_name,
-                                     user_id, 0);
+                       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++;
+               home_added = true;
 #ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "adding home directory", user_name, user_id, 1);
+               audit_logger (AUDIT_ADD_USER, Prog,
+                             "adding home directory",
+                             user_name, (unsigned int) user_id,
+                             SHADOW_AUDIT_SUCCESS);
+#endif
+#ifdef WITH_SELINUX
+               /* Reset SELinux to create files with default contexts */
+               setfscreatecon (NULL);
 #endif
        }
 }
@@ -1605,7 +1795,10 @@ static void create_mail (void)
        mode_t mode;
 
        if (strcasecmp (create_mail_spool, "yes") == 0) {
-               spool = getdef_str ("MAIL_DIR") ? : "/var/mail";
+               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);
@@ -1614,11 +1807,10 @@ static void create_mail (void)
                        return;
                }
 
-               gr = getgrnam ("mail");
-               if (!gr) {
-                       fprintf (stderr,
-                                _
-                                ("Group 'mail' not found. Creating the user mailbox file with 0600 mode.\n"));
+               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 {
@@ -1626,9 +1818,12 @@ static void create_mail (void)
                        mode = 0660;
                }
 
-               if (fchown (fd, user_id, gid) || fchmod (fd, mode))
+               if (   (fchown (fd, user_id, gid) != 0)
+                   || (fchmod (fd, mode) != 0)) {
                        perror (_("Setting mailbox file permissions"));
+               }
 
+               fsync (fd);
                close (fd);
        }
 }
@@ -1638,11 +1833,12 @@ static void create_mail (void)
  */
 int main (int argc, char **argv)
 {
+#ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
        pam_handle_t *pamh = NULL;
-       struct passwd *pampw;
        int retval;
-#endif
+#endif                         /* USE_PAM */
+#endif                         /* ACCT_TOOLS_SETUID */
 
 #ifdef WITH_AUDIT
        audit_help_open ();
@@ -1653,14 +1849,14 @@ int main (int argc, char **argv)
         */
        Prog = Basename (argv[0]);
 
-       setlocale (LC_ALL, "");
-       bindtextdomain (PACKAGE, LOCALEDIR);
-       textdomain (PACKAGE);
+       (void) setlocale (LC_ALL, "");
+       (void) bindtextdomain (PACKAGE, LOCALEDIR);
+       (void) textdomain (PACKAGE);
 
        OPENLOG ("useradd");
 
        sys_ngroups = sysconf (_SC_NGROUPS_MAX);
-       user_groups = malloc ((1 + sys_ngroups) * sizeof (char *));
+       user_groups = (char **) xmalloc ((1 + sys_ngroups) * sizeof (char *));
        /*
         * Initialize the list to be empty
         */
@@ -1676,45 +1872,47 @@ int main (int argc, char **argv)
 
        process_flags (argc, argv);
 
+#ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
-       retval = PAM_SUCCESS;
-
-       pampw = getpwuid (getuid ());
-       if (pampw == NULL) {
-               retval = PAM_USER_UNKNOWN;
-       }
+       {
+               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);
+               }
 
-       if (retval == PAM_SUCCESS) {
                retval = pam_start ("useradd", pampw->pw_name, &conv, &pamh);
        }
 
-       if (retval == PAM_SUCCESS) {
+       if (PAM_SUCCESS == retval) {
                retval = pam_authenticate (pamh, 0);
-               if (retval != PAM_SUCCESS) {
-                       pam_end (pamh, retval);
-               }
        }
 
-       if (retval == PAM_SUCCESS) {
+       if (PAM_SUCCESS == retval) {
                retval = pam_acct_mgmt (pamh, 0);
-               if (retval != PAM_SUCCESS) {
-                       pam_end (pamh, retval);
-               }
        }
 
-       if (retval != PAM_SUCCESS) {
+       if (NULL != pamh) {
+               (void) pam_end (pamh, retval);
+       }
+       if (PAM_SUCCESS != retval) {
                fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
-               exit (1);
+               fail_exit (1);
        }
 #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 ()? 1 : 0);
+               if (gflg || bflg || fflg || eflg || sflg) {
+                       exit ((set_defaults () != 0) ? 1 : 0);
+               }
 
                show_defaults ();
                exit (E_SUCCESS);
@@ -1723,13 +1921,15 @@ int main (int argc, char **argv)
        /*
         * Start with a quick check to see if the user exists.
         */
-       if (getpwnam (user_name)) {
-               fprintf (stderr, _("%s: user %s exists\n"), Prog, user_name);
+       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_USER_CHAUTHTOK, Prog, "adding user",
-                             user_name, -1, 0);
+               audit_logger (AUDIT_ADD_USER, Prog,
+                             "adding user",
+                             user_name, AUDIT_NO_ID,
+                             SHADOW_AUDIT_FAILURE);
 #endif
-               exit (E_NAME_IN_USE);
+               fail_exit (E_NAME_IN_USE);
        }
 
        /*
@@ -1738,17 +1938,19 @@ int main (int argc, char **argv)
         * to that group, use useradd -g username username.
         * --bero
         */
-       if (!gflg) {
-               if (getgrnam (user_name)) {
+       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);
+                                _("%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_USER_CHAUTHTOK, Prog,
-                                     "adding group", user_name, -1, 0);
+                       audit_logger (AUDIT_ADD_USER, Prog,
+                                     "adding group",
+                                     user_name, AUDIT_NO_ID,
+                                     SHADOW_AUDIT_FAILURE);
 #endif
-                       exit (E_NAME_IN_USE);
+                       fail_exit (E_NAME_IN_USE);
                }
        }
 
@@ -1767,13 +1969,36 @@ int main (int argc, char **argv)
                /* 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 */
-               find_new_uid ();
+               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);
+                       }
+               }
        }
 
        /* 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 (!(nflg || gflg)) {
-               find_new_gid ();
+       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 ();
        }
 
@@ -1781,46 +2006,31 @@ int main (int argc, char **argv)
 
        if (mflg) {
                create_home ();
-               if (home_added)
+               if (home_added) {
                        copy_tree (def_template, user_home, user_id, user_gid);
-               else
+               } else {
                        fprintf (stderr,
-                                _
-                                ("%s: warning: the home directory already exists.\n"
-                                 "Not copying any file from skel directory into it.\n"),
-                                Prog);
+                                _("%s: warning: the home directory already exists.\n"
+                                  "Not copying any file from skel directory into it.\n"),
+                                Prog);
+               }
 
-       } else if (getdef_str ("CREATE_HOME")) {
-               /*
-                * RedHat added the CREATE_HOME option in login.defs in their
-                * version of shadow-utils (which makes -m the default, with
-                * new -M option to turn it off). Unfortunately, this
-                * changes the way useradd works (it can be run by scripts
-                * expecting some standard behaviour), compared to other
-                * Unices and other Linux distributions, and also adds a lot
-                * of confusion :-(.
-                * So we now recognize CREATE_HOME and give a warning here
-                * (better than "configuration error ... notify administrator"
-                * errors in every program that reads /etc/login.defs). -MM
-                */
-               fprintf (stderr,
-                        _
-                        ("%s: warning: CREATE_HOME not supported, please use -m instead.\n"),
-                        Prog);
        }
 
-       create_mail ();
+       /* Do not create mail directory for system accounts */
+       if( !rflg ) {
+               create_mail ();
+       }
 
        close_files ();
 
+#ifdef WITH_SELINUX
+       selinux_update_mapping ();
+#endif
+
        nscd_flush_cache ("passwd");
        nscd_flush_cache ("group");
 
-#ifdef USE_PAM
-       if (retval == PAM_SUCCESS)
-               pam_end (pamh, PAM_SUCCESS);
-#endif                         /* USE_PAM */
-
-       exit (E_SUCCESS);
-       /* NOT REACHED */
+       return E_SUCCESS;
 }
+