diff options
Diffstat (limited to 'src/usermod.c')
-rw-r--r-- | src/usermod.c | 275 |
1 files changed, 183 insertions, 92 deletions
diff --git a/src/usermod.c b/src/usermod.c index 05b98715..03bb9b9d 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -68,6 +68,9 @@ #ifdef ENABLE_SUBIDS #include "subordinateio.h" #endif /* ENABLE_SUBIDS */ +#ifdef WITH_SELINUX +#include <selinux/selinux.h> +#endif /* WITH_SELINUX */ #ifdef WITH_TCB #include "tcbfuncs.h" #endif @@ -102,6 +105,7 @@ * Global variables */ const char *Prog; +FILE *shadow_logfd = NULL; static char *user_name; static char *user_newname; @@ -183,6 +187,7 @@ static bool sub_gid_locked = false; static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize, long int date); static int get_groups (char *); +static struct group * get_local_group (char * grp_name); static /*@noreturn@*/void usage (int status); static void new_pwent (struct passwd *); static void new_spent (struct spwd *); @@ -196,7 +201,9 @@ static void grp_update (void); static void process_flags (int, char **); static void close_files (void); +static void close_group_files (void); static void open_files (void); +static void open_group_files (void); static void usr_update (void); static void move_home (void); static void update_lastlog (void); @@ -254,6 +261,11 @@ static int get_groups (char *list) } /* + * Open the group files + */ + open_group_files (); + + /* * So long as there is some data to be converted, strip off each * name and look it up. A mix of numerical and string values for * group identifiers is permitted. @@ -272,7 +284,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 = prefix_getgr_nam_gid (list); + grp = get_local_group (list); /* * There must be a match, either by GID value or by @@ -322,6 +334,8 @@ static int get_groups (char *list) gr_free ((struct group *)grp); } while (NULL != list); + close_group_files (); + user_groups[ngroups] = (char *) 0; /* @@ -334,6 +348,44 @@ static int get_groups (char *list) return 0; } +/* + * get_local_group - checks if a given group name exists locally + * + * get_local_group() checks if a given group name exists locally. + * If the name exists the group information is returned, otherwise NULL is + * returned. + */ +static struct group * get_local_group(char * grp_name) +{ + const struct group *grp; + struct group *result_grp = NULL; + long long int gid; + char *endptr; + + gid = strtoll (grp_name, &endptr, 10); + if ( ('\0' != *grp_name) + && ('\0' == *endptr) + && (ERANGE != errno) + && (gid == (gid_t)gid)) { + grp = gr_locate_gid ((gid_t) gid); + } + else { + grp = gr_locate(grp_name); + } + + if (grp != NULL) { + result_grp = __gr_dup (grp); + if (NULL == result_grp) { + fprintf (stderr, + _("%s: Out of memory. Cannot find group '%s'.\n"), + Prog, grp_name); + fail_exit (E_GRP_UPDATE); + } + } + + return result_grp; +} + #ifdef ENABLE_SUBIDS struct ulong_range { @@ -819,6 +871,8 @@ static void update_group (void) SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name)); fail_exit (E_GRP_UPDATE); } + + gr_free(ngrp); } } @@ -954,6 +1008,8 @@ static void update_gshadow (void) sgr_dbname (), nsgrp->sg_name)); fail_exit (E_GRP_UPDATE); } + + free (nsgrp); } } #endif /* SHADOWGRP */ @@ -984,7 +1040,7 @@ static void grp_update (void) static void process_flags (int argc, char **argv) { const struct group *grp; - + struct stat st; bool anyflag = false; { @@ -1058,6 +1114,12 @@ static void process_flags (int argc, char **argv) } dflg = true; user_newhome = optarg; + if (user_newhome[0] != '/') { + fprintf (stderr, + _("%s: homedir must be an absolute path\n"), + Prog); + exit (E_BAD_ARG); + } break; case 'e': if ('\0' != *optarg) { @@ -1094,6 +1156,7 @@ static void process_flags (int argc, char **argv) } user_newgid = grp->gr_gid; gflg = true; + gr_free (grp); break; case 'G': if (get_groups (optarg) != 0) { @@ -1132,12 +1195,25 @@ static void process_flags (int argc, char **argv) case 'P': /* no-op, handled in process_prefix_flag () */ break; case 's': - if (!VALID (optarg)) { + if ( ( !VALID (optarg) ) + || ( ('\0' != optarg[0]) + && ('/' != optarg[0]) + && ('*' != optarg[0]) )) { fprintf (stderr, - _("%s: invalid field '%s'\n"), + _("%s: invalid shell '%s'\n"), Prog, optarg); exit (E_BAD_ARG); } + if ( '\0' != optarg[0] + && '*' != optarg[0] + && strcmp(optarg, "/sbin/nologin") != 0 + && ( stat(optarg, &st) != 0 + || S_ISDIR(st.st_mode) + || access(optarg, X_OK) != 0)) { + fprintf (stderr, + _("%s: Warning: missing or non-executable shell '%s'\n"), + Prog, optarg); + } user_newshell = optarg; sflg = true; break; @@ -1447,50 +1523,7 @@ static void close_files (void) } if (Gflg || lflg) { - if (gr_close () == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, - "failure while writing changes to %s", - gr_dbname ())); - fail_exit (E_GRP_UPDATE); - } -#ifdef SHADOWGRP - if (is_shadow_grp) { - if (sgr_close () == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, - "failure while writing changes to %s", - sgr_dbname ())); - fail_exit (E_GRP_UPDATE); - } - } -#endif -#ifdef SHADOWGRP - if (is_shadow_grp) { - if (sgr_unlock () == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, - "failed to unlock %s", - sgr_dbname ())); - /* continue */ - } - } -#endif - if (gr_unlock () == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, - "failed to unlock %s", - gr_dbname ())); - /* continue */ - } + close_group_files (); } if (is_shadow_pwd) { @@ -1560,6 +1593,60 @@ static void close_files (void) } /* + * close_group_files - close all of the files that were opened + * + * close_group_files() closes all of the files that were opened related + * with groups. This causes any modified entries to be written out. + */ +static void close_group_files (void) +{ + if (gr_close () == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, + "failure while writing changes to %s", + gr_dbname ())); + fail_exit (E_GRP_UPDATE); + } +#ifdef SHADOWGRP + if (is_shadow_grp) { + if (sgr_close () == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, + "failure while writing changes to %s", + sgr_dbname ())); + fail_exit (E_GRP_UPDATE); + } + } +#endif +#ifdef SHADOWGRP + if (is_shadow_grp) { + if (sgr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, + "failed to unlock %s", + sgr_dbname ())); + /* continue */ + } + } +#endif + if (gr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, + "failed to unlock %s", + gr_dbname ())); + /* continue */ + } +} + +/* * open_files - lock and open the password files * * open_files() opens the two password files. @@ -1594,38 +1681,7 @@ static void open_files (void) } if (Gflg || lflg) { - /* - * Lock and open the group file. This will load all of the - * group entries. - */ - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -#ifdef SHADOWGRP - if (is_shadow_grp && (sgr_lock () == 0)) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); - } - sgr_locked = true; - if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -#endif + open_group_files (); } #ifdef ENABLE_SUBIDS if (vflg || Vflg) { @@ -1662,6 +1718,44 @@ static void open_files (void) } /* + * open_group_files - lock and open the group files + * + * open_group_files() loads all of the group entries. + */ +static void open_group_files (void) +{ + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); + fail_exit (E_GRP_UPDATE); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, gr_dbname ()); + fail_exit (E_GRP_UPDATE); + } + +#ifdef SHADOWGRP + if (is_shadow_grp && (sgr_lock () == 0)) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); + fail_exit (E_GRP_UPDATE); + } + sgr_locked = true; + if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); + fail_exit (E_GRP_UPDATE); + } +#endif +} + +/* * usr_update - create the user entries * * usr_update() creates the password file entries for this user and @@ -1906,8 +2000,7 @@ static void update_lastlog (void) /* Copy the old entry to its new location */ if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid) || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll) - || (fsync (fd) != 0) - || (close (fd) != 0)) { + || (fsync (fd) != 0)) { fprintf (stderr, _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"), Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno)); @@ -1923,16 +2016,15 @@ static void update_lastlog (void) memzero (&ll, sizeof (ll)); if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid) || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll) - || (fsync (fd) != 0) - || (close (fd) != 0)) { + || (fsync (fd) != 0)) { fprintf (stderr, _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"), Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno)); } - } else { - (void) close (fd); } } + + (void) close (fd); } /* @@ -1967,8 +2059,7 @@ static void update_faillog (void) /* Copy the old entry to its new location */ if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid) || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl) - || (fsync (fd) != 0) - || (close (fd) != 0)) { + || (fsync (fd) != 0)) { fprintf (stderr, _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"), Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno)); @@ -1983,16 +2074,15 @@ static void update_faillog (void) /* Reset the new uid's faillog entry */ memzero (&fl, sizeof (fl)); if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid) - || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl) - || (close (fd) != 0)) { + || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)) { fprintf (stderr, _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"), Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno)); } - } else { - (void) close (fd); } } + + (void) close (fd); } #ifndef NO_MOVE_MAILBOX @@ -2118,6 +2208,7 @@ int main (int argc, char **argv) * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); + shadow_logfd = stderr; (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); |