chage cleanups

* src/chage.c: Before pam_end(), the return value of the previous
	pam API was already checked. No need to validate it again.
	* src/chage.c: main() split in new functions: process_flags(),
	check_flags(), check_perms(), open_files(), and close_files().
This commit is contained in:
nekral-guest 2007-12-29 10:42:25 +00:00
parent 8563319b8b
commit 388dcee3e4
2 changed files with 216 additions and 181 deletions

View File

@ -1,3 +1,11 @@
2007-12-29 Nicolas François <nicolas.francois@centraliens.net>
chage cleanups
* src/chage.c: Before pam_end(), the return value of the previous
pam API was already checked. No need to validate it again.
* src/chage.c: main() split in new functions: process_flags(),
check_flags(), check_perms(), open_files(), and close_files().
2007-12-28 Nicolas François <nicolas.francois@centraliens.net> 2007-12-28 Nicolas François <nicolas.francois@centraliens.net>
Same changes for chgpasswd: Same changes for chgpasswd:

View File

@ -65,8 +65,7 @@ static int
mflg = 0, /* set minimum number of days before password change */ mflg = 0, /* set minimum number of days before password change */
Mflg = 0, /* set maximim number of days before password change */ Mflg = 0, /* set maximim number of days before password change */
Wflg = 0; /* set expiration warning days */ Wflg = 0; /* set expiration warning days */
static int amroot = 0;
static int locks;
static long mindays; static long mindays;
static long maxdays; static long maxdays;
@ -83,6 +82,11 @@ static void date_to_str (char *, size_t, time_t);
static int new_fields (void); static int new_fields (void);
static void print_date (time_t); static void print_date (time_t);
static void list_fields (void); static void list_fields (void);
static void process_flags (int argc, char **argv);
static void check_flags (int argc, int opt_index);
static void check_perms (void);
static void open_files (int readonly);
static void close_files (void);
/* /*
* isnum - determine whether or not a string is a number * isnum - determine whether or not a string is a number
@ -296,135 +300,85 @@ static void list_fields (void)
warndays); warndays);
} }
/* static void process_flags (int argc, char **argv)
* chage - change a user's password aging information
*
* This command controls the password aging information.
*
* The valid options are
*
* -d set last password change date (*)
* -E set account expiration date (*)
* -I set password inactive after expiration (*)
* -l show account aging information
* -M set maximim number of days before password change (*)
* -m set minimum number of days before password change (*)
* -W set expiration warning days (*)
*
* (*) requires root permission to execute.
*
* All of the time fields are entered in the internal format which is
* either seconds or days.
*/
int main (int argc, char **argv)
{ {
const struct spwd *sp;
struct spwd spwd;
uid_t ruid;
gid_t rgid;
int amroot;
const struct passwd *pw;
struct passwd pwent;
char name[BUFSIZ];
#ifdef USE_PAM
pam_handle_t *pamh = NULL;
int retval;
#endif
#ifdef WITH_AUDIT
audit_help_open ();
#endif
sanitize_env ();
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
ruid = getuid ();
rgid = getgid ();
amroot = (ruid == 0);
#ifdef WITH_SELINUX
if (amroot && is_selinux_enabled () > 0)
amroot = (selinux_check_passwd_access (PASSWD__ROOTOK) == 0);
#endif
/* /*
* Get the program name so that error messages can use it. * Parse the command line options.
*/ */
Prog = Basename (argv[0]); int option_index = 0;
int c;
static struct option long_options[] = {
{"lastday", required_argument, NULL, 'd'},
{"expiredate", required_argument, NULL, 'E'},
{"help", no_argument, NULL, 'h'},
{"inactive", required_argument, NULL, 'I'},
{"list", no_argument, NULL, 'l'},
{"mindays", required_argument, NULL, 'm'},
{"maxdays", required_argument, NULL, 'M'},
{"warndays", required_argument, NULL, 'W'},
{NULL, 0, NULL, '\0'}
};
{ while ((c =
/* getopt_long (argc, argv, "d:E:hI:lm:M:W:", long_options,
* Parse the command line options. &option_index)) != -1) {
*/ switch (c) {
int option_index = 0; case 'd':
int c; dflg++;
static struct option long_options[] = { if (!isnum (optarg))
{"lastday", required_argument, NULL, 'd'}, lastday = strtoday (optarg);
{"expiredate", required_argument, NULL, 'E'}, else
{"help", no_argument, NULL, 'h'}, lastday = strtol (optarg, 0, 10);
{"inactive", required_argument, NULL, 'I'}, break;
{"list", no_argument, NULL, 'l'}, case 'E':
{"mindays", required_argument, NULL, 'm'}, Eflg++;
{"maxdays", required_argument, NULL, 'M'}, if (!isnum (optarg))
{"warndays", required_argument, NULL, 'W'}, expdays = strtoday (optarg);
{NULL, 0, NULL, '\0'} else
}; expdays = strtol (optarg, 0, 10);
break;
while ((c = case 'h':
getopt_long (argc, argv, "d:E:hI:lm:M:W:", long_options, usage ();
&option_index)) != -1) { break;
switch (c) { case 'I':
case 'd': Iflg++;
dflg++; inactdays = strtol (optarg, 0, 10);
if (!isnum (optarg)) break;
lastday = strtoday (optarg); case 'l':
else lflg++;
lastday = strtol (optarg, 0, 10); break;
break; case 'm':
case 'E': mflg++;
Eflg++; mindays = strtol (optarg, 0, 10);
if (!isnum (optarg)) break;
expdays = strtoday (optarg); case 'M':
else Mflg++;
expdays = strtol (optarg, 0, 10); maxdays = strtol (optarg, 0, 10);
break; break;
case 'h': case 'W':
usage (); Wflg++;
break; warndays = strtol (optarg, 0, 10);
case 'I': break;
Iflg++; default:
inactdays = strtol (optarg, 0, 10); usage ();
break;
case 'l':
lflg++;
break;
case 'm':
mflg++;
mindays = strtol (optarg, 0, 10);
break;
case 'M':
Mflg++;
maxdays = strtol (optarg, 0, 10);
break;
case 'W':
Wflg++;
warndays = strtol (optarg, 0, 10);
break;
default:
usage ();
}
} }
} }
check_flags (argc, optind);
}
static void check_flags (int argc, int opt_index)
{
/* /*
* Make certain the flags do not conflict and that there is a user * Make certain the flags do not conflict and that there is a user
* name on the command line. * name on the command line.
*/ */
if (argc != optind + 1) if (argc != opt_index + 1) {
usage (); usage ();
}
if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg)) { if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg)) {
fprintf (stderr, fprintf (stderr,
@ -432,6 +386,15 @@ int main (int argc, char **argv)
Prog); Prog);
usage (); usage ();
} }
}
/* Additional check done later */
static void check_perms (void)
{
#ifdef USE_PAM
pam_handle_t *pamh = NULL;
int retval;
#endif
/* /*
* An unprivileged user can ask for their own aging information, but * An unprivileged user can ask for their own aging information, but
@ -448,8 +411,6 @@ int main (int argc, char **argv)
exit (E_NOPERM); exit (E_NOPERM);
} }
OPENLOG ("chage");
#ifdef USE_PAM #ifdef USE_PAM
retval = PAM_SUCCESS; retval = PAM_SUCCESS;
@ -485,15 +446,12 @@ int main (int argc, char **argv)
exit (E_NOPERM); exit (E_NOPERM);
} }
#endif /* USE_PAM */ #endif /* USE_PAM */
}
static void open_files (int readonly)
{
/* /*
* We use locks for read-write accesses only (locks implies amroot, * open the password file. This loads all of the password
* but amroot doesn't imply locks).
*/
locks = !lflg;
/*
* Lock and open the password file. This loads all of the password
* file entries into memory. Then we get a pointer to the password * file entries into memory. Then we get a pointer to the password
* file entry for the requested user. * file entry for the requested user.
*/ */
@ -503,24 +461,6 @@ int main (int argc, char **argv)
closelog (); closelog ();
exit (E_NOPERM); exit (E_NOPERM);
} }
if (!(pw = pw_locate (argv[optind]))) {
fprintf (stderr, _("%s: unknown user %s\n"), Prog,
argv[optind]);
closelog ();
exit (E_NOPERM);
}
pwent = *pw;
STRFCPY (name, pwent.pw_name);
if (!spw_file_present ()) {
fprintf (stderr,
_("%s: the shadow password file is not present\n"),
Prog);
SYSLOG ((LOG_ERR, "can't find the shadow password file"));
closelog ();
exit (E_SHADOW_NOTFOUND);
}
/* /*
* For shadow password files we have to lock the file and read in * For shadow password files we have to lock the file and read in
@ -528,7 +468,7 @@ int main (int argc, char **argv)
* does not have to exist in this case; a new entry will be created * does not have to exist in this case; a new entry will be created
* for this user if one does not exist already. * for this user if one does not exist already.
*/ */
if (locks && !spw_lock ()) { if (!readonly && !spw_lock ()) {
fprintf (stderr, fprintf (stderr,
_("%s: can't lock shadow password file\n"), Prog); _("%s: can't lock shadow password file\n"), Prog);
SYSLOG ((LOG_ERR, "failed locking %s", SHADOW_FILE)); SYSLOG ((LOG_ERR, "failed locking %s", SHADOW_FILE));
@ -539,7 +479,7 @@ int main (int argc, char **argv)
#endif #endif
exit (E_NOPERM); exit (E_NOPERM);
} }
if (!spw_open (locks ? O_RDWR : O_RDONLY)) { if (!spw_open (readonly ? O_RDONLY: O_RDWR)) {
fprintf (stderr, fprintf (stderr,
_("%s: can't open shadow password file\n"), Prog); _("%s: can't open shadow password file\n"), Prog);
spw_unlock (); spw_unlock ();
@ -551,7 +491,125 @@ int main (int argc, char **argv)
#endif #endif
exit (E_NOPERM); exit (E_NOPERM);
} }
}
static void close_files (void)
{
/*
* Now close the shadow password file, which will cause all of the
* entries to be re-written.
*/
if (!spw_close ()) {
fprintf (stderr,
_("%s: can't rewrite shadow password file\n"), Prog);
spw_unlock ();
SYSLOG ((LOG_ERR, "failed rewriting %s", SHADOW_FILE));
closelog ();
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "change age",
pw->pw_name, getuid (), 0);
#endif
exit (E_NOPERM);
}
/*
* Close the password file. If any entries were modified, the file
* will be re-written.
*/
if (!pw_close ()) {
fprintf (stderr, _("%s: can't rewrite password file\n"), Prog);
spw_unlock ();
SYSLOG ((LOG_ERR, "failed rewriting %s", PASSWD_FILE));
closelog ();
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "change age",
pw->pw_name, getuid (), 0);
#endif
exit (E_NOPERM);
}
spw_unlock ();
}
/*
* chage - change a user's password aging information
*
* This command controls the password aging information.
*
* The valid options are
*
* -d set last password change date (*)
* -E set account expiration date (*)
* -I set password inactive after expiration (*)
* -l show account aging information
* -M set maximim number of days before password change (*)
* -m set minimum number of days before password change (*)
* -W set expiration warning days (*)
*
* (*) requires root permission to execute.
*
* All of the time fields are entered in the internal format which is
* either seconds or days.
*/
int main (int argc, char **argv)
{
const struct spwd *sp;
struct spwd spwd;
uid_t ruid;
gid_t rgid;
const struct passwd *pw;
struct passwd pwent;
char name[BUFSIZ];
#ifdef WITH_AUDIT
audit_help_open ();
#endif
sanitize_env ();
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
ruid = getuid ();
rgid = getgid ();
amroot = (ruid == 0);
#ifdef WITH_SELINUX
if (amroot && is_selinux_enabled () > 0)
amroot = (selinux_check_passwd_access (PASSWD__ROOTOK) == 0);
#endif
/*
* Get the program name so that error messages can use it.
*/
Prog = Basename (argv[0]);
process_flags (argc, argv);
OPENLOG ("chage");
check_perms ();
if (!spw_file_present ()) {
fprintf (stderr,
_("%s: the shadow password file is not present\n"),
Prog);
SYSLOG ((LOG_ERR, "can't find the shadow password file"));
closelog ();
exit (E_SHADOW_NOTFOUND);
}
open_files (lflg);
if (!(pw = pw_locate (argv[optind]))) {
fprintf (stderr, _("%s: unknown user %s\n"), Prog,
argv[optind]);
closelog ();
exit (E_NOPERM);
}
pwent = *pw;
STRFCPY (name, pwent.pw_name);
/* Drop privileges */
if (lflg && (setregid (rgid, rgid) || setreuid (ruid, ruid))) { if (lflg && (setregid (rgid, rgid) || setreuid (ruid, ruid))) {
fprintf (stderr, _("%s: failed to drop privileges (%s)\n"), fprintf (stderr, _("%s: failed to drop privileges (%s)\n"),
Prog, strerror (errno)); Prog, strerror (errno));
@ -730,46 +788,15 @@ int main (int argc, char **argv)
exit (E_NOPERM); exit (E_NOPERM);
} }
/* close_files ();
* Now close the shadow password file, which will cause all of the
* entries to be re-written.
*/
if (!spw_close ()) {
fprintf (stderr,
_("%s: can't rewrite shadow password file\n"), Prog);
spw_unlock ();
SYSLOG ((LOG_ERR, "failed rewriting %s", SHADOW_FILE));
closelog ();
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "change age",
pw->pw_name, getuid (), 0);
#endif
exit (E_NOPERM);
}
/*
* Close the password file. If any entries were modified, the file
* will be re-written.
*/
if (!pw_close ()) {
fprintf (stderr, _("%s: can't rewrite password file\n"), Prog);
spw_unlock ();
SYSLOG ((LOG_ERR, "failed rewriting %s", PASSWD_FILE));
closelog ();
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "change age",
pw->pw_name, getuid (), 0);
#endif
exit (E_NOPERM);
}
spw_unlock ();
SYSLOG ((LOG_INFO, "changed password expiry for %s", name)); SYSLOG ((LOG_INFO, "changed password expiry for %s", name));
#ifdef USE_PAM #ifdef USE_PAM
if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS);
pam_end (pamh, PAM_SUCCESS);
#endif /* USE_PAM */ #endif /* USE_PAM */
closelog (); closelog ();
exit (E_SUCCESS); exit (E_SUCCESS);
} }