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