diff --git a/lib/commonio.c b/lib/commonio.c index b10da06a..c9893c2d 100644 --- a/lib/commonio.c +++ b/lib/commonio.c @@ -375,21 +375,30 @@ bool commonio_present (const struct commonio_db *db) int commonio_lock_nowait (struct commonio_db *db, bool log) { - char file[1024]; - char lock[1024]; + char* file; + char* lock; + size_t lock_file_len; + size_t file_len; if (db->locked) { return 1; } - - snprintf (file, sizeof file, "%s.%lu", + file_len = strlen(db->filename) + 11;/* %lu max size */ + lock_file_len = strlen(db->filename) + 6; /* sizeof ".lock" */ + file = (char*)malloc(file_len); + lock = (char*)malloc(lock_file_len); + snprintf (file, file_len, "%s.%lu", db->filename, (unsigned long) getpid ()); - snprintf (lock, sizeof lock, "%s.lock", db->filename); + snprintf (lock, lock_file_len, "%s.lock", db->filename); if (do_lock_file (file, lock, log) != 0) { db->locked = true; lock_count++; + free(file); + free(lock); return 1; } + free(file); + free(lock); return 0; } diff --git a/lib/getdef.c b/lib/getdef.c index a181cc2b..d57b12de 100644 --- a/lib/getdef.c +++ b/lib/getdef.c @@ -155,7 +155,7 @@ static struct itemdef knowndef_table[] = { #define LOGINDEFS "/etc/login.defs" #endif -static char def_fname[] = LOGINDEFS; /* login config defs file */ +static const char* def_fname = LOGINDEFS; /* login config defs file */ static bool def_loaded = false; /* are defs already loaded? */ /* local function prototypes */ @@ -424,6 +424,17 @@ out: return (struct itemdef *) NULL; } +/* + * setdef_config_file - set the default configuration file path + * + * must be called prior to any def* calls. + */ + +void setdef_config_file (const char* file) +{ + def_fname = file; +} + /* * def_load - load configuration table * diff --git a/lib/getdef.h b/lib/getdef.h index 15e35ff7..46346d8e 100644 --- a/lib/getdef.h +++ b/lib/getdef.h @@ -40,6 +40,7 @@ extern unsigned long getdef_ulong (const char *, unsigned long); extern unsigned int getdef_unum (const char *, unsigned int); extern /*@observer@*/ /*@null@*/const char *getdef_str (const char *); extern int putdef_str (const char *, const char *); +extern void setdef_config_file (const char* file); /* default UMASK value if not specified in /etc/login.defs */ #define GETDEF_DEFAULT_UMASK 022 diff --git a/lib/prototypes.h b/lib/prototypes.h index 7aaf1a63..be0600b5 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -271,6 +271,21 @@ extern void do_pam_passwd (const char *user, bool silent, bool change_expired); /* port.c */ extern bool isttytime (const char *, const char *, time_t); +/* prefix_flag.c */ +extern const char* process_prefix_flag (const char* short_opt, int argc, char **argv); +extern struct group *prefix_getgrnam(const char *name); +extern struct group *prefix_getgrgid(gid_t gid); +extern struct passwd *prefix_getpwuid(uid_t uid); +extern struct passwd *prefix_getpwnam(const char* name); +extern struct spwd *prefix_getspnam(const char* name); +extern struct group *prefix_getgr_nam_gid(const char *grname); +extern void prefix_setpwent(); +extern struct passwd* prefix_getpwent(); +extern void prefix_endpwent(); +extern void prefix_setgrent(); +extern struct group* prefix_getgrent(); +extern void prefix_endgrent(); + /* pwd2spwd.c */ #ifndef USE_PAM extern struct spwd *pwd_to_spwd (const struct passwd *); diff --git a/libmisc/Makefile.am b/libmisc/Makefile.am index e0b6d8ca..0e42d317 100644 --- a/libmisc/Makefile.am +++ b/libmisc/Makefile.am @@ -44,6 +44,7 @@ libmisc_a_SOURCES = \ obscure.c \ pam_pass.c \ pam_pass_non_interractive.c \ + prefix_flag.c \ pwd2spwd.c \ pwdcheck.c \ pwd_init.c \ diff --git a/libmisc/find_new_gid.c b/libmisc/find_new_gid.c index 2fe6a2a9..d8764087 100644 --- a/libmisc/find_new_gid.c +++ b/libmisc/find_new_gid.c @@ -136,7 +136,7 @@ static int check_gid (const gid_t gid, } /* Check if the GID exists according to NSS */ errno = 0; - if (getgrgid (gid) != NULL) { + if (prefix_getgrgid (gid) != NULL) { return EEXIST; } else { /* getgrgid() was NULL diff --git a/libmisc/find_new_uid.c b/libmisc/find_new_uid.c index b3143134..d01f79b1 100644 --- a/libmisc/find_new_uid.c +++ b/libmisc/find_new_uid.c @@ -136,7 +136,7 @@ static int check_uid(const uid_t uid, } /* Check if the UID exists according to NSS */ errno = 0; - if (getpwuid(uid) != NULL) { + if (prefix_getpwuid(uid) != NULL) { return EEXIST; } else { /* getpwuid() was NULL diff --git a/libmisc/prefix_flag.c b/libmisc/prefix_flag.c new file mode 100644 index 00000000..4115aa11 --- /dev/null +++ b/libmisc/prefix_flag.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2011 , Julian Pidancet + * Copyright (c) 2011 , Nicolas François + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ident "$Id$" + +#include +#include +#include "defines.h" +#include "prototypes.h" +/*@-exitarg@*/ +#include "exitcodes.h" +#include "groupio.h" +#include "pwio.h" +#ifdef SHADOWGRP +#include "sgroupio.h" +#endif +#include "shadowio.h" +#ifdef ENABLE_SUBIDS +#include "subordinateio.h" +#endif /* ENABLE_SUBIDS */ +#include "getdef.h" + +static char *passwd_db_file = NULL; +static char *spw_db_file = NULL; +static char *group_db_file = NULL; +static char *sgroup_db_file = NULL; +static char *suid_db_file = NULL; +static char *sgid_db_file = NULL; +static char *def_conf_file = NULL; +static FILE* fp_pwent = NULL; +static FILE* fp_grent = NULL; + +/* + * process_prefix_flag - prefix all paths if given the --prefix option + * + * This shall be called before accessing the passwd, group, shadow, + * gshadow, useradd's default, login.defs files (non exhaustive list) + * or authenticating the caller. + * + * The audit, syslog, or locale files shall be open before + */ +extern const char* process_prefix_flag (const char* short_opt, int argc, char **argv) +{ + /* + * Parse the command line options. + */ + int i; + const char *prefix = NULL; + + for (i = 0; i < argc; i++) { + if ( (strcmp (argv[i], "--prefix") == 0) + || (strcmp (argv[i], short_opt) == 0)) { + if (NULL != prefix) { + fprintf (stderr, + _("%s: multiple --prefix options\n"), + Prog); + exit (E_BAD_ARG); + } + + if (i + 1 == argc) { + fprintf (stderr, + _("%s: option '%s' requires an argument\n"), + Prog, argv[i]); + exit (E_BAD_ARG); + } + prefix = argv[i + 1]; + } + } + + + + if (prefix != NULL) { + if ( prefix[0] == '\0' || !strcmp(prefix, "/")) + return ""; /* if prefix is "/" then we ignore the flag option */ + /* should we prevent symbolic link from being used as a prefix? */ + + size_t len; + len = strlen(prefix) + strlen(PASSWD_FILE) + 2; + passwd_db_file = xmalloc(len); + snprintf(passwd_db_file, len, "%s/%s", prefix, PASSWD_FILE); + pw_setdbname(passwd_db_file); + + len = strlen(prefix) + strlen(GROUP_FILE) + 2; + group_db_file = xmalloc(len); + snprintf(group_db_file, len, "%s/%s", prefix, GROUP_FILE); + gr_setdbname(group_db_file); + +#ifdef SHADOWGRP + len = strlen(prefix) + strlen(SGROUP_FILE) + 2; + sgroup_db_file = xmalloc(len); + snprintf(sgroup_db_file, len, "%s/%s", prefix, SGROUP_FILE); + sgr_setdbname(sgroup_db_file); +#endif +#ifdef USE_NIS + __setspNIS(0); /* disable NIS for now, at least until it is properly supporting a "prefix" */ +#endif + + len = strlen(prefix) + strlen(SHADOW_FILE) + 2; + spw_db_file = xmalloc(len); + snprintf(spw_db_file, len, "%s/%s", prefix, SHADOW_FILE); + spw_setdbname(spw_db_file); + + + len = strlen(prefix) + strlen("/etc/subuid") + 2; + suid_db_file = xmalloc(len); + snprintf(suid_db_file, len, "%s/%s", prefix, "/etc/subuid"); + sub_uid_setdbname(suid_db_file); + + len = strlen(prefix) + strlen("/etc/subgid") + 2; + sgid_db_file = xmalloc(len); + snprintf(sgid_db_file, len, "%s/%s", prefix, "/etc/subgid"); + sub_gid_setdbname(sgid_db_file); + + len = strlen(prefix) + strlen("/etc/login.defs") + 2; + def_conf_file = xmalloc(len); + snprintf(def_conf_file, len, "%s/%s", prefix, "/etc/login.defs"); + setdef_config_file(def_conf_file); + } + + if (prefix == NULL) + return ""; + return prefix; +} + + +extern struct group *prefix_getgrnam(const char *name) +{ + if (group_db_file) { + FILE* fg; + struct group * grp = NULL; + + fg = fopen(group_db_file, "rt"); + if(!fg) + return NULL; + while(grp = fgetgrent(fg)) { + if(!strcmp(name, grp->gr_name)) + break; + } + fclose(fg); + return grp; + } + else { + return getgrnam(name); + } +} + +extern struct group *prefix_getgrgid(gid_t gid) +{ + if (group_db_file) { + FILE* fg; + struct group * grp = NULL; + + fg = fopen(group_db_file, "rt"); + if(!fg) + return NULL; + while(grp = fgetgrent(fg)) { + if(gid == grp->gr_gid) + break; + } + fclose(fg); + return grp; + } + else { + return getgrgid(gid); + } +} + +extern struct passwd *prefix_getpwuid(uid_t uid) +{ + if (passwd_db_file) { + FILE* fg; + struct passwd *pwd = NULL; + + fg = fopen(passwd_db_file, "rt"); + if(!fg) + return NULL; + while(pwd = fgetpwent(fg)) { + if(uid == pwd->pw_uid) + break; + } + fclose(fg); + return pwd; + } + else { + return getpwuid(uid); + } +} +extern struct passwd *prefix_getpwnam(const char* name) +{ + if (passwd_db_file) { + FILE* fg; + struct passwd *pwd = NULL; + + fg = fopen(passwd_db_file, "rt"); + if(!fg) + return NULL; + while(pwd = fgetpwent(fg)) { + if(!strcmp(name, pwd->pw_name)) + break; + } + fclose(fg); + return pwd; + } + else { + return getpwnam(name); + } +} +extern struct spwd *prefix_getspnam(const char* name) +{ + if (spw_db_file) { + FILE* fg; + struct spwd *sp = NULL; + + fg = fopen(spw_db_file, "rt"); + if(!fg) + return NULL; + while(sp = fgetspent(fg)) { + if(!strcmp(name, sp->sp_namp)) + break; + } + fclose(fg); + return sp; + } + else { + return getspnam(name); + } +} + +extern void prefix_setpwent() +{ + if(!passwd_db_file) { + setpwent(); + return; + } + if (fp_pwent) + fclose (fp_pwent); + + fp_pwent = fopen(passwd_db_file, "rt"); + if(!fp_pwent) + return; +} +extern struct passwd* prefix_getpwent() +{ + if(!passwd_db_file) { + return getpwent(); + } + return fgetpwent(fp_pwent); +} +extern void prefix_endpwent() +{ + if(!passwd_db_file) { + endpwent(); + return; + } + if (fp_pwent) + fclose(fp_pwent); + fp_pwent = NULL; +} + +extern void prefix_setgrent() +{ + if(!group_db_file) { + setgrent(); + return; + } + if (fp_grent) + fclose (fp_grent); + + fp_grent = fopen(group_db_file, "rt"); + if(!fp_grent) + return; +} +extern struct group* prefix_getgrent() +{ + if(!group_db_file) { + return getgrent(); + } + return fgetgrent(fp_grent); +} +extern void prefix_endgrent() +{ + if(!group_db_file) { + endgrent(); + return; + } + if (fp_grent) + fclose(fp_grent); + fp_grent = NULL; +} + +extern struct group *prefix_getgr_nam_gid(const char *grname) +{ + long long int gid; + char *endptr; + + if (NULL == grname) { + return NULL; + } + + if (group_db_file) { + errno = 0; + gid = strtoll (grname, &endptr, 10); + if ( ('\0' != *grname) + && ('\0' == *endptr) + && (ERANGE != errno) + && (gid == (gid_t)gid)) { + return prefix_getgrgid ((gid_t) gid); + } + return prefix_getgrnam (grname); + } + else + return getgr_nam_gid(grname); +} diff --git a/man/groupadd.8.xml b/man/groupadd.8.xml index 93d8b6f7..1e58f093 100644 --- a/man/groupadd.8.xml +++ b/man/groupadd.8.xml @@ -212,6 +212,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + diff --git a/man/groupdel.8.xml b/man/groupdel.8.xml index 438e9e54..41116617 100644 --- a/man/groupdel.8.xml +++ b/man/groupdel.8.xml @@ -109,6 +109,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + diff --git a/man/groupmod.8.xml b/man/groupmod.8.xml index 46666252..7be03f1e 100644 --- a/man/groupmod.8.xml +++ b/man/groupmod.8.xml @@ -186,6 +186,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + diff --git a/man/useradd.8.xml b/man/useradd.8.xml index 8d56301c..582b39c7 100644 --- a/man/useradd.8.xml +++ b/man/useradd.8.xml @@ -453,6 +453,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + ,  SHELL diff --git a/man/userdel.8.xml b/man/userdel.8.xml index 3f95a8d9..b086383b 100644 --- a/man/userdel.8.xml +++ b/man/userdel.8.xml @@ -154,6 +154,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + , diff --git a/man/usermod.8.xml b/man/usermod.8.xml index 6cd469c6..7fea2981 100644 --- a/man/usermod.8.xml +++ b/man/usermod.8.xml @@ -319,6 +319,23 @@ + + + ,  PREFIX_DIR + + + + Apply changes in the PREFIX_DIR + directory and use the configuration files from the + PREFIX_DIR directory. + This option does not chroot and is intended for preparing + a cross-compilation target. + Some limitations: NIS and LDAP users/groups are not verified. + PAM authentication is using the host files. + No SELINUX support. + + + ,  SHELL 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/groupmod.c b/src/groupmod.c index 757c1a40..253f4ec3 100644 --- a/src/groupmod.c +++ b/src/groupmod.c @@ -80,6 +80,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 +130,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 +343,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 +379,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 +411,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 +703,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 +726,7 @@ void update_primary_groups (gid_t ogid, gid_t ngid) } } } - endpwent (); + prefix_endpwent (); } /* @@ -746,6 +752,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 @@ -805,7 +812,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/useradd.c b/src/useradd.c index 6d944056..e4bd7d7f 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 */ } + + if(prefix[0]) { + size_t len; + int wlen; + char* _def_template; /* avoid const warning */ - def_template = xstrdup (cp); + 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, diff --git a/src/userdel.c b/src/userdel.c index 9092b5c1..963a2777 100644 --- a/src/userdel.c +++ b/src/userdel.c @@ -34,6 +34,7 @@ #ident "$Id$" +#include #include #include #include @@ -113,6 +114,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 +152,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 +330,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 +342,7 @@ static void remove_usergroup (void) break; } } - endpwent (); + prefix_endpwent (); } if (NULL == pwd) { @@ -814,9 +818,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,7 +832,19 @@ 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 = alloca (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) { @@ -990,6 +1007,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 +1024,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 +1032,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) { @@ -1030,8 +1049,16 @@ int main (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; #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 { @@ -1104,7 +1131,7 @@ int main (int argc, char **argv) user_name = argv[argc - 1]; { struct passwd *pwd; - pwd = getpwnam (user_name); /* local, no need for xgetpwnam */ + pwd = prefix_getpwnam (user_name); /* local, no need for xgetpwnam */ if (NULL == pwd) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user_name); @@ -1118,7 +1145,18 @@ 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); + } } #ifdef WITH_TCB if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { @@ -1150,7 +1188,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') && user_busy (user_name, user_id) != 0) { if (!fflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, @@ -1201,8 +1239,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 +1254,7 @@ int main (int argc, char **argv) break; } } - endpwent (); + prefix_endpwent (); } #endif /* EXTRA_CHECK_HOME_DIR */ @@ -1268,7 +1306,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 68e31e4f..a6dbcbd4 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -34,6 +34,7 @@ #ident "$Id$" +#include #include #include #include @@ -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 @@ -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,