diff options
Diffstat (limited to 'src/usermod.c')
-rw-r--r-- | src/usermod.c | 365 |
1 files changed, 145 insertions, 220 deletions
diff --git a/src/usermod.c b/src/usermod.c index 03bb9b9d..9473a7d7 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -1,33 +1,10 @@ /* - * Copyright (c) 1991 - 1994, Julianne Frances Haugh - * Copyright (c) 1996 - 2000, Marek Michałkiewicz - * Copyright (c) 2000 - 2006, Tomasz Kłoczko - * Copyright (c) 2007 - 2011, Nicolas François - * All rights reserved. + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz + * SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko + * SPDX-FileCopyrightText: 2007 - 2011, Nicolas François * - * 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. + * SPDX-License-Identifier: BSD-3-Clause */ #include <config.h> @@ -74,6 +51,7 @@ #ifdef WITH_TCB #include "tcbfuncs.h" #endif +#include "shadowlog.h" /* * exit status values @@ -105,7 +83,6 @@ * Global variables */ const char *Prog; -FILE *shadow_logfd = NULL; static char *user_name; static char *user_newname; @@ -147,6 +124,7 @@ static bool mflg = false, /* create user's home directory if it doesn't exist */ oflg = false, /* permit non-unique user ID to be specified with -u */ pflg = false, /* new encrypted password */ + rflg = false, /* remove a user from a single group */ sflg = false, /* new shell program */ #ifdef WITH_SELINUX Zflg = false, /* new selinux user */ @@ -184,10 +162,7 @@ static bool sub_gid_locked = false; /* local function prototypes */ -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 *); @@ -201,9 +176,7 @@ 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); @@ -215,28 +188,6 @@ static void move_mailbox (void); extern int allow_bad_names; -static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize, - long int date) -{ - struct tm *tp; - - if (date < 0) { - strncpy (buf, "never", maxsize); - } else { - time_t t = (time_t) date; - tp = gmtime (&t); -#ifdef HAVE_STRFTIME - strftime (buf, maxsize, "%Y-%m-%d", tp); -#else - (void) snprintf (buf, maxsize, "%04d-%02d-%02d", - tp->tm_year + 1900, - tp->tm_mon + 1, - tp->tm_mday); -#endif /* HAVE_STRFTIME */ - } - buf[maxsize - 1] = '\0'; -} - /* * get_groups - convert a list of group names to an array of group IDs * @@ -261,11 +212,6 @@ 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. @@ -284,7 +230,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 = get_local_group (list); + grp = prefix_getgr_nam_gid (list); /* * There must be a match, either by GID value or by @@ -334,8 +280,6 @@ static int get_groups (char *list) gr_free ((struct group *)grp); } while (NULL != list); - close_group_files (); - user_groups[ngroups] = (char *) 0; /* @@ -348,44 +292,6 @@ 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 { @@ -418,7 +324,6 @@ static struct ulong_range getulong_range(const char *str) result.last = (unsigned long int)last; out: return result; - } struct ulong_range_list_entry { @@ -473,6 +378,9 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -a, --append append the user to the supplemental GROUPS\n" " mentioned by the -G option without removing\n" " the user from other groups\n"), usageout); + (void) fputs (_(" -r, --remove remove the user from only the supplemental GROUPS\n" + " mentioned by the -G option without removing\n" + " 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); @@ -629,6 +537,12 @@ static void new_pwent (struct passwd *pwent) SYSLOG ((LOG_INFO, "change user '%s' home from '%s' to '%s'", pwent->pw_name, pwent->pw_dir, user_newhome)); + + if (strlen(user_newhome) > 1 + && '/' == user_newhome[strlen(user_newhome)-1]) { + user_newhome[strlen(user_newhome)-1]='\0'; + } + pwent->pw_dir = user_newhome; } if (sflg) { @@ -676,10 +590,8 @@ static void new_spent (struct spwd *spent) if (eflg) { /* log dates rather than numbers of days. */ char new_exp[16], old_exp[16]; - date_to_str (new_exp, sizeof(new_exp), - user_newexpire * DAY); - date_to_str (old_exp, sizeof(old_exp), - user_expire * DAY); + date_to_str (sizeof(new_exp), new_exp, user_newexpire * DAY); + date_to_str (sizeof(old_exp), old_exp, user_expire * DAY); #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing expiration date", @@ -800,6 +712,14 @@ static void update_group (void) continue; } + /* + * If rflg+Gflg is passed in AKA -rG invert is_member flag, which removes + * mentioned groups while leaving the others. + */ + if (Gflg && rflg && was_member) { + is_member = !is_member; + } + ngrp = __gr_dup (grp); if (NULL == ngrp) { fprintf (stderr, @@ -915,6 +835,14 @@ static void update_gshadow (void) continue; } + /* + * If rflg+Gflg is passed in AKA -rG invert is_member, to remove targeted + * groups while leaving the user apart of groups not mentioned + */ + if (Gflg && rflg && was_member) { + is_member = !is_member; + } + nsgrp = __sgr_dup (sgrp); if (NULL == nsgrp) { fprintf (stderr, @@ -997,7 +925,7 @@ static void update_gshadow (void) changed = false; - /* + /* * Update the group entry to reflect the changes. */ if (sgr_update (nsgrp) == 0) { @@ -1063,6 +991,7 @@ static void process_flags (int argc, char **argv) {"move-home", no_argument, NULL, 'm'}, {"non-unique", no_argument, NULL, 'o'}, {"password", required_argument, NULL, 'p'}, + {"remove", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, {"prefix", required_argument, NULL, 'P'}, {"shell", required_argument, NULL, 's'}, @@ -1080,7 +1009,7 @@ static void process_flags (int argc, char **argv) {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, - "abc:d:e:f:g:G:hl:Lmop:R:s:u:UP:" + "abc:d:e:f:g:G:hl:Lmop:rR:s:u:UP:" #ifdef ENABLE_SUBIDS "v:w:V:W:" #endif /* ENABLE_SUBIDS */ @@ -1122,18 +1051,14 @@ static void process_flags (int argc, char **argv) } break; case 'e': - if ('\0' != *optarg) { - user_newexpire = strtoday (optarg); - if (user_newexpire < -1) { - fprintf (stderr, - _("%s: invalid date '%s'\n"), - Prog, optarg); - exit (E_BAD_ARG); - } - user_newexpire *= DAY / SCALE; - } else { - user_newexpire = -1; + user_newexpire = strtoday (optarg); + if (user_newexpire < -1) { + fprintf (stderr, + _("%s: invalid date '%s'\n"), + Prog, optarg); + exit (E_BAD_ARG); } + user_newexpire *= DAY / SCALE; eflg = true; break; case 'f': @@ -1190,6 +1115,9 @@ static void process_flags (int argc, char **argv) user_pass = optarg; pflg = true; break; + case 'r': + rflg = true; + break; case 'R': /* no-op, handled in process_root_flag () */ break; case 'P': /* no-op, handled in process_prefix_flag () */ @@ -1329,7 +1257,7 @@ static void process_flags (int argc, char **argv) if (!gflg) { user_newgid = user_gid; } - if(prefix[0]) { + if (prefix[0]) { size_t len = strlen(prefix) + strlen(user_home) + 2; int wlen; prefix_user_home = xmalloc(len); @@ -1391,6 +1319,20 @@ static void process_flags (int argc, char **argv) usage (E_USAGE); } + if (rflg && (!Gflg)) { + fprintf (stderr, + _("%s: %s flag is only allowed with the %s flag\n"), + Prog, "-r", "-G"); + usage (E_USAGE); + } + + if (rflg && aflg) { + fprintf (stderr, + _("%s: %s and %s are mutually exclusive flags\n"), + Prog, "-r", "-a"); + usage (E_USAGE); + } + if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) { fprintf (stderr, _("%s: the -L, -p, and -U flags are exclusive\n"), @@ -1523,7 +1465,50 @@ static void close_files (void) } if (Gflg || lflg) { - close_group_files (); + 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 */ + } } if (is_shadow_pwd) { @@ -1593,60 +1578,6 @@ 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. @@ -1681,7 +1612,38 @@ static void open_files (void) } if (Gflg || lflg) { - open_group_files (); + /* + * 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 } #ifdef ENABLE_SUBIDS if (vflg || Vflg) { @@ -1718,44 +1680,6 @@ 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 @@ -2208,7 +2132,8 @@ 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; + log_set_progname(Prog); + log_set_logfd(stderr); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); @@ -2313,7 +2238,7 @@ int main (int argc, char **argv) if (sub_uid_remove(user_name, ptr->range.first, count) == 0) { fprintf (stderr, _("%s: failed to remove uid range %lu-%lu from '%s'\n"), - Prog, ptr->range.first, ptr->range.last, + Prog, ptr->range.first, ptr->range.last, sub_uid_dbname ()); fail_exit (E_SUB_UID_UPDATE); } @@ -2326,7 +2251,7 @@ int main (int argc, char **argv) if (sub_uid_add(user_name, ptr->range.first, count) == 0) { fprintf (stderr, _("%s: failed to add uid range %lu-%lu to '%s'\n"), - Prog, ptr->range.first, ptr->range.last, + Prog, ptr->range.first, ptr->range.last, sub_uid_dbname ()); fail_exit (E_SUB_UID_UPDATE); } @@ -2339,7 +2264,7 @@ int main (int argc, char **argv) if (sub_gid_remove(user_name, ptr->range.first, count) == 0) { fprintf (stderr, _("%s: failed to remove gid range %lu-%lu from '%s'\n"), - Prog, ptr->range.first, ptr->range.last, + Prog, ptr->range.first, ptr->range.last, sub_gid_dbname ()); fail_exit (E_SUB_GID_UPDATE); } @@ -2352,7 +2277,7 @@ int main (int argc, char **argv) if (sub_gid_add(user_name, ptr->range.first, count) == 0) { fprintf (stderr, _("%s: failed to add gid range %lu-%lu to '%s'\n"), - Prog, ptr->range.first, ptr->range.last, + Prog, ptr->range.first, ptr->range.last, sub_gid_dbname ()); fail_exit (E_SUB_GID_UPDATE); } |