/* RE_SID: @(%)/tmp_mnt/vol/sha/qsilver_SCCS/unix/pop3/solaris2/SCCS/s.pop_pass.c 1.2 93/02/25 21:32:17 SMI */
/*
 * Copyright (c) 1989 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char copyright[] = "Copyright (c) 1990 Regents of the University of California.\nAll rights reserved.\n";
static char SccsId[] = "@(#)@(#)pop_pass.c	2.3  2.3 4/2/91";
#endif not lint

#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <pwd.h>
#include "popper.h"

#define SHADOW_SUPPORT
#ifdef SHADOW_SUPPORT
#include <shadow.h>
#endif

#define	NUMUIDS	64
/* If you wish to restrict the UID values for which you will allow
 * connection to the POP2 daemon, you can specify it here.
 * you specify ranges of acceptable UID values using entries in
 * uidrlo[] and uidrhi[]:
 *	for i in 0...
 *		uidrlo[i] .. uidrhi[i] is a legal range.
 *
 * So, to enable the range 101 to 60002, you would set
 *	int	uidrsize = 1;
 *	int	uidrlo[NUMUIDS] = {   101 };
 *	int	uidrhi[NUMUIDS] = { 60002 };
 */
int	uidrsize = 0;
int	uidrlo[NUMUIDS];
int	uidrhi[NUMUIDS];

/*
** val_uid(u)
**
** Return non-zero if the uid is legal - within range
** and zero otherwise.
**
** If uid_range is NULL, always succeed -- this is
** equivalent to no checking.
*/
int
val_uid(u)
int u;
{
    int i;

    if ( uidrsize == 0 )
	return( 1 );		/* no range checking at all */

    for ( i = 0; i < uidrsize; i++ ) {
	if ( (u >= uidrlo[i]) && (u <= uidrhi[i]) )
	    return(1);		/* legal in this range */
    }
    /* Not legal in any specified range. */
    return(0);
}

/* Get the actual password field value */
struct passwd  *
get_password(usrnam, msgpp)
char           *usrnam;
char	      **msgpp;
{
struct passwd  *p;
static struct passwd localp;
char           *pswd;
char           *ushell;
int		ok = 0;


#ifdef SHADOW_SUPPORT
struct spwd    *sp;
int             shadowfile;
long		today;
long		expday;
#endif

#ifdef SHADOW_SUPPORT
	/*
        **--------------------------------------------------------------
	** Check the existence of SHADOW.  If it is there, then we are
	** running a two-password-file system.
        **--------------------------------------------------------------
	*/
	if (access(SHADOW, 0))
	   shadowfile = 0;	/* SHADOW is not there */
	else
	   shadowfile = 1;

	setpwent();
	if (shadowfile)
	   (void) setspent();	/* Setting the shadow password
					 * file */
	if ((p = getpwnam(usrnam)) == (struct passwd *)NULL ||
	   (shadowfile && (sp = getspnam(usrnam)) == (struct spwd *)NULL))
	return ((struct passwd *)NULL);

	if (shadowfile) 
           {
/*
 * New - check password expiry situation
 *
 *	compute today (as days since Jan.1 1970)
 */
		today = (long)time((time_t *)0)/86400;
/*
 *	If sp_expire is greater than zero and is less than today, the password
 *	has expired.
 */
		if(sp->sp_expire > 0 && today > sp->sp_expire) {
			*msgpp = "This account may no longer be used.";
			return ((struct passwd *)NULL);
		}
/*
 *	If sp_max is greater zero, the password will expire on
 *	sp_lstchg+sp_max. If today is greater than this, it already did.
 */
		if(sp->sp_max > 0) {
			expday = sp->sp_max+sp->sp_lstchg;
			if(today > expday) {
				*msgpp = "The password for this account has expired.";
				return ((struct passwd *)NULL);
			}
/*
 *	Next, subtract the warning period, sp_warn, from
 *	expday. This is the first day on which warnings should
 *	be issued. If today >= expday, set msgp, but proceed.
 */
			if(sp->sp_warn >= 0) {
				expday -= sp->sp_warn;
				if(today > expday) {
					*msgpp = "The password for this account will shortly expire. Please change it.";
				}
			}
		}
	   pswd = sp->sp_pwdp;
	   (void) endspent();
	   } 
        else
	   pswd = p->pw_passwd;

#else
	p = getpwnam(usrnam);
	if (p == (struct passwd *)NULL)
		return ((struct passwd *)NULL);
	pswd = p->pw_passwd;
#endif

#ifdef ISC_2_0
	/* 
        **-----------------------------------------------------------
	** We may have an 'x' in which case look in /etc/shadow ..
        **-----------------------------------------------------------
        */
	if (((strlen(pswd)) == 1) && pswd[0] == 'x') 
	   {
	   struct spwd    *shadow = getspnam(usrnam);

	   if (!shadow)
	      return ((struct passwd *)NULL);
	   pswd = shadow->sp_pwdp;
	   }
#endif
	localp = *p;
	localp.pw_passwd = pswd;
/*
 * Fix SDR#pcn1857: If the shell field is null, this entry is legal
 * (assuming that the default shell, "/bin/sh" is always legal).
 */
	if(localp.pw_shell == NULL || strlen(localp.pw_shell) == 0)
		return(&localp);

#ifdef USE_GETUSERSHELL

	setusershell();
	while(ushell = getusershell()){
		if(!strcmp(ushell, localp.pw_shell)) {
			ok = 1;
			break;
		}
	}
	endusershell();
	if(!ok)
		return ((struct passwd *)NULL);
#else
/*
 * the best we can do is to ensure that the shell ends in "sh"
 */
	ushell = localp.pw_shell;
	if((int)strlen(ushell) < 2)
		return ((struct passwd *)NULL);
	ushell += strlen(ushell) - 2;
	if(strcmp(ushell, "sh"))
		return ((struct passwd *)NULL);

#endif
	if(!val_uid(localp.pw_uid)) {
		*msgpp = "No access via pcnfsd to this account.";
		return((struct passwd *)NULL);
	}
	return (&localp);
}

/* 
 *  pass:   Obtain the user password from a POP client
 */

int pop_pass (p)
POP     *   p;
{
    register struct passwd  *   pw;
    char		* msgp = (char *)NULL;
    char *crypt();

    /*  Look for the user in the password file */
    if ((pw = get_password(p->user, &msgp)) == NULL)
        return (pop_msg(p,POP_FAILURE,
            "Password supplied for \"%s\" is incorrect.",p->user));

    /*  We don't accept connections from users with null passwords */
    if (pw->pw_passwd == NULL)
        return (pop_msg(p,POP_FAILURE,
            "Password supplied for \"%s\" is incorrect.",p->user));

    /*  Compare the supplied password with the password file entry */
    if (strcmp (crypt (p->pop_parm[1], pw->pw_passwd), pw->pw_passwd) != 0)
        return (pop_msg(p,POP_FAILURE,
            "Password supplied for \"%s\" is incorrect.",p->user));

    /*  Build the name of the user's maildrop */
    (void)sprintf(p->drop_name,"%s/%s",POP_MAILDIR,p->user);

    /*  Make a temporary copy of the user's maildrop */
    /*    and set the group and user id */
    if (pop_dropcopy(p,pw) != POP_SUCCESS) return (POP_FAILURE);

    /*  Get information about the maildrop */
    if (pop_dropinfo(p) != POP_SUCCESS) return(POP_FAILURE);

    /*  Initialize the last-message-accessed number */
    p->last_msg = 0;

    /*  Authorization completed successfully */
    return (pop_msg (p,POP_SUCCESS,
        "%s has %d message(s) (%d octets).",
            p->user,p->msg_count,p->drop_size));
}
