| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <config.h> |
| |
| #ident "$Id$" |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <grp.h> |
| #include <lastlog.h> |
| #include <pwd.h> |
| #ifdef ACCT_TOOLS_SETUID |
| #ifdef USE_PAM |
| #include "pam_defs.h" |
| #endif |
| #endif |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include "chkname.h" |
| #include "defines.h" |
| #include "faillog.h" |
| #include "getdef.h" |
| #include "groupio.h" |
| #include "nscd.h" |
| #include "sssd.h" |
| #include "prototypes.h" |
| #include "pwauth.h" |
| #include "pwio.h" |
| #ifdef SHADOWGRP |
| #include "sgroupio.h" |
| #endif |
| #include "shadowio.h" |
| #ifdef ENABLE_SUBIDS |
| #include "subordinateio.h" |
| #endif |
| #ifdef WITH_TCB |
| #include "tcbfuncs.h" |
| #endif |
| |
| #ifndef SKEL_DIR |
| #define SKEL_DIR "/etc/skel" |
| #endif |
| #ifndef USER_DEFAULTS_FILE |
| #define USER_DEFAULTS_FILE "/etc/default/useradd" |
| #define NEW_USER_FILE "/etc/default/nuaddXXXXXX" |
| #endif |
| |
| |
| |
| #ifndef LASTLOG_FILE |
| #define LASTLOG_FILE "/var/log/lastlog" |
| #endif |
| |
| |
| |
| const char *Prog; |
| |
| |
| |
| |
| static gid_t def_group = 100; |
| static const char *def_gname = "other"; |
| static const char *def_home = "/home"; |
| static const char *def_shell = "/sbin/nologin"; |
| static const char *def_template = SKEL_DIR; |
| static const char *def_create_mail_spool = "no"; |
| |
| static long def_inactive = -1; |
| static const char *def_expire = ""; |
| |
| #define VALID(s) (strcspn (s, ":\n") == strlen (s)) |
| |
| static const char *user_name = ""; |
| static const char *user_pass = "!!"; |
| static uid_t user_id; |
| static gid_t user_gid; |
| static const char *user_comment = ""; |
| static const char *user_home = ""; |
| static const char *user_shell = ""; |
| static const char *create_mail_spool = ""; |
| |
| static const char *prefix = ""; |
| static const char *prefix_user_home = NULL; |
| |
| #ifdef WITH_SELINUX |
| static const char *user_selinux = ""; |
| #endif |
| |
| static long user_expire = -1; |
| static bool is_shadow_pwd; |
| |
| #ifdef SHADOWGRP |
| static bool is_shadow_grp; |
| static bool sgr_locked = false; |
| #endif |
| #ifdef ENABLE_SUBIDS |
| static bool is_sub_uid = false; |
| static bool is_sub_gid = false; |
| static bool sub_uid_locked = false; |
| static bool sub_gid_locked = false; |
| static uid_t sub_uid_start; |
| static unsigned long sub_uid_count; |
| static gid_t sub_gid_start; |
| static unsigned long sub_gid_count; |
| #endif |
| static bool pw_locked = false; |
| static bool gr_locked = false; |
| static bool spw_locked = false; |
| static char **user_groups; |
| static long sys_ngroups; |
| static bool do_grp_update = false; |
| |
| static bool |
| bflg = false, |
| cflg = false, |
| dflg = false, |
| Dflg = false, |
| eflg = false, |
| fflg = false, |
| gflg = false, |
| Gflg = false, |
| kflg = false, |
| lflg = false, |
| mflg = false, |
| Mflg = false, |
| Nflg = false, |
| oflg = false, |
| rflg = false, |
| sflg = false, |
| uflg = false, |
| Uflg = false; |
| |
| #ifdef WITH_SELINUX |
| #define Zflg ('\0' != *user_selinux) |
| #endif |
| |
| static bool home_added = false; |
| |
| |
| |
| |
| |
| #define E_SUCCESS 0 |
| #define E_PW_UPDATE 1 |
| #define E_USAGE 2 |
| #define E_BAD_ARG 3 |
| #define E_UID_IN_USE 4 |
| #define E_NOTFOUND 6 |
| #define E_NAME_IN_USE 9 |
| #define E_GRP_UPDATE 10 |
| #define E_HOMEDIR 12 |
| #define E_SE_UPDATE 14 |
| #ifdef ENABLE_SUBIDS |
| #define E_SUB_UID_UPDATE 16 |
| #define E_SUB_GID_UPDATE 18 |
| #endif |
| |
| #define DGROUP "GROUP=" |
| #define DHOME "HOME=" |
| #define DSHELL "SHELL=" |
| #define DINACT "INACTIVE=" |
| #define DEXPIRE "EXPIRE=" |
| #define DSKEL "SKEL=" |
| #define DCREATE_MAIL_SPOOL "CREATE_MAIL_SPOOL=" |
| |
| |
| static void fail_exit (int); |
| static void get_defaults (void); |
| static void show_defaults (void); |
| static int set_defaults (void); |
| static int get_groups (char *); |
| static void usage (int status); |
| static void new_pwent (struct passwd *); |
| |
| static long scale_age (long); |
| static void new_spent (struct spwd *); |
| static void grp_update (void); |
| |
| static void process_flags (int argc, char **argv); |
| static void close_files (void); |
| static void open_files (void); |
| static void open_shadow (void); |
| static void faillog_reset (uid_t); |
| static void lastlog_reset (uid_t); |
| static void tallylog_reset (char *); |
| static void usr_update (void); |
| static void create_home (void); |
| static void create_mail (void); |
| |
| |
| |
| |
| static void fail_exit (int code) |
| { |
| int type; |
| |
| if (home_added) { |
| if (rmdir (prefix_user_home) != 0) { |
| fprintf (stderr, |
| _("%s: %s was created, but could not be removed\n"), |
| Prog, prefix_user_home); |
| SYSLOG ((LOG_ERR, "failed to remove %s", prefix_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 ())); |
| |
| } |
| } |
| 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 ())); |
| |
| } |
| } |
| 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 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 ())); |
| |
| } |
| } |
| #endif |
| #ifdef ENABLE_SUBIDS |
| if (sub_uid_locked) { |
| if (sub_uid_unlock () == 0) { |
| fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); |
| |
| } |
| } |
| if (sub_gid_locked) { |
| if (sub_gid_unlock () == 0) { |
| fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); |
| |
| } |
| } |
| #endif |
| |
| #ifdef WITH_AUDIT |
| if (code == E_PW_UPDATE || code >= E_GRP_UPDATE) |
| type = AUDIT_USER_MGMT; |
| else |
| type = AUDIT_ADD_USER; |
| |
| audit_logger (type, Prog, |
| "add-user", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| SYSLOG ((LOG_INFO, "failed adding user '%s', exit code: %d", user_name, code)); |
| exit (code); |
| } |
| |
| #define MATCH(x,y) (strncmp((x),(y),strlen(y)) == 0) |
| |
| |
| |
| |
| |
| |
| |
| |
| static void get_defaults (void) |
| { |
| FILE *fp; |
| char *default_file = USER_DEFAULTS_FILE; |
| char buf[1024]; |
| char *cp; |
| |
| if(prefix[0]) { |
| size_t len; |
| int wlen; |
| |
| len = strlen(prefix) + strlen(USER_DEFAULTS_FILE) + 2; |
| default_file = malloc(len); |
| if (default_file == NULL) |
| return; |
| wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); |
| assert (wlen == (int) len -1); |
| } |
| |
| |
| |
| |
| |
| fp = fopen (default_file, "r"); |
| if (NULL == fp) { |
| goto getdef_err; |
| } |
| |
| |
| |
| |
| |
| while (fgets (buf, (int) sizeof buf, fp) == buf) { |
| cp = strrchr (buf, '\n'); |
| if (NULL != cp) { |
| *cp = '\0'; |
| } |
| |
| cp = strchr (buf, '='); |
| if (NULL == cp) { |
| continue; |
| } |
| |
| cp++; |
| |
| |
| |
| |
| if (MATCH (buf, DGROUP)) { |
| const struct group *grp = prefix_getgr_nam_gid (cp); |
| if (NULL == grp) { |
| fprintf (stderr, |
| _("%s: group '%s' does not exist\n"), |
| Prog, cp); |
| fprintf (stderr, |
| _("%s: the %s configuration in %s will be ignored\n"), |
| Prog, DGROUP, default_file); |
| } else { |
| def_group = grp->gr_gid; |
| def_gname = xstrdup (grp->gr_name); |
| } |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DHOME)) { |
| def_home = xstrdup (cp); |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DSHELL)) { |
| def_shell = xstrdup (cp); |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DINACT)) { |
| if ( (getlong (cp, &def_inactive) == 0) |
| || (def_inactive < -1)) { |
| fprintf (stderr, |
| _("%s: invalid numeric argument '%s'\n"), |
| Prog, cp); |
| fprintf (stderr, |
| _("%s: the %s configuration in %s will be ignored\n"), |
| Prog, DINACT, default_file); |
| def_inactive = -1; |
| } |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DEXPIRE)) { |
| def_expire = xstrdup (cp); |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DSKEL)) { |
| if ('\0' == *cp) { |
| cp = SKEL_DIR; |
| } |
| |
| if(prefix[0]) { |
| size_t len; |
| int wlen; |
| char* _def_template; |
| |
| len = strlen(prefix) + strlen(cp) + 2; |
| _def_template = xmalloc(len); |
| wlen = snprintf(_def_template, len, "%s/%s", prefix, cp); |
| assert (wlen == (int) len -1); |
| def_template = _def_template; |
| } |
| else { |
| def_template = xstrdup (cp); |
| } |
| } |
| |
| |
| |
| |
| else if (MATCH (buf, DCREATE_MAIL_SPOOL)) { |
| if (*cp == '\0') { |
| cp = "no"; |
| } |
| |
| def_create_mail_spool = xstrdup (cp); |
| } |
| } |
| (void) fclose (fp); |
| getdef_err: |
| if(prefix[0]) { |
| free(default_file); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void show_defaults (void) |
| { |
| printf ("GROUP=%u\n", (unsigned int) def_group); |
| printf ("HOME=%s\n", def_home); |
| printf ("INACTIVE=%ld\n", def_inactive); |
| printf ("EXPIRE=%s\n", def_expire); |
| printf ("SHELL=%s\n", def_shell); |
| printf ("SKEL=%s\n", def_template); |
| printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static int set_defaults (void) |
| { |
| FILE *ifp; |
| FILE *ofp; |
| char buf[1024]; |
| char *new_file = NULL; |
| char *default_file = USER_DEFAULTS_FILE; |
| char *cp; |
| int ofd; |
| int wlen; |
| bool out_group = false; |
| bool out_home = false; |
| bool out_inactive = false; |
| bool out_expire = false; |
| bool out_shell = false; |
| bool out_skel = false; |
| bool out_create_mail_spool = false; |
| size_t len; |
| int ret = -1; |
| |
| |
| len = strlen(prefix) + strlen(NEW_USER_FILE) + 2; |
| new_file = malloc(len); |
| if (new_file == NULL) { |
| fprintf (stderr, |
| _("%s: cannot create new defaults file: %s\n"), |
| Prog, strerror(errno)); |
| return -1; |
| } |
| wlen = snprintf(new_file, len, "%s%s%s", prefix, prefix[0]?"/":"", NEW_USER_FILE); |
| assert (wlen <= (int) len -1); |
| |
| if(prefix[0]) { |
| len = strlen(prefix) + strlen(USER_DEFAULTS_FILE) + 2; |
| default_file = malloc(len); |
| if (default_file == NULL) { |
| fprintf (stderr, |
| _("%s: cannot create new defaults file: %s\n"), |
| Prog, strerror(errno)); |
| goto setdef_err; |
| } |
| wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); |
| assert (wlen == (int) len -1); |
| } |
| |
| |
| |
| |
| ofd = mkstemp (new_file); |
| if (-1 == ofd) { |
| fprintf (stderr, |
| _("%s: cannot create new defaults file\n"), |
| Prog); |
| goto setdef_err; |
| } |
| |
| ofp = fdopen (ofd, "w"); |
| if (NULL == ofp) { |
| fprintf (stderr, |
| _("%s: cannot open new defaults file\n"), |
| Prog); |
| goto setdef_err; |
| } |
| |
| |
| |
| |
| |
| |
| ifp = fopen (default_file, "r"); |
| if (NULL == ifp) { |
| fprintf (ofp, "# useradd defaults file\n"); |
| goto skip; |
| } |
| |
| while (fgets (buf, (int) sizeof buf, ifp) == buf) { |
| cp = strrchr (buf, '\n'); |
| if (NULL != cp) { |
| *cp = '\0'; |
| } else { |
| |
| |
| |
| if (feof (ifp) == 0) { |
| fprintf (stderr, |
| _("%s: line too long in %s: %s..."), |
| Prog, default_file, buf); |
| (void) fclose (ifp); |
| goto setdef_err; |
| } |
| } |
| |
| if (!out_group && MATCH (buf, DGROUP)) { |
| fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group); |
| out_group = true; |
| } else if (!out_home && MATCH (buf, DHOME)) { |
| fprintf (ofp, DHOME "%s\n", def_home); |
| out_home = true; |
| } else if (!out_inactive && MATCH (buf, DINACT)) { |
| fprintf (ofp, DINACT "%ld\n", def_inactive); |
| out_inactive = true; |
| } else if (!out_expire && MATCH (buf, DEXPIRE)) { |
| fprintf (ofp, DEXPIRE "%s\n", def_expire); |
| out_expire = true; |
| } else if (!out_shell && MATCH (buf, DSHELL)) { |
| fprintf (ofp, DSHELL "%s\n", def_shell); |
| out_shell = true; |
| } else if (!out_skel && MATCH (buf, DSKEL)) { |
| fprintf (ofp, DSKEL "%s\n", def_template); |
| out_skel = true; |
| } else if (!out_create_mail_spool |
| && MATCH (buf, DCREATE_MAIL_SPOOL)) { |
| fprintf (ofp, |
| DCREATE_MAIL_SPOOL "%s\n", |
| def_create_mail_spool); |
| out_create_mail_spool = true; |
| } else |
| fprintf (ofp, "%s\n", buf); |
| } |
| (void) fclose (ifp); |
| |
| skip: |
| |
| |
| |
| |
| |
| if (!out_group) |
| fprintf (ofp, DGROUP "%u\n", (unsigned int) def_group); |
| if (!out_home) |
| fprintf (ofp, DHOME "%s\n", def_home); |
| if (!out_inactive) |
| fprintf (ofp, DINACT "%ld\n", def_inactive); |
| if (!out_expire) |
| fprintf (ofp, DEXPIRE "%s\n", def_expire); |
| if (!out_shell) |
| fprintf (ofp, DSHELL "%s\n", def_shell); |
| if (!out_skel) |
| fprintf (ofp, DSKEL "%s\n", def_template); |
| |
| if (!out_create_mail_spool) |
| fprintf (ofp, DCREATE_MAIL_SPOOL "%s\n", def_create_mail_spool); |
| |
| |
| |
| |
| |
| (void) fflush (ofp); |
| if ( (ferror (ofp) != 0) |
| || (fsync (fileno (ofp)) != 0) |
| || (fclose (ofp) != 0)) { |
| unlink (new_file); |
| goto setdef_err; |
| } |
| |
| |
| |
| |
| wlen = snprintf (buf, sizeof buf, "%s-", default_file); |
| assert (wlen < (int) sizeof buf); |
| unlink (buf); |
| if ((link (default_file, buf) != 0) && (ENOENT != errno)) { |
| int err = errno; |
| fprintf (stderr, |
| _("%s: Cannot create backup file (%s): %s\n"), |
| Prog, buf, strerror (err)); |
| unlink (new_file); |
| goto setdef_err; |
| } |
| |
| |
| |
| |
| if (rename (new_file, default_file) != 0) { |
| int err = errno; |
| fprintf (stderr, |
| _("%s: rename: %s: %s\n"), |
| Prog, new_file, strerror (err)); |
| goto setdef_err; |
| } |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_USYS_CONFIG, Prog, |
| "changing useradd defaults", |
| NULL, AUDIT_NO_ID, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| SYSLOG ((LOG_INFO, |
| "useradd defaults: GROUP=%u, HOME=%s, SHELL=%s, INACTIVE=%ld, " |
| "EXPIRE=%s, SKEL=%s, CREATE_MAIL_SPOOL=%s", |
| (unsigned int) def_group, def_home, def_shell, |
| def_inactive, def_expire, def_template, |
| def_create_mail_spool)); |
| ret = 0; |
| setdef_err: |
| free(new_file); |
| if(prefix[0]) { |
| free(default_file); |
| } |
| |
| return ret; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static int get_groups (char *list) |
| { |
| char *cp; |
| const struct group *grp; |
| int errors = 0; |
| int ngroups = 0; |
| |
| if ('\0' == *list) { |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| do { |
| |
| |
| |
| cp = strchr (list, ','); |
| if (NULL != cp) { |
| *cp++ = '\0'; |
| } |
| |
| |
| |
| |
| |
| grp = prefix_getgr_nam_gid (list); |
| |
| |
| |
| |
| |
| |
| |
| if (NULL == grp) { |
| fprintf (stderr, |
| _("%s: group '%s' does not exist\n"), |
| Prog, list); |
| errors++; |
| } |
| list = cp; |
| |
| |
| |
| |
| |
| if (NULL == grp) { |
| continue; |
| } |
| |
| #ifdef USE_NIS |
| |
| |
| |
| |
| if (__isgrNIS ()) { |
| fprintf (stderr, |
| _("%s: group '%s' is a NIS group.\n"), |
| Prog, grp->gr_name); |
| continue; |
| } |
| #endif |
| |
| if (ngroups == sys_ngroups) { |
| fprintf (stderr, |
| _("%s: too many groups specified (max %d).\n"), |
| Prog, ngroups); |
| break; |
| } |
| |
| |
| |
| |
| user_groups[ngroups++] = xstrdup (grp->gr_name); |
| } while (NULL != list); |
| |
| user_groups[ngroups] = (char *) 0; |
| |
| |
| |
| |
| if (0 != errors) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| |
| static void usage (int status) |
| { |
| FILE *usageout = (E_SUCCESS != status) ? stderr : stdout; |
| (void) fprintf (usageout, |
| _("Usage: %s [options] LOGIN\n" |
| " %s -D\n" |
| " %s -D [options]\n" |
| "\n" |
| "Options:\n"), |
| Prog, Prog, Prog); |
| (void) fputs (_(" -b, --base-dir BASE_DIR base directory for the home directory of the\n" |
| " new account\n"), usageout); |
| (void) fputs (_(" -c, --comment COMMENT GECOS field of the new account\n"), usageout); |
| (void) fputs (_(" -d, --home-dir HOME_DIR home directory of the new account\n"), usageout); |
| (void) fputs (_(" -D, --defaults print or change default useradd configuration\n"), usageout); |
| (void) fputs (_(" -e, --expiredate EXPIRE_DATE expiration date of the new account\n"), usageout); |
| (void) fputs (_(" -f, --inactive INACTIVE password inactivity period of the new account\n"), usageout); |
| (void) fputs (_(" -g, --gid GROUP name or ID of the primary group of the new\n" |
| " account\n"), usageout); |
| (void) fputs (_(" -G, --groups GROUPS list of supplementary groups of the new\n" |
| " account\n"), usageout); |
| (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); |
| (void) fputs (_(" -k, --skel SKEL_DIR use this alternative skeleton directory\n"), usageout); |
| (void) fputs (_(" -K, --key KEY=VALUE override /etc/login.defs defaults\n"), usageout); |
| (void) fputs (_(" -l, --no-log-init do not add the user to the lastlog and\n" |
| " faillog databases\n"), usageout); |
| (void) fputs (_(" -m, --create-home create the user's home directory\n"), usageout); |
| (void) fputs (_(" -M, --no-create-home do not create the user's home directory\n"), usageout); |
| (void) fputs (_(" -N, --no-user-group do not create a group with the same name as\n" |
| " the user\n"), usageout); |
| (void) fputs (_(" -o, --non-unique allow to create users with duplicate\n" |
| " (non-unique) UID\n"), usageout); |
| (void) fputs (_(" -p, --password PASSWORD encrypted password of the new account\n"), usageout); |
| (void) fputs (_(" -r, --system create a system account\n"), usageout); |
| (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); |
| (void) fputs (_(" -P, --prefix PREFIX_DIR prefix directory where are located the /etc/* files\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); |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void new_pwent (struct passwd *pwent) |
| { |
| memzero (pwent, sizeof *pwent); |
| pwent->pw_name = (char *) user_name; |
| if (is_shadow_pwd) { |
| pwent->pw_passwd = (char *) SHADOW_PASSWD_STRING; |
| } else { |
| pwent->pw_passwd = (char *) user_pass; |
| } |
| |
| pwent->pw_uid = user_id; |
| pwent->pw_gid = user_gid; |
| pwent->pw_gecos = (char *) user_comment; |
| pwent->pw_dir = (char *) user_home; |
| pwent->pw_shell = (char *) user_shell; |
| } |
| |
| static long scale_age (long x) |
| { |
| if (x <= 0) { |
| return x; |
| } |
| |
| return x * (DAY / SCALE); |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void new_spent (struct spwd *spent) |
| { |
| memzero (spent, sizeof *spent); |
| spent->sp_namp = (char *) user_name; |
| spent->sp_pwdp = (char *) user_pass; |
| spent->sp_lstchg = (long) gettime () / SCALE; |
| if (0 == spent->sp_lstchg) { |
| |
| spent->sp_lstchg = -1; |
| } |
| if (!rflg) { |
| spent->sp_min = scale_age (getdef_num ("PASS_MIN_DAYS", -1)); |
| spent->sp_max = scale_age (getdef_num ("PASS_MAX_DAYS", -1)); |
| spent->sp_warn = scale_age (getdef_num ("PASS_WARN_AGE", -1)); |
| spent->sp_inact = scale_age (def_inactive); |
| spent->sp_expire = scale_age (user_expire); |
| } else { |
| spent->sp_min = -1; |
| spent->sp_max = -1; |
| spent->sp_warn = -1; |
| spent->sp_inact = -1; |
| spent->sp_expire = -1; |
| } |
| spent->sp_flag = SHADOW_SP_FLAG_UNSET; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void grp_update (void) |
| { |
| const struct group *grp; |
| struct group *ngrp; |
| |
| #ifdef SHADOWGRP |
| const struct sgrp *sgrp; |
| struct sgrp *nsgrp; |
| #endif |
| |
| |
| |
| |
| |
| |
| |
| for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) { |
| |
| |
| |
| |
| |
| if (!is_on_list (user_groups, grp->gr_name)) { |
| continue; |
| } |
| |
| |
| |
| |
| |
| ngrp = __gr_dup (grp); |
| if (NULL == ngrp) { |
| fprintf (stderr, |
| _("%s: Out of memory. Cannot update %s.\n"), |
| Prog, gr_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); |
| fail_exit (E_GRP_UPDATE); |
| } |
| |
| |
| |
| |
| |
| ngrp->gr_mem = add_list (ngrp->gr_mem, user_name); |
| if (gr_update (ngrp) == 0) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry '%s'\n"), |
| Prog, gr_dbname (), ngrp->gr_name); |
| SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); |
| fail_exit (E_GRP_UPDATE); |
| } |
| #ifdef WITH_AUDIT |
| audit_logger_with_group (AUDIT_USER_MGMT, Prog, |
| "add-user-to-group", |
| user_name, AUDIT_NO_ID, ngrp->gr_name, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| SYSLOG ((LOG_INFO, |
| "add '%s' to group '%s'", |
| user_name, ngrp->gr_name)); |
| } |
| |
| #ifdef SHADOWGRP |
| if (!is_shadow_grp) |
| return; |
| |
| |
| |
| |
| |
| |
| for (sgr_rewind (), sgrp = sgr_next (); NULL != sgrp; sgrp = sgr_next ()) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (gr_locate (sgrp->sg_name) == NULL) { |
| continue; |
| } |
| |
| if (!is_on_list (user_groups, sgrp->sg_name)) { |
| continue; |
| } |
| |
| |
| |
| |
| |
| nsgrp = __sgr_dup (sgrp); |
| if (NULL == nsgrp) { |
| fprintf (stderr, |
| _("%s: Out of memory. Cannot update %s.\n"), |
| Prog, sgr_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); |
| fail_exit (E_GRP_UPDATE); |
| } |
| |
| |
| |
| |
| |
| nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_name); |
| if (sgr_update (nsgrp) == 0) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry '%s'\n"), |
| Prog, sgr_dbname (), nsgrp->sg_name); |
| SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); |
| |
| fail_exit (E_GRP_UPDATE); |
| } |
| #ifdef WITH_AUDIT |
| audit_logger_with_group (AUDIT_USER_MGMT, Prog, |
| "add-to-shadow-group", |
| user_name, AUDIT_NO_ID, nsgrp->sg_name, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| SYSLOG ((LOG_INFO, |
| "add '%s' to shadow group '%s'", |
| user_name, nsgrp->sg_name)); |
| } |
| #endif |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void process_flags (int argc, char **argv) |
| { |
| const struct group *grp; |
| bool anyflag = false; |
| char *cp; |
| |
| { |
| |
| |
| |
| int c; |
| static struct option long_options[] = { |
| {"base-dir", required_argument, NULL, 'b'}, |
| {"comment", required_argument, NULL, 'c'}, |
| {"home-dir", required_argument, NULL, 'd'}, |
| {"defaults", no_argument, NULL, 'D'}, |
| {"expiredate", required_argument, NULL, 'e'}, |
| {"inactive", required_argument, NULL, 'f'}, |
| {"gid", required_argument, NULL, 'g'}, |
| {"groups", required_argument, NULL, 'G'}, |
| {"help", no_argument, NULL, 'h'}, |
| {"skel", required_argument, NULL, 'k'}, |
| {"key", required_argument, NULL, 'K'}, |
| {"no-log-init", no_argument, NULL, 'l'}, |
| {"create-home", no_argument, NULL, 'm'}, |
| {"no-create-home", no_argument, NULL, 'M'}, |
| {"no-user-group", no_argument, NULL, 'N'}, |
| {"non-unique", no_argument, NULL, 'o'}, |
| {"password", required_argument, NULL, 'p'}, |
| {"system", no_argument, NULL, 'r'}, |
| {"root", required_argument, NULL, 'R'}, |
| {"prefix", required_argument, NULL, 'P'}, |
| {"shell", required_argument, NULL, 's'}, |
| {"uid", required_argument, NULL, 'u'}, |
| {"user-group", no_argument, NULL, 'U'}, |
| #ifdef WITH_SELINUX |
| {"selinux-user", required_argument, NULL, 'Z'}, |
| #endif |
| {NULL, 0, NULL, '\0'} |
| }; |
| while ((c = getopt_long (argc, argv, |
| #ifdef WITH_SELINUX |
| "b:c:d:De:f:g:G:hk:K:lmMnNop:rR:P:s:u:UZ:", |
| #else |
| "b:c:d:De:f:g:G:hk:K:lmMnNop:rR:P:s:u:U", |
| #endif |
| long_options, NULL)) != -1) { |
| switch (c) { |
| case 'b': |
| if ( ( !VALID (optarg) ) |
| || ( optarg[0] != '/' )) { |
| fprintf (stderr, |
| _("%s: invalid base directory '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| def_home = optarg; |
| bflg = true; |
| break; |
| case 'c': |
| if (!VALID (optarg)) { |
| fprintf (stderr, |
| _("%s: invalid comment '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| user_comment = optarg; |
| cflg = true; |
| break; |
| case 'd': |
| if ( ( !VALID (optarg) ) |
| || ( optarg[0] != '/' )) { |
| fprintf (stderr, |
| _("%s: invalid home directory '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| user_home = optarg; |
| dflg = true; |
| break; |
| case 'D': |
| if (anyflag) { |
| usage (E_USAGE); |
| } |
| Dflg = true; |
| break; |
| case 'e': |
| if ('\0' != *optarg) { |
| user_expire = strtoday (optarg); |
| if (user_expire < -1) { |
| fprintf (stderr, |
| _("%s: invalid date '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| } else { |
| user_expire = -1; |
| } |
| |
| |
| |
| |
| |
| if ((-1 != user_expire) && !is_shadow_pwd) { |
| fprintf (stderr, |
| _("%s: shadow passwords required for -e\n"), |
| Prog); |
| exit (E_USAGE); |
| } |
| if (Dflg) { |
| def_expire = optarg; |
| } |
| eflg = true; |
| break; |
| case 'f': |
| if ( (getlong (optarg, &def_inactive) == 0) |
| || (def_inactive < -1)) { |
| fprintf (stderr, |
| _("%s: invalid numeric argument '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| |
| |
| |
| |
| if ((-1 != def_inactive) && !is_shadow_pwd) { |
| fprintf (stderr, |
| _("%s: shadow passwords required for -f\n"), |
| Prog); |
| exit (E_USAGE); |
| } |
| fflg = true; |
| break; |
| case 'g': |
| grp = prefix_getgr_nam_gid (optarg); |
| if (NULL == grp) { |
| fprintf (stderr, |
| _("%s: group '%s' does not exist\n"), |
| Prog, optarg); |
| exit (E_NOTFOUND); |
| } |
| if (Dflg) { |
| def_group = grp->gr_gid; |
| def_gname = optarg; |
| } else { |
| user_gid = grp->gr_gid; |
| } |
| gflg = true; |
| break; |
| case 'G': |
| if (get_groups (optarg) != 0) { |
| exit (E_NOTFOUND); |
| } |
| if (NULL != user_groups[0]) { |
| do_grp_update = true; |
| } |
| Gflg = true; |
| break; |
| case 'h': |
| usage (E_SUCCESS); |
| break; |
| case 'k': |
| def_template = optarg; |
| kflg = true; |
| break; |
| case 'K': |
| |
| |
| |
| |
| |
| cp = strchr (optarg, '='); |
| if (NULL == cp) { |
| fprintf (stderr, |
| _("%s: -K requires KEY=VALUE\n"), |
| Prog); |
| exit (E_BAD_ARG); |
| } |
| |
| *cp = '\0'; |
| cp++; |
| if (putdef_str (optarg, cp) < 0) { |
| exit (E_BAD_ARG); |
| } |
| break; |
| case 'l': |
| lflg = true; |
| break; |
| case 'm': |
| mflg = true; |
| break; |
| case 'M': |
| Mflg = true; |
| break; |
| case 'n': |
| case 'N': |
| Nflg = true; |
| break; |
| case 'o': |
| oflg = true; |
| break; |
| case 'p': |
| if (!VALID (optarg)) { |
| fprintf (stderr, |
| _("%s: invalid field '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| user_pass = optarg; |
| break; |
| case 'r': |
| rflg = true; |
| break; |
| case 'R': |
| break; |
| case 'P': |
| break; |
| case 's': |
| if ( ( !VALID (optarg) ) |
| || ( ('\0' != optarg[0]) |
| && ('/' != optarg[0]) |
| && ('*' != optarg[0]) )) { |
| fprintf (stderr, |
| _("%s: invalid shell '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| user_shell = optarg; |
| def_shell = optarg; |
| sflg = true; |
| break; |
| case 'u': |
| if ( (get_uid (optarg, &user_id) == 0) |
| || (user_id == (gid_t)-1)) { |
| fprintf (stderr, |
| _("%s: invalid user ID '%s'\n"), |
| Prog, optarg); |
| exit (E_BAD_ARG); |
| } |
| uflg = true; |
| break; |
| case 'U': |
| Uflg = true; |
| break; |
| #ifdef WITH_SELINUX |
| case 'Z': |
| if (prefix[0]) { |
| fprintf (stderr, |
| _("%s: -Z cannot be used with --prefix\n"), |
| Prog); |
| exit (E_BAD_ARG); |
| } |
| if (is_selinux_enabled () > 0) { |
| user_selinux = optarg; |
| } else { |
| fprintf (stderr, |
| _("%s: -Z requires SELinux enabled kernel\n"), |
| Prog); |
| |
| exit (E_BAD_ARG); |
| } |
| break; |
| #endif |
| default: |
| usage (E_USAGE); |
| } |
| anyflag = true; |
| } |
| } |
| |
| if (!gflg && !Nflg && !Uflg) { |
| |
| Uflg = getdef_bool ("USERGROUPS_ENAB"); |
| } |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| if (Dflg) { |
| if (optind != argc) { |
| usage (E_USAGE); |
| } |
| |
| if (uflg || Gflg || dflg || cflg || mflg) { |
| usage (E_USAGE); |
| } |
| } else { |
| if (optind != argc - 1) { |
| usage (E_USAGE); |
| } |
| |
| user_name = argv[optind]; |
| if (!is_valid_user_name (user_name)) { |
| fprintf (stderr, |
| _("%s: invalid user name '%s'\n"), |
| Prog, user_name); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ADD_USER, Prog, |
| "add-user", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| exit (E_BAD_ARG); |
| } |
| if (!dflg) { |
| char *uh; |
| size_t len = strlen (def_home) + strlen (user_name) + 2; |
| int wlen; |
| |
| uh = xmalloc (len); |
| wlen = snprintf (uh, len, "%s/%s", def_home, user_name); |
| assert (wlen == (int) len -1); |
| |
| user_home = uh; |
| } |
| if(prefix[0]) { |
| size_t len = strlen(prefix) + strlen(user_home) + 2; |
| int wlen; |
| char* _prefix_user_home; |
| _prefix_user_home = xmalloc(len); |
| wlen = snprintf(_prefix_user_home, len, "%s/%s", prefix, user_home); |
| assert (wlen == (int) len -1); |
| prefix_user_home = _prefix_user_home; |
| } |
| else { |
| prefix_user_home = user_home; |
| } |
| } |
| |
| if (!eflg) { |
| user_expire = strtoday (def_expire); |
| } |
| |
| if (!gflg) { |
| user_gid = def_group; |
| } |
| |
| if (!sflg) { |
| user_shell = def_shell; |
| } |
| |
| create_mail_spool = def_create_mail_spool; |
| |
| if (!rflg) { |
| |
| |
| if (getdef_bool ("CREATE_HOME")) { |
| mflg = true; |
| } |
| } |
| |
| if (Mflg) { |
| |
| mflg = false; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void close_files (void) |
| { |
| if (pw_close () == 0) { |
| fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); |
| fail_exit (E_PW_UPDATE); |
| } |
| if (is_shadow_pwd && (spw_close () == 0)) { |
| fprintf (stderr, |
| _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); |
| fail_exit (E_PW_UPDATE); |
| } |
| if (do_grp_update) { |
| if (gr_close () == 0) { |
| fprintf (stderr, |
| _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); |
| fail_exit (E_GRP_UPDATE); |
| } |
| #ifdef SHADOWGRP |
| if (is_shadow_grp && (sgr_close () == 0)) { |
| fprintf (stderr, |
| _("%s: failure while writing changes to %s\n"), |
| Prog, sgr_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); |
| fail_exit (E_GRP_UPDATE); |
| } |
| #endif |
| } |
| #ifdef ENABLE_SUBIDS |
| if (is_sub_uid && (sub_uid_close () == 0)) { |
| fprintf (stderr, |
| _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); |
| fail_exit (E_SUB_UID_UPDATE); |
| } |
| if (is_sub_gid && (sub_gid_close () == 0)) { |
| fprintf (stderr, |
| _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); |
| SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); |
| fail_exit (E_SUB_GID_UPDATE); |
| } |
| #endif |
| 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 |
| |
| } |
| 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 |
| |
| } |
| 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 |
| |
| } |
| gr_locked = false; |
| #ifdef SHADOWGRP |
| if (is_shadow_grp) { |
| if (sgr_unlock () == 0) { |
| fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ADD_USER, Prog, |
| "unlocking-gshadow-file", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| |
| } |
| sgr_locked = false; |
| } |
| #endif |
| #ifdef ENABLE_SUBIDS |
| if (is_sub_uid) { |
| if (sub_uid_unlock () == 0) { |
| fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ADD_USER, Prog, |
| "unlocking-subordinate-user-file", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| |
| } |
| sub_uid_locked = false; |
| } |
| if (is_sub_gid) { |
| if (sub_gid_unlock () == 0) { |
| fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); |
| SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ADD_USER, Prog, |
| "unlocking-subordinate-group-file", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| |
| } |
| sub_gid_locked = false; |
| } |
| #endif |
| } |
| |
| |
| |
| |
| |
| |
| static void open_files (void) |
| { |
| if (pw_lock () == 0) { |
| fprintf (stderr, |
| _("%s: cannot lock %s; try again later.\n"), |
| Prog, pw_dbname ()); |
| exit (E_PW_UPDATE); |
| } |
| pw_locked = true; |
| if (pw_open (O_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); |
| fail_exit (E_PW_UPDATE); |
| } |
| |
| |
| |
| |
| |
| |
| if (gr_lock () == 0) { |
| fprintf (stderr, |
| _("%s: cannot lock %s; try again later.\n"), |
| Prog, gr_dbname ()); |
| fail_exit (E_GRP_UPDATE); |
| } |
| gr_locked = true; |
| if (gr_open (O_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); |
| fail_exit (E_GRP_UPDATE); |
| } |
| #ifdef SHADOWGRP |
| if (is_shadow_grp) { |
| if (sgr_lock () == 0) { |
| fprintf (stderr, |
| _("%s: cannot lock %s; try again later.\n"), |
| Prog, sgr_dbname ()); |
| fail_exit (E_GRP_UPDATE); |
| } |
| sgr_locked = true; |
| if (sgr_open (O_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, |
| _("%s: cannot open %s\n"), |
| Prog, sgr_dbname ()); |
| fail_exit (E_GRP_UPDATE); |
| } |
| } |
| #endif |
| #ifdef ENABLE_SUBIDS |
| if (is_sub_uid) { |
| if (sub_uid_lock () == 0) { |
| fprintf (stderr, |
| _("%s: cannot lock %s; try again later.\n"), |
| Prog, sub_uid_dbname ()); |
| fail_exit (E_SUB_UID_UPDATE); |
| } |
| sub_uid_locked = true; |
| if (sub_uid_open (O_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, |
| _("%s: cannot open %s\n"), |
| Prog, sub_uid_dbname ()); |
| fail_exit (E_SUB_UID_UPDATE); |
| } |
| } |
| if (is_sub_gid) { |
| if (sub_gid_lock () == 0) { |
| fprintf (stderr, |
| _("%s: cannot lock %s; try again later.\n"), |
| Prog, sub_gid_dbname ()); |
| fail_exit (E_SUB_GID_UPDATE); |
| } |
| sub_gid_locked = true; |
| if (sub_gid_open (O_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, |
| _("%s: cannot open %s\n"), |
| Prog, sub_gid_dbname ()); |
| fail_exit (E_SUB_GID_UPDATE); |
| } |
| } |
| #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_CREAT | O_RDWR) == 0) { |
| fprintf (stderr, |
| _("%s: cannot open %s\n"), |
| Prog, spw_dbname ()); |
| fail_exit (E_PW_UPDATE); |
| } |
| } |
| |
| static char *empty_list = NULL; |
| |
| |
| |
| |
| |
| |
| |
| |
| static void new_grent (struct group *grent) |
| { |
| memzero (grent, sizeof *grent); |
| grent->gr_name = (char *) user_name; |
| #ifdef SHADOWGRP |
| if (is_shadow_grp) { |
| grent->gr_passwd = SHADOW_PASSWD_STRING; |
| } else |
| #endif |
| { |
| grent->gr_passwd = "!"; |
| } |
| grent->gr_gid = user_gid; |
| grent->gr_mem = &empty_list; |
| } |
| |
| #ifdef SHADOWGRP |
| |
| |
| |
| |
| |
| |
| |
| static void new_sgent (struct sgrp *sgent) |
| { |
| memzero (sgent, sizeof *sgent); |
| sgent->sg_name = (char *) user_name; |
| sgent->sg_passwd = "!"; |
| sgent->sg_adm = &empty_list; |
| sgent->sg_mem = &empty_list; |
| } |
| #endif |
| |
| |
| |
| |
| |
| |
| |
| |
| static void grp_add (void) |
| { |
| struct group grp; |
| |
| #ifdef SHADOWGRP |
| struct sgrp sgrp; |
| #endif |
| |
| |
| |
| |
| new_grent (&grp); |
| #ifdef SHADOWGRP |
| new_sgent (&sgrp); |
| #endif |
| |
| |
| |
| |
| 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, |
| "add-group", |
| grp.gr_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| fail_exit (E_GRP_UPDATE); |
| } |
| #ifdef SHADOWGRP |
| |
| |
| |
| 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, |
| "add-group", |
| grp.gr_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| fail_exit (E_GRP_UPDATE); |
| } |
| #endif |
| SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid)); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ADD_GROUP, Prog, |
| "add-group", |
| grp.gr_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| do_grp_update = true; |
| } |
| |
| static void faillog_reset (uid_t uid) |
| { |
| struct faillog fl; |
| int fd; |
| off_t offset_uid = (off_t) (sizeof fl) * uid; |
| |
| if (access (FAILLOG_FILE, F_OK) != 0) { |
| return; |
| } |
| |
| memzero (&fl, sizeof (fl)); |
| |
| fd = open (FAILLOG_FILE, O_RDWR); |
| if ( (-1 == fd) |
| || (lseek (fd, offset_uid, SEEK_SET) != offset_uid) |
| || (write (fd, &fl, sizeof (fl)) != (ssize_t) sizeof (fl)) |
| || (fsync (fd) != 0) |
| || (close (fd) != 0)) { |
| fprintf (stderr, |
| _("%s: failed to reset the faillog entry of UID %lu: %s\n"), |
| Prog, (unsigned long) uid, strerror (errno)); |
| SYSLOG ((LOG_WARN, "failed to reset the faillog entry of UID %lu", (unsigned long) uid)); |
| |
| } |
| } |
| |
| static void lastlog_reset (uid_t uid) |
| { |
| struct lastlog ll; |
| int fd; |
| off_t offset_uid = (off_t) (sizeof ll) * uid; |
| |
| if (access (LASTLOG_FILE, F_OK) != 0) { |
| return; |
| } |
| |
| memzero (&ll, sizeof (ll)); |
| |
| fd = open (LASTLOG_FILE, O_RDWR); |
| if ( (-1 == fd) |
| || (lseek (fd, offset_uid, SEEK_SET) != offset_uid) |
| || (write (fd, &ll, sizeof (ll)) != (ssize_t) sizeof (ll)) |
| || (fsync (fd) != 0) |
| || (close (fd) != 0)) { |
| fprintf (stderr, |
| _("%s: failed to reset the lastlog entry of UID %lu: %s\n"), |
| Prog, (unsigned long) uid, strerror (errno)); |
| SYSLOG ((LOG_WARN, "failed to reset the lastlog entry of UID %lu", (unsigned long) uid)); |
| |
| } |
| } |
| |
| static void tallylog_reset (char *user_name) |
| { |
| const char pam_tally2[] = "/sbin/pam_tally2"; |
| const char *pname; |
| pid_t childpid; |
| int failed; |
| int status; |
| |
| if (access(pam_tally2, X_OK) == -1) |
| return; |
| |
| failed = 0; |
| switch (childpid = fork()) |
| { |
| case -1: |
| failed = 1; |
| break; |
| case 0: |
| pname = strrchr(pam_tally2, '/'); |
| if (pname == NULL) |
| pname = pam_tally2; |
| else |
| pname++; |
| execl(pam_tally2, pname, "--user", user_name, "--reset", "--quiet", NULL); |
| |
| perror(pam_tally2); |
| exit(42); |
| |
| break; |
| default: |
| if (waitpid(childpid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) |
| failed = 1; |
| break; |
| } |
| |
| if (failed) |
| { |
| fprintf (stderr, |
| _("%s: failed to reset the tallylog entry of user \"%s\"\n"), |
| Prog, user_name); |
| SYSLOG ((LOG_WARN, "failed to reset the tallylog entry of user \"%s\"", user_name)); |
| } |
| |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void usr_update (void) |
| { |
| struct passwd pwent; |
| struct spwd spent; |
| |
| |
| |
| |
| |
| new_pwent (&pwent); |
| new_spent (&spent); |
| |
| |
| |
| |
| |
| 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)); |
| |
| |
| |
| |
| |
| |
| |
| |
| if ((!lflg) && (prefix_getpwuid (user_id) == NULL)) { |
| faillog_reset (user_id); |
| lastlog_reset (user_id); |
| } |
| |
| |
| |
| |
| if (pw_update (&pwent) == 0) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry '%s'\n"), |
| Prog, pw_dbname (), pwent.pw_name); |
| fail_exit (E_PW_UPDATE); |
| } |
| |
| |
| |
| |
| if (is_shadow_pwd && (spw_update (&spent) == 0)) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry '%s'\n"), |
| Prog, spw_dbname (), spent.sp_namp); |
| fail_exit (E_PW_UPDATE); |
| } |
| #ifdef ENABLE_SUBIDS |
| if (is_sub_uid && |
| (sub_uid_add(user_name, sub_uid_start, sub_uid_count) == 0)) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry\n"), |
| Prog, sub_uid_dbname ()); |
| fail_exit (E_SUB_UID_UPDATE); |
| } |
| if (is_sub_gid && |
| (sub_gid_add(user_name, sub_gid_start, sub_gid_count) == 0)) { |
| fprintf (stderr, |
| _("%s: failed to prepare the new %s entry\n"), |
| Prog, sub_uid_dbname ()); |
| fail_exit (E_SUB_GID_UPDATE); |
| } |
| #endif |
| |
| #ifdef WITH_AUDIT |
| |
| |
| |
| |
| |
| audit_logger (AUDIT_ADD_USER, Prog, |
| "add-user", |
| user_name, AUDIT_NO_ID, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| |
| |
| |
| if (do_grp_update) { |
| grp_update (); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void create_home (void) |
| { |
| if (access (prefix_user_home, F_OK) != 0) { |
| #ifdef WITH_SELINUX |
| if (set_selinux_file_context (prefix_user_home, NULL) != 0) { |
| fprintf (stderr, |
| _("%s: cannot set SELinux context for home directory %s\n"), |
| Prog, user_home); |
| fail_exit (E_HOMEDIR); |
| } |
| #endif |
| |
| if (mkdir (prefix_user_home, 0) != 0) { |
| fprintf (stderr, |
| _("%s: cannot create directory %s\n"), |
| Prog, prefix_user_home); |
| fail_exit (E_HOMEDIR); |
| } |
| (void) chown (prefix_user_home, user_id, user_gid); |
| chmod (prefix_user_home, |
| 0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK)); |
| home_added = true; |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_USER_MGMT, Prog, |
| "add-home-dir", |
| user_name, (unsigned int) user_id, |
| SHADOW_AUDIT_SUCCESS); |
| #endif |
| #ifdef WITH_SELINUX |
| |
| if (reset_selinux_file_context () != 0) { |
| fprintf (stderr, |
| _("%s: cannot reset SELinux file creation context\n"), |
| Prog); |
| fail_exit (E_HOMEDIR); |
| } |
| #endif |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void create_mail (void) |
| { |
| if (strcasecmp (create_mail_spool, "yes") == 0) { |
| const char *spool; |
| char *file; |
| int fd; |
| struct group *gr; |
| gid_t gid; |
| mode_t mode; |
| |
| spool = getdef_str ("MAIL_DIR"); |
| if (NULL == spool) { |
| spool = "/var/mail"; |
| } |
| file = alloca (strlen (prefix) + strlen (spool) + strlen (user_name) + 2); |
| if(prefix[0]) |
| sprintf (file, "%s/%s/%s", prefix, spool, user_name); |
| else |
| sprintf (file, "%s/%s", spool, user_name); |
| fd = open (file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0); |
| if (fd < 0) { |
| perror (_("Creating mailbox file")); |
| return; |
| } |
| |
| gr = prefix_getgrnam ("mail"); |
| if (NULL == gr) { |
| fputs (_("Group 'mail' not found. Creating the user mailbox file with 0600 mode.\n"), |
| stderr); |
| gid = user_gid; |
| mode = 0600; |
| } else { |
| gid = gr->gr_gid; |
| mode = 0660; |
| } |
| |
| if ( (fchown (fd, user_id, gid) != 0) |
| || (fchmod (fd, mode) != 0)) { |
| perror (_("Setting mailbox file permissions")); |
| } |
| |
| fsync (fd); |
| close (fd); |
| } |
| } |
| |
| |
| |
| |
| int main (int argc, char **argv) |
| { |
| int rv = E_SUCCESS; |
| #ifdef ACCT_TOOLS_SETUID |
| #ifdef USE_PAM |
| pam_handle_t *pamh = NULL; |
| int retval; |
| #endif |
| #endif |
| |
| #ifdef ENABLE_SUBIDS |
| uid_t uid_min; |
| uid_t uid_max; |
| #endif |
| |
| |
| |
| |
| Prog = Basename (argv[0]); |
| |
| (void) setlocale (LC_ALL, ""); |
| (void) bindtextdomain (PACKAGE, LOCALEDIR); |
| (void) textdomain (PACKAGE); |
| |
| process_root_flag ("-R", argc, argv); |
| |
| prefix = process_prefix_flag("-P", argc, argv); |
| |
| OPENLOG ("useradd"); |
| #ifdef WITH_AUDIT |
| audit_help_open (); |
| #endif |
| |
| sys_ngroups = sysconf (_SC_NGROUPS_MAX); |
| user_groups = (char **) xmalloc ((1 + sys_ngroups) * sizeof (char *)); |
| |
| |
| |
| user_groups[0] = (char *) 0; |
| |
| |
| is_shadow_pwd = spw_file_present (); |
| #ifdef SHADOWGRP |
| is_shadow_grp = sgr_file_present (); |
| #endif |
| |
| get_defaults (); |
| |
| process_flags (argc, argv); |
| |
| #ifdef ENABLE_SUBIDS |
| uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL); |
| uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL); |
| is_sub_uid = sub_uid_file_present () && !rflg && |
| (!user_id || (user_id <= uid_max && user_id >= uid_min)); |
| is_sub_gid = sub_gid_file_present () && !rflg && |
| (!user_id || (user_id <= uid_max && user_id >= uid_min)); |
| #endif |
| |
| #ifdef ACCT_TOOLS_SETUID |
| #ifdef USE_PAM |
| { |
| struct passwd *pampw; |
| pampw = getpwuid (getuid ()); |
| if (pampw == NULL) { |
| fprintf (stderr, |
| _("%s: Cannot determine your user name.\n"), |
| Prog); |
| fail_exit (1); |
| } |
| |
| retval = pam_start ("useradd", pampw->pw_name, &conv, &pamh); |
| } |
| |
| if (PAM_SUCCESS == retval) { |
| retval = pam_authenticate (pamh, 0); |
| } |
| |
| if (PAM_SUCCESS == retval) { |
| retval = pam_acct_mgmt (pamh, 0); |
| } |
| |
| if (PAM_SUCCESS != retval) { |
| fprintf (stderr, _("%s: PAM: %s\n"), |
| Prog, pam_strerror (pamh, retval)); |
| SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); |
| if (NULL != pamh) { |
| (void) pam_end (pamh, retval); |
| } |
| fail_exit (1); |
| } |
| (void) pam_end (pamh, retval); |
| #endif |
| #endif |
| |
| |
| |
| |
| |
| if (Dflg) { |
| if (gflg || bflg || fflg || eflg || sflg) { |
| exit ((set_defaults () != 0) ? 1 : 0); |
| } |
| |
| show_defaults (); |
| exit (E_SUCCESS); |
| } |
| |
| |
| |
| |
| if (prefix_getpwnam (user_name) != NULL) { |
| fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name); |
| fail_exit (E_NAME_IN_USE); |
| } |
| |
| |
| |
| |
| |
| |
| |
| if (Uflg) { |
| |
| if (prefix_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); |
| fail_exit (E_NAME_IN_USE); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| open_files (); |
| |
| if (!oflg) { |
| |
| |
| |
| 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 (prefix_getpwuid (user_id) != NULL) { |
| fprintf (stderr, |
| _("%s: UID %lu is not unique\n"), |
| Prog, (unsigned long) user_id); |
| fail_exit (E_UID_IN_USE); |
| } |
| } |
| } |
| |
| #ifdef WITH_TCB |
| if (getdef_bool ("USE_TCB")) { |
| if (shadowtcb_create (user_name, user_id) == SHADOWTCB_FAILURE) { |
| fprintf (stderr, |
| _("%s: Failed to create tcb directory for %s\n"), |
| Prog, user_name); |
| fail_exit (E_UID_IN_USE); |
| } |
| } |
| #endif |
| open_shadow (); |
| |
| |
| |
| if (Uflg) { |
| if (find_new_gid (rflg, &user_gid, &user_id) < 0) { |
| fprintf (stderr, |
| _("%s: can't create group\n"), |
| Prog); |
| fail_exit (4); |
| } |
| grp_add (); |
| } |
| |
| #ifdef ENABLE_SUBIDS |
| if (is_sub_uid) { |
| if (find_new_sub_uids(user_name, &sub_uid_start, &sub_uid_count) < 0) { |
| fprintf (stderr, |
| _("%s: can't create subordinate user IDs\n"), |
| Prog); |
| fail_exit(E_SUB_UID_UPDATE); |
| } |
| } |
| if (is_sub_gid) { |
| if (find_new_sub_gids(user_name, &sub_gid_start, &sub_gid_count) < 0) { |
| fprintf (stderr, |
| _("%s: can't create subordinate group IDs\n"), |
| Prog); |
| fail_exit(E_SUB_GID_UPDATE); |
| } |
| } |
| #endif |
| |
| usr_update (); |
| |
| close_files (); |
| |
| nscd_flush_cache ("passwd"); |
| nscd_flush_cache ("group"); |
| sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); |
| |
| |
| |
| |
| |
| |
| if (!lflg && getpwuid (user_id) != NULL) { |
| tallylog_reset (user_name); |
| } |
| |
| #ifdef WITH_SELINUX |
| if (Zflg && *user_selinux) { |
| if (is_selinux_enabled () > 0) { |
| if (set_seuser (user_name, user_selinux) != 0) { |
| fprintf (stderr, |
| _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"), |
| Prog, user_name, user_selinux); |
| #ifdef WITH_AUDIT |
| audit_logger (AUDIT_ROLE_ASSIGN, Prog, |
| "add-selinux-user-mapping", |
| user_name, (unsigned int) user_id, |
| SHADOW_AUDIT_FAILURE); |
| #endif |
| rv = E_SE_UPDATE; |
| } |
| } |
| } |
| #endif |
| |
| if (mflg) { |
| create_home (); |
| if (home_added) { |
| copy_tree (def_template, prefix_user_home, false, true, |
| (uid_t)-1, user_id, (gid_t)-1, user_gid); |
| } else { |
| fprintf (stderr, |
| _("%s: warning: the home directory already exists.\n" |
| "Not copying any file from skel directory into it.\n"), |
| Prog); |
| } |
| |
| } |
| |
| |
| if (!rflg) { |
| create_mail (); |
| } |
| |
| return rv; |
| } |
| |