/* Copyright (C) 1999 Beau Kuiper

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "ftpd.h"
#include "auth.h"

/* This file contains code to autheticate against unix password files */

typedef struct 
{
	struct passwd *passent;
#ifdef HAVE_GETSPNAM
	struct spwd *sppassent;
#endif
} PASSWDSTRUCT;

int getday(void)
{
	time_t currenttime = time(NULL);
	struct tm *currenttm = localtime(&currenttime);
	int yeardif = currenttm->tm_year - 70;
	int days;
	
	days = yeardif * 365 + currenttm->tm_yday;
	
	/* THIS STUFF IS Y2K COMPLIANT */
	days += (yeardif + 1) / 4;
		
	/* I don't ever expect this code to be used beyond 2100, but so did
	   too many cobol programmers in the 1970's !! */
	if (currenttm->tm_year > 200)		/* simple algorithm */
	{
		yeardif -= 31;
		days -= (yeardif) / 100; /* take centaries off */
		days += (yeardif) / 400; /* add on 400 year leapyears */
	}
	return(days);
} 
	
void *unixauth_gethandle(FTPSTATE *peer, TOKENSET *tset, char *username, int *err)
{
	PASSWDSTRUCT *newhandle;
	char *passwdmode;
	
	newhandle = mallocwrapper(sizeof(PASSWDSTRUCT));

	passwdmode = mktokconfstr(tset, auth_getcursectionid(peer), "unix_passwdtype", "");
		
	newhandle->passent = getpwnam(username);
	if (newhandle->passent == NULL)
		goto error;

#ifdef HAVE_GETSPNAM
	
	/* this will try to autodetect shadow passwordness */
	if ((strcasecmp(passwdmode, "shadow") == 0) || 
	    (strcmp(newhandle->passent->pw_passwd, "x") == 0))
	{
		if (strcasecmp(passwdmode, "normal") == 0)
			newhandle->sppassent = NULL;
		else
		{
			newhandle->sppassent = getspnam(username);
			if (newhandle->sppassent == NULL)
				goto error;
		}
	}
	else
		newhandle->sppassent = NULL;

#endif
	freewrapper(passwdmode);
	*err = AUTH_OK;
	return(newhandle);

error:
	*err = AUTH_USERNKNOW;
	freewrapper(passwdmode);
	freewrapper(newhandle);
	return(NULL);
}

void unixauth_freehandle(void *handle)
{
	freewrapper(handle);
}

int unixauth_checkpasswd(void *h, char *password, char **errmsg)
{
	PASSWDSTRUCT *handle = (PASSWDSTRUCT *)h;
	
#ifdef HAVE_GETSPNAM
	if (handle->sppassent)
	{
		int days = getday();	
		int expiredate = handle->sppassent->sp_max + handle->sppassent->sp_lstchg +
			         + handle->sppassent->sp_inact;

		/* do the account expiration checking! */		
		if ((days > handle->sppassent->sp_expire) && 
		   (handle->sppassent->sp_expire != -1))
		{
			*errmsg = safe_snprintf("Account has expired");
			return(FALSE);
		}
		
		if ((handle->sppassent->sp_inact != -1) && (handle->sppassent->sp_max != -1)
		   && (handle->sppassent->sp_lstchg != -1) && (days > expiredate))
		{
			*errmsg = safe_snprintf("Account disabled, needs new password");
			return(FALSE);
		}
		
		return(chkpassword(handle->sppassent->sp_pwdp, password));
	}	
#endif 
	return(chkpassword(handle->passent->pw_passwd, password));
}

char *unixauth_gethomedir(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_dir);
}

char *unixauth_getrootdir(void *h)
{
	return("/");
}

uid_t unixauth_getuseruid(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_uid);
}

gid_t unixauth_getusergid(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_gid);
}

gid_t *unixauth_getusersupgid(void *h)
{
	return(getusergrouplist(((PASSWDSTRUCT *)h)->passent->pw_name));
}

PERMSTRUCT unixauth_commands =
{
	unixauth_checkpasswd,
	unixauth_gethomedir,
	unixauth_getrootdir,
	unixauth_getuseruid,
	unixauth_getusergid,
	unixauth_getusersupgid,
	unixauth_gethandle,
	unixauth_freehandle,
	NULL,
};


syntax highlighted by Code2HTML, v. 0.9.1