/*
- * 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>
/*
* Global variables
*/
+char *Prog;
+
/*
* These defaults are used if there is no defaults file.
*/
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 */
#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);
*/
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)
{
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++;
* 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);
}
/*
* 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);
}
/*
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;
}
* 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:
/*
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;
}
/*
* 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;
}
/*
* 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;
}
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
/*
* 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
* 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;
* 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
/*
*/
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;
}
* 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;
}
/*
* 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);
}
/*
{
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;
static long scale_age (long x)
{
- if (x <= 0)
+ if (x <= 0) {
return x;
+ }
return x * (DAY / SCALE);
}
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;
}
/*
*
* 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)
{
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 */
}
* 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
* 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 */
}
* 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 */
}
/*
static void process_flags (int argc, char **argv)
{
const struct group *grp;
- int anyflag = 0;
+ bool anyflag = false;
char *cp;
{
{"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'},
{"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) {
} 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':
/*
* 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;
+ }
}
/*
*/
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
}
*/
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
}
/*
* 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 */
}
}
{
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 */
}
}
struct passwd pwent;
struct spwd spent;
- if (!oflg)
- find_new_uid ();
-
/*
* Fill in the password structure with any new fields, making
* copies of strings.
* 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
* 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);
}
/*
* 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
*
*/
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
}
}
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);
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 {
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);
}
}
*/
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 ();
*/
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
*/
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);
/*
* 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);
}
/*
* 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);
}
}
/* 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 ();
}
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;
}
+