- Every non-const pointer converts automatically to void *. - Every pointer converts automatically to void *. - void * converts to any other pointer. - const void * converts to any other const pointer. - Integer variables convert to each other. I changed the declaration of a few variables in order to allow removing a cast. However, I didn't attempt to edit casts inside comparisons, since they are very delicate. I also kept casts in variadic functions, since they are necessary, and in allocation functions, because I have other plans for them. I also changed a few casts to int that are better as ptrdiff_t. This change has triggered some warnings about const correctness issues, which have also been fixed in this patch (see for example src/login.c). Signed-off-by: Alejandro Colomar <alx@kernel.org>
		
			
				
	
	
		
			290 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			290 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
 | 
						|
 * SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
 | 
						|
 * SPDX-FileCopyrightText: 2002 - 2005, Tomasz Kłoczko
 | 
						|
 * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 */
 | 
						|
 | 
						|
#include <config.h>
 | 
						|
 | 
						|
#ident "$Id$"
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include "defines.h"
 | 
						|
#include "faillog.h"
 | 
						|
#include "getdef.h"
 | 
						|
#include "failure.h"
 | 
						|
#define	YEAR	(365L*DAY)
 | 
						|
/*
 | 
						|
 * failure - make failure entry
 | 
						|
 *
 | 
						|
 *	failure() creates a new (struct faillog) entry or updates an
 | 
						|
 *	existing one with the current failed login information.
 | 
						|
 */
 | 
						|
void failure (uid_t uid, const char *tty, struct faillog *fl)
 | 
						|
{
 | 
						|
	int fd;
 | 
						|
	off_t offset_uid = (off_t) (sizeof *fl) * uid;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Don't do anything if failure logging isn't set up.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (access (FAILLOG_FILE, F_OK) != 0) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	fd = open (FAILLOG_FILE, O_RDWR);
 | 
						|
	if (fd < 0) {
 | 
						|
		SYSLOG ((LOG_WARN,
 | 
						|
		         "Can't write faillog entry for UID %lu in %s.",
 | 
						|
		         (unsigned long) uid, FAILLOG_FILE));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The file is indexed by UID value meaning that shared UID's
 | 
						|
	 * share failure log records.  That's OK since they really
 | 
						|
	 * share just about everything else ...
 | 
						|
	 */
 | 
						|
 | 
						|
	if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
 | 
						|
	    || (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
 | 
						|
		/* This is not necessarily a failure. The file is
 | 
						|
		 * initially zero length.
 | 
						|
		 *
 | 
						|
		 * If lseek() or read() failed for any other reason, this
 | 
						|
		 * might reset the counter. But the new failure will be
 | 
						|
		 * logged.
 | 
						|
		 */
 | 
						|
		memzero (fl, sizeof *fl);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Update the record.  We increment the failure count to log the
 | 
						|
	 * latest failure.  The only concern here is overflow, and we'll
 | 
						|
	 * check for that.  The line name and time of day are both
 | 
						|
	 * updated as well.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (fl->fail_cnt + 1 > 0) {
 | 
						|
		fl->fail_cnt++;
 | 
						|
	}
 | 
						|
 | 
						|
	strncpy (fl->fail_line, tty, sizeof (fl->fail_line) - 1);
 | 
						|
	(void) time (&fl->fail_time);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Seek back to the correct position in the file and write the
 | 
						|
	 * record out.  Ideally we should lock the file in case the same
 | 
						|
	 * account is being logged simultaneously.  But the risk doesn't
 | 
						|
	 * seem that great.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
 | 
						|
	    || (write (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)
 | 
						|
	    || (close (fd) != 0)) {
 | 
						|
		SYSLOG ((LOG_WARN,
 | 
						|
		         "Can't write faillog entry for UID %lu in %s.",
 | 
						|
		         (unsigned long) uid, FAILLOG_FILE));
 | 
						|
		(void) close (fd);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static bool too_many_failures (const struct faillog *fl)
 | 
						|
{
 | 
						|
	time_t now;
 | 
						|
 | 
						|
	if ((0 == fl->fail_max) || (fl->fail_cnt < fl->fail_max)) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (0 == fl->fail_locktime) {
 | 
						|
		return true;	/* locked until reset manually */
 | 
						|
	}
 | 
						|
 | 
						|
	(void) time (&now);
 | 
						|
	if ((fl->fail_time + fl->fail_locktime) < now) {
 | 
						|
		return false;	/* enough time since last failure */
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * failcheck - check for failures > allowable
 | 
						|
 *
 | 
						|
 *	failcheck() is called AFTER the password has been validated.  If the
 | 
						|
 *	account has been "attacked" with too many login failures, failcheck()
 | 
						|
 *	returns 0 to indicate that the login should be denied even though
 | 
						|
 *	the password is valid.
 | 
						|
 *
 | 
						|
 *	failed indicates if the login failed AFTER the password has been
 | 
						|
 *	       validated.
 | 
						|
 */
 | 
						|
 | 
						|
int failcheck (uid_t uid, struct faillog *fl, bool failed)
 | 
						|
{
 | 
						|
	int fd;
 | 
						|
	struct faillog fail;
 | 
						|
	off_t offset_uid = (off_t) (sizeof *fl) * uid;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Suppress the check if the log file isn't there.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (access (FAILLOG_FILE, F_OK) != 0) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	fd = open (FAILLOG_FILE, failed?O_RDONLY:O_RDWR);
 | 
						|
	if (fd < 0) {
 | 
						|
		SYSLOG ((LOG_WARN,
 | 
						|
		         "Can't open the faillog file (%s) to check UID %lu. "
 | 
						|
		         "User access authorized.",
 | 
						|
		         FAILLOG_FILE, (unsigned long) uid));
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Get the record from the file and determine if the user has
 | 
						|
	 * exceeded the failure limit.  If "max" is zero, any number
 | 
						|
	 * of failures are permitted.  Only when "max" is non-zero and
 | 
						|
	 * "cnt" is greater than or equal to "max" is the account
 | 
						|
	 * considered to be locked.
 | 
						|
	 *
 | 
						|
	 * If read fails, there is no record for this user yet (the
 | 
						|
	 * file is initially zero length and extended by writes), so
 | 
						|
	 * no need to reset the count.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
 | 
						|
	    || (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
 | 
						|
		(void) close (fd);
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (too_many_failures (fl)) {
 | 
						|
		(void) close (fd);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The record is updated if this is not a failure.  The count will
 | 
						|
	 * be reset to zero, but the rest of the information will be left
 | 
						|
	 * in the record in case someone wants to see where the failed
 | 
						|
	 * login originated.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (!failed) {
 | 
						|
		fail = *fl;
 | 
						|
		fail.fail_cnt = 0;
 | 
						|
 | 
						|
		if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
 | 
						|
		    || (write (fd, &fail, sizeof fail) != (ssize_t) sizeof fail)
 | 
						|
		    || (close (fd) != 0)) {
 | 
						|
			SYSLOG ((LOG_WARN,
 | 
						|
			         "Can't reset faillog entry for UID %lu in %s.",
 | 
						|
			         (unsigned long) uid, FAILLOG_FILE));
 | 
						|
			(void) close (fd);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		(void) close (fd);
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * failprint - print line of failure information
 | 
						|
 *
 | 
						|
 *	failprint takes a (struct faillog) entry and formats it into a
 | 
						|
 *	message which is displayed at login time.
 | 
						|
 */
 | 
						|
 | 
						|
void failprint (const struct faillog *fail)
 | 
						|
{
 | 
						|
	struct tm *tp;
 | 
						|
	char lasttimeb[256];
 | 
						|
	char *lasttime = lasttimeb;
 | 
						|
	time_t NOW;
 | 
						|
 | 
						|
	if (0 == fail->fail_cnt) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	tp = localtime (&(fail->fail_time));
 | 
						|
	(void) time (&NOW);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Print all information we have.
 | 
						|
	 */
 | 
						|
	(void) strftime (lasttimeb, sizeof lasttimeb, "%c", tp);
 | 
						|
 | 
						|
	/*@-formatconst@*/
 | 
						|
	(void) printf (ngettext ("%d failure since last login.\n"
 | 
						|
	                         "Last was %s on %s.\n",
 | 
						|
	                         "%d failures since last login.\n"
 | 
						|
	                         "Last was %s on %s.\n",
 | 
						|
	                         (unsigned long) fail->fail_cnt),
 | 
						|
	               fail->fail_cnt, lasttime, fail->fail_line);
 | 
						|
	/*@=formatconst@*/
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * failtmp - update the cumulative failure log
 | 
						|
 *
 | 
						|
 *	failtmp updates the (struct utmp) formatted failure log which
 | 
						|
 *	maintains a record of all login failures.
 | 
						|
 */
 | 
						|
 | 
						|
void failtmp (const char *username, const struct utmp *failent)
 | 
						|
{
 | 
						|
	const char *ftmp;
 | 
						|
	int fd;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Get the name of the failure file.  If no file has been defined
 | 
						|
	 * in login.defs, don't do this.
 | 
						|
	 */
 | 
						|
 | 
						|
	ftmp = getdef_str ("FTMP_FILE");
 | 
						|
	if (NULL == ftmp) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Open the file for append.  It must already exist for this
 | 
						|
	 * feature to be used.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (access (ftmp, F_OK) != 0) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	fd = open (ftmp, O_WRONLY | O_APPEND);
 | 
						|
	if (-1 == fd) {
 | 
						|
		SYSLOG ((LOG_WARN,
 | 
						|
		         "Can't append failure of user %s to %s.",
 | 
						|
		         username, ftmp));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Append the new failure record and close the log file.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (   (write (fd, failent, sizeof *failent) != (ssize_t) sizeof *failent)
 | 
						|
	    || (close (fd) != 0)) {
 | 
						|
		SYSLOG ((LOG_WARN,
 | 
						|
		         "Can't append failure of user %s to %s.",
 | 
						|
		         username, ftmp));
 | 
						|
		(void) close (fd);
 | 
						|
	}
 | 
						|
}
 | 
						|
 |