diff options
author | Balint Reczey <balint.reczey@canonical.com> | 2019-03-03 23:31:24 +0100 |
---|---|---|
committer | Balint Reczey <balint.reczey@canonical.com> | 2019-03-03 23:31:24 +0100 |
commit | b0729855e8fb744192a0395ea24673557818172c (patch) | |
tree | f2c3f13b5ba33b0b1f3627c24cd4005e0972ff86 /src | |
parent | 589f97ade4610b98cc532c8142343d4c33694e72 (diff) | |
download | shadow-b0729855e8fb744192a0395ea24673557818172c.tar.gz |
New upstream version 4.6upstream/4.6
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/chage.c | 6 | ||||
-rw-r--r-- | src/chpasswd.c | 2 | ||||
-rw-r--r-- | src/groupadd.c | 13 | ||||
-rw-r--r-- | src/groupdel.c | 17 | ||||
-rw-r--r-- | src/groupmems.c | 2 | ||||
-rw-r--r-- | src/groupmod.c | 30 | ||||
-rw-r--r-- | src/grpconv.c | 1 | ||||
-rw-r--r-- | src/login.c | 2 | ||||
-rw-r--r-- | src/newgidmap.c | 89 | ||||
-rw-r--r-- | src/newgrp.c | 75 | ||||
-rw-r--r-- | src/newusers.c | 5 | ||||
-rw-r--r-- | src/pwck.c | 6 | ||||
-rw-r--r-- | src/pwconv.c | 1 | ||||
-rw-r--r-- | src/su.c | 20 | ||||
-rw-r--r-- | src/suauth.c | 2 | ||||
-rw-r--r-- | src/useradd.c | 148 | ||||
-rw-r--r-- | src/userdel.c | 83 | ||||
-rw-r--r-- | src/usermod.c | 104 |
19 files changed, 458 insertions, 160 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 12ef6308..3c98a8d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,23 +53,25 @@ usbin_PROGRAMS = \ noinst_PROGRAMS = id sulogin suidbins = su -suidubins = chage chfn chsh expiry gpasswd newgrp passwd +suidubins = chage chfn chsh expiry gpasswd newgrp +if !WITH_TCB +suidubins += passwd +endif if ACCT_TOOLS_SETUID -suidubins += chage chgpasswd chpasswd groupadd groupdel groupmod newusers useradd userdel usermod +suidubins += chgpasswd chpasswd groupadd groupdel groupmod newusers useradd userdel usermod endif if ENABLE_SUBIDS suidubins += newgidmap newuidmap endif if WITH_TCB -suidubins -= passwd shadowsgidubins = passwd endif LDADD = $(INTLLIBS) \ - $(LIBTCB) \ $(top_builddir)/libmisc/libmisc.a \ - $(top_builddir)/lib/libshadow.la + $(top_builddir)/lib/libshadow.la \ + $(LIBTCB) if ACCT_TOOLS_SETUID LIBPAM_SUID = $(LIBPAM) diff --git a/src/chage.c b/src/chage.c index 617e90f1..05d2349b 100644 --- a/src/chage.c +++ b/src/chage.c @@ -154,7 +154,7 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -l, --list show account aging information\n"), usageout); (void) fputs (_(" -m, --mindays MIN_DAYS set minimum number of days before password\n" " change to MIN_DAYS\n"), usageout); - (void) fputs (_(" -M, --maxdays MAX_DAYS set maximim number of days before password\n" + (void) fputs (_(" -M, --maxdays MAX_DAYS set maximum number of days before password\n" " change to MAX_DAYS\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); (void) fputs (_(" -W, --warndays WARN_DAYS set expiration warning days to WARN_DAYS\n"), usageout); @@ -362,7 +362,7 @@ static void list_fields (void) /* * Start with the easy numbers - the number of days before the * password can be changed, the number of days after which the - * password must be chaged, the number of days before the password + * password must be changed, the number of days before the password * expires that the user is told, and the number of days after the * password expires that the account becomes unusable. */ @@ -780,7 +780,7 @@ static void get_defaults (/*@null@*/const struct spwd *sp) * -E set account expiration date (*) * -I set password inactive after expiration (*) * -l show account aging information - * -M set maximim number of days before password change (*) + * -M set maximum number of days before password change (*) * -m set minimum number of days before password change (*) * -W set expiration warning days (*) * diff --git a/src/chpasswd.c b/src/chpasswd.c index f9856726..918b27ee 100644 --- a/src/chpasswd.c +++ b/src/chpasswd.c @@ -464,7 +464,7 @@ int main (int argc, char **argv) #ifdef USE_PAM if (use_pam){ - if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) { + if (do_pam_passwd_non_interactive ("chpasswd", name, newpwd) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, line, name); diff --git a/src/groupadd.c b/src/groupadd.c index 179438fb..b57006c5 100644 --- a/src/groupadd.c +++ b/src/groupadd.c @@ -77,6 +77,8 @@ static gid_t group_id; static /*@null@*/char *group_passwd; static /*@null@*/char *empty_list = NULL; +static const char *prefix = ""; + static bool oflg = false; /* permit non-unique group ID to be specified with -g */ static bool gflg = false; /* ID value for the new group */ static bool fflg = false; /* if group already exists, do nothing and exit(0) */ @@ -123,6 +125,7 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -p, --password PASSWORD use this encrypted password for the new group\n"), usageout); (void) fputs (_(" -r, --system create a system account\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); + (void) fputs (_(" -P, --prefix PREFIX_DIR directory prefix\n"), usageout); (void) fputs ("\n", usageout); exit (status); } @@ -386,10 +389,11 @@ static void process_flags (int argc, char **argv) {"password", required_argument, NULL, 'p'}, {"system", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, + {"prefix", required_argument, NULL, 'P'}, {NULL, 0, NULL, '\0'} }; - while ((c = getopt_long (argc, argv, "fg:hK:op:rR:", + while ((c = getopt_long (argc, argv, "fg:hK:op:rR:P:", long_options, NULL)) != -1) { switch (c) { case 'f': @@ -446,6 +450,8 @@ static void process_flags (int argc, char **argv) break; case 'R': /* no-op, handled in process_root_flag () */ break; + case 'P': /* no-op, handled in process_prefix_flag () */ + break; default: usage (E_USAGE); } @@ -480,7 +486,7 @@ static void check_flags (void) * Check if the group already exist. */ /* local, no need for xgetgrnam */ - if (getgrnam (group_name) != NULL) { + if (prefix_getgrnam (group_name) != NULL) { /* The group already exist */ if (fflg) { /* OK, no need to do anything */ @@ -492,7 +498,7 @@ static void check_flags (void) exit (E_NAME_IN_USE); } - if (gflg && (getgrgid (group_id) != NULL)) { + if (gflg && (prefix_getgrgid (group_id) != NULL)) { /* A GID was specified, and a group already exist with that GID * - either we will use this GID anyway (-o) * - either we ignore the specified GID and @@ -578,6 +584,7 @@ int main (int argc, char **argv) (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("groupadd"); #ifdef WITH_AUDIT diff --git a/src/groupdel.c b/src/groupdel.c index 11e522b1..70bed010 100644 --- a/src/groupdel.c +++ b/src/groupdel.c @@ -62,6 +62,8 @@ static char *group_name; static gid_t group_id = -1; static bool check_group_busy = true; +static const char* prefix = ""; + #ifdef SHADOWGRP static bool is_shadow_grp; #endif @@ -97,6 +99,7 @@ static /*@noreturn@*/void usage (int status) Prog); (void) fputs (_(" -h, --help display this help message and exit\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 (_(" -f, --force delete group even if it is the primary group of a user\n"), usageout); (void) fputs ("\n", usageout); exit (status); @@ -283,11 +286,11 @@ static void group_busy (gid_t gid) * Nice slow linear search. */ - setpwent (); + prefix_setpwent (); - while ( ((pwd = getpwent ()) != NULL) && (pwd->pw_gid != gid) ); + while ( ((pwd = prefix_getpwent ()) != NULL) && (pwd->pw_gid != gid) ); - endpwent (); + prefix_endpwent (); /* * If pwd isn't NULL, it stopped because the gid's matched. @@ -320,10 +323,11 @@ static void process_flags (int argc, char **argv) static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"root", required_argument, NULL, 'R'}, + {"prefix", required_argument, NULL, 'P'}, {NULL, 0, NULL, '\0'} }; - while ((c = getopt_long (argc, argv, "hfR:", + while ((c = getopt_long (argc, argv, "hfR:P:", long_options, NULL)) != -1) { switch (c) { case 'h': @@ -331,6 +335,8 @@ static void process_flags (int argc, char **argv) /*@notreached@*/break; case 'R': /* no-op, handled in process_root_flag () */ break; + case 'P': /* no-op, handled in process_prefix_flag () */ + break; case 'f': check_group_busy = false; break; @@ -374,6 +380,7 @@ int main (int argc, char **argv) (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("groupdel"); #ifdef WITH_AUDIT @@ -434,7 +441,7 @@ int main (int argc, char **argv) /* * Start with a quick check to see if the group exists. */ - grp = getgrnam (group_name); /* local, no need for xgetgrnam */ + grp = prefix_getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), diff --git a/src/groupmems.c b/src/groupmems.c index 4a49e10b..fc91c8b1 100644 --- a/src/groupmems.c +++ b/src/groupmems.c @@ -278,7 +278,7 @@ static void remove_user (const char *user, } /* - * purge_members - Rmeove every members of the specified group + * purge_members - Remove every members of the specified group */ static void purge_members (const struct group *grp) { diff --git a/src/groupmod.c b/src/groupmod.c index 757c1a40..b293b98f 100644 --- a/src/groupmod.c +++ b/src/groupmod.c @@ -66,6 +66,11 @@ #define E_NOTFOUND 6 /* specified group doesn't exist */ #define E_NAME_IN_USE 9 /* group name already in use */ #define E_GRP_UPDATE 10 /* can't update group file */ +#define E_CLEANUP_SERVICE 11 /* can't setup cleanup service */ +#define E_PAM_USERNAME 12 /* can't determine your username for use with pam */ +#define E_PAM_ERROR 13 /* pam returned an error, see Syslog facility id groupmod */ + + /* * Global variables */ @@ -80,6 +85,8 @@ static char *group_passwd; static gid_t group_id; static gid_t group_newid; +static const char* prefix = ""; + static struct cleanup_info_mod info_passwd; static struct cleanup_info_mod info_group; #ifdef SHADOWGRP @@ -128,6 +135,7 @@ static void usage (int status) (void) fputs (_(" -p, --password PASSWORD change the password to this (encrypted)\n" " PASSWORD\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 ("\n", usageout); exit (status); } @@ -340,7 +348,7 @@ static void check_new_name (void) * If the entry is found, too bad. */ /* local, no need for xgetgrnam */ - if (getgrnam (group_newname) != NULL) { + if (prefix_getgrnam (group_newname) != NULL) { fprintf (stderr, _("%s: group '%s' already exists\n"), Prog, group_newname); @@ -376,9 +384,10 @@ static void process_flags (int argc, char **argv) {"non-unique", no_argument, NULL, 'o'}, {"password", required_argument, NULL, 'p'}, {"root", required_argument, NULL, 'R'}, + {"prefix", required_argument, NULL, 'P'}, {NULL, 0, NULL, '\0'} }; - while ((c = getopt_long (argc, argv, "g:hn:op:R:", + while ((c = getopt_long (argc, argv, "g:hn:op:R:P:", long_options, NULL)) != -1) { switch (c) { case 'g': @@ -407,6 +416,8 @@ static void process_flags (int argc, char **argv) break; case 'R': /* no-op, handled in process_root_flag () */ break; + case 'P': /* no-op, handled in process_prefix_flag () */ + break; default: usage (E_USAGE); } @@ -697,8 +708,8 @@ void update_primary_groups (gid_t ogid, gid_t ngid) { struct passwd *pwd; - setpwent (); - while ((pwd = getpwent ()) != NULL) { + prefix_setpwent (); + while ((pwd = prefix_getpwent ()) != NULL) { if (pwd->pw_gid == ogid) { const struct passwd *lpwd; struct passwd npwd; @@ -720,7 +731,7 @@ void update_primary_groups (gid_t ogid, gid_t ngid) } } } - endpwent (); + prefix_endpwent (); } /* @@ -746,6 +757,7 @@ int main (int argc, char **argv) (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("groupmod"); #ifdef WITH_AUDIT @@ -756,7 +768,7 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); - exit (1); + exit (E_CLEANUP_SERVICE); } process_flags (argc, argv); @@ -770,7 +782,7 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); - exit (1); + exit (E_PAM_USERNAME); } retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh); @@ -791,7 +803,7 @@ int main (int argc, char **argv) if (NULL != pamh) { (void) pam_end (pamh, retval); } - exit (1); + exit (E_PAM_ERROR); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ @@ -805,7 +817,7 @@ int main (int argc, char **argv) /* * Start with a quick check to see if the group exists. */ - grp = getgrnam (group_name); /* local, no need for xgetgrnam */ + grp = prefix_getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), diff --git a/src/grpconv.c b/src/grpconv.c index f681f07f..f95f4960 100644 --- a/src/grpconv.c +++ b/src/grpconv.c @@ -198,6 +198,7 @@ int main (int argc, char **argv) Prog, sg->sg_name, sgr_dbname ()); fail_exit (3); } + (void) sgr_rewind (); } /* diff --git a/src/login.c b/src/login.c index 2d2e704e..e287cb0b 100644 --- a/src/login.c +++ b/src/login.c @@ -1163,7 +1163,7 @@ int main (int argc, char **argv) * entries. * Use the x variants because we need to keep the * entry for a long time, and there might be other - * getxxyy in between. + * getxxyyy in between. */ pw_free (pwd); pwd = xgetpwnam (username); diff --git a/src/newgidmap.c b/src/newgidmap.c index b1e33513..59a2e75c 100644 --- a/src/newgidmap.c +++ b/src/newgidmap.c @@ -46,32 +46,37 @@ */ const char *Prog; -static bool verify_range(struct passwd *pw, struct map_range *range) + +static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow_setgroups) { /* An empty range is invalid */ if (range->count == 0) return false; - /* Test /etc/subgid */ - if (have_sub_gids(pw->pw_name, range->lower, range->count)) + /* Test /etc/subgid. If the mapping is valid then we allow setgroups. */ + if (have_sub_gids(pw->pw_name, range->lower, range->count)) { + *allow_setgroups = true; return true; + } - /* Allow a process to map its own gid */ - if ((range->count == 1) && (pw->pw_gid == range->lower)) + /* Allow a process to map its own gid. */ + if ((range->count == 1) && (pw->pw_gid == range->lower)) { + /* noop -- if setgroups is enabled already we won't disable it. */ return true; + } return false; } static void verify_ranges(struct passwd *pw, int ranges, - struct map_range *mappings) + struct map_range *mappings, bool *allow_setgroups) { struct map_range *mapping; int idx; mapping = mappings; for (idx = 0; idx < ranges; idx++, mapping++) { - if (!verify_range(pw, mapping)) { + if (!verify_range(pw, mapping, allow_setgroups)) { fprintf(stderr, _( "%s: gid range [%lu-%lu) -> [%lu-%lu) not allowed\n"), Prog, mapping->upper, @@ -89,6 +94,70 @@ static void usage(void) exit(EXIT_FAILURE); } +void write_setgroups(int proc_dir_fd, bool allow_setgroups) +{ + int setgroups_fd; + char *policy, policy_buffer[4096]; + + /* + * Default is "deny", and any "allow" will out-rank a "deny". We don't + * forcefully write an "allow" here because the process we are writing + * mappings for may have already set themselves to "deny" (and "allow" + * is the default anyway). So allow_setgroups == true is a noop. + */ + policy = "deny\n"; + if (allow_setgroups) + return; + + setgroups_fd = openat(proc_dir_fd, "setgroups", O_RDWR|O_CLOEXEC); + if (setgroups_fd < 0) { + /* + * If it's an ENOENT then we are on too old a kernel for the setgroups + * code to exist. Emit a warning and bail on this. + */ + if (ENOENT == errno) { + fprintf(stderr, _("%s: kernel doesn't support setgroups restrictions\n"), Prog); + goto out; + } + fprintf(stderr, _("%s: couldn't open process setgroups: %s\n"), + Prog, + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* + * Check whether the policy is already what we want. /proc/self/setgroups + * is write-once, so attempting to write after it's already written to will + * fail. + */ + if (read(setgroups_fd, policy_buffer, sizeof(policy_buffer)) < 0) { + fprintf(stderr, _("%s: failed to read setgroups: %s\n"), + Prog, + strerror(errno)); + exit(EXIT_FAILURE); + } + if (!strncmp(policy_buffer, policy, strlen(policy))) + goto out; + + /* Write the policy. */ + if (lseek(setgroups_fd, 0, SEEK_SET) < 0) { + fprintf(stderr, _("%s: failed to seek setgroups: %s\n"), + Prog, + strerror(errno)); + exit(EXIT_FAILURE); + } + if (dprintf(setgroups_fd, "%s", policy) < 0) { + fprintf(stderr, _("%s: failed to setgroups %s policy: %s\n"), + Prog, + policy, + strerror(errno)); + exit(EXIT_FAILURE); + } + +out: + close(setgroups_fd); +} + /* * newgidmap - Set the gid_map for the specified process */ @@ -103,6 +172,7 @@ int main(int argc, char **argv) struct stat st; struct passwd *pw; int written; + bool allow_setgroups = false; Prog = Basename (argv[0]); @@ -145,7 +215,7 @@ int main(int argc, char **argv) (unsigned long) getuid ())); return EXIT_FAILURE; } - + /* Get the effective uid and effective gid of the target process */ if (fstat(proc_dir_fd, &st) < 0) { fprintf(stderr, _("%s: Could not stat directory for target %u\n"), @@ -177,8 +247,9 @@ int main(int argc, char **argv) if (!mappings) usage(); - verify_ranges(pw, ranges, mappings); + verify_ranges(pw, ranges, mappings, &allow_setgroups); + write_setgroups(proc_dir_fd, allow_setgroups); write_mapping(proc_dir_fd, ranges, mappings, "gid_map"); sub_gid_close(); diff --git a/src/newgrp.c b/src/newgrp.c index b8d3ddc8..2ca5e822 100644 --- a/src/newgrp.c +++ b/src/newgrp.c @@ -83,15 +83,29 @@ static void usage (void) } } +static bool ingroup(const char *name, struct group *gr) +{ + char **look; + bool notfound = true; + + look = gr->gr_mem; + while (*look && notfound) + notfound = strcmp (*look++, name); + + return !notfound; +} + /* - * find_matching_group - search all groups of a given group id for + * find_matching_group - search all groups of a gr's group id for * membership of a given username + * but check gr itself first */ -static /*@null@*/struct group *find_matching_group (const char *name, gid_t gid) +static /*@null@*/struct group *find_matching_group (const char *name, struct group *gr) { - struct group *gr; - char **look; - bool notfound = true; + gid_t gid = gr->gr_gid; + + if (ingroup(name, gr)) + return gr; setgrent (); while ((gr = getgrent ()) != NULL) { @@ -103,14 +117,8 @@ static /*@null@*/struct group *find_matching_group (const char *name, gid_t gid) * A group with matching GID was found. * Test for membership of 'name'. */ - look = gr->gr_mem; - while ((NULL != *look) && notfound) { - notfound = (strcmp (*look, name) != 0); - look++; - } - if (!notfound) { + if (ingroup(name, gr)) break; - } } endgrent (); return gr; @@ -248,7 +256,7 @@ failure: /* * syslog_sg - log the change of group to syslog * - * The loggout will also be logged when the user will quit the + * The logout will also be logged when the user will quit the * sg/newgrp session. */ static void syslog_sg (const char *name, const char *group) @@ -387,6 +395,7 @@ int main (int argc, char **argv) { bool initflag = false; int i; + bool is_member = false; bool cflag = false; int err = 0; gid_t gid; @@ -625,22 +634,36 @@ int main (int argc, char **argv) goto failure; } +#ifdef HAVE_SETGROUPS + /* when using pam_group, she will not be listed in the groups + * database. However getgroups() will return the group. So + * if she is listed there already it is ok to grant membership. + */ + for (i = 0; i < ngroups; i++) { + if (grp->gr_gid == grouplist[i]) { + is_member = true; + break; + } + } +#endif /* HAVE_SETGROUPS */ /* * For splitted groups (due to limitations of NIS), check all * groups of the same GID like the requested group for * membership of the current user. */ - grp = find_matching_group (name, grp->gr_gid); - if (NULL == grp) { - /* - * No matching group found. As we already know that - * the group exists, this happens only in the case - * of a requested group where the user is not member. - * - * Re-read the group entry for further processing. - */ - grp = xgetgrnam (group); - assert (NULL != grp); + if (!is_member) { + grp = find_matching_group (name, grp); + if (NULL == grp) { + /* + * No matching group found. As we already know that + * the group exists, this happens only in the case + * of a requested group where the user is not member. + * + * Re-read the group entry for further processing. + */ + grp = xgetgrnam (group); + assert (NULL != grp); + } } #ifdef SHADOWGRP sgrp = getsgnam (group); @@ -653,7 +676,9 @@ int main (int argc, char **argv) /* * Check if the user is allowed to access this group. */ - check_perms (grp, pwd, group); + if (!is_member) { + check_perms (grp, pwd, group); + } /* * all successful validations pass through this point. The group id diff --git a/src/newusers.c b/src/newusers.c index c38aec4b..8e4bef97 100644 --- a/src/newusers.c +++ b/src/newusers.c @@ -290,7 +290,8 @@ static int add_group (const char *name, const char *gid, gid_t *ngid, uid_t uid) fprintf (stderr, _("%s: invalid group name '%s'\n"), Prog, grent.gr_name); - free (grent.gr_name); + if (grent.gr_name) + free (grent.gr_name); return -1; } @@ -1237,7 +1238,7 @@ int main (int argc, char **argv) unsigned int i; /* Now update the passwords using PAM */ for (i = 0; i < nusers; i++) { - if (do_pam_passwd_non_interractive ("newusers", usernames[i], passwords[i]) != 0) { + if (do_pam_passwd_non_interactive ("newusers", usernames[i], passwords[i]) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, lines[i], usernames[i]); @@ -281,7 +281,7 @@ static void open_files (void) * Open the files. Use O_RDONLY if we are in read_only mode, O_RDWR * otherwise. */ - if (pw_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0) { + if (pw_open (read_only ? O_RDONLY : O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); if (use_system_pw_file) { @@ -290,7 +290,7 @@ static void open_files (void) fail_exit (E_CANTOPEN); } if (is_shadow && !use_tcb) { - if (spw_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0) { + if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); if (use_system_spw_file) { @@ -566,7 +566,7 @@ static void check_pw_file (int *errors, bool *changed) continue; } spw_locked = true; - if (spw_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0) { + if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); diff --git a/src/pwconv.c b/src/pwconv.c index e2d61f87..d6ee31a8 100644 --- a/src/pwconv.c +++ b/src/pwconv.c @@ -237,6 +237,7 @@ int main (int argc, char **argv) Prog, sp->sp_namp, spw_dbname ()); fail_exit (E_FAILURE); } + (void) spw_rewind(); } /* @@ -436,7 +436,7 @@ static void prepare_pam_close_session (void) static void usage (int status) { (void) - fputs (_("Usage: su [options] [LOGIN]\n" + fputs (_("Usage: su [options] [-] [username [args]]\n" "\n" "Options:\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n" @@ -446,7 +446,8 @@ static void usage (int status) " --preserve-environment do not reset environment variables, and\n" " keep the same shell\n" " -s, --shell SHELL use SHELL instead of the default in passwd\n" - "\n"), (E_SUCCESS != status) ? stderr : stdout); + "\n" + "If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout); exit (status); } @@ -809,23 +810,10 @@ static void process_flags (int argc, char **argv) if ((optind < argc) && (strcmp (argv[optind], "-") == 0)) { fakelogin = true; optind++; - if ( (optind < argc) - && (strcmp (argv[optind], "--") == 0)) { - optind++; - } } - /* - * The next argument must be either a user ID, or some flag to a - * subshell. Pretty sticky since you can't have an argument which - * doesn't start with a "-" unless you specify the new user name. - * Any remaining arguments will be passed to the user's login shell. - */ - if ((optind < argc) && ('-' != argv[optind][0])) { + if (optind < argc) { STRFCPY (name, argv[optind++]); /* use this login id */ - if ((optind < argc) && (strcmp (argv[optind], "--") == 0)) { - optind++; - } } if ('\0' == name[0]) { /* use default user */ struct passwd *root_pw = getpwnam ("root"); diff --git a/src/suauth.c b/src/suauth.c index a5bbe4c4..619a593b 100644 --- a/src/suauth.c +++ b/src/suauth.c @@ -152,7 +152,7 @@ int check_su_auth (const char *actual_id, return OWNPWORD; } else { SYSLOG ((LOG_ERR, - "%s, line %d: unrecognised action!\n", + "%s, line %d: unrecognized action!\n", SUAUTHFILE, lines)); } } diff --git a/src/useradd.c b/src/useradd.c index 0e0fa1f8..e721e52b 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -115,6 +115,10 @@ 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 /*@notnull@*/const char *user_selinux = ""; #endif /* WITH_SELINUX */ @@ -226,11 +230,11 @@ static void create_mail (void); static void fail_exit (int code) { if (home_added) { - if (rmdir (user_home) != 0) { + if (rmdir (prefix_user_home) != 0) { fprintf (stderr, _("%s: %s was created, but could not be removed\n"), - Prog, user_home); - SYSLOG ((LOG_ERR, "failed to remove %s", user_home)); + Prog, prefix_user_home); + SYSLOG ((LOG_ERR, "failed to remove %s", prefix_user_home)); } } @@ -339,14 +343,25 @@ static void fail_exit (int code) 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); + wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); + assert (wlen == (int) len -1); + } + /* * Open the defaults file for reading. */ - fp = fopen (USER_DEFAULTS_FILE, "r"); + fp = fopen (default_file, "r"); if (NULL == fp) { return; } @@ -372,14 +387,14 @@ static void get_defaults (void) * Primary GROUP identifier */ if (MATCH (buf, DGROUP)) { - const struct group *grp = getgr_nam_gid (cp); + 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, USER_DEFAULTS_FILE); + Prog, DGROUP, default_file); } else { def_group = grp->gr_gid; def_gname = xstrdup (grp->gr_name); @@ -411,7 +426,7 @@ static void get_defaults (void) Prog, cp); fprintf (stderr, _("%s: the %s configuration in %s will be ignored\n"), - Prog, DINACT, USER_DEFAULTS_FILE); + Prog, DINACT, default_file); def_inactive = -1; } } @@ -430,8 +445,21 @@ static void get_defaults (void) if ('\0' == *cp) { cp = SKEL_DIR; /* XXX warning: const */ } - - def_template = xstrdup (cp); + + if(prefix[0]) { + size_t len; + int wlen; + char* _def_template; /* avoid const warning */ + + 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); + } } /* @@ -446,6 +474,10 @@ static void get_defaults (void) } } (void) fclose (fp); + + if(prefix[0]) { + free(default_file); + } } /* @@ -477,7 +509,8 @@ static int set_defaults (void) FILE *ifp; FILE *ofp; char buf[1024]; - static char new_file[] = NEW_USER_FILE; + char* new_file = NEW_USER_FILE; + char* default_file = USER_DEFAULTS_FILE; char *cp; int ofd; int wlen; @@ -489,6 +522,20 @@ static int set_defaults (void) bool out_skel = false; bool out_create_mail_spool = false; + if(prefix[0]) { + size_t len; + + len = strlen(prefix) + strlen(NEW_USER_FILE) + 2; + new_file = malloc(len); + wlen = snprintf(new_file, len, "%s/%s", prefix, NEW_USER_FILE); + assert (wlen == (int) len -1); + + len = strlen(prefix) + strlen(USER_DEFAULTS_FILE) + 2; + default_file = malloc(len); + wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); + assert (wlen == (int) len -1); + } + /* * Create a temporary file to copy the new output to. */ @@ -513,7 +560,7 @@ static int set_defaults (void) * temporary file, using any new values. Each line is checked * to insure that it is not output more than once. */ - ifp = fopen (USER_DEFAULTS_FILE, "r"); + ifp = fopen (default_file, "r"); if (NULL == ifp) { fprintf (ofp, "# useradd defaults file\n"); goto skip; @@ -530,7 +577,7 @@ static int set_defaults (void) if (feof (ifp) == 0) { fprintf (stderr, _("%s: line too long in %s: %s..."), - Prog, USER_DEFAULTS_FILE, buf); + Prog, default_file, buf); (void) fclose (ifp); return -1; } @@ -602,10 +649,10 @@ static int set_defaults (void) /* * Rename the current default file to its backup name. */ - wlen = snprintf (buf, sizeof buf, "%s-", USER_DEFAULTS_FILE); + wlen = snprintf (buf, sizeof buf, "%s-", default_file); assert (wlen < (int) sizeof buf); unlink (buf); - if ((link (USER_DEFAULTS_FILE, buf) != 0) && (ENOENT != errno)) { + if ((link (default_file, buf) != 0) && (ENOENT != errno)) { int err = errno; fprintf (stderr, _("%s: Cannot create backup file (%s): %s\n"), @@ -617,7 +664,7 @@ static int set_defaults (void) /* * Rename the new default file to its correct name. */ - if (rename (new_file, USER_DEFAULTS_FILE) != 0) { + if (rename (new_file, default_file) != 0) { int err = errno; fprintf (stderr, _("%s: rename: %s: %s\n"), @@ -636,6 +683,12 @@ static int set_defaults (void) (unsigned int) def_group, def_home, def_shell, def_inactive, def_expire, def_template, def_create_mail_spool)); + + if(prefix[0]) { + free(new_file); + free(default_file); + } + return 0; } @@ -675,7 +728,7 @@ static int get_groups (char *list) * Names starting with digits are treated as numerical * GID values, otherwise the string is looked up as is. */ - grp = getgr_nam_gid (list); + grp = prefix_getgr_nam_gid (list); /* * There must be a match, either by GID value or by @@ -775,6 +828,7 @@ static void usage (int status) (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); @@ -1049,6 +1103,7 @@ static void process_flags (int argc, char **argv) {"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'}, @@ -1059,9 +1114,9 @@ static void process_flags (int argc, char **argv) }; while ((c = getopt_long (argc, argv, #ifdef WITH_SELINUX - "b:c:d:De:f:g:G:hk:K:lmMNop:rR:s:u:UZ:", + "b:c:d:De:f:g:G:hk:K:lmMNop:rR:P:s:u:UZ:", #else /* !WITH_SELINUX */ - "b:c:d:De:f:g:G:hk:K:lmMNop:rR:s:u:U", + "b:c:d:De:f:g:G:hk:K:lmMNop:rR:P:s:u:U", #endif /* !WITH_SELINUX */ long_options, NULL)) != -1) { switch (c) { @@ -1152,7 +1207,7 @@ static void process_flags (int argc, char **argv) fflg = true; break; case 'g': - grp = getgr_nam_gid (optarg); + grp = prefix_getgr_nam_gid (optarg); if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), @@ -1232,6 +1287,8 @@ static void process_flags (int argc, char **argv) break; case 'R': /* no-op, handled in process_root_flag () */ break; + case 'P': /* no-op, handled in process_prefix_flag () */ + break; case 's': if ( ( !VALID (optarg) ) || ( ('\0' != optarg[0]) @@ -1261,6 +1318,12 @@ static void process_flags (int argc, char **argv) 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 { @@ -1360,6 +1423,18 @@ static void process_flags (int argc, char **argv) user_home = uh; } + if(prefix[0]) { + size_t len = strlen(prefix) + strlen(user_home) + 2; + int wlen; + char* _prefix_user_home; /* to avoid const warning */ + _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) { @@ -1872,7 +1947,7 @@ static void usr_update (void) * are left unchanged). --marekm */ /* local, no need for xgetpwuid */ - if ((!lflg) && (getpwuid (user_id) == NULL)) { + if ((!lflg) && (prefix_getpwuid (user_id) == NULL)) { faillog_reset (user_id); lastlog_reset (user_id); } @@ -1942,9 +2017,9 @@ static void usr_update (void) */ static void create_home (void) { - if (access (user_home, F_OK) != 0) { + if (access (prefix_user_home, F_OK) != 0) { #ifdef WITH_SELINUX - if (set_selinux_file_context (user_home) != 0) { + if (set_selinux_file_context (prefix_user_home) != 0) { fprintf (stderr, _("%s: cannot set SELinux context for home directory %s\n"), Prog, user_home); @@ -1952,10 +2027,10 @@ static void create_home (void) } #endif /* XXX - create missing parent directories. --marekm */ - if (mkdir (user_home, 0) != 0) { + if (mkdir (prefix_user_home, 0) != 0) { fprintf (stderr, _("%s: cannot create directory %s\n"), - Prog, user_home); + Prog, prefix_user_home); #ifdef WITH_AUDIT audit_logger (AUDIT_ADD_USER, Prog, "adding home directory", @@ -1964,8 +2039,8 @@ static void create_home (void) #endif fail_exit (E_HOMEDIR); } - chown (user_home, user_id, user_gid); - chmod (user_home, + (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 @@ -2007,15 +2082,18 @@ static void create_mail (void) if (NULL == spool) { spool = "/var/mail"; } - file = alloca (strlen (spool) + strlen (user_name) + 2); - sprintf (file, "%s/%s", spool, user_name); + 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 = getgrnam ("mail"); /* local, no need for xgetgrnam */ + gr = prefix_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); @@ -2064,6 +2142,8 @@ int main (int argc, char **argv) process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag("-P", argc, argv); + OPENLOG ("useradd"); #ifdef WITH_AUDIT audit_help_open (); @@ -2147,7 +2227,7 @@ int main (int argc, char **argv) /* * Start with a quick check to see if the user exists. */ - if (getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */ + if (prefix_getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */ fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_ADD_USER, Prog, @@ -2166,7 +2246,7 @@ int main (int argc, char **argv) */ if (Uflg) { /* local, no need for xgetgrnam */ - if (getgrnam (user_name) != NULL) { + 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); @@ -2201,7 +2281,7 @@ int main (int argc, char **argv) fail_exit (E_UID_IN_USE); } } else { - if (getpwuid (user_id) != NULL) { + if (prefix_getpwuid (user_id) != NULL) { fprintf (stderr, _("%s: UID %lu is not unique\n"), Prog, (unsigned long) user_id); @@ -2264,7 +2344,7 @@ int main (int argc, char **argv) if (mflg) { create_home (); if (home_added) { - copy_tree (def_template, user_home, false, false, + copy_tree (def_template, prefix_user_home, false, false, (uid_t)-1, user_id, (gid_t)-1, user_gid); } else { fprintf (stderr, @@ -2285,7 +2365,7 @@ int main (int argc, char **argv) /* * tallylog_reset needs to be able to lookup * a valid existing user name, - * so we canot call it before close_files() + * so we cannot call it before close_files() */ if (!lflg && getpwuid (user_id) != NULL) { tallylog_reset (user_name); diff --git a/src/userdel.c b/src/userdel.c index 9092b5c1..c8de1d31 100644 --- a/src/userdel.c +++ b/src/userdel.c @@ -34,6 +34,7 @@ #ident "$Id$" +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> @@ -96,6 +97,7 @@ static char *user_home; static bool fflg = false; static bool rflg = false; static bool Zflg = false; +static bool Rflg = false; static bool is_shadow_pwd; @@ -113,6 +115,8 @@ static bool sub_uid_locked = false; static bool sub_gid_locked = false; #endif /* ENABLE_SUBIDS */ +static const char* prefix = ""; + /* local function prototypes */ static void usage (int status); static void update_groups (void); @@ -149,6 +153,7 @@ static void usage (int status) (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); (void) fputs (_(" -r, --remove remove home directory and mail spool\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); #ifdef WITH_SELINUX (void) fputs (_(" -Z, --selinux-user remove any SELinux user mapping for the user\n"), usageout); #endif /* WITH_SELINUX */ @@ -326,8 +331,8 @@ static void remove_usergroup (void) * Scan the passwd file to check if this group is still * used as a primary group. */ - setpwent (); - while ((pwd = getpwent ()) != NULL) { + prefix_setpwent (); + while ((pwd = prefix_getpwent ()) != NULL) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } @@ -338,7 +343,7 @@ static void remove_usergroup (void) break; } } - endpwent (); + prefix_endpwent (); } if (NULL == pwd) { @@ -814,9 +819,10 @@ static int is_owner (uid_t uid, const char *path) static int remove_mailbox (void) { const char *maildir; - char mailfile[1024]; + char* mailfile; int i; int errors = 0; + size_t len; maildir = getdef_str ("MAIL_DIR"); #ifdef MAIL_SPOOL_DIR @@ -827,13 +833,26 @@ static int remove_mailbox (void) if (NULL == maildir) { return 0; } - snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name); + + len = strlen (prefix) + strlen (maildir) + strlen (user_name) + 2; + mailfile = xmalloc (len); + + if (prefix[0]) { + (void) snprintf (mailfile, len, "%s/%s/%s", + prefix, maildir, user_name); + } + else { + (void) snprintf (mailfile, len, "%s/%s", + maildir, user_name); + } + mailfile[len-1] = '\0'; if (access (mailfile, F_OK) != 0) { if (ENOENT == errno) { fprintf (stderr, _("%s: %s mail spool (%s) not found\n"), Prog, user_name, mailfile); + free(mailfile); return 0; } else { fprintf (stderr, @@ -846,6 +865,7 @@ static int remove_mailbox (void) user_name, (unsigned int) user_id, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ + free(mailfile); return -1; } } @@ -874,6 +894,7 @@ static int remove_mailbox (void) SHADOW_AUDIT_SUCCESS); } #endif /* WITH_AUDIT */ + free(mailfile); return errors; } i = is_owner (user_id, mailfile); @@ -890,8 +911,10 @@ static int remove_mailbox (void) user_name, (unsigned int) user_id, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ + free(mailfile); return 1; } else if (i == -1) { + free(mailfile); return 0; /* mailbox doesn't exist */ } if (unlink (mailfile) != 0) { @@ -917,6 +940,7 @@ static int remove_mailbox (void) SHADOW_AUDIT_SUCCESS); } #endif /* WITH_AUDIT */ + free(mailfile); return errors; } @@ -925,7 +949,7 @@ static int remove_tcbdir (const char *user_name, uid_t user_id) { char *buf; int ret = 0; - size_t bufsize = (sizeof TCB_DIR) + strlen (user_name) + 2; + size_t buflen = (sizeof TCB_DIR) + strlen (user_name) + 2; if (!getdef_bool ("USE_TCB")) { return 0; @@ -990,6 +1014,7 @@ int main (int argc, char **argv) (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("userdel"); #ifdef WITH_AUDIT @@ -1006,6 +1031,7 @@ int main (int argc, char **argv) {"help", no_argument, NULL, 'h'}, {"remove", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, + {"prefix", required_argument, NULL, 'P'}, #ifdef WITH_SELINUX {"selinux-user", no_argument, NULL, 'Z'}, #endif /* WITH_SELINUX */ @@ -1013,9 +1039,9 @@ int main (int argc, char **argv) }; while ((c = getopt_long (argc, argv, #ifdef WITH_SELINUX - "fhrR:Z", + "fhrR:P:Z", #else /* !WITH_SELINUX */ - "fhrR:", + "fhrR:P:", #endif /* !WITH_SELINUX */ long_options, NULL)) != -1) { switch (c) { @@ -1029,9 +1055,18 @@ int main (int argc, char **argv) rflg = true; break; case 'R': /* no-op, handled in process_root_flag () */ + Rflg = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ 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) { Zflg = true; } else { @@ -1103,9 +1138,12 @@ int main (int argc, char **argv) */ user_name = argv[argc - 1]; { - struct passwd *pwd; - pwd = getpwnam (user_name); /* local, no need for xgetpwnam */ + const struct passwd *pwd; + + pw_open(O_RDONLY); + pwd = pw_locate (user_name); /* we care only about local users */ if (NULL == pwd) { + pw_close(); fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user_name); #ifdef WITH_AUDIT @@ -1118,7 +1156,19 @@ int main (int argc, char **argv) } user_id = pwd->pw_uid; user_gid = pwd->pw_gid; - user_home = xstrdup (pwd->pw_dir); + + if(prefix[0]) { + + size_t len = strlen(prefix) + strlen(pwd->pw_dir) + 2; + int wlen; + user_home = xmalloc(len); + wlen = snprintf(user_home, len, "%s/%s", prefix, pwd->pw_dir); + assert (wlen == (int) len -1); + } + else { + user_home = xstrdup (pwd->pw_dir); + } + pw_close(); } #ifdef WITH_TCB if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { @@ -1150,7 +1200,7 @@ int main (int argc, char **argv) * Note: This is a best effort basis. The user may log in between, * a cron job may be started on her behalf, etc. */ - if (user_busy (user_name, user_id) != 0) { + if ((prefix[0] == '\0') && !Rflg && user_busy (user_name, user_id) != 0) { if (!fflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, @@ -1201,8 +1251,8 @@ int main (int argc, char **argv) * prevent accidents if someone has /home or / as home * directory... --marekm */ - setpwent (); - while ((pwd = getpwent ())) { + prefix_setpwent (); + while ((pwd = prefix_getpwent ())) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } @@ -1216,7 +1266,7 @@ int main (int argc, char **argv) break; } } - endpwent (); + prefix_endpwent (); } #endif /* EXTRA_CHECK_HOME_DIR */ @@ -1268,7 +1318,8 @@ int main (int argc, char **argv) * Cancel any crontabs or at jobs. Have to do this before we remove * the entry from /etc/passwd. */ - user_cancel (user_name); + if(prefix[0] == '\0') + user_cancel (user_name); close_files (); #ifdef WITH_TCB diff --git a/src/usermod.c b/src/usermod.c index 9c5e479f..e571426f 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -34,6 +34,7 @@ #ident "$Id$" +#include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> @@ -124,6 +125,10 @@ static long user_newinactive; static long sys_ngroups; static char **user_groups; /* NULL-terminated list */ +static const char* prefix = ""; +static char* prefix_user_home = NULL; +static char* prefix_user_newhome = NULL; + static bool aflg = false, /* append to existing secondary group set */ cflg = false, /* new comment (GECOS) field */ @@ -264,7 +269,7 @@ static int get_groups (char *list) * Names starting with digits are treated as numerical GID * values, otherwise the string is looked up as is. */ - grp = getgr_nam_gid (list); + grp = prefix_getgr_nam_gid (list); /* * There must be a match, either by GID value or by @@ -411,7 +416,7 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -G, --groups GROUPS new list of supplementary GROUPS\n"), usageout); (void) fputs (_(" -a, --append append the user to the supplemental GROUPS\n" " mentioned by the -G option without removing\n" - " him/her from other groups\n"), usageout); + " the user from other groups\n"), usageout); (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); (void) fputs (_(" -l, --login NEW_LOGIN new value of the login name\n"), usageout); (void) fputs (_(" -L, --lock lock the user account\n"), usageout); @@ -420,6 +425,7 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -o, --non-unique allow using duplicate (non-unique) UID\n"), usageout); (void) fputs (_(" -p, --password PASSWORD use encrypted password for the new password\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); + (void) fputs (_(" -P, --prefix PREFIX_DIR prefix directory where are located the /etc/* files\n"), usageout); (void) fputs (_(" -s, --shell SHELL new login shell for the user account\n"), usageout); (void) fputs (_(" -u, --uid UID new UID for the user account\n"), usageout); (void) fputs (_(" -U, --unlock unlock the user account\n"), usageout); @@ -997,6 +1003,7 @@ static void process_flags (int argc, char **argv) {"non-unique", no_argument, NULL, 'o'}, {"password", required_argument, NULL, 'p'}, {"root", required_argument, NULL, 'R'}, + {"prefix", required_argument, NULL, 'P'}, {"shell", required_argument, NULL, 's'}, {"uid", required_argument, NULL, 'u'}, {"unlock", no_argument, NULL, 'U'}, @@ -1012,7 +1019,7 @@ static void process_flags (int argc, char **argv) {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, - "ac:d:e:f:g:G:hl:Lmop:R:s:u:U" + "ac:d:e:f:g:G:hl:Lmop:R:s:u:UP:" #ifdef ENABLE_SUBIDS "v:w:V:W:" #endif /* ENABLE_SUBIDS */ @@ -1114,6 +1121,8 @@ static void process_flags (int argc, char **argv) break; case 'R': /* no-op, handled in process_root_flag () */ break; + case 'P': /* no-op, handled in process_prefix_flag () */ + break; case 's': if (!VALID (optarg)) { fprintf (stderr, @@ -1177,6 +1186,12 @@ static void process_flags (int argc, char **argv) #endif /* ENABLE_SUBIDS */ #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; Zflg = true; @@ -1204,7 +1219,7 @@ static void process_flags (int argc, char **argv) { const struct passwd *pwd; /* local, no need for xgetpwnam */ - pwd = getpwnam (user_name); + pwd = prefix_getpwnam (user_name); if (NULL == pwd) { fprintf (stderr, _("%s: user '%s' does not exist\n"), @@ -1230,6 +1245,22 @@ static void process_flags (int argc, char **argv) if (!gflg) { user_newgid = user_gid; } + if(prefix[0]) { + size_t len = strlen(prefix) + strlen(user_home) + 2; + int wlen; + prefix_user_home = xmalloc(len); + wlen = snprintf(prefix_user_home, len, "%s/%s", prefix, user_home); + assert (wlen == (int) len -1); + + len = strlen(prefix) + strlen(user_newhome) + 2; + prefix_user_newhome = xmalloc(len); + wlen = snprintf(prefix_user_newhome, len, "%s/%s", prefix, user_newhome); + assert (wlen == (int) len -1); + } + else { + prefix_user_home = user_home; + prefix_user_newhome = user_newhome; + } #ifdef USE_NIS /* @@ -1256,7 +1287,7 @@ static void process_flags (int argc, char **argv) { const struct spwd *spwd = NULL; /* local, no need for xgetspnam */ - if (is_shadow_pwd && ((spwd = getspnam (user_name)) != NULL)) { + if (is_shadow_pwd && ((spwd = prefix_getspnam (user_name)) != NULL)) { user_expire = spwd->sp_expire; user_inactive = spwd->sp_inact; } @@ -1346,7 +1377,7 @@ static void process_flags (int argc, char **argv) } /* local, no need for xgetpwnam */ - if (lflg && (getpwnam (user_newname) != NULL)) { + if (lflg && (prefix_getpwnam (user_newname) != NULL)) { fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_newname); @@ -1354,7 +1385,7 @@ static void process_flags (int argc, char **argv) } /* local, no need for xgetpwuid */ - if (uflg && !oflg && (getpwuid (user_newid) != NULL)) { + if (uflg && !oflg && (prefix_getpwuid (user_newid) != NULL)) { fprintf (stderr, _("%s: UID '%lu' already exists\n"), Prog, (unsigned long) user_newid); @@ -1731,7 +1762,7 @@ static void move_home (void) { struct stat sb; - if (access (user_newhome, F_OK) == 0) { + if (access (prefix_user_newhome, F_OK) == 0) { /* * If the new home directory already exist, the user * should not use -m. @@ -1742,7 +1773,7 @@ static void move_home (void) fail_exit (E_HOMEDIR); } - if (stat (user_home, &sb) == 0) { + if (stat (prefix_user_home, &sb) == 0) { /* * Don't try to move it if it is not a directory * (but /dev/null for example). --marekm @@ -1764,11 +1795,11 @@ static void move_home (void) } #endif - if (rename (user_home, user_newhome) == 0) { + if (rename (prefix_user_home, prefix_user_newhome) == 0) { /* FIXME: rename above may have broken symlinks * pointing to the user's home directory * with an absolute path. */ - if (chown_tree (user_newhome, + if (chown_tree (prefix_user_newhome, user_id, uflg ? user_newid : (uid_t)-1, user_gid, gflg ? user_newgid : (gid_t)-1) != 0) { fprintf (stderr, @@ -1785,16 +1816,16 @@ static void move_home (void) return; } else { if (EXDEV == errno) { - if (copy_tree (user_home, user_newhome, true, + if (copy_tree (prefix_user_home, prefix_user_newhome, true, true, user_id, uflg ? user_newid : (uid_t)-1, user_gid, gflg ? user_newgid : (gid_t)-1) == 0) { - if (remove_tree (user_home, true) != 0) { + if (remove_tree (prefix_user_home, true) != 0) { fprintf (stderr, _("%s: warning: failed to completely remove old home directory %s"), - Prog, user_home); + Prog, prefix_user_home); } #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, @@ -1807,11 +1838,11 @@ static void move_home (void) return; } - (void) remove_tree (user_newhome, true); + (void) remove_tree (prefix_user_newhome, true); } fprintf (stderr, _("%s: cannot rename directory %s to %s\n"), - Prog, user_home, user_newhome); + Prog, prefix_user_home, prefix_user_newhome); fail_exit (E_HOMEDIR); } } @@ -1949,9 +1980,11 @@ static void update_faillog (void) static void move_mailbox (void) { const char *maildir; - char mailfile[1024], newmailfile[1024]; + char* mailfile; + char* newmailfile; int fd; struct stat st; + size_t len; maildir = getdef_str ("MAIL_DIR"); #ifdef MAIL_SPOOL_DIR @@ -1962,6 +1995,8 @@ static void move_mailbox (void) if (NULL == maildir) { return; } + len = strlen (prefix) + strlen (maildir) + strlen (user_name) + 2; + mailfile = alloca (len); /* * O_NONBLOCK is to make sure open won't hang on mandatory locks. @@ -1969,9 +2004,16 @@ static void move_mailbox (void) * replacing /var/spool/mail/luser with a hard link to /etc/passwd * between stat and chown). --marekm */ - (void) snprintf (mailfile, sizeof mailfile, "%s/%s", - maildir, user_name); - mailfile[(sizeof mailfile) - 1] = '\0'; + if (prefix[0]) { + (void) snprintf (mailfile, len, "%s/%s/%s", + prefix, maildir, user_name); + } + else { + (void) snprintf (mailfile, len, "%s/%s", + maildir, user_name); + } + mailfile[len-1] = '\0'; + fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { /* no need for warnings if the mailbox doesn't exist */ @@ -2008,9 +2050,17 @@ static void move_mailbox (void) (void) close (fd); if (lflg) { - (void) snprintf (newmailfile, sizeof newmailfile, "%s/%s", - maildir, user_newname); - newmailfile[(sizeof newmailfile) - 1] = '\0'; + len = strlen (prefix) + strlen (maildir) + strlen (user_newname) + 2; + newmailfile = alloca(len); + if (prefix[0]) { + (void) snprintf (newmailfile, len, "%s/%s/%s", + prefix, maildir, user_newname); + } + else { + (void) snprintf (newmailfile, len, "%s/%s", + maildir, user_newname); + } + newmailfile[len - 1] = '\0'; if ( (link (mailfile, newmailfile) != 0) || (unlink (mailfile) != 0)) { perror (_("failed to rename mailbox")); @@ -2048,6 +2098,7 @@ int main (int argc, char **argv) (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); + prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("usermod"); #ifdef WITH_AUDIT @@ -2072,8 +2123,9 @@ int main (int argc, char **argv) /* * The home directory, the username and the user's UID should not * be changed while the user is logged in. + * Note: no need to check if a prefix is specified... */ - if ( (uflg || lflg || dflg + if ( (prefix[0] == '\0') && (uflg || lflg || dflg #ifdef ENABLE_SUBIDS || Vflg || Wflg #endif /* ENABLE_SUBIDS */ @@ -2250,7 +2302,7 @@ int main (int argc, char **argv) } if (!mflg && (uflg || gflg)) { - if (access (dflg ? user_newhome : user_home, F_OK) == 0) { + if (access (dflg ? prefix_user_newhome : prefix_user_home, F_OK) == 0) { /* * Change the UID on all of the files owned by * `user_id' to `user_newid' in the user's home @@ -2267,7 +2319,7 @@ int main (int argc, char **argv) user_newname, (unsigned int) user_newid, 1); } #endif - if (chown_tree (dflg ? user_newhome : user_home, + if (chown_tree (dflg ? prefix_user_newhome : prefix_user_home, user_id, uflg ? user_newid : (uid_t)-1, user_gid, |