/* =======================================================================
 * Copyright 1996, Jan D. Wolter and Steven R. Weiss, 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.
 *
 * 3. The names of the authors must not be used to endorse or
 *    promote products derived from this software without prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY
 * EXPRESSED 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 AUTHORS
 * 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.
 * =======================================================================
 */

#include "pwauth.h"

#ifdef RESET_FAIL_COUNT
#define MODE_FAILLOG O_RDWR
#else
#define MODE_FAILLOG O_RDONLY
#endif

#ifdef FAILLOG_JFH
/* CHECK_FAILS - Check if the account is disable dued to the maximum number of
 * failed logins being exceeded.  Returns true if the account is OK to log
 * into or if the faillog information doesn't exist.  This never resets the
 * bad login count to zero.  It should, but I don't really believe anyone
 * uses this anymore, so I'm not going to bother implementing that.
 */

int check_fails()
{
    int result= 1;
    struct faillog flog;
    int flfd;

    if (!haveuid) return 1;

    if ((flfd= open(PATH_FAILLOG,MODE_FAILLOG)) >= 0)
    {
	lseek(flfd, hisuid * sizeof(struct faillog), 0);
	result= (read(flfd, &flog, sizeof(struct faillog))
	         != sizeof(struct faillog)) || 
	        flog.fail_max == 0 || flog.fail_cur < flog.fail_max;
	close(flfd);
    }
    return result;
}


/* LOG_FAILURE - Do whatever we need to do to log a failed login attempt.
 */

void log_failure()
{
    int flfd;
    struct faillog flog;

    if (!haveuid) return;

    /* Log the failure in /var/adm/faillog - JFH style */
    if ((flfd= open(PATH_FAILLOG,O_RDWR)) > 0)
    {
	/* Read the user's record (indexed by uid) */
	lseek(flfd, hisuid * sizeof(struct faillog), 0);
	if (read(flfd,&flog,sizeof(struct faillog)) != sizeof(struct faillog))
	{
	    flog.fail_cnt= 0;
	    flog.fail_max= 0;
	    flog.fail_time= 0L;
	    flog.failline[0]= '\0';
	}

	/* Increment the count (checking for overflow) */
	if (flog.fail_cnt + 1 > 0)
		flog.fail_cnt++;
	flog.fail_time= time(0L);
	strcpy(flog.fail_line,"http");

	/* Write it back out */
	lseek(flfd, hisuid * sizeof(struct faillog), 0);
	write(flfd, &flog, sizeof(struct faillog));
	close(flfd);
    }
}
#endif /* FAILLOG_JFH */


#ifdef FAILLOG_OPENBSD
/* CHECK_FAILS - Check if the account is disabled due to the maximum number of
 * failed logins being exceeded.  Returns true if the account is OK to log
 * into or if the faillog information doesn't exist.  If the count has not
 * been exceeded, then the count is reset to zero.
 */

int check_fails()
{
    int result= 1;
#if defined(MAX_FAIL_COUNT) || defined(RESET_FAIL_COUNT)
    struct badlogin flog;
    int flfd;

    if (!haveuid) return 1;

    if ((flfd= open(PATH_FAILLOG,MODE_FAILLOG)) >= 0)
    {
	/* Read user's record (indexed by uid) */
	lseek(flfd, hisuid * sizeof(struct badlogin), 0);
	if (read(flfd, &flog, sizeof(struct badlogin)) !=
		sizeof(struct badlogin) || flog.bl_time == 0)
	    memset(&flog, 0, sizeof(struct badlogin));
#ifdef MAX_FAIL_COUNT
	result= (flog.count < MAX_FAIL_COUNT);
#endif
#ifdef RESET_FAIL_COUNT
	if (result && flog.count > 0)
	{
	    flog.count= 0;
	    lseek(flfd, hisuid * sizeof(struct badlogin), 0);
	    write(flfd, &flog, sizeof(struct badlogin));
	}
#endif
	close(flfd);
    }
#endif /* MAX_FAIL_COUNT || RESET_FAIL_COUNT */
    return result;
}


/* LOG_FAILURE - Do whatever we need to do to log a failed login attempt.
 */

log_failure()
{
    int flfd;
    struct badlogin flog;
    char *host;

    if (!haveuid) return;

    /* Log the failure in /var/log/failedlogin - OpenBSD style */
    if ((flfd= open(PATH_FAILLOG,O_RDWR)) > 0)
    {
	/* Read user's record (indexed by uid) */
	lseek(flfd, hisuid * sizeof(struct badlogin), 0);
	if (read(flfd, &flog, sizeof(struct badlogin)) !=
		sizeof(struct badlogin) || flog.bl_time == 0)
	    memset(&flog, 0, sizeof(struct badlogin));

	/* Increment the count (checking for overflow) */
	if (flog.count + 1 > 0)
		flog.count++;

	/* Set time and remote host names */
	time(&flog.bl_time);
	strncpy(flog.bl_line, "http", sizeof(flog.bl_line));
	if ((host= getenv("HOST")) == NULL) host= getenv("IP");
	if (host != NULL)
	    strncpy(flog.bl_host, host, sizeof(flog.bl_host));
	else
	    flog.bl_host[0]= '\0';
	flog.bl_name[0]= '\0';		/* Remote name always unknown */

	/* Write it back out */
	lseek(flfd, hisuid * sizeof(struct badlogin), 0);
	write(flfd, &flog, sizeof(struct badlogin));
	close(flfd);
    }
}
#endif
