shadow/src/faillog.c

723 lines
18 KiB
C
Raw Normal View History

/*
* SPDX-FileCopyrightText: 1989 - 1993, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2002 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2011, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <assert.h>
#include "defines.h"
#include "faillog.h"
#include "prototypes.h"
/*@-exitarg@*/
#include "exitcodes.h"
#include "shadowlog.h"
/* local function prototypes */
NORETURN static void usage (int status);
static void print_one (/*@null@*/const struct passwd *pw, bool force);
static void set_locktime (long locktime);
static bool set_locktime_one (uid_t uid, long locktime);
static void setmax (short max);
static bool setmax_one (uid_t uid, short max);
static void print (void);
static bool reset_one (uid_t uid);
static void reset (void);
/*
* Global variables
*/
const char *Prog; /* Program name */
static FILE *fail; /* failure file stream */
static time_t seconds; /* that number of days in seconds */
static unsigned long umin; /* if uflg and has_umin, only display users with uid >= umin */
static bool has_umin = false;
static unsigned long umax; /* if uflg and has_umax, only display users with uid <= umax */
static bool has_umax = false;
static bool errors = false;
static bool aflg = false; /* set if all users are to be printed always */
static bool uflg = false; /* set if user is a valid user id */
static bool tflg = false; /* print is restricted to most recent days */
static bool lflg = false; /* set the locktime */
static bool mflg = false; /* set maximum failed login counters */
static bool rflg = false; /* reset the counters of login failures */
static struct stat statbuf; /* fstat buffer for file size */
#define NOW time(NULL)
NORETURN
static void
usage (int status)
{
FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
(void) fprintf (usageout,
_("Usage: %s [options]\n"
"\n"
"Options:\n"),
Prog);
(void) fputs (_(" -a, --all display faillog records for all users\n"), usageout);
(void) fputs (_(" -h, --help display this help message and exit\n"), usageout);
(void) fputs (_(" -l, --lock-secs SEC after failed login lock account for SEC seconds\n"), usageout);
(void) fputs (_(" -m, --maximum MAX set maximum failed login counters to MAX\n"), usageout);
(void) fputs (_(" -r, --reset reset the counters of login failures\n"), usageout);
(void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout);
(void) fputs (_(" -t, --time DAYS display faillog records more recent than DAYS\n"), usageout);
(void) fputs (_(" -u, --user LOGIN/RANGE display faillog record or maintains failure\n"
" counters and limits (if used with -r, -m,\n"
" or -l) only for the specified LOGIN(s)\n"), usageout);
(void) fputs ("\n", usageout);
exit (status);
}
static void print_one (/*@null@*/const struct passwd *pw, bool force)
{
static bool once = false;
struct tm *tm;
off_t offset;
struct faillog fl;
time_t now;
char *cp;
char ptime[80];
if (NULL == pw) {
return;
}
offset = (off_t) pw->pw_uid * sizeof (fl);
if (offset + sizeof (fl) <= statbuf.st_size) {
/* fseeko errors are not really relevant for us. */
int err = fseeko (fail, offset, SEEK_SET);
assert (0 == err);
/* faillog is a sparse file. Even if no entries were
* entered for this user, which should be able to get the
* empty entry in this case.
*/
if (fread (&fl, sizeof (fl), 1, fail) != 1) {
fprintf (stderr,
_("%s: Failed to get the entry for UID %lu\n"),
Prog, (unsigned long int)pw->pw_uid);
return;
}
} else {
/* Outsize of the faillog file.
* Behave as if there were a missing entry (same behavior
* as if we were reading an non existing entry in the
* sparse faillog file).
*/
memzero (&fl, sizeof (fl));
}
/* Nothing to report */
if (!force && (0 == fl.fail_time)) {
return;
}
(void) time(&now);
/* Filter out entries that do not match with the -t option */
if (tflg && ((now - fl.fail_time) > seconds)) {
return;
}
/* Print the header only once */
if (!once) {
puts (_("Login Failures Maximum Latest On\n"));
once = true;
}
tm = localtime (&fl.fail_time);
if (!tm) {
fprintf (stderr, "Cannot read time from faillog.\n");
2020-03-09 23:31:32 +05:30
return;
}
strftime (ptime, sizeof (ptime), "%D %H:%M:%S %z", tm);
cp = ptime;
printf ("%-9s %5d %5d ",
pw->pw_name, fl.fail_cnt, fl.fail_max);
printf ("%s %s", cp, fl.fail_line);
if (0 != fl.fail_locktime) {
if ( ((fl.fail_time + fl.fail_locktime) > now)
&& (0 != fl.fail_cnt)) {
printf (_(" [%lus left]"),
(unsigned long) fl.fail_time + fl.fail_locktime - now);
} else {
printf (_(" [%lds lock]"),
fl.fail_locktime);
}
}
putchar ('\n');
}
static void print (void)
{
if (uflg && has_umin && has_umax && (umin==umax)) {
print_one (getpwuid (umin), true);
2008-08-31 00:03:37 +05:30
} else {
/* We only print records for existing users.
* Loop based on the user database instead of reading the
* whole file. We will have to query the database anyway
* so except for very small ranges and large user
* database, this should not be a performance issue.
*/
struct passwd *pwent;
setpwent ();
while ( (pwent = getpwent ()) != NULL ) {
if ( uflg
&& ( (has_umin && (pwent->pw_uid < (uid_t)umin))
|| (has_umax && (pwent->pw_uid > (uid_t)umax)))) {
continue;
}
print_one (pwent, aflg);
}
endpwent ();
}
}
/*
* reset_one - Reset the fail count for one user
*
* This returns a boolean indicating if an error occurred.
*/
static bool reset_one (uid_t uid)
{
off_t offset;
struct faillog fl;
offset = (off_t) uid * sizeof (fl);
if (offset + sizeof (fl) <= statbuf.st_size) {
/* fseeko errors are not really relevant for us. */
int err = fseeko (fail, offset, SEEK_SET);
assert (0 == err);
/* faillog is a sparse file. Even if no entries were
* entered for this user, which should be able to get the
* empty entry in this case.
*/
if (fread (&fl, sizeof (fl), 1, fail) != 1) {
fprintf (stderr,
_("%s: Failed to get the entry for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
2008-08-31 00:03:37 +05:30
}
} else {
/* Outsize of the faillog file.
* Behave as if there were a missing entry (same behavior
* as if we were reading an non existing entry in the
* sparse faillog file).
*/
memzero (&fl, sizeof (fl));
}
if (0 == fl.fail_cnt) {
/* If the count is already null, do not write in the file.
* This avoids writing 0 when no entries were present for
* the user.
*/
return false;
}
fl.fail_cnt = 0;
if ( (fseeko (fail, offset, SEEK_SET) == 0)
&& (fwrite (&fl, sizeof (fl), 1, fail) == 1)) {
(void) fflush (fail);
return false;
}
fprintf (stderr,
_("%s: Failed to reset fail count for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
}
static void reset (void)
{
if (uflg && has_umin && has_umax && (umin==umax)) {
if (reset_one (umin)) {
errors = true;
}
} else {
/* There is no need to reset outside of the faillog
* database.
*/
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
if (uidmax > 1) {
uidmax--;
}
if (has_umax && (uid_t)umax < uidmax) {
uidmax = umax;
}
/* Reset all entries in the specified range.
* Non existing entries will not be touched.
*/
if (aflg) {
/* Entries for non existing users are also reset.
*/
2010-03-18 05:37:00 +05:30
uid_t uid = 0;
2010-03-18 05:37:00 +05:30
/* Make sure we stay in the umin-umax range if specified */
if (has_umin) {
uid = umin;
2010-03-18 05:37:00 +05:30
}
2010-03-18 05:37:00 +05:30
while (uid <= uidmax) {
if (reset_one (uid)) {
errors = true;
}
uid++;
}
} else {
/* Only reset records for existing users.
*/
struct passwd *pwent;
setpwent ();
while ( (pwent = getpwent ()) != NULL ) {
if ( uflg
&& ( (has_umin && (pwent->pw_uid < (uid_t)umin))
|| (pwent->pw_uid > (uid_t)uidmax))) {
continue;
}
if (reset_one (pwent->pw_uid)) {
errors = true;
}
}
endpwent ();
}
}
}
/*
* setmax_one - Set the maximum failed login counter for one user
*
* This returns a boolean indicating if an error occurred.
*/
static bool setmax_one (uid_t uid, short max)
{
off_t offset;
struct faillog fl;
offset = (off_t) uid * sizeof (fl);
if (offset + sizeof (fl) <= statbuf.st_size) {
/* fseeko errors are not really relevant for us. */
int err = fseeko (fail, offset, SEEK_SET);
assert (0 == err);
/* faillog is a sparse file. Even if no entries were
* entered for this user, which should be able to get the
* empty entry in this case.
*/
if (fread (&fl, sizeof (fl), 1, fail) != 1) {
fprintf (stderr,
_("%s: Failed to get the entry for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
}
} else {
/* Outsize of the faillog file.
* Behave as if there were a missing entry (same behavior
* as if we were reading an non existing entry in the
* sparse faillog file).
*/
memzero (&fl, sizeof (fl));
}
if (max == fl.fail_max) {
/* If the max is already set to the right value, do not
* write in the file.
* This avoids writing 0 when no entries were present for
* the user and the max argument is 0.
*/
return false;
}
fl.fail_max = max;
2008-06-11 00:48:44 +05:30
if ( (fseeko (fail, offset, SEEK_SET) == 0)
&& (fwrite (&fl, sizeof (fl), 1, fail) == 1)) {
(void) fflush (fail);
return false;
}
fprintf (stderr,
_("%s: Failed to set max for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
}
static void setmax (short max)
{
if (uflg && has_umin && has_umax && (umin==umax)) {
if (setmax_one (umin, max)) {
errors = true;
}
} else {
/* Set max for entries in the specified range.
* If max is unchanged for an entry, the entry is not touched.
* If max is null, and no entries exist for this user, no
* entries will be created.
*/
if (aflg) {
2010-03-18 05:37:00 +05:30
/* Entries for non existing user are also taken into
* account (in order to define policy for future users).
*/
uid_t uid = 0;
/* The default umax value is based on the size of the
* faillog database.
*/
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
if (uidmax > 1) {
uidmax--;
}
2010-03-18 05:37:00 +05:30
/* Make sure we stay in the umin-umax range if specified */
if (has_umin) {
uid = umin;
2010-03-18 05:37:00 +05:30
}
if (has_umax) {
uidmax = umax;
2010-03-18 05:37:00 +05:30
}
2010-03-18 05:37:00 +05:30
while (uid <= uidmax) {
if (setmax_one (uid, max)) {
errors = true;
}
uid++;
}
} else {
/* Only change records for existing users.
*/
struct passwd *pwent;
setpwent ();
while ( (pwent = getpwent ()) != NULL ) {
if ( uflg
&& ( (has_umin && (pwent->pw_uid < (uid_t)umin))
|| (has_umax && (pwent->pw_uid > (uid_t)umax)))) {
continue;
}
if (setmax_one (pwent->pw_uid, max)) {
errors = true;
}
}
endpwent ();
}
}
}
/*
* set_locktime_one - Set the locktime for one user
*
* This returns a boolean indicating if an error occurred.
*/
static bool set_locktime_one (uid_t uid, long locktime)
{
off_t offset;
struct faillog fl;
offset = (off_t) uid * sizeof (fl);
if (offset + sizeof (fl) <= statbuf.st_size) {
/* fseeko errors are not really relevant for us. */
int err = fseeko (fail, offset, SEEK_SET);
assert (0 == err);
/* faillog is a sparse file. Even if no entries were
* entered for this user, which should be able to get the
* empty entry in this case.
*/
if (fread (&fl, sizeof (fl), 1, fail) != 1) {
fprintf (stderr,
_("%s: Failed to get the entry for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
}
} else {
/* Outsize of the faillog file.
* Behave as if there were a missing entry (same behavior
* as if we were reading an non existing entry in the
* sparse faillog file).
*/
memzero (&fl, sizeof (fl));
}
if (locktime == fl.fail_locktime) {
/* If the locktime is already set to the right value, do not
* write in the file.
* This avoids writing 0 when no entries were present for
* the user and the locktime argument is 0.
*/
return false;
}
fl.fail_locktime = locktime;
if ( (fseeko (fail, offset, SEEK_SET) == 0)
&& (fwrite (&fl, sizeof (fl), 1, fail) == 1)) {
(void) fflush (fail);
return false;
}
fprintf (stderr,
_("%s: Failed to set locktime for UID %lu\n"),
Prog, (unsigned long int)uid);
return true;
}
static void set_locktime (long locktime)
{
if (uflg && has_umin && has_umax && (umin==umax)) {
if (set_locktime_one (umin, locktime)) {
errors = true;
}
} else {
/* Set locktime for entries in the specified range.
* If locktime is unchanged for an entry, the entry is not touched.
* If locktime is null, and no entries exist for this user, no
* entries will be created.
*/
if (aflg) {
2010-03-18 05:37:00 +05:30
/* Entries for non existing user are also taken into
* account (in order to define policy for future users).
*/
uid_t uid = 0;
/* The default umax value is based on the size of the
* faillog database.
*/
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
if (uidmax > 1) {
uidmax--;
}
2010-03-18 05:37:00 +05:30
/* Make sure we stay in the umin-umax range if specified */
if (has_umin) {
uid = umin;
2010-03-18 05:37:00 +05:30
}
if (has_umax) {
uidmax = umax;
2010-03-18 05:37:00 +05:30
}
2010-03-18 05:37:00 +05:30
while (uid <= uidmax) {
if (set_locktime_one (uid, locktime)) {
errors = true;
}
uid++;
}
} else {
/* Only change records for existing users.
*/
struct passwd *pwent;
setpwent ();
while ( (pwent = getpwent ()) != NULL ) {
if ( uflg
&& ( (has_umin && (pwent->pw_uid < (uid_t)umin))
|| (has_umax && (pwent->pw_uid > (uid_t)umax)))) {
continue;
}
if (set_locktime_one (pwent->pw_uid, locktime)) {
errors = true;
}
}
endpwent ();
}
}
}
int main (int argc, char **argv)
{
long fail_locktime = 0;
short fail_max = 0; // initialize to silence compiler warning
long days = 0;
/*
* Get the program name. The program name is used as a prefix to
* most error messages.
*/
Prog = Basename (argv[0]);
log_set_progname(Prog);
log_set_logfd(stderr);
(void) setlocale (LC_ALL, "");
(void) bindtextdomain (PACKAGE, LOCALEDIR);
(void) textdomain (PACKAGE);
process_root_flag ("-R", argc, argv);
{
int c;
static struct option long_options[] = {
{"all", no_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{"lock-secs", required_argument, NULL, 'l'},
{"maximum", required_argument, NULL, 'm'},
{"reset", no_argument, NULL, 'r'},
{"root", required_argument, NULL, 'R'},
{"time", required_argument, NULL, 't'},
{"user", required_argument, NULL, 'u'},
{NULL, 0, NULL, '\0'}
};
while ((c = getopt_long (argc, argv, "ahl:m:rR:t:u:",
long_options, NULL)) != -1) {
switch (c) {
case 'a':
aflg = true;
break;
case 'h':
usage (E_SUCCESS);
/*@notreached@*/break;
case 'l':
if (getlong (optarg, &fail_locktime) == 0) {
fprintf (stderr,
_("%s: invalid numeric argument '%s'\n"),
Prog, optarg);
exit (E_BAD_ARG);
}
lflg = true;
break;
case 'm':
{
long int lmax;
if ( (getlong (optarg, &lmax) == 0)
|| ((long int)(short) lmax != lmax)) {
fprintf (stderr,
_("%s: invalid numeric argument '%s'\n"),
Prog, optarg);
exit (E_BAD_ARG);
}
fail_max = lmax;
mflg = true;
break;
}
case 'r':
rflg = true;
break;
case 'R': /* no-op, handled in process_root_flag () */
break;
case 't':
if (getlong (optarg, &days) == 0) {
fprintf (stderr,
_("%s: invalid numeric argument '%s'\n"),
Prog, optarg);
exit (E_BAD_ARG);
}
seconds = (time_t) days * DAY;
tflg = true;
break;
case 'u':
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
{
/*
* The user can be:
* - a login name
* - numerical
* - a numerical login ID
* - a range (-x, x-, x-y)
*/
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
struct passwd *pwent;
uflg = true;
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
/* local, no need for xgetpwnam */
pwent = getpwnam (optarg);
if (NULL != pwent) {
umin = pwent->pw_uid;
has_umin = true;
umax = umin;
has_umax = true;
} else {
if (getrange (optarg,
&umin, &has_umin,
&umax, &has_umax) == 0) {
fprintf (stderr,
_("%s: Unknown user or range: %s\n"),
Prog, optarg);
exit (E_BAD_ARG);
}
}
break;
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
}
default:
usage (E_USAGE);
}
}
if (argc > optind) {
fprintf (stderr,
_("%s: unexpected argument: %s\n"),
Prog, argv[optind]);
usage (EXIT_FAILURE);
}
}
if (tflg && (lflg || mflg || rflg)) {
usage (E_USAGE);
}
/* Open the faillog database */
if (lflg || mflg || rflg) {
fail = fopen (FAILLOG_FILE, "r+");
} else {
fail = fopen (FAILLOG_FILE, "r");
}
if (NULL == fail) {
fprintf (stderr,
_("%s: Cannot open %s: %s\n"),
Prog, FAILLOG_FILE, strerror (errno));
exit (E_NOPERM);
}
/* Get the size of the faillog */
if (fstat (fileno (fail), &statbuf) != 0) {
fprintf (stderr,
_("%s: Cannot get the size of %s: %s\n"),
Prog, FAILLOG_FILE, strerror (errno));
exit (E_NOPERM);
}
if (lflg) {
set_locktime (fail_locktime);
}
if (mflg) {
setmax (fail_max);
}
if (rflg) {
reset ();
}
if (!(lflg || mflg || rflg)) {
print ();
}
if (lflg || mflg || rflg) {
if ( (ferror (fail) != 0)
|| (fflush (fail) != 0)
|| (fsync (fileno (fail)) != 0)
|| (fclose (fail) != 0)) {
fprintf (stderr,
_("%s: Failed to write %s: %s\n"),
Prog, FAILLOG_FILE, strerror (errno));
(void) fclose (fail);
errors = true;
}
} else {
(void) fclose (fail);
}
exit (errors ? E_NOPERM : E_SUCCESS);
}