#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
#include <stdio.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include "sgroupio.h"
#endif
#include "shadowio.h"
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
#ifndef SKEL_DIR
#define SKEL_DIR "/etc/skel"
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 bool is_shadow_pwd;
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 */
+ 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 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 process_flags (int argc, char **argv);
static void close_files (void);
static void open_files (void);
+static void open_shadow (void);
static void faillog_reset (uid_t);
static void lastlog_reset (uid_t);
static void usr_update (void);
/*
* 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)) {
+ else if (MATCH (buf, DINACT)) {
if ( (getlong (cp, &def_inactive) == 0)
|| (def_inactive < -1)) {
fprintf (stderr,
Prog, optarg);
fprintf (stderr,
_("%s: the %s configuration in %s will be ignored\n"),
- Prog, INACT, def_file);
+ 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)) {
+ else if (MATCH (buf, DSKEL)) {
if ('\0' == *cp) {
cp = SKEL_DIR; /* XXX warning: const */
}
/*
* Create by default user mail spool or not ?
*/
- else if (MATCH (buf, CREATE_MAIL_SPOOL)) {
+ else if (MATCH (buf, DCREATE_MAIL_SPOOL)) {
if (*cp == '\0') {
- cp = CREATE_MAIL_SPOOL; /* XXX warning: const */
+ cp = "no"; /* XXX warning: const */
}
def_create_mail_spool = xstrdup (cp);
static char new_file[] = NEW_USER_FILE;
char *cp;
int ofd;
+ int wlen;
bool out_group = false;
bool out_home = false;
bool out_inactive = false;
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 = true;
- } else if (!out_home && MATCH (buf, HOME)) {
- fprintf (ofp, HOME "%s\n", def_home);
+ } else if (!out_home && MATCH (buf, DHOME)) {
+ fprintf (ofp, DHOME "%s\n", def_home);
out_home = true;
- } else if (!out_inactive && MATCH (buf, INACT)) {
- fprintf (ofp, INACT "%ld\n", def_inactive);
+ } else if (!out_inactive && MATCH (buf, DINACT)) {
+ fprintf (ofp, DINACT "%ld\n", def_inactive);
out_inactive = true;
- } else if (!out_expire && MATCH (buf, EXPIRE)) {
- fprintf (ofp, EXPIRE "%s\n", def_expire);
+ } else if (!out_expire && MATCH (buf, DEXPIRE)) {
+ fprintf (ofp, DEXPIRE "%s\n", def_expire);
out_expire = true;
- } else if (!out_shell && MATCH (buf, SHELL)) {
- fprintf (ofp, SHELL "%s\n", def_shell);
+ } else if (!out_shell && MATCH (buf, DSHELL)) {
+ fprintf (ofp, DSHELL "%s\n", def_shell);
out_shell = true;
- } else if (!out_skel && MATCH (buf, SKEL)) {
- fprintf (ofp, SKEL "%s\n", def_template);
+ } 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)) {
+ && MATCH (buf, DCREATE_MAIL_SPOOL)) {
fprintf (ofp,
- CREATE_MAIL_SPOOL "%s\n",
+ DCREATE_MAIL_SPOOL "%s\n",
def_create_mail_spool);
out_create_mail_spool = true;
} else
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
/*
* Rename the current default file to its backup name.
*/
- snprintf (buf, sizeof buf, "%s-", def_file);
+ wlen = snprintf (buf, sizeof buf, "%s-", def_file);
+ assert (wlen < (int) sizeof buf);
if ((rename (def_file, buf) != 0) && (ENOENT != errno)) {
- snprintf (buf, sizeof buf, _("%s: rename: %s"), Prog, def_file);
- perror (buf);
+ 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) != 0) {
- snprintf (buf, sizeof buf, _("%s: rename: %s"), Prog, new_file);
- perror (buf);
+ int err = errno;
+ fprintf (stderr,
+ _("%s: rename: %s: %s"),
+ Prog, new_file, strerror (err));
return -1;
}
#ifdef WITH_AUDIT
/*
* usage - display usage message and exit
*/
-static void usage (void)
+static void usage (int status)
{
- fputs (_("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"
- " -l, --no-log-init do not add the user to the lastlog and\n"
- " faillog databases\n"
- " -m, --create-home create home directory for the new user\n"
- " account\n"
- " -M, --no-create-home do not create user's home directory\n"
- " (overrides /etc/login.defs)\n"
- " -N, --no-user-group do not create a group with the same name as\n"
- " the user\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"
- " -r, --system create a system 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"
- " -U, --user-group create a group with the same name as the user\n"
- "\n"), stderr);
- 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);
}
/*
{"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:lmMNop:rs:u:U",
+#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':
break;
case 'D':
if (anyflag) {
- usage ();
+ usage (E_USAGE);
}
Dflg = true;
break;
fprintf (stderr,
_("%s: invalid numeric argument '%s'\n"),
Prog, optarg);
- usage ();
+ usage (E_USAGE);
}
/*
* -f -1 is allowed
Gflg = true;
break;
case 'h':
- usage ();
+ usage (E_SUCCESS);
break;
case 'k':
def_template = optarg;
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 = true;
}
fprintf (stderr,
_("%s: %s flag is only allowed with the %s flag\n"),
Prog, "-o", "-u");
- usage ();
+ usage (E_USAGE);
}
if (kflg && !mflg) {
fprintf (stderr,
_("%s: %s flag is only allowed with the %s flag\n"),
Prog, "-k", "-m");
- usage ();
+ usage (E_USAGE);
}
if (Uflg && gflg) {
fprintf (stderr,
_("%s: options %s and %s conflict\n"),
Prog, "-U", "-g");
- usage ();
+ usage (E_USAGE);
}
if (Uflg && Nflg) {
fprintf (stderr,
_("%s: options %s and %s conflict\n"),
Prog, "-U", "-N");
- usage ();
+ usage (E_USAGE);
}
if (mflg && Mflg) {
fprintf (stderr,
_("%s: options %s and %s conflict\n"),
Prog, "-m", "-M");
- usage ();
+ usage (E_USAGE);
}
/*
*/
if (Dflg) {
if (optind != argc) {
- usage ();
+ usage (E_USAGE);
}
if (uflg || oflg || Gflg || dflg || cflg || mflg) {
- usage ();
+ usage (E_USAGE);
}
} else {
if (optind != argc - 1) {
- usage ();
+ usage (E_USAGE);
}
user_name = argv[optind];
}
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 (!rflg) {
/* for system accounts defaults are ignored and we
* do not create a home dir */
- if (getdef_bool("CREATE_HOME")) {
+ if (getdef_bool ("CREATE_HOME")) {
mflg = true;
}
}
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);
- }
- }
+
+ /* shadow file will be opened by open_shadow(); */
/*
* Lock and open the group file.
#endif
}
+static void open_shadow (void)
+{
+ if (!is_shadow_pwd) {
+ return;
+ }
+ if (spw_lock () == 0) {
+ fprintf (stderr,
+ _("%s: cannot lock %s; try again later.\n"),
+ Prog, spw_dbname ());
+ fail_exit (E_PW_UPDATE);
+ }
+ spw_locked = true;
+ if (spw_open (O_RDWR) == 0) {
+ fprintf (stderr,
+ _("%s: cannot open %s\n"),
+ Prog, spw_dbname ());
+ fail_exit (E_PW_UPDATE);
+ }
+}
+
static char *empty_list = NULL;
/*
}
}
+#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) != 0) {
+#ifdef WITH_SELINUX
+ selinux_file_context (user_home);
+#endif
/* XXX - create missing parent directories. --marekm */
if (mkdir (user_home, 0) != 0) {
fprintf (stderr,
"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
}
}
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
*/
}
}
+#ifdef WITH_TCB
+ if (getdef_bool ("USE_TCB")) {
+ if (shadowtcb_create (user_name, user_id) == 0) {
+ fprintf (stderr,
+ _("%s: Failed to create tcb directory for %s\n"),
+ Prog, user_name);
+ fail_exit (E_UID_IN_USE);
+ }
+ }
+#endif
+ open_shadow ();
+
/* do we have to add a group for that user? This is why we need to
* open the group files in the open_files() function --gafton */
if (Uflg) {
}
/* Do not create mail directory for system accounts */
- if( !rflg ) {
+ if (!rflg) {
create_mail ();
}
close_files ();
+#ifdef WITH_SELINUX
+ selinux_update_mapping ();
+#endif
+
nscd_flush_cache ("passwd");
nscd_flush_cache ("group");