2007-10-07 17:14:02 +05:30
|
|
|
/*
|
2008-04-27 06:10:09 +05:30
|
|
|
* Copyright (c) 1989 - 1993, Julianne Frances Haugh
|
|
|
|
* Copyright (c) 1996 - 2000, Marek Michałkiewicz
|
|
|
|
* Copyright (c) 2002 - 2006, Tomasz Kłoczko
|
2009-03-14 04:19:20 +05:30
|
|
|
* Copyright (c) 2007 - 2009, Nicolas François
|
2007-10-07 17:14:02 +05:30
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* 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.
|
2008-04-27 06:10:09 +05:30
|
|
|
* 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.
|
2007-10-07 17:14:02 +05:30
|
|
|
*
|
2008-04-27 06:10:09 +05:30
|
|
|
* 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.
|
2007-10-07 17:14:02 +05:30
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2007-11-11 05:16:11 +05:30
|
|
|
#ident "$Id$"
|
2007-10-07 17:17:01 +05:30
|
|
|
|
2007-10-07 17:16:52 +05:30
|
|
|
#include <getopt.h>
|
2007-10-07 17:14:02 +05:30
|
|
|
#include <pwd.h>
|
2007-10-07 17:16:52 +05:30
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2007-10-07 17:14:02 +05:30
|
|
|
#include <time.h>
|
2009-03-14 04:19:20 +05:30
|
|
|
#include <assert.h>
|
2007-10-07 17:14:02 +05:30
|
|
|
#include "defines.h"
|
2007-10-07 17:16:52 +05:30
|
|
|
#include "exitcodes.h"
|
2007-10-07 17:14:02 +05:30
|
|
|
#include "faillog.h"
|
2007-10-07 17:16:52 +05:30
|
|
|
#include "prototypes.h"
|
2007-10-07 17:17:01 +05:30
|
|
|
/*
|
|
|
|
* Global variables
|
|
|
|
*/
|
2007-10-07 17:14:59 +05:30
|
|
|
static FILE *fail; /* failure file stream */
|
|
|
|
static time_t seconds; /* that number of days in seconds */
|
2009-03-14 04:19:20 +05:30
|
|
|
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 */
|
2007-10-07 17:14:02 +05:30
|
|
|
|
|
|
|
static struct stat statbuf; /* fstat buffer for file size */
|
|
|
|
|
|
|
|
#define NOW (time((time_t *) 0))
|
|
|
|
|
2007-10-07 17:14:59 +05:30
|
|
|
static void usage (void)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2008-01-25 02:12:12 +05:30
|
|
|
fputs (_("Usage: faillog [options]\n"
|
2008-01-25 02:24:42 +05:30
|
|
|
"\n"
|
|
|
|
"Options:\n"
|
|
|
|
" -a, --all display faillog records for all users\n"
|
|
|
|
" -h, --help display this help message and exit\n"
|
2009-03-14 04:19:20 +05:30
|
|
|
" -l, --lock-time SEC after failed login lock account to SEC seconds\n"
|
2008-01-25 02:24:42 +05:30
|
|
|
" -m, --maximum MAX set maximum failed login counters to MAX\n"
|
|
|
|
" -r, --reset reset the counters of login failures\n"
|
|
|
|
" -t, --time DAYS display faillog records more recent than DAYS\n"
|
|
|
|
" -u, --user LOGIN display faillog record or maintains failure\n"
|
|
|
|
" counters and limits (if used with -r, -m or -l\n"
|
|
|
|
" options) only for user with LOGIN\n"
|
|
|
|
"\n"), stderr);
|
2007-10-07 17:16:52 +05:30
|
|
|
exit (E_USAGE);
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2009-04-23 16:53:53 +05:30
|
|
|
static void print_one (/*@null@*/const struct passwd *pw, bool force)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2008-06-11 00:46:02 +05:30
|
|
|
static bool once = false;
|
2007-10-07 17:14:59 +05:30
|
|
|
struct tm *tm;
|
2009-03-14 04:19:20 +05:30
|
|
|
off_t offset;
|
|
|
|
struct faillog fl;
|
2007-10-07 17:14:02 +05:30
|
|
|
time_t now;
|
2007-10-07 17:14:59 +05:30
|
|
|
|
2007-10-07 17:14:02 +05:30
|
|
|
#ifdef HAVE_STRFTIME
|
2009-03-14 04:19:20 +05:30
|
|
|
char *cp;
|
2007-10-07 17:14:02 +05:30
|
|
|
char ptime[80];
|
|
|
|
#endif
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
if (NULL == pw) {
|
|
|
|
return;
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
offset = pw->pw_uid * sizeof (fl);
|
|
|
|
if (offset <= (statbuf.st_size - sizeof (fl))) {
|
|
|
|
/* fseeko errors are not really relevant for us. */
|
2009-04-25 03:57:09 +05:30
|
|
|
int err = fseeko (fail, offset, SEEK_SET);
|
|
|
|
assert (0 == err);
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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 ((char *) &fl, sizeof (fl), 1, fail) != 1) {
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to get the entry for UID %lu\n"),
|
|
|
|
(unsigned long int)pw->pw_uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return;
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +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));
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/* Nothing to report */
|
|
|
|
if (!force && (0 == fl.fail_time)) {
|
|
|
|
return;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
(void) time(&now);
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/* Filter out entries that do not match with the -t option */
|
|
|
|
if (tflg && ((now - fl.fail_time) > seconds)) {
|
|
|
|
return;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/* Print the header only once */
|
|
|
|
if (!once) {
|
|
|
|
puts (_("Login Failures Maximum Latest On\n"));
|
|
|
|
once = true;
|
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
tm = localtime (&fl.fail_time);
|
|
|
|
#ifdef HAVE_STRFTIME
|
|
|
|
strftime (ptime, sizeof (ptime), "%D %H:%M:%S %z", tm);
|
|
|
|
cp = ptime;
|
|
|
|
#endif
|
|
|
|
printf ("%-9s %5d %5d ",
|
|
|
|
pw->pw_name, fl.fail_cnt, fl.fail_max);
|
|
|
|
/* FIXME: cp is not defined ifndef HAVE_STRFTIME */
|
|
|
|
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);
|
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
putchar ('\n');
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
static void print (void)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2009-03-14 04:19:20 +05:30
|
|
|
if (uflg && has_umin && has_umax && (umin==umax)) {
|
|
|
|
print_one (getpwuid ((uid_t)umin), true);
|
2008-08-31 00:03:37 +05:30
|
|
|
} else {
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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.
|
|
|
|
*/
|
2008-03-05 05:40:25 +05:30
|
|
|
struct passwd *pwent;
|
|
|
|
|
|
|
|
setpwent ();
|
2008-03-18 04:35:59 +05:30
|
|
|
while ( (pwent = getpwent ()) != NULL ) {
|
2009-03-14 04:19:20 +05:30
|
|
|
if ( uflg
|
|
|
|
&& ( (has_umin && (pwent->pw_uid < (uid_t)umin))
|
|
|
|
|| (has_umax && (pwent->pw_uid > (uid_t)umax)))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
print_one (pwent, aflg);
|
2008-03-05 05:40:25 +05:30
|
|
|
}
|
Ensure that getpwent() is used in setpwent(), getpwent(),
endpwend() sequences (ditto for getgrent(), getspent(), and
getsgent()). The only real (minor) issue was in login, which kept
the passwd file open.
* libmisc/entry.c: Remove unneeded setspent() and endspent() (only
getspnam is called in the middle).
* libmisc/find_new_ids.c: Make sure to close the password and
group files with endpwent() and endgrent().
* libmisc/pwdcheck.c: Remove unneeded endspent() (only getspnam()
is called before).
* src/lastlog.c, src/passwd.c, src/groupmod.c, src/faillog.c,
src/groups.c: Make sure to close
the password file with endpwent().
* src/login.c: Remove unneeded setpwent() (only xgetpwnam is
called before).
* src/login.c, src/newgrp.c: Fix typos in comments.
2008-04-17 03:22:46 +05:30
|
|
|
endpwent ();
|
2008-03-05 05:40:25 +05:30
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/*
|
|
|
|
* 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)
|
2007-10-07 17:15:58 +05:30
|
|
|
{
|
|
|
|
off_t offset;
|
2009-03-14 04:19:20 +05:30
|
|
|
struct faillog fl;
|
|
|
|
|
|
|
|
offset = uid * sizeof (fl);
|
|
|
|
if (offset <= (statbuf.st_size - sizeof (fl))) {
|
|
|
|
/* fseeko errors are not really relevant for us. */
|
2009-04-25 03:57:09 +05:30
|
|
|
int err = fseeko (fail, offset, SEEK_SET);
|
|
|
|
assert (0 == err);
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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 ((char *) &fl, sizeof (fl), 1, fail) != 1) {
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to get the entry for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
2008-08-31 00:03:37 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
} else {
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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));
|
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
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;
|
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
fl.fail_cnt = 0;
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
if ( (fseeko (fail, offset, SEEK_SET) == 0)
|
|
|
|
&& (fwrite ((char *) &fl, sizeof (fl), 1, fail) == 1)) {
|
|
|
|
(void) fflush (fail);
|
|
|
|
return false;
|
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to reset fail count for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reset (void)
|
|
|
|
{
|
|
|
|
if (uflg && has_umin && has_umax && (umin==umax)) {
|
|
|
|
if (reset_one ((uid_t)umin)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Reset all entries in the specified range.
|
|
|
|
* Non existing entries will not be touched.
|
|
|
|
* Entries for non existing users are also reset.
|
|
|
|
*/
|
|
|
|
uid_t uid = 0;
|
|
|
|
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
|
|
|
|
|
|
|
|
/* Make sure we stay in the umin-umax range if specified */
|
|
|
|
if (has_umin) {
|
|
|
|
uid = (uid_t)umin;
|
|
|
|
}
|
|
|
|
if (has_umax && (uid_t)umax < uidmax) {
|
|
|
|
uidmax = (uid_t)umax;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (uid < uidmax) {
|
|
|
|
if (reset_one (uid)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
|
|
|
uid++;
|
2007-10-07 17:15:58 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/*
|
|
|
|
* 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, int max)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2007-10-07 17:14:59 +05:30
|
|
|
off_t offset;
|
2009-03-14 04:19:20 +05:30
|
|
|
struct faillog fl;
|
|
|
|
|
|
|
|
offset = (off_t) uid * sizeof (fl);
|
|
|
|
if (offset <= (statbuf.st_size - sizeof (fl))) {
|
|
|
|
/* fseeko errors are not really relevant for us. */
|
2009-04-25 03:57:09 +05:30
|
|
|
int err = fseeko (fail, offset, SEEK_SET);
|
|
|
|
assert (0 == err);
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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 ((char *) &fl, sizeof (fl), 1, fail) != 1) {
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to get the entry for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +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 (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;
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
fl.fail_max = max;
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2008-06-11 00:48:44 +05:30
|
|
|
if ( (fseeko (fail, offset, SEEK_SET) == 0)
|
2009-03-14 04:19:20 +05:30
|
|
|
&& (fwrite ((char *) &fl, sizeof (fl), 1, fail) == 1)) {
|
2009-04-23 16:53:53 +05:30
|
|
|
(void) fflush (fail);
|
2009-03-14 04:19:20 +05:30
|
|
|
return false;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to set max for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
|
2007-10-07 17:15:58 +05:30
|
|
|
static void setmax (int max)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2009-03-14 04:19:20 +05:30
|
|
|
if (uflg && has_umin && has_umax && (umin==umax)) {
|
|
|
|
if (setmax_one ((uid_t)umin, max)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
} else {
|
2009-03-14 04:19:20 +05:30
|
|
|
/* Set max for all 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.
|
|
|
|
* Entries for non existing user are also taken into
|
|
|
|
* account (in order to define policy for future users).
|
|
|
|
*/
|
|
|
|
uid_t uid = 0;
|
|
|
|
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
|
|
|
|
|
|
|
|
/* Make sure we stay in the umin-umax range if specified */
|
|
|
|
if (has_umin) {
|
|
|
|
uid = (uid_t)umin;
|
|
|
|
}
|
|
|
|
if (has_umax && (uid_t)umax < uidmax) {
|
|
|
|
uidmax = (uid_t)umax;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (uid < uidmax) {
|
|
|
|
if (setmax_one (uid, max)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
|
|
|
uid++;
|
Ensure that getpwent() is used in setpwent(), getpwent(),
endpwend() sequences (ditto for getgrent(), getspent(), and
getsgent()). The only real (minor) issue was in login, which kept
the passwd file open.
* libmisc/entry.c: Remove unneeded setspent() and endspent() (only
getspnam is called in the middle).
* libmisc/find_new_ids.c: Make sure to close the password and
group files with endpwent() and endgrent().
* libmisc/pwdcheck.c: Remove unneeded endspent() (only getspnam()
is called before).
* src/lastlog.c, src/passwd.c, src/groupmod.c, src/faillog.c,
src/groups.c: Make sure to close
the password file with endpwent().
* src/login.c: Remove unneeded setpwent() (only xgetpwnam is
called before).
* src/login.c, src/newgrp.c: Fix typos in comments.
2008-04-17 03:22:46 +05:30
|
|
|
}
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
/*
|
|
|
|
* 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)
|
2007-10-07 17:14:02 +05:30
|
|
|
{
|
2007-10-07 17:14:59 +05:30
|
|
|
off_t offset;
|
2009-03-14 04:19:20 +05:30
|
|
|
struct faillog fl;
|
|
|
|
|
|
|
|
offset = (off_t) uid * sizeof (fl);
|
|
|
|
if (offset <= (statbuf.st_size - sizeof (fl))) {
|
|
|
|
/* fseeko errors are not really relevant for us. */
|
2009-04-25 03:57:09 +05:30
|
|
|
int err = fseeko (fail, offset, SEEK_SET);
|
|
|
|
assert (0 == err);
|
2009-03-14 04:19:20 +05:30
|
|
|
/* 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 ((char *) &fl, sizeof (fl), 1, fail) != 1) {
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to get the entry for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +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));
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
if (locktime == fl.fail_locktime) {
|
|
|
|
/* 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_locktime = locktime;
|
2007-10-07 17:14:02 +05:30
|
|
|
|
2009-04-23 16:53:53 +05:30
|
|
|
if ( (fseeko (fail, offset, SEEK_SET) == 0)
|
|
|
|
&& (fwrite ((char *) &fl, sizeof (fl), 1, fail) == 1)) {
|
|
|
|
(void) fflush (fail);
|
2009-03-14 04:19:20 +05:30
|
|
|
return false;
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
fprintf (stderr,
|
2009-04-23 16:53:53 +05:30
|
|
|
_("faillog: Failed to set locktime for UID %lu\n"),
|
|
|
|
(unsigned long int)uid);
|
2009-03-14 04:19:20 +05:30
|
|
|
return true;
|
2007-10-07 17:14:02 +05:30
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
|
|
|
|
static void set_locktime (long locktime)
|
|
|
|
{
|
2009-03-14 04:19:20 +05:30
|
|
|
if (uflg && has_umin && has_umax && (umin==umax)) {
|
|
|
|
if (set_locktime_one ((uid_t)umin, locktime)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
} else {
|
2009-03-14 04:19:20 +05:30
|
|
|
/* Set locktime for all 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.
|
|
|
|
* Entries for non existing user are also taken into
|
|
|
|
* account (in order to define policy for future users).
|
|
|
|
*/
|
|
|
|
uid_t uid = 0;
|
|
|
|
uid_t uidmax = statbuf.st_size / sizeof (struct faillog);
|
|
|
|
|
|
|
|
/* Make sure we stay in the umin-umax range if specified */
|
|
|
|
if (has_umin) {
|
|
|
|
uid = (uid_t)umin;
|
|
|
|
}
|
|
|
|
if (has_umax && (uid_t)umax < uidmax) {
|
|
|
|
uidmax = (uid_t)umax;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (uid < uidmax) {
|
|
|
|
if (set_locktime_one (uid, locktime)) {
|
|
|
|
errors = true;
|
|
|
|
}
|
|
|
|
uid++;
|
Ensure that getpwent() is used in setpwent(), getpwent(),
endpwend() sequences (ditto for getgrent(), getspent(), and
getsgent()). The only real (minor) issue was in login, which kept
the passwd file open.
* libmisc/entry.c: Remove unneeded setspent() and endspent() (only
getspnam is called in the middle).
* libmisc/find_new_ids.c: Make sure to close the password and
group files with endpwent() and endgrent().
* libmisc/pwdcheck.c: Remove unneeded endspent() (only getspnam()
is called before).
* src/lastlog.c, src/passwd.c, src/groupmod.c, src/faillog.c,
src/groups.c: Make sure to close
the password file with endpwent().
* src/login.c: Remove unneeded setpwent() (only xgetpwnam is
called before).
* src/login.c, src/newgrp.c: Fix typos in comments.
2008-04-17 03:22:46 +05:30
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char **argv)
|
|
|
|
{
|
2009-03-14 04:19:20 +05:30
|
|
|
long fail_locktime;
|
|
|
|
long fail_max;
|
|
|
|
long days;
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2008-06-11 00:46:02 +05:30
|
|
|
(void) setlocale (LC_ALL, "");
|
|
|
|
(void) bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
(void) textdomain (PACKAGE);
|
2007-10-07 17:15:58 +05:30
|
|
|
|
|
|
|
{
|
|
|
|
int option_index = 0;
|
|
|
|
int c;
|
|
|
|
static struct option long_options[] = {
|
2007-10-07 17:16:07 +05:30
|
|
|
{"all", no_argument, NULL, 'a'},
|
2007-10-07 17:15:58 +05:30
|
|
|
{"help", no_argument, NULL, 'h'},
|
2007-10-07 17:17:45 +05:30
|
|
|
{"lock-secs", required_argument, NULL, 'l'},
|
|
|
|
{"maximum", required_argument, NULL, 'm'},
|
2007-10-07 17:15:58 +05:30
|
|
|
{"reset", no_argument, NULL, 'r'},
|
2007-10-07 17:17:45 +05:30
|
|
|
{"time", required_argument, NULL, 't'},
|
|
|
|
{"user", required_argument, NULL, 'u'},
|
2007-10-07 17:15:58 +05:30
|
|
|
{NULL, 0, NULL, '\0'}
|
|
|
|
};
|
2009-03-14 04:19:20 +05:30
|
|
|
while ((c = getopt_long (argc, argv, "ahl:m:rt:u:",
|
|
|
|
long_options, &option_index)) != -1) {
|
2007-10-07 17:16:07 +05:30
|
|
|
switch (c) {
|
2007-10-07 17:15:58 +05:30
|
|
|
case 'a':
|
2008-06-11 00:46:02 +05:30
|
|
|
aflg = true;
|
2007-10-07 17:15:58 +05:30
|
|
|
break;
|
2007-10-07 17:16:07 +05:30
|
|
|
case 'h':
|
|
|
|
usage ();
|
|
|
|
break;
|
2007-10-07 17:15:58 +05:30
|
|
|
case 'l':
|
2009-03-14 04:19:20 +05:30
|
|
|
if (getlong (optarg, &fail_locktime) == 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: invalid numeric argument '%s'\n"),
|
|
|
|
"faillog", optarg);
|
|
|
|
usage ();
|
|
|
|
}
|
|
|
|
lflg = true;
|
2007-10-07 17:15:58 +05:30
|
|
|
break;
|
|
|
|
case 'm':
|
2009-03-14 04:19:20 +05:30
|
|
|
if (getlong (optarg, &fail_max) == 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: invalid numeric argument '%s'\n"),
|
|
|
|
"faillog", optarg);
|
|
|
|
usage ();
|
|
|
|
}
|
|
|
|
mflg = true;
|
2007-10-07 17:15:58 +05:30
|
|
|
break;
|
|
|
|
case 'r':
|
2009-03-14 04:19:20 +05:30
|
|
|
rflg = true;
|
2007-10-07 17:15:58 +05:30
|
|
|
break;
|
2007-10-07 17:16:07 +05:30
|
|
|
case 't':
|
2009-03-14 04:19:20 +05:30
|
|
|
if (getlong (optarg, &days) == 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("%s: invalid numeric argument '%s'\n"),
|
|
|
|
"faillog", optarg);
|
|
|
|
usage ();
|
|
|
|
}
|
2008-06-14 02:58:11 +05:30
|
|
|
seconds = (time_t) days * DAY;
|
2008-06-11 00:46:02 +05:30
|
|
|
tflg = true;
|
2007-10-07 17:16:07 +05:30
|
|
|
break;
|
2007-10-07 17:15:58 +05:30
|
|
|
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
|
|
|
{
|
2009-03-14 04:19:20 +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;
|
2007-10-07 17:15:58 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
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 */
|
2007-10-07 17:15:58 +05:30
|
|
|
pwent = getpwnam (optarg);
|
2009-03-14 04:19:20 +05:30
|
|
|
if (NULL != pwent) {
|
|
|
|
umin = (unsigned long) pwent->pw_uid;
|
|
|
|
has_umin = true;
|
|
|
|
umax = umin;
|
|
|
|
has_umax = true;
|
|
|
|
} else {
|
|
|
|
if (getrange (optarg,
|
|
|
|
&umin, &has_umin,
|
|
|
|
&umax, &has_umax) == 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("lastlog: Unknown user or range: %s\n"),
|
|
|
|
optarg);
|
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
2007-10-07 17:15:58 +05:30
|
|
|
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
|
|
|
}
|
2007-10-07 17:15:58 +05:30
|
|
|
default:
|
|
|
|
usage ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
if (aflg && uflg) {
|
|
|
|
usage ();
|
|
|
|
}
|
|
|
|
if (tflg && (lflg || mflg || rflg)) {
|
|
|
|
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,
|
|
|
|
_("faillog: Cannot open %s: %s\n"),
|
|
|
|
FAILLOG_FILE, strerror (errno));
|
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the size of the faillog */
|
|
|
|
if (fstat (fileno (fail), &statbuf) != 0) {
|
|
|
|
fprintf (stderr,
|
|
|
|
_("faillog: Cannot get the size of %s: %s\n"),
|
|
|
|
FAILLOG_FILE, strerror (errno));
|
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lflg) {
|
|
|
|
set_locktime (fail_locktime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mflg) {
|
|
|
|
setmax (fail_max);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rflg) {
|
|
|
|
reset ();
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
|
|
|
if (!(lflg || mflg || rflg)) {
|
2007-10-07 17:15:58 +05:30
|
|
|
print ();
|
2008-06-11 00:46:02 +05:30
|
|
|
}
|
2009-03-14 04:19:20 +05:30
|
|
|
|
2007-10-07 17:15:58 +05:30
|
|
|
fclose (fail);
|
2007-10-07 17:16:52 +05:30
|
|
|
|
2009-03-14 04:19:20 +05:30
|
|
|
exit (errors ? EXIT_FAILURE : EXIT_SUCCESS);
|
2007-10-07 17:15:58 +05:30
|
|
|
}
|
2008-06-11 00:46:02 +05:30
|
|
|
|