summaryrefslogtreecommitdiff
path: root/src/usermod.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/usermod.c')
-rw-r--r--src/usermod.c365
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);
}