/*
 *
 *	RADIUS
 *	Remote Authentication Dial In User Service
 *
 * ASCEND: @(#)radiusd.c	1.4 (95/07/25 00:55:34)
 *
 *
 *	Livingston Enterprises, Inc.
 *	6920 Koll Center Parkway
 *	Pleasanton, CA   94566
 *
 *	Copyright 1992 Livingston Enterprises, Inc.
 *
 *	Permission to use, copy, modify, and distribute this software for any
 *	purpose and without fee is hereby granted, provided that this
 *	copyright and permission notice appear on all copies and supporting
 *	documentation, the name of Livingston Enterprises, Inc. not be used
 *	in advertising or publicity pertaining to distribution of the
 *	program without specific prior permission, and notice be given
 *	in supporting documentation that copying and distribution is by
 *	permission of Livingston Enterprises, Inc.   
 *
 *	Livingston Enterprises, Inc. makes no representations about
 *	the suitability of this software for any purpose.  It is
 *	provided "as is" without express or implied warranty.
 *
 *	Modified by Ascend Communications, Inc. to support authentication
 *	via the Enigma Logic SafeWord library, version 4.0 (sync and async
 *	modes but not semisync). No retries supported (CHALLENGE, PASS, or
 *	FAIL are the only possible replies). Also, only dynamic passwords
 *	are supported (no fixed passwords, etc).
 *
 *      Modified by Ascend Communications, Inc. to support authentication
 *      via the Security Dynamics ACE library. Next_passcode is supported,
 *	new_pin support is not complete.
 */

/* don't look here for the version, run radiusd -v or look in version.c */
static char sccsid[] =
"@(#)radiusd.c	1.17 Copyright 1992 Livingston Enterprises Inc";

#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/file.h>
#include	<netinet/in.h>

#include	<stdio.h>
#include	<netdb.h>
#include	<fcntl.h>
#include	<pwd.h>
#include	<time.h>
#include	<ctype.h>
#include	<unistd.h>
#include	<signal.h>
#include	<errno.h>
#include	<sys/wait.h>

#include	<sys/time.h>	/* gettimeofday() */
#include	<pwd.h>
#include	<memory.h>
#include	<string.h>

#if !defined(NOSHADOW)
#include	<shadow.h>
#endif /* !NOSHADOW */

#include	"cache.h"
#include	"radius.h"
#include	"protos.h"
#include	"des.h"


#undef	PWD_DEBUG	/* undef to avoid clear-text password debug info */

#define UNIX_PWD	"UNIX"	/* Unix reserved password */

    /* enumeration of results of authentication */
typedef enum authResults {
	SAFEWORD_FAILED	= -100,
	SAFEWORD_PASSED,
	SAFEWORD_CHALLENGING,
	ACE_FAILED,
	ACE_PASSED,
	ACE_CHALLENGING,
	ACE_NEXT_PASSCODE,
	ACE_NEW_PIN
} authResults;

#if !defined(sys5)
typedef unsigned long	ulong;
#endif

FILE	*errf;

	/* associate strings with the error messages */
	/* keep these in the same order as the typedefs in radius.h */
DBASE_ERRMSG db_errmsgs[NUM_DBASE_ERRORS] = {
	{DEFAULT_PARSE_ERR,		"Default Parse Error"},
	{DICT_VALFIND_ERR,		"Dict-Value Find Error"},
	{BINARY_FILTER_ERR,		"Binary Filter Error"},
	{MISSING_EQUALS,		"Missing Equals Sign"},
	{DICT_ATTRFIND_ERR,		"Dict-Attribute Find Error"},
	{REPLY_FIRST_IS_NULL,		"INTERNAL: No Valid Reply Attributes"},
	{MISSING_NEWLINE,		"Missing Newline"},
	{NO_USER_OR_DEFAULT_NAME,	"Neither User Nor Default Name"},
	{ZERO_LENGTH_NAME,		"Zero Length Name"},
	{NO_USER_FILE,			"Missing Radius Users File"},
	{NO_WHITESPACE,			"Missing Whitespace After User Name"},
	{USERFILE_RENAME_FAILED,	"Failed Renaming Users File"},
	{USERFILE_READ_ERR,		"Failed Reading Users File"},
	{USERFILE_WRITE_ERR,		"Failed Writing Users File"},
	{END_OF_USERS_LIST,		"Reached End of Users File"},
	{MEMORY_ERR,			"Out of Memory"},
	{DICTFILE_READ_ERR,		"Failed Reading Dictionary File"},
	{DICTFILE_ATTRLINE_ERR,		"Error on Dictionary Attribute Line"},
	{DICTFILE_ATTRNAME_ERR,		"Dictionary Attribute Name Error"},
	{DICTFILE_ATTRVALUE_ERR,	"Dictionary Attribute Name Error"},
	{DICTFILE_ATTRTYPE_ERR,		"Dictionary Attribute Type Error"},
	{DICTFILE_VALLINE_ERR,		"Error on Dictionary Value Line"},
	{DICTFILE_VALATTR_ERR,		"Dictionary Value Attribute Error"},
	{DICTFILE_VALNAME_ERR,		"Dictionary Value Name Error"},
	{DICTFILE_VALVALUE_ERR,		"Dictionary Value Value Error"},
	{UNIX_GETPWNAME_ERR,		"Unix Get Pwd Name Error"},
	{UNIX_GETSHDWNAME_ERR,		"Unix Get Shadow Pwd Name Error"},
	{UNIX_BAD_PASSWORD,		"Unix Bad Password"},
	{CLIENTFILE_READ_ERR,		"Failed Reading Clients File"},
	{WRONG_NAS_ADDR,		"Wrong NAS Address"},
	{LOGFILE_APPEND_ERR,		"Failed Appending to Log File"},
	{NULL_VALUEPAIR,		"INTERNAL: Null Value-Pair Parameter"},
	{PWD_EXPIRED,			"Password Expired"},
	{PIPE_CREATE_ERR,		"Pipe Creation Failed"}
};

/*------------------------------------------------------------*/

	/* function prototypes */
extern int main P__((int argc, char **argv));
static void usage P__((void));
extern char *get_errmsg P__((int errcode));
#if defined(sys5) || defined(BSDI)
extern char *crypt P__((CONST char *, CONST char *));
#else
extern char *crypt P__((u_char *, u_char *));
#endif
/*------------------------------------------------------------*/



#if defined(SAFEWORD)
/*-------------- Safeword Interface Begin --------------*/

	/* SafeWord interface files */
#include "custpb.h"	/* interface information */
#include "custfail.h"	/* failure codes */

	/* forward function declarations */
extern void pbmain P__((struct pblk *));	/* invoke SafeWord */
extern void initpb P__((struct pblk *));	/* init SafeWord param blk */
extern void dbgPblk P__((struct pblk *pb));	/* show SafeWord param blk */
extern int safeword_chall P__((AuthInfo *authInfo, struct pblk *pb));
extern int safeword_eval P__((AuthInfo *authInfo, struct pblk *pb));
		/* request authentication via SafeWord */
extern int safeword_pass P__((AuthInfo *authInfo, struct pblk *pb));

	/* useful definitions */
#define EL_PWD	"SAFEWORD"	/* Enigma Logic's reserved password */

#define BEG_PB_1ST(pb)    (&(pb)->uport)
#define END_PB_1ST(pb)    (&(pb)->msg1)

#define BEG_PB_2ND(pb)    (&(pb)->errcode)
#define END_PB_2ND(pb)    (&(pb)->status + sizeof((pb)->status))

#define LEN_PB_1ST(pb)    ((ulong)END_PB_1ST((pb)) - (ulong)BEG_PB_1ST((pb)))
#define LEN_PB_2ND(pb)    ((ulong)END_PB_2ND((pb)) - (ulong)BEG_PB_2ND((pb)))

#define LEN_PB_TOTAL(pb)  (LEN_PB_1ST(pb) + LEN_PB_2ND(pb))

/*-------------- Safeword Interface End --------------*/
#endif

#if defined(ACE)
/*-------------- Ace Interface Begin --------------*/
#include "sdi_athd.h"
#include "sdi_size.h"
#include "sdi_type.h"
#include "sdacmvls.h"
#include "sdconf.h"

	/* Security Dynamics header files do not include prototypes
		for these 4 functions */
extern void creadcfg P__((void));
extern int sd_init P__((struct SD_CLIENT *sd));
extern int sd_check P__((char *passcode, char *name, struct SD_CLIENT *sd));
extern int sd_next P__((char *passcode, struct SD_CLIENT *sd));

extern void dbgSdClient P__((struct SD_CLIENT *sd));
extern int ace_eval P__((AuthInfo *authInfo, struct SD_CLIENT *sd));
extern int ace_next P__((AuthInfo *authInfo, struct SD_CLIENT *sd));
	/* request authentication via ACE */
extern int ace_pass P__((AuthInfo *authInfo, struct SD_CLIENT *sd));

union	config_record configure;

	/* useful definitions */
#define SD_PWD	"ACE"	/* Security Dynamics reserved password */
#define ACE_DUMMY_STATE	"ACE_DUMMY_STATE"
#define LEN_ACE_DUMMY_STATE	strlen(ACE_DUMMY_STATE)

#define BEG_SD_1ST(sd)    (&(sd)->application_id)
#define END_SD_1ST(sd)    (&(sd)->application_id + sizeof((sd)->application_id))

#define BEG_SD_2ND(sd)    (&(sd)->passcode_time)
#define END_SD_2ND(sd)    (&(sd)->passcode_time + sizeof((sd)->passcode_time))

#define BEG_SD_3RD(sd)    (&(sd)->ignition_key)
#define END_SD_3RD(sd)    (&(sd)->alphanumeric + sizeof((sd)->alphanumeric))

#define LEN_SD_1ST(sd)    ((ulong)END_SD_1ST((sd)) - (ulong)BEG_SD_1ST((sd)))
#define LEN_SD_2ND(sd)    ((ulong)END_SD_2ND((sd)) - (ulong)BEG_SD_2ND((sd)))
#define LEN_SD_3RD(sd)    ((ulong)END_SD_3RD((sd)) - (ulong)BEG_SD_3RD((sd)))

#define LEN_SD_TOTAL(sd)  (LEN_SD_1ST(sd) + LEN_SD_2ND(sd) + LEN_SD_3RD(sd))

/*-------------- Ace Interface End --------------*/
#endif

#define MAX_RCVBUF_SIZE		(4096)
#if defined( ORIG )
# define MAX_SNDBUF_SIZE	(4096)
#else
# define MAX_SNDBUF_SIZE	(8192)
#endif

	/* Order of these enum values is important! */
typedef enum ACCT_FLAGS {
	ACCT_NONE,
	ACCT_BY_NAME,
	ACCT_ANY
} ACCT_FLAG;

char		recv_buffer[MAX_RCVBUF_SIZE];
char		send_buffer[MAX_SNDBUF_SIZE];
char		*progname;
int		sockfd;
int		acctfd;
int		acct_flag;
int		debug_flag;
int		spawn_flag;
int		acct_pid;
char		*radius_dir;
char		*radius_users;
char		*radacct_dir;
UINT4		expiration_seconds;
UINT4		warning_seconds;
int		allow_passchange;
int		allow_token_caching;
CACHE		*cache;
extern int	errno;
static AUTH_REQ	*first_request;
extern int	warning;
int		rdCachefd;
int		wrCachefd;

/**********************************************************************/

int
main(argc, argv)
int	argc;
char	**argv;
{
	int			salen;
	int			result;
	struct	sockaddr	salocal;
	struct	sockaddr	saremote;
	struct	sockaddr_in	*sin;
	struct	servent		*svp;
        u_short                 lport;
	AUTH_REQ		*authreq;
	char			argval;
	int			t;
	int			pid;
	int			cons;
	fd_set			readfds;
	int			status;
	char			isAcctPkt;
	int			sockOptFlag;

	progname = *argv++;
	argc--;

	debug_flag = 0;
	spawn_flag = 1;
	radacct_dir = RADACCT_DIR;
	radius_dir = RADIUS_DIR;
	radius_users = RADIUS_USERS;
	errf = stdout;	/* or stderr */
	warning = 0;
	allow_passchange = 0;
	allow_token_caching = 0;
	rdCachefd = -1;
	wrCachefd = -1;
	acctfd = -1;
	sockfd = -1;
	acct_flag = ACCT_ANY;

	signal(SIGHUP, sig_hup);
	signal(SIGINT, sig_fatal);
	signal(SIGQUIT, sig_fatal);
	signal(SIGILL, sig_fatal);
	signal(SIGTRAP, sig_fatal);
	signal(SIGIOT, sig_fatal);
	signal(SIGFPE, sig_fatal);
	signal(SIGTERM, sig_fatal);
	signal(SIGCHLD, sig_cleanup);

	while(argc) {

		if( **argv != '-' ) {
			usage();
		}

		argval = *(*argv + 1);
		argc--;
		argv++;

		switch(argval) {

		case 'A':
			if(argc == 0) {
				usage();
			}
			if( !strcmp(*argv, "none") ) {
				acct_flag = ACCT_NONE;
			}
			else if( !strcmp(*argv, "services") ) {
				acct_flag = ACCT_BY_NAME;
			}
			else if( !strcmp(*argv, "incr") ) {
				acct_flag = ACCT_ANY;
			}
			else {
				usage();
			}
			argc--;
			argv++;
			break;

		case 'a':
			if(argc == 0) {
				usage();
			}
			radacct_dir = *argv;
			argc--;
			argv++;
			break;
		
		case 'c':
			allow_token_caching = 1;
			break;

		case 'd':
			if(argc == 0) {
				usage();
			}
			radius_dir = *argv;
			argc--;
			argv++;
			break;
		
		case 'p':
			allow_passchange = 1;
			break;

		case 's':	/* Single process mode */
			spawn_flag = 0;
			break;

		case 'u':
			if(argc == 0) {
				usage();
			}
			radius_users = *argv;
			argc--;
			argv++;
			break;

		case 'v':
			radversion();
			break;

		case 'w':
			warning = 1;
			break;

		case 'x':
			debug_flag = 1;
			break;
		
		default:
			usage();
			break;
		}
	}

	/* Initialize the dictionary */
	if( (result = dict_init()) != 0) {
		exit(result);
	}

	/* Initialize Configuration Values */
	if( (result = config_init()) != 0) {
		exit(result);
	}

	/* Initialize DES, once and for all. */
	if ( (result = des_init()) != 0) {
		exit(result);
	}

	/* Init token caching */
	if( allow_token_caching ) {
		int	pd[2];
		int	stat;

		cache = (CACHE *)malloc(sizeof(*cache));
		if( !cache ) {
			DEBUG("Cache Creation Failed\r\n");
			log_err("Cache Creation Failed\n");
			exit(MEMORY_ERR);
		}
		cache_init(cache, BUCKETS);
		if( spawn_flag ) {
			stat = pipe(pd);
			if( stat != 0 ) {
				(void)perror("Pipe Creation Failed:");
				log_err("Pipe Creation Failed\n");
				exit(PIPE_CREATE_ERR);
			}
			else {
				rdCachefd = pd[0];
				wrCachefd = pd[1];
			}
		}
	}

	svp = getservbyname ("radius", "udp");
	if (svp == (struct servent *) 0) {
		fprintf (errf, "%s: No such service: radius/udp\n",
			progname);
		exit(-1);
	}
	lport = (u_short) svp->s_port;

	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		(void) perror ("auth socket");
		exit(-1);
	}

	sockOptFlag = 1;	/* enable */
	if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
		     (char*) &sockOptFlag, sizeof(sockOptFlag)) < 0 ) {
		(void)perror("setsockopt auth SO_REUSEADDR");
		exit(-1);
	}

	sin = (struct sockaddr_in *) & salocal;
        memset ((char *) sin, '\0', sizeof (salocal));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = INADDR_ANY;
	sin->sin_port = lport;

	result = bind (sockfd, & salocal, sizeof (*sin));
	if (result < 0) {
		(void) perror ("auth bind");
		exit(-1);
	}

	/*
	 * Open Accounting Socket.
	 */
	if( acct_flag >= ACCT_BY_NAME ) {
		svp = getservbyname ("radacct", "udp");
		if( svp != (struct servent *) 0 ) {
			lport = (u_short) svp->s_port;
		}
		else {
			fprintf (errf, "%s: Service %s/%s not defined ",
				progname, "radacct", "udp");

			if( acct_flag >= ACCT_ANY ) {
				lport = htons(ntohs(lport) +1);
				fprintf(errf, "-Using port %d\n", ntohs(lport));
			}
			else {
				fprintf(errf, "-Aborting\n");
				exit(-1);
			}
		}

		acctfd = socket (AF_INET, SOCK_DGRAM, 0);
		if (acctfd < 0) {
			(void) perror ("acct socket");
			exit(-1);
		}

		sockOptFlag = 1;	/* enable */
		if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
		     (char*) &sockOptFlag, sizeof(sockOptFlag)) < 0 ) {
			(void)perror("setsockopt acct SO_REUSEADDR");
			exit(-1);
		}

		sin = (struct sockaddr_in *) & salocal;
		memset ((char *) sin, '\0', sizeof (salocal));
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = INADDR_ANY;
		sin->sin_port = lport;

		result = bind (acctfd, & salocal, sizeof (*sin));
		if (result < 0) {
			(void) perror ("acct bind");
			exit(-1);
		}
	}

	/*
	 *	Disconnect from session
	 */
	if(debug_flag == 0) {
		pid = fork();
		if(pid < 0) {
			fprintf(errf, "%s: Couldn't fork\n",
						progname);
			exit(-1);
		}
		if(pid > 0) {
			exit(0);
		}
	}

	/*
	 *	Disconnect from tty
	 */
	for (t = 32; t >= 3; t--) {
		if(t != sockfd && t != acctfd
		&& t != rdCachefd && t != wrCachefd ) {
			close(t);
		}
	}

#if !defined(M_UNIX)
	/*
	 * Open system console as stderr
	 */
	cons = open("/dev/console", O_WRONLY | O_NOCTTY);
	if(cons != 2) {
		dup2(cons, 2);
		close(cons);
	}
#endif
	/*
	 * If we are able to spawn processes, we will start a child
	 * to listen for Accounting requests.  If not, we will 
	 * listen for them ourself.
	 */
	if( spawn_flag && (acctfd >= 0) ) {
		acct_pid = fork();
		if(acct_pid < 0) {
			fprintf(errf, "%s: Couldn't fork\n",
						progname);
			exit(-1);
		}
		if(acct_pid > 0) {
			close(acctfd);
			acctfd = -1;
		}
		else {
			close(sockfd);
			sockfd = -1;
			if( allow_token_caching ) {
				free(cache);
				close(rdCachefd);
				rdCachefd = -1;
				close(wrCachefd);
				wrCachefd = -1;
			}
		}
	}
		

	/*
	 *	Receive user requests
	 */
	sin = (struct sockaddr_in *) & saremote;

	for(;;) {

		FD_ZERO(&readfds);
		if(sockfd >= 0) {
			FD_SET(sockfd, &readfds);
		}
		if(acctfd >= 0) {
			FD_SET(acctfd, &readfds);
		}

		if( rdCachefd >= 0 ) {
			FD_SET(rdCachefd, &readfds);
		}

		status = select(32, &readfds, NULL, NULL, NULL);
		if(status == -1) {
			if (errno == EINTR)
				continue;
			(void)perror("main:select");
			fprintf(errf, "%s: select status=%d\n",
				progname, status);
			sig_fatal(101);
		}
		if( rdCachefd >= 0 && FD_ISSET(rdCachefd, &readfds) ) {
			CacheMsg	msg;
			char		buf[128];
			int		stat;
			int		n = 0;

			while( n != sizeof(msg) ) {
				n += read(rdCachefd, ((char *)&msg)+n,
					sizeof(msg)-n);
			}
			switch( msg.type ) {
			case CACHE_INSERT:
				stat = cache_insert(cache,
						msg.key, strlen(msg.key)+1,
						msg.val, strlen(msg.val)+1,
						msg.time, msg.idle);
				sprintf(buf, "Insert cache <%s> FAILED\n",
						msg.key);
				break;
			case CACHE_DELETE:
				stat = cache_delete(cache,
						msg.key, strlen(msg.key)+1);
				sprintf(buf, "Delete cache <%s> FAILED\n",
						msg.key);
				break;
			case CACHE_IDLE_UPDATE:
				stat = cache_idle_update(cache,
						msg.key, strlen(msg.key)+1,
						msg.idle);
				sprintf(buf, "Idle Update cache <%s> FAILED\n",
						msg.key);
				break;
			default:
				sprintf(buf, "Unknown cache msg type %d\n",
					msg.type);
				stat = FALSE;
				break;
			}
			if( stat != TRUE ) {
				log_err(buf);
				DEBUG("%s", buf);
			}
		}
		if(sockfd >= 0 && FD_ISSET(sockfd, &readfds)) {
			salen = sizeof (saremote);
			result = recvfrom (sockfd, (char *) recv_buffer,
				(int) sizeof(recv_buffer),
				(int) 0, & saremote, & salen);

			if(result > 0) {
				authreq = radrecv(
					ntohl(sin->sin_addr.s_addr),
					ntohs(sin->sin_port),
					(u_char *)recv_buffer,
					result, &isAcctPkt);
				if( authreq ) {
					radrespond(authreq, sockfd, isAcctPkt);
				}
			}
			else if(result < 0 && errno == EINTR) {
				result = 0;
			}
			else {
				fprintf(errf, "%s: recvfrom result=%d\n",
					progname, result);
			}
		}
		if(acctfd >=0 && FD_ISSET(acctfd, &readfds)) {
			salen = sizeof (saremote);
			result = recvfrom (acctfd, (char *) recv_buffer,
				(int) sizeof(recv_buffer),
				(int) 0, & saremote, & salen);

			if(result > 0) {
				authreq = radrecv(
					ntohl(sin->sin_addr.s_addr),
					ntohs(sin->sin_port),
					(u_char *)recv_buffer,
					result, &isAcctPkt);
				if( authreq ) {
					radrespond(authreq, acctfd, isAcctPkt);
				}
			}
			else if(result < 0 && errno == EINTR) {
				result = 0;
			}
		}
	}
	return 0;	/* never reached */
}

/*************************************************************************
 *
 *	Function: radrecv
 *
 *	Purpose: Receive UDP client requests, build an authorization request
 *		 structure, and attach attribute-value pairs contained in
 *		 the request to the new structure.
 *
 *************************************************************************/

AUTH_REQ *
radrecv(host, udp_port, buffer, length, isAcctPkt)
UINT4	host;
u_short	udp_port;
u_char	*buffer;
int	length;
char	*isAcctPkt;
{
	u_char		*ptr;
	AUTH_HDR	*auth;
	int		totallen;
	int		attribute;
	int		attrlen;
	DICT_ATTR	*attr;
	UINT4		lvalue;
	VALUE_PAIR	*first_pair;
	VALUE_PAIR	*prev;
	VALUE_PAIR	*pair;
	AUTH_REQ	*authreq;

	*isAcctPkt = FALSE;

	/*
	 * Pre-allocate the new request data structure
	 */

	if((authreq = (AUTH_REQ *)malloc(sizeof(AUTH_REQ))) ==
						(AUTH_REQ *)NULL) {
		fprintf(errf, "%s: no memory\n", progname);
		exit(MEMORY_ERR);
	}

	auth = (AUTH_HDR *)buffer;
	totallen = ntohs(auth->length);

	DEBUG("radrecv: Request from host %lx code=%d, id=%d, length=%d\r\n",
		(u_long)host, auth->code, auth->id, totallen);

	/*
	 * Fill header fields
	 */
	authreq->ipaddr = host;
	authreq->udp_port = udp_port;
	authreq->id = auth->id;
	authreq->code = auth->code;
	memcpy(authreq->vector, auth->vector, AUTH_VECTOR_LEN);
	authreq->secret[0] = '\0';	/* ensure known contents */

	/*
	 * Extract attribute-value pairs
	 */
	ptr = auth->data;
	length -= AUTH_HDR_LEN;
	first_pair = (VALUE_PAIR *)NULL;
	prev = (VALUE_PAIR *)NULL;

	while(length > 0) {

		attribute = *ptr++;
		attrlen = *ptr++;
		if(attrlen < 2) {
			length = 0;
			continue;
		}
		attrlen -= 2;
		if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) {
		    DEBUG("Received unknown attribute %d\r\n", attribute);
		}
		else if ( attrlen >= AUTH_STRING_LEN ) {
		    DEBUG("attribute %d too long, %d >= %d\r\n", attribute,
			attrlen, AUTH_STRING_LEN);
		}
		else {
			if( attribute == PW_ACCT_STATUS_TYPE ) {
				*isAcctPkt = TRUE;
			}

			if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
						(VALUE_PAIR *)NULL) {
				fprintf(errf, "%s: no memory\n",
						progname);
				exit(-1);
			}
			strcpy(pair->name, attr->name);
			pair->attribute = attr->value;
			pair->type = attr->type;
			pair->size = attrlen;
			pair->next = (VALUE_PAIR *)NULL;

			switch(attr->type) {

			case PW_TYPE_STRING:
				memcpy(pair->strvalue, ptr, attrlen);
				pair->strvalue[attrlen] = '\0';
				debug_pair(stdout, pair);
				if(first_pair == (VALUE_PAIR *)NULL) {
					first_pair = pair;
				}
				else {
					prev->next = pair;
				}
				prev = pair;
				break;
			
			case PW_TYPE_INTEGER:
			case PW_TYPE_IPADDR:
				memcpy(&lvalue, ptr, sizeof(UINT4));
				pair->lvalue = ntohl(lvalue);
				debug_pair(stdout, pair);
				if(first_pair == (VALUE_PAIR *)NULL) {
					first_pair = pair;
				}
				else {
					prev->next = pair;
				}
				prev = pair;
				break;
#if defined( BINARY_FILTERS )
                        case PW_TYPE_FILTER_BINARY:
                                memcpy(pair->strvalue, ptr, attrlen);
                                pair->strvalue[attrlen] = '\0';
                                debug_pair(stdout, pair);
                                pair->next = first_pair;
                                first_pair = pair;
                                break;
#endif /* BINARY_FILTERS */
			
			default:
				DEBUG("    %s (Unknown Type %d)\r\n",
					attr->name,attr->type);
				free(pair);
				break;
			}

		}
		ptr += attrlen;
		length -= attrlen + 2;
	}
	authreq->request = first_pair;

	if( (auth->code == PW_ACCOUNTING_REQUEST) && *isAcctPkt ) {
		authreq = authenticateAcctReq(authreq, auth);
	}

	return(authreq);
}

/*************************************************************************
 *
 *	Function: authenticateAcctReq
 *
 *	Purpose: Authenticate an accounting request packet 
 *
 *************************************************************************/

AUTH_REQ *
authenticateAcctReq(authreq, auth)
AUTH_REQ	*authreq;
AUTH_HDR	*auth;
{
	u_char	*buffer;
	int	totallen;
	u_char	saveDigest[AUTH_VECTOR_LEN];
	u_char	digest[AUTH_VECTOR_LEN];
	int	secretlen;
	u_char	secret[256];
	char	tmpbuf[256];
	UINT4	ipaddr;
	u_char	error = FALSE;
	int	stat;

	totallen = ntohs(auth->length);
	stat = get_client_info( authreq->ipaddr, &ipaddr, secret, tmpbuf );
	secretlen = strlen( (CONST char *)secret );
	if( stat != 0 ) {
		sprintf(tmpbuf, "authenticateAcctReq: from %s, ID %d : %s\n",
			ip_hostname(authreq->ipaddr),
			authreq->id, get_errmsg(stat));
		log_err(tmpbuf);
		memset(secret, 0, sizeof(secret));
		error = TRUE;
	}
	else if( totallen + secretlen > MAX_RCVBUF_SIZE ) {
		sprintf( tmpbuf,
			"rcv req from %s plus secret : too long: s - ID: %d\n",
				ip_hostname(authreq->ipaddr), authreq->id);
		log_err( tmpbuf );
		error = TRUE;
	}
	else {
		strcpy((char *)authreq->secret, (CONST char *)secret);
		memcpy( saveDigest, auth->vector, AUTH_VECTOR_LEN );
		memset( auth->vector, 0, AUTH_VECTOR_LEN );
		buffer = (u_char *)auth;
		memcpy( &buffer[totallen], secret, secretlen );
		md5_calc( digest, (u_char *)auth, totallen + secretlen );
		if ( memcmp( digest, saveDigest, AUTH_VECTOR_LEN) ) {
			sprintf( tmpbuf,
				"Bad authenticator: from %s - ID: %d\n",
				ip_hostname(authreq->ipaddr), authreq->id);
			log_err( tmpbuf );
			error = TRUE;
		}
	}

	if (error) {
		pairfree( authreq->request );
		memset( authreq, 0, sizeof(*authreq) );
		free( authreq );
		authreq = 0;
	}
	return authreq;
}

/*************************************************************************
 *
 *	Function: radrespond
 *
 *	Purpose: Respond to supported requests:
 *
 *		 PW_AUTHENTICATION_REQUEST - Authentication request from
 *				a client network access server.
 *
 *		 PW_ACCOUNTING_REQUEST - Accounting request from
 *				a client network access server.
 *
 *		 PW_PASSWORD_REQUEST - User request to change a password.
 *
 *************************************************************************/

int
radrespond(authreq, activefd, isAcctPkt)
AUTH_REQ	*authreq;
int		activefd;
char		isAcctPkt;
{
	switch(authreq->code) {

	case PW_AUTHENTICATION_REQUEST:
		if(spawn_flag) {
			rad_spawn_child(authreq, activefd);
		}
		else {
			rad_authenticate(authreq, activefd);
		}
		break;
	
	case PW_ACCOUNTING_REQUEST:
		if( isAcctPkt ) {
			rad_accounting(authreq, activefd);
		}
#if defined( ASCEND_LOGOUT )
		else {
                	send_accounting_response(authreq, activefd,
				"added by Ascend");
		}
#endif /* ASCEND_LOGOUT */
		break;

	case PW_PASSWORD_REQUEST:
		rad_passchange(authreq, activefd);
		break;

	default:
		break;
	}
	return(0);
}



/*************************************************************************
 *
 *	Function: rad_spawn_child
 *
 *	Purpose: Spawns child processes to perform password authentication
 *		 and respond to RADIUS clients.  This functions also
 *		 cleans up complete child requests, and verifies that there
 *		 is only one process responding to each request (duplicate
 *		 requests are filtered out.
 *
 *************************************************************************/

int
rad_spawn_child(authreq, activefd)
AUTH_REQ	*authreq;
int		activefd;
{
	AUTH_REQ	*curreq;
	AUTH_REQ	*prevreq;
	UINT4		curtime;
	int		request_count;
	char		msg[128];
	int		child_pid;

	curtime = (UINT4)time(0);
	request_count = 0;
	curreq = first_request;
	prevreq = (AUTH_REQ *)NULL;
	while(curreq != (AUTH_REQ *)NULL) {
		if(curreq->child_pid == -1 &&
				curreq->timestamp + CLEANUP_DELAY <= curtime) {
			/* Request completed, delete it */
			if(prevreq == (AUTH_REQ *)NULL) {
				first_request = curreq->next;
				pairfree(curreq->request);
				free(curreq);
				curreq = first_request;
			}
			else {
				prevreq->next = curreq->next;
				pairfree(curreq->request);
				free(curreq);
				curreq = prevreq->next;
			}
		}
		else if(curreq->ipaddr == authreq->ipaddr &&
					curreq->id == authreq->id) {
			/* This is a duplicate request - just drop it */
			sprintf(msg, "Dropping duplicate: from %s - ID: %d\n",
				ip_hostname(authreq->ipaddr), authreq->id);
			log_err(msg);
			pairfree(authreq->request);
			free(authreq);
			return(0);
		}
		else {
			if(curreq->timestamp + MAX_REQUEST_TIME <= curtime &&
						curreq->child_pid != -1) {
				/* This request seems to have hung - kill it */
				child_pid = curreq->child_pid;
				sprintf(msg,
					"Killing unresponsive child pid %d\n",
								child_pid);
				log_err(msg);
				curreq->child_pid = -1;
				kill(child_pid, SIGHUP);
			}
			prevreq = curreq;
			curreq = curreq->next;
			request_count++;
		}
	}

	/* This is a new request */
	if(request_count > MAX_REQUESTS) {
		sprintf(msg, "Dropping request (too many): from %s - ID: %d\n",
				ip_hostname(authreq->ipaddr), authreq->id);
		log_err(msg);
		pairfree(authreq->request);
		free(authreq);
		return(0);
	}

	/* Add this request to the list */
	authreq->next = (AUTH_REQ *)NULL;
	authreq->child_pid = -1;
	authreq->timestamp = curtime;

	if(prevreq == (AUTH_REQ *)NULL) {
		first_request = authreq;
	}
	else {
		prevreq->next = authreq;
	}

	/* fork our child */
	child_pid = fork();
	if(child_pid < 0) {
		sprintf(msg, "Fork failed for request from %s - ID: %d\n",
				ip_hostname(authreq->ipaddr), authreq->id);
		log_err(msg);
	}
	if(child_pid == 0) {
		/* This is the child, it should go ahead and respond */
		close(rdCachefd);
		rad_authenticate(authreq, activefd);
		exit(0);
	}

	/* Register the Child */
	authreq->child_pid = child_pid;
	return(0);
}

void
sig_cleanup(sig)
int	sig;
{
	int		status;
        pid_t		pid;
	AUTH_REQ	*curreq;
 
	(void)sig;	/* ignore param */

        for (;;) {
		pid = waitpid((pid_t)-1,&status,WNOHANG);
		signal(SIGCHLD, sig_cleanup);
                if (pid <= 0)
                        return;

#if defined (aix)
		kill(pid, SIGKILL);
#endif

		if(pid == acct_pid) {
			sig_fatal(100);
		}
		curreq = first_request;
		while(curreq != (AUTH_REQ *)NULL) {
			if(curreq->child_pid == pid) {
				curreq->child_pid = -1;
				curreq->timestamp = (UINT4)time(0);
				break;
			}
			curreq = curreq->next;
		}
        }
}

#if defined( ASCEND_LOGOUT )
/*************************************************************************
 *
 *      Function: send_accounting_response
 *
 *      Purpose: Reply to the request with an ACKNOWLEDGE.  Also attach
 *               any user message provided.
 *
 *************************************************************************/
 
void
send_accounting_response(authreq, activefd, msg)
AUTH_REQ	*authreq;
int		activefd;
char		*msg;
{
        AUTH_HDR                *auth;
        u_short                 total_length;
        struct  sockaddr_in     saremote;
        struct  sockaddr_in     *sin;
        u_char                  *ptr;
        int                     len;
        u_char                  digest[AUTH_VECTOR_LEN];
        int                     secretlen;

        auth = (AUTH_HDR *)send_buffer;

        /* Build standard header */
        auth->code = PW_ACCOUNTING_RESPONSE;
        auth->id = authreq->id;
        memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);

        DEBUG("Accounting Ack of id %d to %lx -port- %x (%s)\r\n",
              authreq->id, authreq->ipaddr, authreq->udp_port,
              ip_hostname(authreq->ipaddr));

        total_length = AUTH_HDR_LEN;
        ptr = auth->data;

        /* Append the user message */
        if(msg != (char *)NULL) {
                len = strlen(msg);
                if(len > 0 && len < 250) {
                        *ptr++ = PW_PORT_MESSAGE;
                        *ptr++ = len + 2;
                        memcpy(ptr, msg, len);
                        ptr += len;
                        total_length += len + 2;
                }
        }

        auth->length = htons(total_length);

        /* Calcuate the response digest */
        secretlen = strlen((char *)authreq->secret);
        memcpy(send_buffer + total_length, authreq->secret, secretlen);
        md5_calc(digest, (u_char *)auth, total_length + secretlen);
        memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
        memset(send_buffer + total_length, 0, secretlen);

        sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, 0, sizeof (saremote));
        sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

        /* Send it to the user */
        sendto(activefd, (char *)auth, (int)total_length, (int)0,
                        (struct sockaddr *)sin, sizeof(struct sockaddr_in));
}
#endif /* ASCEND_LOGOUT */

/*************************************************************************
 *
 *	Function: getUnixPwdType
 *
 *	Purpose: Determine if the password indicates UNIX authentication
 *
 *************************************************************************/

PasswordType
getUnixPwdType(pwd)
char	*pwd;
{
	if( strcmp(pwd, UNIX_PWD) == 0) {
		return PWD_SPECIAL;
	}

	return PWD_NORMAL;
}

/*************************************************************************
 *
 *	Function: getTokenPwdType
 *
 *	Purpose: Determine if the password indicates token authentication
 *
 *************************************************************************/

PasswordType
getTokenPwdType(pwd)
char	*pwd;
{
#if defined(SAFEWORD)
	if( strcmp(pwd, EL_PWD) == 0) {
		return PWD_SPECIAL;
	}
#endif
#if defined(ACE)
	if( strcmp(pwd, SD_PWD) == 0) {
		return PWD_SPECIAL;
	}
#endif
	return PWD_NORMAL;
}

/*************************************************************************
 *
 *	Function: getPasswordType
 *
 *	Purpose: Determine if the password is 'special'
 *
 *************************************************************************/

PasswordType
getPasswordType(pwd)
char	*pwd;
{
	if( getUnixPwdType(pwd) == PWD_SPECIAL ) {
		return PWD_SPECIAL;
	}
	else {
		return getTokenPwdType(pwd);
	}
}

#if( 0 )
/*************************************************************************
 *
 *	Function: get_attribute
 *
 *	Purpose: Given an attribute list, an attribute identifier, and
 *		an indirect pointer to an attribute struct, return a
 *		pointer to the original list; the attribute
 *		struct pointer will be set to the matching attribute
 *		struct, if any.
 *
 *************************************************************************/

VALUE_PAIR *
get_attribute(list, attributeId, attribute)
VALUE_PAIR	*list;
int		attributeId;
VALUE_PAIR	**attribute;
{
	VALUE_PAIR *cur = list;

	*attribute = (VALUE_PAIR *)0;

	while( cur ) {
		if( cur->attribute == attributeId ) {
			*attribute = cur;
			break;
		}
		else {
			cur = cur->next;
		}
	}
	return list;
}
#endif

/*************************************************************************
 *
 *	Function: cut_attribute
 *
 *	Purpose: Given an attribute list, an attribute identifier, and
 *		an indirect pointer to an attribute struct, return a list
 *		of all the attributes in the original list EXCEPT the first
 *		occurence of one matching the attribute id; the attribute
 *		struct pointer will be set to the matching attribute
 *		struct, if any.
 *
 *************************************************************************/

VALUE_PAIR *
cut_attribute(list, attributeId, attribute)
VALUE_PAIR	*list;
int		attributeId;
VALUE_PAIR	**attribute;
{
	VALUE_PAIR *head = (VALUE_PAIR *)0;

	*attribute = (VALUE_PAIR *)0;
	if( !list ) {
		head = list;
	}
	else if( list->attribute == attributeId ) {
		*attribute = list;
		head = list->next;
	}
	else {
		VALUE_PAIR *cur;

		cur = head = list;
		list = list->next;
		while( list != (VALUE_PAIR *)NULL ) {
			if( list->attribute == attributeId ) {
				*attribute = list;
				cur->next = list->next;
				(*attribute)->next = (VALUE_PAIR *)0;
				break;
			}
			else {
				cur->next = list;
				cur = cur->next;
				list = list->next;
			}
		}
	}
	return head;
}

/*************************************************************************
 *
 *	Function: get_attr_lvalue
 *
 *	Purpose: Given an attribute list and an attribute identifier
 *		return the the lvalue of the specified attribute
 *		or zero if the attribute is not in the list.
 *
 *		In other languages we could return the *value*, whether
 *		that was the lvalue or the strvalue, etc. but C mandates
 *		a single function return type.  There are of course ways
 *		around this but they are overkill for this function, and
 *		kind of ugly, as well.
 *
 *************************************************************************/

UINT4
get_attr_lvalue(list, attribute)
VALUE_PAIR	*list;
int		attribute;
{
	UINT4		lval;

	while(list != (VALUE_PAIR *)NULL) {
		if(list->attribute == attribute) {
			break;
		}
		list = list->next;
	}

	if(list == (VALUE_PAIR *)NULL) {
		lval = 0;
	}
	else {
		lval = list->lvalue;
	}

        DEBUG("get_attr_lvalue(%d) == %ld\r\n", attribute, lval);
	return lval;
}

/*************************************************************************
 *
 *	Function: get_attr_strvalue
 *
 *	Purpose: Given an attribute list and an attribute identifier
 *		return the the strvalue of the specified attribute
 *		or zero if the attribute is not in the list.
 *
 *************************************************************************/

char *
get_attr_strvalue(list, attribute)
VALUE_PAIR	*list;
int		attribute;
{
	char	*strval;

	while(list != (VALUE_PAIR *)NULL) {
		if(list->attribute == attribute) {
			break;
		}
		list = list->next;
	}

	if(list == (VALUE_PAIR *)NULL) {
		strval = (char *)0;
	}
	else {
		strval = list->strvalue;
	}

        DEBUG("get_attr_strvalue(%d) == %s\r\n", attribute, strval);
	return strval;
}

/*************************************************************************
 *
 *	Function: rad_passchange
 *
 *	Purpose: Change a users password
 *
 *************************************************************************/

void
rad_passchange(authreq, activefd)
AUTH_REQ	*authreq;
int		activefd;
{
	VALUE_PAIR	*namepair;
	VALUE_PAIR	*check_item;
	VALUE_PAIR	*newpasspair = (VALUE_PAIR *)NULL;
	VALUE_PAIR	*oldpasspair = (VALUE_PAIR *)NULL;
	VALUE_PAIR	*curpass;
	VALUE_PAIR	*user_check;
	VALUE_PAIR	*user_reply;
	char		pw_digest[AUTH_VECTOR_LEN];
	char		string[512];
	char		passbuf[AUTH_PASS_LEN];
	int		i;
	int		secretlen;
	char		msg[128];
	int		vecLen;
	int		retval;
	UINT4		user_expiration_days;
	UINT4		user_expiration_seconds;

	if( allow_passchange == 0 ) {
		sprintf(msg,
			"Passchange: from %s: Password Changing NOT Allowed\n",
				ip_hostname(authreq->ipaddr));
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/* Get the username */
	namepair = authreq->request;
	while(namepair != (VALUE_PAIR *)NULL) {
		if(namepair->attribute == PW_USER_NAME) {
			break;
		}
		namepair = namepair->next;
	}
	if(namepair == (VALUE_PAIR *)NULL) {
		sprintf(msg,
			"Passchange: from %s - No User name supplied\n",
			ip_hostname(authreq->ipaddr));
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/*
	 * Look the user up in the database
	 */
	if( (retval = user_find(namepair->strvalue, &user_check, &user_reply))
	  != 0) {
		DEBUG("Passchange: user_find(%s) error = %s\r\n",
			namepair->strvalue, get_errmsg(retval));
		sprintf(msg, "Passchange: from %s - %s: %s\n",
			ip_hostname(authreq->ipaddr), get_errmsg(retval),
			namepair->strvalue);
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/*
	 * Validate the user -
	 *
	 * We have to unwrap this in a special way to decrypt the
	 * old and new passwords.  The MD5 calculation is based
	 * on the old password.  The vector is different.  The old
	 * password is encrypted using the encrypted new password
	 * as its vector.  The new password is encrypted using the
	 * random encryption vector in the request header.
	 */

	/* Extract the attr-value pairs for the old and new passwords */
	check_item = authreq->request;
	while(check_item != (VALUE_PAIR *)NULL) {
		if(check_item->attribute == PW_PASSWORD) {
			newpasspair = check_item;
		}
		else if(check_item->attribute == PW_OLD_PASSWORD) {
			oldpasspair = check_item;
		}
		check_item = check_item->next;
	}

	/* Verify that both encrypted passwords were supplied */
	if(newpasspair == (VALUE_PAIR *)NULL ||
	   oldpasspair == (VALUE_PAIR *)NULL)
	{
		/* Missing one of the passwords */
		sprintf(msg,
			"Passchange: from %s - Missing Password: %s\n",
			ip_hostname(authreq->ipaddr), namepair->strvalue);
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		pairfree(user_check);
		pairfree(user_reply);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/* Get the current password from the database */
	curpass = user_check;
	while(curpass != (VALUE_PAIR *)NULL) {
		if(curpass->attribute == PW_PASSWORD) {
			break;
		}
		curpass = curpass->next;
	}
	if((curpass == (VALUE_PAIR *)NULL) || curpass->strvalue == (char *)NULL) {
		/* Missing our local copy of the password */
		sprintf(msg,
			"Passchange: from %s - Missing Local Password: %s\n",
			ip_hostname(authreq->ipaddr), namepair->strvalue);
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		pairfree(user_check);
		pairfree(user_reply);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}
	if( getPasswordType(curpass->strvalue) != PWD_NORMAL ) {
		/* Can't change passwords that aren't in users file */
		sprintf(msg,
			"Passchange: from %s - system password change not allowed: %s\n",
				ip_hostname(authreq->ipaddr),
				namepair->strvalue);
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		pairfree(user_check);
		pairfree(user_reply);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/* Decrypt the old password */
	secretlen = strlen(curpass->strvalue);
	vecLen = newpasspair->size;
#if( 0 )
	DEBUG("strlen(newpasspair->strvalue): %d, newpasspair->size: %d\r\n",
		strlen(newpasspair->strvalue), newpasspair->size);
#endif
	memcpy(string, curpass->strvalue, secretlen);
	memcpy(string + secretlen, newpasspair->strvalue, vecLen);
	md5_calc((u_char *)pw_digest, (u_char *)string, vecLen + secretlen);

	memcpy(passbuf, oldpasspair->strvalue, oldpasspair->size);
	for(i = 0;i < oldpasspair->size;i++) {
		passbuf[i] ^= pw_digest[i % vecLen];
	}

	/* Did they supply the correct password ??? */
	if(memcmp(passbuf, curpass->strvalue, curpass->size) != 0) {
		sprintf(msg,
			"Passchange: from %s - Bad Pwd for %s:\n",
				ip_hostname(authreq->ipaddr),
				namepair->strvalue);
		log_err(msg);

#if defined( PWD_DEBUG )
		sprintf(msg,
			"\tCont'd: Exp Pwd(%d, %.*s); User Exp Pwd(%d, %.*s)\n",
				curpass->size, curpass->size, curpass->strvalue,
				newpasspair->size, newpasspair->size, passbuf);
		log_err(msg);
#endif
		send_reject(authreq, (char *)NULL, activefd);
		pairfree(authreq->request);
		pairfree(user_check);
		pairfree(user_reply);
		memset(authreq, 0, sizeof(AUTH_REQ));
		free(authreq);
		return;
	}

	/* Decrypt the new password */
	memcpy(string, curpass->strvalue, secretlen);
	memcpy(string + secretlen, authreq->vector, AUTH_VECTOR_LEN);
	md5_calc((u_char *)pw_digest, (u_char *)string, AUTH_VECTOR_LEN + secretlen);
	memcpy(passbuf, newpasspair->strvalue, newpasspair->size);
	for( i = 0; i < newpasspair->size; i++ ) {
		passbuf[i] ^= pw_digest[i % AUTH_VECTOR_LEN];
	}

	/* Update the users password */
	/* Note: It is impossible for newpasspair->size
	 *	to be larger than sizeof(curpass->strvalue), so
	 *	this strncpy is safe
	 *
	 *	Also, we *always* have room for the terminating NUL
	 */
	strncpy(curpass->strvalue, passbuf, newpasspair->size);
	curpass->strvalue[newpasspair->size] = 0;

	/* Look for an existing expiration duration entry */
	user_expiration_days = get_attr_lvalue(user_reply, ASCEND_PW_LIFETIME);
	user_expiration_seconds = user_expiration_days * (UINT4)SECONDS_PER_DAY;

	/* Add a new expiration date if we are aging passwords */
	if( user_expiration_seconds != (UINT4)0 ) {
		set_expiration(user_check, user_expiration_seconds);
	}
	else if( expiration_seconds != (UINT4)0 ) {
		set_expiration(user_check, expiration_seconds);
	}

	/* Update the database */
	if(user_update(namepair->strvalue, user_check, user_reply) != 0) {
		send_reject(authreq, (char *)NULL, activefd);
		sprintf(msg,
			"Passchange: unable to update password for %s\n",
			namepair->strvalue);
		log_err(msg);
	}
	else {
		send_pwack(authreq, activefd);
	}
	pairfree(authreq->request);
	pairfree(user_check);
	pairfree(user_reply);
	memset(authreq, 0, sizeof(AUTH_REQ));
	free(authreq);
	return;
}

/*************************************************************************
 *
 *	Function: authCleanup
 *
 *	Purpose: Standard cleanup code when auth is finished
 *
 *************************************************************************/

void
authCleanup(authInfo)
AuthInfo	*authInfo;
{
	pairfree(authInfo->cutList);
	authInfo->cutList = (VALUE_PAIR *)0;
	pairfree(authInfo->authreq->request);
	memset(authInfo->authreq, 0, sizeof(AUTH_REQ));
	free(authInfo->authreq);
	authInfo->authreq = (AUTH_REQ *)0;
}

#if defined(SAFEWORD)
/*************************************************************************
 *
 *	Function: authSafewordPwd
 *
 *	Purpose: Setup for and invoke a SafeWord authentication request
 *
 *************************************************************************/
int
authSafewordPwd(authInfo)
AuthInfo	*authInfo;
{
	int do_safeword_pass P__((AuthInfo *authInfo, struct pblk *pb));
	struct pblk *pb;
	char msg[128];

	DEBUG("authSafewordPwd\r\n");
    	authInfo->user_msg = (char *)NULL;
	pb = (struct pblk *)malloc(sizeof(*pb));
	if( !pb ) {
		sprintf(msg, "authSafewordPwd: malloc err for user %s\n",
			authInfo->authName->strvalue);
		DEBUG("%s\r\n", msg);
		log_err(msg);
		authInfo->user_msg = (char *)NULL;
		return -1;
	}

	return do_safeword_pass(authInfo, pb);
}

/*************************************************************************
 *
 *	Function: do_safeword_pass
 *
 *	Purpose: Process and reply to a SafeWord authentication request
 *
 *************************************************************************/

int
do_safeword_pass(authInfo, pb)
AuthInfo	*authInfo;
struct pblk	*pb;
{
	char stateInfo[253];
	char *si;
	char msg[128];
	int result;

	result = safeword_pass(authInfo, pb);
	switch( result ) {
	default:
		sprintf(msg,
			"safeword_pass BIZARRE result %d, user %s, NAS %s\n",
			result, authInfo->authName->strvalue,
			ip_hostname(authInfo->authreq->ipaddr));
		DEBUG("%s\r\n", msg);
		log_err(msg);
		authInfo->user_msg = (char *)NULL;
		result = SAFEWORD_FAILED;
		break;
				
	case SAFEWORD_FAILED:
		sprintf(msg, "safeword_pass FAILED, user %s, NAS %s\n",
			authInfo->authName->strvalue,
			ip_hostname(authInfo->authreq->ipaddr));
		DEBUG("%s\r\n", msg);
		log_err(msg);
		authInfo->user_msg = (char *)NULL;
		result = SAFEWORD_FAILED;
		break;
				
	case SAFEWORD_CHALLENGING:
		DEBUG("safeword_pass CHALLENGING\r\n");
		if( authInfo->chkImmediate ) {
				/* short-circuit challenge */
			si = authInfo->authState->strvalue;
    			memcpy(si, BEG_PB_1ST(pb), LEN_PB_1ST(pb));
			si += LEN_PB_1ST(pb);
    			memcpy(si, BEG_PB_2ND(pb), LEN_PB_2ND(pb));
			authInfo->authState->size = LEN_PB_TOTAL(pb);
				/* once only, please! */
			authInfo->chkImmediate = (VALUE_PAIR *)0;
				/* NB: pb is zeroed and freed by this
					tail-recursive call) */
			return do_safeword_pass(authInfo, pb);
		}
		else {
			si = stateInfo;
    			memcpy(si, BEG_PB_1ST(pb), LEN_PB_1ST(pb));
			si += LEN_PB_1ST(pb);
    			memcpy(si, BEG_PB_2ND(pb), LEN_PB_2ND(pb));
			send_challenge(	authInfo->authreq,
					pb->chal, strlen(pb->chal),
					stateInfo, LEN_PB_TOTAL(pb),
					authInfo->fd);
			result = -SAFEWORD_CHALLENGING;
		}
		break;

	case SAFEWORD_PASSED:
		DEBUG("safeword_pass PASSED\r\n");
		result = 0;
		break;
	}
	memset(pb, 0, sizeof(*pb));
	free(pb);
	return result;
}
#endif

#if defined(ACE)
/*************************************************************************
 *
 *	Function: authAcePwd
 *
 *	Purpose: Process and reply to an ACE authentication request
 *
 *************************************************************************/

int
authAcePwd(authInfo)
AuthInfo	*authInfo;
{
	char stateInfo[253];
	char *si;
	struct SD_CLIENT *sd = (struct SD_CLIENT *)malloc(sizeof(*sd));
	char *dummyState = ACE_DUMMY_STATE;
	int stateLen = strlen(dummyState);
	char *nextMsg = "(without PIN)";
	char *nextCaution = "-- See your System Administrator!";
	char *ncMsg;
	char msg[128];
	int result;

	DEBUG("authAcePwd\r\n");
    	authInfo->user_msg=(char *)NULL;
	result = ace_pass(authInfo, sd);
	switch( result ) {
	default:
		sprintf(msg, "ace_pass BIZARRE result %d for user %s, NAS %s\n",
			result, authInfo->authName->strvalue,
			ip_hostname(authInfo->authreq->ipaddr));
		DEBUG("%s\r\n", msg);
		log_err(msg);
		authInfo->user_msg = (char *)NULL;
		result = ACE_FAILED;
		break;
			
	case ACE_FAILED:
		sprintf(msg, "ace_pass FAILED for user %s, NAS %s\n",
			authInfo->authName->strvalue,
			ip_hostname(authInfo->authreq->ipaddr));
		DEBUG("%s\r\n", msg);
		log_err(msg);
		authInfo->user_msg = (char *)NULL;
		result = ACE_FAILED;
		break;
			
	case ACE_CHALLENGING:
		DEBUG("ace_pass CHALLENGING\r\n");
		strcpy(stateInfo, dummyState);
		send_challenge( authInfo->authreq, "", 0,
				stateInfo, stateLen, authInfo->fd);
		result = -ACE_CHALLENGING;
		break;

	case ACE_PASSED:
		DEBUG("ace_pass PASSED\r\n");
		result = 0;
		break;

	case ACE_NEXT_PASSCODE:
		DEBUG("ace_pass NEXT_CODE\r\n");
		if( spawn_flag != 0 ) {
			sprintf(msg, "ACE Next_Code processing REQUIRES \'radiusd -s\'\r\n");
			DEBUG("%s", msg);
			log_err(msg);
#if(0)
			authInfo->user_msg = (char *)NULL;
			result = -ACE_NEXT_PASSCODE;
			break;
#else
			/* ok, let the user try - it cannot work, but at least
				s/he'll understand a bit better why not */
			ncMsg = nextCaution;
#endif
		}
		else {
			ncMsg = nextMsg;
		}

		si = stateInfo;

		*si = (char)(LEN_SD_TOTAL(sd) + 1);
		si++;
    		memcpy(si, BEG_SD_1ST(sd), LEN_SD_1ST(sd));
		si += LEN_SD_1ST(sd);
    		memcpy(si, BEG_SD_2ND(sd), LEN_SD_2ND(sd));
		si += LEN_SD_2ND(sd);
    		memcpy(si, BEG_SD_3RD(sd), LEN_SD_3RD(sd));
		send_nextcode(	authInfo->authreq, ncMsg, strlen(ncMsg),
				stateInfo, LEN_SD_TOTAL(sd), authInfo->fd);
		result = -ACE_NEXT_PASSCODE;
		break;

	case ACE_NEW_PIN:
		DEBUG("ace_pass NEXT_PIN\r\n");
		send_newpin(
			authInfo->authreq,
			stateInfo, strlen(stateInfo),
			authInfo->fd);
		result = -ACE_NEW_PIN;
		break;
	}

	memset(sd, 0, sizeof(*sd));
	free(sd);
	return result;
}
#endif

/*************************************************************************
 *
 *	Function: authChapToken
 *
 *	Purpose: Process and reply to a CHAP token authentication request
 *
 *************************************************************************/

int
authChapToken(authInfo)
AuthInfo	*authInfo;
{
	char	msg[128];
	char	string[AUTH_PASS_LEN + 64];
	char	*ptr;
	u_char	size;
	int	result;
	u_char	*pw_digest = (u_char *)authInfo->pw_digest;

	DEBUG("authChapToken\r\n");

	size = authInfo->authPwd->size;
	if( size != AUTH_VECTOR_LEN + 1 ) {
		sprintf(msg,
			"CHAP Token - Bad Pwd Size(%d): user %s, NAS %s\n",
				size, authInfo->authName->strvalue,
				ip_hostname(authInfo->ipaddr));
		log_err(msg);
		DEBUG("%s", msg);
		return -1;
	}
	ptr = string;
	if( authInfo->authState && authInfo->chkSecret ) {
#if defined( PWD_DEBUG )
		DEBUG("chkSecret(%s)\r\n", authInfo->chkSecret->strvalue);
#endif
		size = authInfo->chkSecret->size - 1;	/* skip final NUL */
		*ptr++ = authInfo->authPwd->strvalue[0];	/* pkt_id */
		memcpy(ptr, authInfo->chkSecret->strvalue, size);
		ptr += size;
		memcpy(ptr, authInfo->vector, AUTH_VECTOR_LEN);
		md5_calc(pw_digest, (u_char *)string,
			1 + AUTH_VECTOR_LEN + size);
		/* shift down to remove the pkt_id */
#if defined(OSUN)
		/* sigh... just use the unsafe memcpy ... */
		memcpy(authInfo->authPwd->strvalue,
			&(authInfo->authPwd->strvalue[1]), CHAP_VALUE_LENGTH);
#else
		memmove(authInfo->authPwd->strvalue,
			&(authInfo->authPwd->strvalue[1]), CHAP_VALUE_LENGTH);
#endif
		authInfo->authPwd->size -= 1;
	}
	else {
		/* authState indicates requestor understands token
		   authentication, and chkSecret is needed to decode
		   the password.  If this request is missing either
		   attribute then it must be rejected.
		*/
		return -1;
	}

	result = -1;
	if( authInfo->tokExp && (allow_token_caching != 0) ) {
		char *val;	/* the temporary shared password */
		val = (char *)cache_search(cache, authInfo->authName->strvalue,
					authInfo->authName->size);
		if( val && val != ELEM_DELETED ) {
			int	ix;
			int	len;
			int	stat;
			char	*user;
			char	pwd[CHAP_VALUE_LENGTH];

#if defined( PWD_DEBUG )
			DEBUG("Cache val for %s: <%s>\r\n",
				authInfo->authName->strvalue, val);
#endif
			memcpy(pwd, authInfo->authPwd->strvalue,
				CHAP_VALUE_LENGTH);
			/* decrypt the given password */
			for( ix = 0; ix < CHAP_VALUE_LENGTH; ix++ ) {
				pwd[ix] ^= authInfo->pw_digest[ix];
			}
			/* see if we got the <password><.><username> format */
			user = strchr(pwd, '.');
			if( user ) {
				*user = '\0';
				user++;
			}

			len = MAX((int)strlen(val)+1, (int)strlen(pwd)+1);
			len = MIN(len, CHAP_VALUE_LENGTH);
#if defined( PWD_DEBUG )
			DEBUG("pwd(%d): %s\r\n", len, pwd);
#endif
			stat = memcmp(pwd, val, len);
			if( stat == 0 ) {
				authInfo->cacheAuth = TRUE;
				return stat;	/* authenticated */
			}
		}
		else if( val == ELEM_DELETED ) {
			DEBUG("Cache elem for %s was deleted\r\n",
				authInfo->authName->strvalue);
			if( spawn_flag == 0 ) {
#if(1)
				;	/* cache_search() already deleted it */
#else
				int stat = cache_delete(cache,
					authInfo->authName->strvalue,
					authInfo->authName->size);
				if( stat != TRUE ) {
					char	buf[128];

					sprintf(buf, "Err: deleting <%s>\n",
						authInfo->authName->strvalue);
					log_err(buf);
					DEBUG("%s", buf);
				}
#endif
			}
			else {	/* child copy deleted,
				   now ask parent to delete */
				CacheMsg	msg;
				int		n = 0;

				msg.type = CACHE_DELETE;
				memcpy(msg.key,
					authInfo->authName->strvalue,
					authInfo->authName->size);
				while( n != sizeof(msg) ) {
					n += write(wrCachefd, ((char *)&msg)+n,
						sizeof(msg)-n);
				}
			}
		}
		else {
			DEBUG("Cache elem for %s was not found\r\n",
				authInfo->authName->strvalue);
		}

		/* token cache missed */
		result = -1;	/* assume failure */
		if( 0 ) {
			;	/* because no particular token is guaranteed
					to be supported */
		}
#if defined(SAFEWORD)
		/* check for SafeWord Authentication */
       		else if( strcmp(authInfo->chkPwd->strvalue, EL_PWD) == 0) {
			result = authSafewordPwd(authInfo);
		}
#endif
#if defined(ACE)
		/* check for Security Dynamics Authentication */
		else if( strcmp(authInfo->chkPwd->strvalue, SD_PWD) == 0) {
			result = authAcePwd(authInfo);
		}
#endif
	}
	else {
		/* TOKEN passwords without expiry not supported with CHAP */
		sprintf(msg, "CHAP Token Attempt: user %s, NAS %s\n",
				authInfo->authName->strvalue,
				ip_hostname(authInfo->ipaddr));
		log_err(msg);
		DEBUG("%s", msg);
		result = -1;
	}
	return result;
}

/*************************************************************************
 *
 *	Function: authChapPwd
 *
 *	Purpose: Process and reply to a CHAP authentication request
 *
 *************************************************************************/

int
authChapPwd(authInfo)
AuthInfo	*authInfo;
{
	char	msg[128];
	char	string[AUTH_PASS_LEN + 64];
	char	*ptr;
	int	result = -1;	/* init to failure */

	DEBUG("authChapPwd\r\n");
	if( getTokenPwdType(authInfo->chkPwd->strvalue) == PWD_SPECIAL ) {
		result = authChapToken(authInfo);
		if( (allow_token_caching != 0) && (result == 0) ) {
			int	stat;
			int	ix;
			int	len;
			char	clrPwd[CHAP_VALUE_LENGTH];
			char	*user = (char *)NULL;
			time_t	idle;

			if( authInfo->chkIdle ) {
				idle = authInfo->chkIdle->lvalue;
			}
			else {
				idle = 0;
			}

			memcpy(clrPwd, authInfo->authPwd->strvalue,
				CHAP_VALUE_LENGTH);
			for( ix = 0; ix < CHAP_VALUE_LENGTH; ix++ ) {
				clrPwd[ix] ^= authInfo->pw_digest[ix];
			}

			/* see if we got the <password><.><username> format */
			user = strchr(clrPwd, '.');
			if( user ) {
				*user = '\0';	/* skip username */
			}
			len = MIN((int)strlen(clrPwd)+1, CHAP_VALUE_LENGTH);

			if( authInfo->cacheAuth == TRUE ) {
				/* no cache insert on cache hits */
				if( !idle ) {
					/* no cache update if no idle time */
					return result;
				}
				else if( spawn_flag == 0 ) {
					stat = cache_idle_update(cache,
						authInfo->authName->strvalue,
						authInfo->authName->size,
						idle);
					if( stat != TRUE ) {
						log_err("authChapPwd: update failed\n");
					}
				}
				else {
					int		n = 0;
					CacheMsg	msg;

					msg.type = CACHE_IDLE_UPDATE;
					memcpy(msg.key,
						authInfo->authName->strvalue,
						authInfo->authName->size);
#if(0)
					memcpy(msg.val, clrPwd, len);
#endif
					msg.time = 0;
					msg.idle = idle;
					while( n != sizeof(msg) ) {
						n += write(wrCachefd,
							((char *)&msg)+n,
							sizeof(msg)-n);
					}
				}
				return result;
			}

#if defined(PWD_DEBUG)
			DEBUG("clrPwd(%d): %s\r\n", len, clrPwd);
#endif
			if( spawn_flag == 0 ) {
				stat = cache_insert(cache,
					authInfo->authName->strvalue,
					authInfo->authName->size,
					clrPwd, len,
					authInfo->tokExpSecs, idle);
				if( stat != TRUE ) {
					log_err("authChapPwd: insert failed\n");
				}
			}
			else {
				int		n = 0;
				CacheMsg	msg;

				msg.type = CACHE_INSERT;
				memcpy(msg.key,
					authInfo->authName->strvalue,
					authInfo->authName->size);
				memcpy(msg.val, clrPwd, len);
				msg.time = authInfo->tokExpSecs;
				msg.idle = idle;
				while( n != sizeof(msg) ) {
					n += write(wrCachefd, ((char *)&msg)+n,
						sizeof(msg)-n);
				}
			}
		}
	}
	else if( getUnixPwdType(authInfo->chkPwd->strvalue) == PWD_SPECIAL ) {
		/* UNIX passwords not supported with CHAP */
		sprintf(msg, "CHAP Unix Attempt: user %s, NAS %s\n",
				authInfo->authName->strvalue,
				ip_hostname(authInfo->ipaddr));
		log_err(msg);
		DEBUG("%s", msg);
		result = -1;
	}
	else {	/* normal 'static' password */
		int size;

		/* Use normal MD5 to verify */
		ptr = string;
		size = authInfo->chkPwd->size - 1;	/* skip final NUL */
		*ptr++ = authInfo->authPwd->strvalue[0];	/* pkt_id */
		memcpy(ptr, authInfo->chkPwd->strvalue, size);
		ptr += size;
		memcpy(ptr, authInfo->vector, AUTH_VECTOR_LEN);
		md5_calc((u_char *)authInfo->pw_digest, (u_char *)string,
			1 + AUTH_VECTOR_LEN + size);
		
		/* Compare them */
		if( memcmp(authInfo->pw_digest,
				authInfo->authPwd->strvalue + 1,
				CHAP_VALUE_LENGTH) != 0)
		{
			result = -1;
		}
		else {
			result = 0;
		}
	}
	return result;
}

/*************************************************************************
 *
 *      Function: authAraDesPwd
 *
 *      Purpose: validate and reply to an ARADES auth. request
 *
 *************************************************************************/

int
authAraDesPwd( authInfo)
AuthInfo       *authInfo;
{
        int i, pwdlen, result = -1;
	u_char password[ARA_PASS_LEN];
	u_char scratch[ARA_PASS_LEN];

	DEBUG("authAraDesPwd\r\n");
        pwdlen= strlen(authInfo->chkPwd->strvalue);
        memset(password, 0, ARA_PASS_LEN);

	/* ARA-DES only uses first 8 bytes MAX */
        if (pwdlen > ARA_PASS_LEN) {
		pwdlen = ARA_PASS_LEN;
	}
        for (i=0; i < pwdlen; i++) {
                password[i] = (u_char) ( authInfo->chkPwd->strvalue[i] << 1);
	}
	/* Now use this as the  key for the des encryptor. */
        dessetkey( (char *) password);

	/* Encrpyt the random eight bytes that we had sent. */
        memcpy(scratch, authInfo->vector, 8);
        endes((char *) scratch);

	/* If they match to what we got, the remote is validated. */
        if (memcmp(scratch, authInfo->authPwd->strvalue, 8) == 0) {
                result = 0;
        } 
	return ( result);
}

/*************************************************************************
 *
 *	Function: authPapPwd
 *
 *	Purpose: Process and reply to a PAP authentication request
 *
 *************************************************************************/

int
authPapPwd(authInfo)
AuthInfo	*authInfo;
{
	int	i;
	char	string[AUTH_PASS_LEN + 1];
	int	result = -1;	/* init to failure */
	int	len;

	DEBUG("authPapPwd\r\n");
	authInfo->user_msg = (char *)NULL;

	/* Decrypt the password */
	len = authInfo->authPwd->size;
	if( len > AUTH_PASS_LEN ) {
		char msg[128];

		sprintf(msg,
			"authPapPwd: from %s, user %s: pwd too long (%d)\n",
			ip_hostname(authInfo->authreq->ipaddr), len);
		log_err(msg);
		len = AUTH_PASS_LEN;
	}
	memcpy(string, authInfo->authPwd->strvalue, len);
	for(i = 0;i < len;i++) {
		string[i] ^= authInfo->pw_digest[i % AUTH_VECTOR_LEN];
	}
	string[len] = '\0';

#if defined( PWD_DEBUG )
	DEBUG("chkPwd->strvalue is %s\r\ndecrypted pwd is %s\r\n",
		authInfo->chkPwd->strvalue, string);
#endif

		/* check for Unix Authentication */
	if( getUnixPwdType(authInfo->chkPwd->strvalue) == PWD_SPECIAL ) {
		if(unix_pass(authInfo->authName->strvalue, string) == 0) {
			result = 0;
		}
	}
#if defined(SAFEWORD)
	/* check for SafeWord Authentication */
       	else if( strcmp(authInfo->chkPwd->strvalue, EL_PWD) == 0) {
		result = authSafewordPwd(authInfo);
	}
#endif
#if defined(ACE)
	/* check for Security Dynamics Authentication */
	else if( strcmp(authInfo->chkPwd->strvalue, SD_PWD) == 0) {
		if( authInfo->chkImmediate ) {
			/* short-circuit dummy challenge */
			memcpy(authInfo->authState->strvalue,
				ACE_DUMMY_STATE, LEN_ACE_DUMMY_STATE+1);
			authInfo->authState->size = LEN_ACE_DUMMY_STATE+1;
		}
		result = authAcePwd(authInfo);
	}
#endif
	/* DEFAULT Password is neither UNIX nor SAFEWORD nor ACE */
	else if(strcmp(authInfo->chkPwd->strvalue, string) == 0) {
		result = 0;
	}

	return result;
}

/*************************************************************************
 *
 *	Function: rad_authenticate
 *
 *	Purpose: Process and reply to an authentication request
 *
 *************************************************************************/

void
rad_authenticate(authreq, activefd)
AUTH_REQ	*authreq;
int		activefd;
{
	VALUE_PAIR	*check_item;
	VALUE_PAIR	*auth_item;
	VALUE_PAIR	*authList;
	VALUE_PAIR	*checkList;
	VALUE_PAIR	*replyList;
	VALUE_PAIR	*authName;
	VALUE_PAIR	*authPwd;
	VALUE_PAIR	*authState;
	VALUE_PAIR	*chkPwd;
	VALUE_PAIR	*chkImmediate;
	VALUE_PAIR	*chkIdle;
	VALUE_PAIR	*tokExp;
	VALUE_PAIR	*pwdExp;
	VALUE_PAIR	*cutCur;
	int		result;
	char		pw_digest[AUTH_VECTOR_LEN];
	char		msg[128];
	char		umsg[128];
	char		*user_msg;
	int		retval;
	PasswordType	passwordType;
	int		passwordExpired;
	int		tokenExpired;
	AuthInfo	authInfo;

	DEBUG("rad_authenticate()\r\n");

		/* init the info to pass around */
	memset(&authInfo, 0, sizeof(authInfo));	/* paranoia */
	authInfo.fd		= activefd;
	authInfo.authreq	= authreq;
	authInfo.ipaddr		= authreq->ipaddr;
	authInfo.vector		= authreq->vector;
	authInfo.pw_digest	= pw_digest;
	authInfo.cacheAuth	= FALSE;
	authInfo.cutList	= (VALUE_PAIR *)NULL;
	authInfo.user_msg	= (char *)NULL;
	authInfo.tokExp		= (VALUE_PAIR *)NULL;
	authInfo.chkSecret	= (VALUE_PAIR *)NULL;
	authInfo.chkImmediate	= (VALUE_PAIR *)NULL;
	authInfo.chkIdle	= (VALUE_PAIR *)NULL;
	authInfo.authState	= (VALUE_PAIR *)NULL;
	authInfo.tokExpSecs	= (time_t)0;

	/*
	 * Name Attribute is special:
	 *	- Must be present in the auth request
	 *	- Must match a name in the users file
	 * Password Attribute is special:
	 *	- Must be present in the users file record
	 *	- Must be present in the auth request as one of:
	 *		- PW_PASSWORD
	 *		- PW_CHAP_PASSWORD
	 *		- PW_ASCEND_ARADES
	 *	- If PW_PASSWORD then either:
	 *		- static password
	 *		- UNIX password
	 *		- token password w/o token caching
	 *	- Else if PW_CHAP_PASSWORD then either:
	 *		- static password
	 *		- token password with token caching
	 *	- Else if PW_ASCEND_ARADES
	 *		- static password 
	 *
	 * All other attributes:
	 *	- Check the auth request value against the check request value
	 *
	 * Some check attributes determine what kind of password checking we
	 * do rather than being checked against auth request attributes,
	 *
	 * Some reply attributes specify per-user characteristics rather
	 * than being returned to the authentication requestor.
	 *
	 */

	/* Get the username from the request */
	authList = cut_attribute(authreq->request, PW_USER_NAME, &authName);
	if((authName == (VALUE_PAIR *)NULL) || 
	   ((int)strlen(authName->strvalue) <= 0)) {
		sprintf(msg, "Authenticate: from %s - No User Name\n",
			ip_hostname(authreq->ipaddr));
		log_err(msg);
		authCleanup(&authInfo);
		return;
	}
	else {
		authInfo.authName = authName;
		authInfo.cutList = authName;
		cutCur = authName;
	}

	/* Verify the client and Calculate the MD5 Password Digest */
	if(calc_digest((u_char *)pw_digest, authreq) != 0) {
		/* We dont respond when this fails */
		DEBUG("rad_auth() calc_digest ret'd error\r\n");
		sprintf(msg, "Authenticate: from %s - Security Breach: %s\n",
			ip_hostname(authreq->ipaddr), authName->strvalue);
		log_err(msg);
		authCleanup(&authInfo);
		return;
	}

	/* Get the user from the database */
	if( (retval = user_find(authName->strvalue, &checkList, &replyList))
	  != 0) {
		DEBUG("rad_auth(): user_find(%s) error = %s\r\n",
			authName->strvalue, get_errmsg(retval));
		sprintf(msg, "Authenticate: from %s - %s: %s\n",
			ip_hostname(authreq->ipaddr), get_errmsg(retval),
			authName->strvalue);
		log_err(msg);
		send_reject(authreq, (char *)NULL, activefd);
		authCleanup(&authInfo);
		return;
	}

	/* init the flags - we will check them later */
	passwordType = PWD_UNKNOWN;
	passwordExpired = FALSE;
	tokenExpired = FALSE;

	/* start by examining the user record check list:
		it MUST have a password entry and MAY have other entries.
	*/
	checkList = cut_attribute(checkList, PW_PASSWORD, &chkPwd);
	if( !chkPwd ) {	/* MUST have password in record! */
		sprintf(msg, "Authenticate: from %s - No pwd in record!\n",
			ip_hostname(authreq->ipaddr));
		log_err(msg);
		DEBUG("%s", msg);
		send_reject(authreq, (char *)NULL, activefd);
		authCleanup(&authInfo);
		return;
	}
	else {
		authInfo.chkPwd = chkPwd;
		cutCur->next = chkPwd;
		cutCur = cutCur->next;
	}

	checkList = cut_attribute(checkList, ASCEND_PW_EXPIRATION, &pwdExp);
	if( pwdExp ) {
		VALUE_PAIR *lifeTime;
		VALUE_PAIR *warnTime;
		UINT4 usr_exp_days = (UINT4)0;
		UINT4 usr_warn_days = (UINT4)0;

		DEBUG("rad_auth(): check_item: ASCEND_PW_EXPIRATION\r\n");

		cutCur->next = pwdExp;
		cutCur = cutCur->next;

		/* Has this user's password expired */
		replyList =
			cut_attribute(replyList, ASCEND_PW_LIFETIME, &lifeTime);
		if( lifeTime ) {
			cutCur->next = lifeTime;
			cutCur = cutCur->next;
			usr_exp_days = lifeTime->lvalue;
		}

		replyList =
			cut_attribute(replyList, ASCEND_PW_WARNTIME, &warnTime);
		if( warnTime ) {
			cutCur->next = warnTime;
			cutCur = cutCur->next;
			usr_warn_days = warnTime->lvalue;
		}
		retval = pw_expired(pwdExp->lvalue,
					usr_exp_days, usr_warn_days);
		if(retval < 0) {
			passwordExpired = TRUE;
			DEBUG("rad_auth(): password has EXPIRED\r\n");
		}
		else if( retval > 0 ) {
			DEBUG("rad_auth(): password WARNING: %d\r\n", retval);
			sprintf(umsg,
			  	"Password Will Expire in %d Days\r\n", retval);
			user_msg = umsg;
		}
	}

	authList = cut_attribute(authList, PW_PASSWORD, &authPwd);
	if( !authPwd ) {
		authList = cut_attribute(authList,
					PW_CHAP_PASSWORD, &authPwd);
	}
        if ( !authPwd ) {
                authList = cut_attribute(authList,
                                        PW_ASCEND_ARADES, &authPwd);
        }
	if( !authPwd ) {	/* MUST have received password! */
		sprintf(msg, "Authenticate: from %s - No pwd in request!\n",
			ip_hostname(authreq->ipaddr));
		log_err(msg);
		DEBUG("%s", msg);
		send_reject(authreq, (char *)NULL, activefd);
		authCleanup(&authInfo);
		return;
	}
	else {
		authInfo.authPwd = authPwd;
		cutCur->next = authPwd;
		cutCur = cutCur->next;
	}

	authList = cut_attribute(authList, PW_STATE, &authState);
	if( authState ) {
		authInfo.authState = authState;
		cutCur->next = authState;
		cutCur = cutCur->next;
	}

	/*
	 * Check token expiration time if we are doing token caching.
	 */
	checkList = cut_attribute(checkList, ASCEND_TOKEN_EXPIRY, &tokExp);
	if( tokExp ) {
		VALUE_PAIR *chkSecret;
		char	*sharedRcvSecret;
		UINT4	tokExpSecs;

		DEBUG("rad_auth(): checkList: ASCEND_TOKEN_EXPIRY = %ld\r\n",
			tokExp->lvalue);

		authInfo.tokExp = tokExp;
		cutCur->next = tokExp;
		cutCur = cutCur->next;

			/* Paranoia for the masses */
		tokExpSecs = (UINT4)0;
		sharedRcvSecret = (char *)0;

		/* Is token cache expiry correctly specified */
		tokExpSecs = tokExp->lvalue * (UINT4)SECONDS_PER_MINUTE;
		if( tokExpSecs ) {
			replyList = cut_attribute(replyList,
						ASCEND_RECV_SECRET, &chkSecret);
			if( chkSecret ) {
				authInfo.chkSecret = chkSecret;
				cutCur->next = chkSecret;
				cutCur = cutCur->next;
				sharedRcvSecret = chkSecret->strvalue;
			}
		}
		if( tokExpSecs && sharedRcvSecret ) {
			authInfo.tokExpSecs = tokExpSecs;
		}
	}

	checkList = cut_attribute(checkList, ASCEND_TOKEN_IMMEDIATE,
			&chkImmediate);
	if( chkImmediate && (chkImmediate->lvalue == TOK_IMM_YES) ) {
		DEBUG("rad_auth(): checkList: ASCEND_TOKEN_IMMEDIATE = %ld\r\n",
			chkImmediate->lvalue);
		authInfo.chkImmediate = chkImmediate;
		cutCur->next = chkImmediate;
		cutCur = cutCur->next;
	}

	checkList = cut_attribute(checkList, ASCEND_TOKEN_IDLE, &chkIdle);
	if( chkIdle && (chkIdle->lvalue != 0) ) {
		DEBUG("rad_auth(): checkList: ASCEND_TOKEN_IDLE = %ld\r\n",
			chkIdle->lvalue);

		chkIdle->lvalue *= SECONDS_PER_MINUTE;
		authInfo.chkIdle = chkIdle;
		cutCur->next = chkIdle;
		cutCur = cutCur->next;
	}

	/*
	 * Any future special attributes in the auth request should be
	 * dealt with here and gotten from the authList.
	 */


	/*
	 * Special handling for passwords which are encrypted,
	 * and sometimes authenticated against the UNIX passwd database.
	 * Also they can come using the Three-Way CHAP.
	 * Or be token passwords.
	 * Or be tokens with caching
	 * Or ...
	 */
	passwordType = getPasswordType(chkPwd->strvalue);
	DEBUG("User record PASSWORD type is %s\r\n",
		(passwordType == PWD_SPECIAL) ? "Special" : "Normal");

	result = -1;
        switch ( authPwd->attribute ) {

        case PW_CHAP_PASSWORD:
                result = authChapPwd(&authInfo);
                break;

        case PW_PASSWORD:
                result = authPapPwd(&authInfo);
                break;

        case PW_ASCEND_ARADES:
                result = authAraDesPwd(&authInfo);
                break;

        default:
                break;
        }

	/*
	 * At this point all of the special attributes have been checked.
	 * A result of 0 means that they have been satisfied; negative
	 * result means failure, positive means special handling.
	 */
	if( result < 0 ) {	/* auth failure */
		send_reject(authreq, authInfo.user_msg, activefd);
		authCleanup(&authInfo);
		return;
	}
	else if( result > 0 ) {	/* token stuff; neither pass nor fail */
		authCleanup(&authInfo);
		return;
	}

	/*
	 * Look for the remaining matching check items
	 */
	user_msg = (char *)NULL;
	check_item = checkList;
	result = 0;
	while(result == 0 && check_item != (VALUE_PAIR *)NULL) {
		/*
		 * Look for the matching attribute in the request.
		 */
		authList = cut_attribute(authList,
					check_item->attribute,
					&auth_item);
		if(auth_item == (VALUE_PAIR *)NULL) {
			result = -1;
			break;
		}
		else {
			cutCur->next = auth_item;
			cutCur = cutCur->next;
			switch(check_item->type) {
			case PW_TYPE_STRING:
				if(strcmp(check_item->strvalue,
						auth_item->strvalue) != 0) {
					result = -1;
				}
				break;

			case PW_TYPE_INTEGER:
			case PW_TYPE_IPADDR:
				if(check_item->lvalue != auth_item->lvalue) {
					result = -1;
				}
				break;

			default:
				result = -1;
				break;
			}
		}
		check_item = check_item->next;
	}

	if(result != 0) {
		send_reject(authreq, user_msg, activefd);
	}
	else {	/* everything ok, except that the password may have expired */
		if( passwordExpired != FALSE ) {	/* pwd is too old */
			/* require that:
			 *	- password changing is allowed
			 *	- the expired pwd was given
			 */
		    if( allow_passchange == TRUE ) {
			switch( passwordType ) {
			default:
			case PWD_UNKNOWN:
			case PWD_SPECIAL:
				user_msg = "Password Has Expired\r\n";
				send_reject(authreq, user_msg, activefd);
				break;
			case PWD_NORMAL:
				user_msg = "Password Has Expired\r\n";
				send_pwexpired(authreq, user_msg, activefd);
				break;
			}
		    }
		    else {
			user_msg = "Password Has Expired\r\n";
			send_reject(authreq, user_msg, activefd);
		    }
		}
		else {
			send_accept(authreq, replyList, user_msg, activefd);
		}
	}
	authCleanup(&authInfo);
	pairfree(checkList);
	pairfree(replyList);
}

/*************************************************************************
 *
 *	Function: send_reject
 *
 *	Purpose: Reply to the request with a REJECT.  Also attach
 *		 any user message provided.
 *
 *************************************************************************/

void
send_reject(authreq, msg, activefd)
AUTH_REQ	*authreq;
char		*msg;
int		activefd;
{
	AUTH_HDR		*auth;
	struct	sockaddr	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;
	int			total_length;
	u_char			*ptr;
	int			len;

	auth = (AUTH_HDR *)send_buffer;

	/* Build standard response header */
	if(authreq->code == PW_PASSWORD_REQUEST) {
		auth->code = PW_PASSWORD_REJECT;
	}
	else {
		auth->code = PW_AUTHENTICATION_REJECT;
	}
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);
	total_length = AUTH_HDR_LEN;

	/* Append the user message */
	if(msg != (char *)NULL) {
		len = strlen(msg);
		if(len > 0 && len < AUTH_STRING_LEN) {
			ptr = auth->data;
			*ptr++ = PW_PORT_MESSAGE;
			*ptr++ = len + 2;
			memcpy(ptr, msg, len);
			ptr += len;
			total_length += len + 2;
		}
	}

	/* Set total length in the header */
	auth->length = htons(total_length);

	/* Calculate the response digest */
	secretlen = strlen((char *)authreq->secret);
	memcpy(send_buffer + total_length, authreq->secret, secretlen);
	md5_calc((u_char *)digest, (u_char *)auth, total_length + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + total_length, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	DEBUG("Sending Reject of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));
	
	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)total_length, (int)0,
		(struct sockaddr *) &saremote, sizeof(struct sockaddr_in));
}

/*************************************************************************
 *
 *	Function: send_challenge
 *
 *	Purpose: Reply to the request with a CHALLENGE.  Also attach
 *		 any user message provided and a state value.
 *
 *************************************************************************/

void
send_challenge(authreq, msg, msgLen, state, stateLen, activefd)
AUTH_REQ	*authreq;
char		*msg;
int		msgLen;
char		*state;
int		stateLen;
int		activefd;
{
	AUTH_HDR		*auth;
	struct	sockaddr_in	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;
	int			total_length;
	u_char			*ptr;

	auth = (AUTH_HDR *)send_buffer;

	DEBUG("Entering send_challenge()\r\n");

	/* Build standard response header */
	auth->code = PW_ACCESS_CHALLENGE;
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);
	total_length = AUTH_HDR_LEN;
	ptr = auth->data;

	/* Append the challenge */
	if( msgLen < AUTH_STRING_LEN ) {
		DEBUG("...Adding challenge, len=%d\r\n", msgLen);
		*ptr++ = PW_PORT_MESSAGE;
		*ptr++ = msgLen + 2;
		memcpy(ptr, msg, msgLen);
		if( debug_flag == 1 ) {
			int	i;
			printf("challenge before encrypt:\r\n");
			for( i = 0; i < msgLen; i++ ) {
				printf("%c ", ptr[i]);
			}
			printf("\r\n");
		}
		ptr += msgLen;
		total_length += msgLen + 2;
	}

	/* Append the state info */
	if( state != (char *)NULL ) {
		DEBUG("...Adding state, len=%d\r\n", stateLen);
		*ptr++ = PW_STATE;
		*ptr++ = stateLen + 2;
		memcpy(ptr, state, stateLen);
		ptr += stateLen;
		total_length += stateLen + 2;
	}

	/* Set total length in the header */
	auth->length = htons(total_length);

	DEBUG("send_chall(), auth->length = %d\r\n", total_length);

	/* Calculate the response digest */
	secretlen = strlen((char *)authreq->secret);
	memcpy(send_buffer + total_length, authreq->secret, secretlen);

	if( debug_flag == 1 ) {
	    int	i;

	    printf("sendBuf before encrypt:\r\n");
	    for( i = 0; i < total_length+secretlen; i++ ) {
		printf("%x ", (u_char)(send_buffer[i]));
	    }
	    printf("\r\n");
	}

	md5_calc((u_char *)digest, (u_char *)auth, total_length + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + total_length, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	DEBUG("Sending Challenge of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));
	
	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)total_length, (int)0,
	       (struct sockaddr *) &saremote, sizeof(struct sockaddr_in));
}

/*************************************************************************
 *
 *	Function: send_pwack
 *
 *	Purpose: Reply to the request with an ACKNOWLEDGE.
 *		 User password has been successfully changed.
 *
 *************************************************************************/

void
send_pwack(authreq, activefd)
AUTH_REQ	*authreq;
int		activefd;
{
	AUTH_HDR		*auth;
	struct	sockaddr	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;

	auth = (AUTH_HDR *)send_buffer;

	/* Build standard response header */
	auth->code = PW_PASSWORD_ACK;
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);
	auth->length = htons(AUTH_HDR_LEN);

	/* Calculate the response digest */
	secretlen = strlen((char *)authreq->secret);
	memcpy(send_buffer + AUTH_HDR_LEN, authreq->secret, secretlen);
	md5_calc((u_char *)digest, (u_char *)auth, AUTH_HDR_LEN + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + AUTH_HDR_LEN, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	DEBUG("Sending PW Ack of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));
	
	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)AUTH_HDR_LEN, (int)0,
			&saremote, sizeof(struct sockaddr_in));
}

/*************************************************************************
 *
 *	Function: send_pwexpired
 *
 *	Purpose: Reply to the request with a PW_PASSWORD_EXPIRED
 *
 *************************************************************************/

void
send_pwexpired(authreq, msg, activefd)
AUTH_REQ	*authreq;
char		*msg;
int		activefd;
{
	AUTH_HDR		*auth;
	struct	sockaddr	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;
	int			total_length;
	u_char			*ptr;

	auth = (AUTH_HDR *)send_buffer;

	/* Build standard response header */
	auth->code = PW_PASSWORD_EXPIRED;
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);

	total_length = AUTH_HDR_LEN;
	ptr = auth->data;

	/* Append the user message */
	if(msg != (char *)NULL) {
		int len = strlen(msg);
		if(len > 0 && len < AUTH_STRING_LEN) {
			*ptr++ = PW_PORT_MESSAGE;
			*ptr++ = len + 2;
			memcpy(ptr, msg, len);
			ptr += len;
			total_length += len + 2;
		}
	}

	auth->length = htons(total_length);

	/* Calculate the response digest */
	secretlen = strlen((CONST char *)authreq->secret);	/* ALWAYS 0 */
	memcpy(send_buffer + total_length, authreq->secret, secretlen);
	md5_calc((u_char *)digest, (u_char *)auth, total_length + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + total_length, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
	memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	DEBUG("Sending PW Expired of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));
	
	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)total_length, (int)0,
			&saremote, sizeof(struct sockaddr_in));
}

#if defined( ASCEND_SECRET )

/*************************************************************************
 *
 *	Function: make_secret
 *
 *	Purpose: Build an encrypted secret value to return in a reply
 *		 packet.  The secret is hidden by xoring with a MD5 digest
 *		 created from the shared secret and the authentication
 *		 vector.  We put them into MD5 in the reverse order from
 *		 that used when encrypting passwords to RADIUS.
 *
 *************************************************************************/

void
make_secret(digest, vector, secret, value)
u_char	*digest;
u_char	*vector;
u_char	*secret;
char	*value;
{
    u_char	buffer[ AUTH_STRING_LEN ];
    int		secretLen = strlen( (CONST char *)secret );
    int		ix;

    memcpy( buffer, vector, AUTH_VECTOR_LEN );
    memcpy( buffer + AUTH_VECTOR_LEN, secret, secretLen );
    md5_calc( digest, buffer, AUTH_VECTOR_LEN + secretLen );
    memset( buffer, 0, AUTH_STRING_LEN );
    for ( ix = 0; ix < AUTH_VECTOR_LEN; ix += 1 ) {
	digest[ ix ] ^= value[ ix ];
    }
}

#endif /* ASCEND_SECRET */


/*************************************************************************
 *
 *	Function: send_accept
 *
 *	Purpose: Reply to the request with an ACKNOWLEDGE.  Also attach
 *		 reply attribute value pairs and any user message provided.
 *
 *************************************************************************/

void
send_accept(authreq, reply, msg, activefd)
AUTH_REQ	*authreq;
VALUE_PAIR	*reply;
char		*msg;
int		activefd;
{
	AUTH_HDR		*auth;
	u_short			total_length;
	struct	sockaddr	saremote;
	struct	sockaddr_in	*sin;
	u_char			*ptr;
	int			len;
	UINT4			lvalue;
	u_char			digest[AUTH_VECTOR_LEN];
	int			secretlen;

	auth = (AUTH_HDR *)send_buffer;

	/* Build standard header */
	auth->code = PW_AUTHENTICATION_ACK;
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);

	DEBUG("Sending Ack of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));

	total_length = AUTH_HDR_LEN;

	/* Load up the configuration values for the user */
	ptr = auth->data;
	while(reply != (VALUE_PAIR *)NULL) {
		debug_pair(stdout, reply);
		*ptr++ = reply->attribute;
		switch(reply->type) {
		case PW_TYPE_STRING:
#if defined( ASCEND_SECRET )
			if (( reply->attribute == ASCEND_SEND_SECRET ) ||
			    ( reply->attribute == ASCEND_RECV_SECRET )) {
				make_secret( digest, authreq->vector,
					     authreq->secret, reply->strvalue );
				*ptr++ = AUTH_VECTOR_LEN + 2;
				memcpy( ptr, digest, AUTH_VECTOR_LEN );
				ptr += AUTH_VECTOR_LEN;
				total_length += AUTH_VECTOR_LEN + 2;
				break;
			}
#endif
			len = strlen(reply->strvalue);
			if (len >= AUTH_STRING_LEN) {
				len = AUTH_STRING_LEN - 1;
			}
			*ptr++ = len + 2;
			memcpy(ptr, reply->strvalue,len);
			ptr += len;
			total_length += len + 2;
			break;
			
		case PW_TYPE_INTEGER:
		case PW_TYPE_IPADDR:
			*ptr++ = sizeof(UINT4) + 2;
			lvalue = htonl(reply->lvalue);
			memcpy(ptr, &lvalue, sizeof(UINT4));
			ptr += sizeof(UINT4);
			total_length += sizeof(UINT4) + 2;
			break;

#if defined( BINARY_FILTERS )
		case PW_TYPE_FILTER_BINARY:

			/* The binary representation of the filter is in
			   reply->strvalue.  It's length is in reply->lvalue */

			*ptr++ = reply->lvalue + 2;
			memcpy( ptr, reply->strvalue, reply->lvalue );
			ptr += reply->lvalue;
			total_length += reply->lvalue + 2;
			break;
#endif /* BINARY_FILTERS */

		default:
			break;
		}

		reply = reply->next;
	}

	/* Append the user message */
	if(msg != (char *)NULL) {
		len = strlen(msg);
		if(len > 0 && len < AUTH_STRING_LEN) {
			*ptr++ = PW_PORT_MESSAGE;
			*ptr++ = len + 2;
			memcpy(ptr, msg, len);
			ptr += len;
			total_length += len + 2;
		}
	}

	auth->length = htons(total_length);

	/* Append secret and calculate the response digest */
	secretlen = strlen((char *)authreq->secret);
	memcpy(send_buffer + total_length, authreq->secret, secretlen);
	md5_calc((u_char *)digest, (u_char *)auth, total_length + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + total_length, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)total_length, (int)0,
			&saremote, sizeof(struct sockaddr_in));
}

/*************************************************************************
 *
 *	Function: unix_pass
 *
 *	Purpose: Check the users password against the standard UNIX
 *		 password table.
 *
 *************************************************************************/

int
unix_pass(name, passwd)
char	*name;
char	*passwd;
{
	struct passwd	*pwd;
	char		*encpw;
	char		*encrypted_pass;
#if !defined(NOSHADOW)
#if defined(M_UNIX)
	struct passwd	*spwd;
#else
	struct spwd	*spwd;
#endif
#endif /* !NOSHADOW */
	
	/* Get encrypted password from password file */
	if((pwd = getpwnam(name)) == NULL) {
		return UNIX_GETPWNAME_ERR;
	}

	encrypted_pass = pwd->pw_passwd;

#if !defined(NOSHADOW)
	if(strcmp(pwd->pw_passwd, "x") == 0) {
		if((spwd = getspnam(name)) == NULL) {
			return UNIX_GETSHDWNAME_ERR;
		}
#if defined(M_UNIX)
		encrypted_pass = spwd->pw_passwd;
#else
		encrypted_pass = spwd->sp_pwdp;
#endif	/* M_UNIX */
	}
#endif	/* !NOSHADOW */

	/* Run encryption algorythm */
#if defined(SOLARIS)
	encpw = crypt((CONST char *)passwd, (CONST char *)encrypted_pass);
#else
	encpw = crypt((u_char *)passwd, (u_char *)encrypted_pass);
#endif
	/* Check it */
	if(strcmp(encpw, encrypted_pass)) {
		return UNIX_BAD_PASSWORD;
	}
	return(0);
}

/*************************************************************************
 *
 *	Function: get_client_info
 *
 *	Purpose: Return the IP address and secret of the given host
 *
 *	Input:	 rqstIpAddr
 *
 *	Returns: If rqstIpAddr match
 *			set IP addr, hostname, and secret
 *			return 0;
 *		 else
 *			set IP addr = 0
 *			return error code
 *
 *************************************************************************/

int
get_client_info(rqstIpAddr, ipaddr, secret, hostnm)
UINT4	rqstIpAddr;
UINT4	*ipaddr;
u_char	*secret;
char	*hostnm;
{
	FILE	*clientfd;
	u_char	buf[1024];

	*ipaddr = (UINT4)0;
	secret[0] = hostnm[0] = '\0';

	/* Find the client in the database */
	sprintf((char *)buf, "%s/%s", radius_dir, RADIUS_CLIENTS);
	if((clientfd = fopen((CONST char *)buf, "r")) == (FILE *)NULL) {
		int error = errno;
		fprintf(stderr,
			"%s: err(%d): Couldn't open %s to find client\n",
				progname, error, buf);
		return CLIENTFILE_READ_ERR;
	}
	while(fgets((char *)buf, sizeof(buf), clientfd) != (char *)NULL) {
		if(*buf == '#') {
			continue;
		}
		if(sscanf((CONST char *)buf, "%s%s", hostnm, secret) != 2) {
			continue;
		}

		/*
		 * Validate the requesting IP address -
		 * Not secure, but worth the check for accidental requests
		 */
		*ipaddr = get_ipaddr(hostnm);
		if(*ipaddr == rqstIpAddr) {
			break;
		}
		*ipaddr = (UINT4)0;
		secret[0] = hostnm[0] = '\0';
	}
	fclose(clientfd);

	return (*ipaddr) ? 0 : WRONG_NAS_ADDR;
}

/*************************************************************************
 *
 *	Function: calc_digest
 *
 *	Purpose: Validates the requesting client NAS.  Calculates the
 *		 digest to be used for decrypting the users password
 *		 based on the clients private key.
 *
 *************************************************************************/

int
calc_digest(digest, authreq)
u_char		*digest;
AUTH_REQ	*authreq;
{
	u_char	buffer[512];
	u_char	secret[256];
	char	hostnm[256];
	char	msg[128];
	int	secretlen;
	UINT4	ipaddr;
	int	stat;

	stat = get_client_info( authreq->ipaddr, &ipaddr, secret, hostnm );
	memset(buffer, 0, sizeof(buffer));
	if( stat != 0 ) {
		sprintf(msg, "Calc_digest: from %s, ID %d : %s\n",
			ip_hostname(authreq->ipaddr),
			authreq->id, get_errmsg(stat));
		log_err(msg);
		memset(secret, 0, sizeof(secret));
		return stat;
	}

	/* Use the secret to setup the decryption digest */
	secretlen = strlen((char *)secret);
	strcpy((char *)buffer, (char *)secret);
	memcpy((char *)(buffer + secretlen), (char *)authreq->vector,
		AUTH_VECTOR_LEN);
	md5_calc((u_char *)digest, buffer, secretlen + AUTH_VECTOR_LEN);
	strcpy((char *)authreq->secret, (char *)secret);
	memset(buffer, 0, sizeof(buffer));
	memset(secret, 0, sizeof(secret));
	return(0);
}

/*************************************************************************
 *
 *	Function: debug_pair
 *
 *	Purpose: Print the Attribute-value pair to the desired File.
 *
 *************************************************************************/

void
debug_pair(fd, pair)
FILE		*fd;
VALUE_PAIR	*pair;
{
	if(debug_flag) {
		fputs("    ", fd);
		fprint_attr_val(fd, pair);
		fputs("\n", fd);
	}
}

/*************************************************************************
 *
 *	Function: usage
 *
 *	Purpose: Display the syntax for starting this program.
 *
 *************************************************************************/

static void
usage()
{
	fprintf(errf,
		"Usage: %s [ -a acct_dir ] [ -c ] [ -d db_dir ] [ -p ]\n\t[ -s ] [ -u db_file ] [ -v ] [ -w ] [ -x ] [ -A none|services|incr ]\n",
			progname);
	exit(-1);
}

/*************************************************************************
 *
 *	Function: log_err
 *
 *	Purpose: Log the error message provided to the error log with
		 a time stamp.
 *
 *************************************************************************/

int
log_err(msg)
char	*msg;
{
	FILE	*msgfd;
	char	buffer[128];
	time_t	timeval;

	sprintf(buffer, "%s/%s", radius_dir, RADIUS_LOG);
	if((msgfd = fopen(buffer, "a")) == (FILE *)NULL) {
		int error = errno;
		fprintf(errf,
			"%s: err(%d): Couldn't open %s for logging\n",
				progname, error, buffer);
		return LOGFILE_APPEND_ERR;
	}
	timeval = time(0);
	fprintf(msgfd, "%-24.24s: %s", ctime(&timeval), msg);
	fclose(msgfd);
	return(0);
}

/*************************************************************************
 *
 *	Function: config_init
 *
 *	Purpose: intializes configuration values:
 *
 *		 expiration_seconds - When updating a user password,
 *			the amount of time to add to the current time
 *			to set the time when the password will expire.
 *			This is stored as the VALUE Lifetime-In-Days
 *			in the dictionary as number of days.
 *
 *		warning_seconds - When acknowledging a user authentication
 *			time remaining for valid password to notify user
 *			of password expiration.
 *			This is stored as the VALUE Days-Of-Warning
 *			in the dictionary as number of days.
 *
 *************************************************************************/

int
config_init()
{
	DICT_VALUE	*dval;

	if((dval = dict_valfind(PW_LIFETIME)) == (DICT_VALUE *)NULL) {
		DEBUG("config_init(): dict_valfind(%s) failed\r\n",
			PW_LIFETIME);
		expiration_seconds = (UINT4)0;
	}
	else {
		expiration_seconds = dval->value * (UINT4)SECONDS_PER_DAY;
	}
	if((dval = dict_valfind(PW_WARNTIME)) == (DICT_VALUE *)NULL) {
		warning_seconds = (UINT4)0;
	}
	else {
		warning_seconds = dval->value * (UINT4)SECONDS_PER_DAY;
	}
#if( 0 )
	DEBUG("config_init(): expire-secs = %ld\r\n", expiration_seconds);
#endif
	return(0);
}

/*************************************************************************
 *
 *	Function: set_expiration
 *
 *	Purpose: Set the new expiration time by updating or adding
		 the Expiration attribute-value pair.
 *
 *************************************************************************/

int
set_expiration(user_check, expiration)
VALUE_PAIR	*user_check;
UINT4		expiration;
{
	VALUE_PAIR	*exppair;
	VALUE_PAIR	*prev;
	struct timeval	tp;
#if !defined(SOLARIS)
	struct timezone	tzp;
#endif

	if(user_check == (VALUE_PAIR *)NULL) {
		return NULL_VALUEPAIR;
	}

	/* Look for an existing expiration entry */
	exppair = user_check;
	prev = (VALUE_PAIR *)NULL;
	while(exppair != (VALUE_PAIR *)NULL) {
		if(exppair->attribute == ASCEND_PW_EXPIRATION) {
			break;
		}
		prev = exppair;
		exppair = exppair->next;
	}
	if(exppair == (VALUE_PAIR *)NULL) {
		/* Add a new attr-value pair */
		if((exppair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
					(VALUE_PAIR *)NULL) {
			fprintf(errf, "%s: no memory\n", progname);
			exit(MEMORY_ERR);
		}
		/* Initialize it */
		strcpy(exppair->name, PW_EXPIRATION_NAME);
		exppair->attribute = ASCEND_PW_EXPIRATION;
		exppair->type = PW_TYPE_DATE;
		*exppair->strvalue = '\0';
		exppair->lvalue = (UINT4)0;
		exppair->next = (VALUE_PAIR *)NULL;

		/* Attach it to the list. */
		prev->next = exppair;
	}

	/* calculate a new expiration */
#if defined(SOLARIS) || defined(aix)
	gettimeofday(&tp);
#else
	gettimeofday(&tp, &tzp);
#endif
	exppair->lvalue = tp.tv_sec + expiration;
	return(0);
}

/*************************************************************************
 *
 *	Function: pw_expired
 *
 *	Purpose: Tests to see if the users password has expired.
 *
 *	Return: Number of days before expiration if a warning is required
 *		otherwise 0 for success and PWD_EXPIRED for failure.
 *
 *************************************************************************/

int
pw_expired(exptime, usr_exp_days, usr_warn_days)
UINT4	exptime;
UINT4	usr_exp_days;
UINT4	usr_warn_days;
{
	UINT4	exp_secs;
	UINT4	warn_secs;
	struct timeval	tp;
#if !defined(SOLARIS)
	struct timezone	tzp;
#endif
	UINT4	exp_remain;
	int	exp_remain_int;

#if( 0 )
	DEBUG("pw_expired(time=%ld, exp=%ld, warn=%ld)\r\n",
		exptime, usr_exp_days, usr_warn_days);
#endif
	if( usr_exp_days ) {
		exp_secs = usr_exp_days * (UINT4)SECONDS_PER_DAY;
	}
	else {
		exp_secs = expiration_seconds;
	}
	if( usr_warn_days ) {
		warn_secs = usr_warn_days * (UINT4)SECONDS_PER_DAY;
	}
	else {
		warn_secs = warning_seconds;
	}

	if( exp_secs == (UINT4)0 ) {
		return(0);
	}

#if defined(SOLARIS) || defined(aix)
	gettimeofday(&tp);
#else
	gettimeofday(&tp, &tzp);
#endif
	if(tp.tv_sec > exptime) {
		return PWD_EXPIRED;
	}
	if(warn_secs != (UINT4)0) {
		if(tp.tv_sec > exptime - warn_secs) {
			exp_remain = exptime - tp.tv_sec;
			exp_remain /= (UINT4)SECONDS_PER_DAY;
			exp_remain_int = exp_remain;
			return(exp_remain_int);
		}
	}
	return(0);
}

void
sig_fatal(sig)
int	sig;
{

	if(acct_pid > 0) {
		kill(acct_pid, SIGKILL);
	}

	fprintf(errf, "%s: exit on signal (%d)\n", progname, sig);
	fflush(errf);
	exit(1);
}

void
sig_hup(sig)
int	sig;
{
	return;
}

/*************************************************************************
 *
 *	Function: send_nextcode
 *
 *	Purpose: Reply to the request with a Next Code request.  Append
 *		any message given, and also attach a state value.
 *
 *************************************************************************/

void
send_nextcode(authreq, msg, msgLen, state, stateLen, activefd)
AUTH_REQ	*authreq;
char		*msg;
int		msgLen;
char		*state;
int		stateLen;
int		activefd;
{
	AUTH_HDR		*auth;
	struct	sockaddr_in	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;
	int			total_length;
	u_char			*ptr;

	auth = (AUTH_HDR *)send_buffer;

	DEBUG("Entering send_nextcode()\r\n");

	/* Build standard response header */
	auth->code = PW_NEXT_PASSCODE;
	auth->id = authreq->id;
	memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN);
	total_length = AUTH_HDR_LEN;

	ptr = auth->data;

	/* Append the nextcode msg */
	if( msgLen < AUTH_STRING_LEN ) {
		DEBUG("...Adding nextcode msg, len=%d\r\n", msgLen);
		*ptr++ = PW_PORT_MESSAGE;
		*ptr++ = msgLen + 2;
		memcpy(ptr, msg, msgLen);
		if( debug_flag == 1 ) {
			int	i;
			printf("nextcode msg before encrypt:\r\n");
			for( i = 0; i < msgLen; i++ ) {
				printf("%c", ptr[i]);
			}
			printf("\r\n");
		}
		ptr += msgLen;
		total_length += msgLen + 2;
	}

	/* Append the state info */
	if( state != (char *)NULL ) {
	    DEBUG("...Adding state <%s>, len=%d\r\n", state, stateLen);
	    *ptr++ = PW_STATE;
	    *ptr++ = stateLen + 2;
	    memcpy(ptr, state, stateLen);
	    ptr += stateLen;
	    total_length += stateLen + 2;
	}

	/* Set total length in the header */
	auth->length = htons(total_length);

	DEBUG("send_nextcode(), auth->length = %d\r\n", total_length);

	/* Calculate the response digest */
	secretlen = strlen((char *)authreq->secret);
	memcpy(send_buffer + total_length, authreq->secret, secretlen);

	if( debug_flag == 1 ) {
	    int	i;
	    printf("sendBuf before encrypt:\r\n");
	    for( i = 0; i < total_length+secretlen; i++ ) {
		printf("%x ", (u_char)(send_buffer[i]));
	    }
	    printf("\r\n");
	}

	md5_calc((u_char *)digest, (u_char *)auth, total_length + secretlen);
	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
	memset(send_buffer + total_length, 0, secretlen);

	sin = (struct sockaddr_in *) &saremote;
        memset ((char *) sin, '\0', sizeof (saremote));
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = htonl(authreq->ipaddr);
	sin->sin_port = htons(authreq->udp_port);

	DEBUG("Sending NextCode of id %d to %lx (%s)\r\n",
		authreq->id, (u_long)authreq->ipaddr,
		ip_hostname(authreq->ipaddr));
	
	/* Send it to the user */
	sendto(activefd, (char *)auth, (int)total_length, (int)0,
		(struct sockaddr *) &saremote, sizeof(struct sockaddr_in));
}

/*************************************************************************
 *
 *	Function: send_newpin
 *
 *	Purpose: Reply to the request with a Next Code request.  Also
 *		attach a state value.
 *
 *************************************************************************/

	/* not implemented yet */
void
send_newpin(authreq, state, stateLen, activefd)
AUTH_REQ	*authreq;
char		*state;
int		stateLen;
int		activefd;
{
	AUTH_HDR		*auth;
#if(0)
	struct	sockaddr_in	saremote;
	struct	sockaddr_in	*sin;
	char			digest[AUTH_VECTOR_LEN];
	int			secretlen;
	int			total_length;
	u_char			*ptr;
#endif

	auth = (AUTH_HDR *)send_buffer;

	(void)authreq;
	(void)state;
	(void)stateLen;
	(void)activefd;

	DEBUG("Entering send_newpin(): NOT IMPLEMENTED\r\n");

}

/************************************************************************/
/*									*/
/*		Map error codes to messages				*/
/*									*/
/************************************************************************/

char *
get_errmsg(errcode)
int errcode;
{
    char *emsg = (char *)0;
    char logbuf[128];

    if( (errcode > FIRST_DBASE_ERROR) && (errcode < LAST_DBASE_ERROR) ) {
        int index = (errcode - FIRST_DBASE_ERROR) - 1;
	if( db_errmsgs[index].ecode == errcode ) {
            emsg = db_errmsgs[index].emsg;
	} else {
            emsg = "INTERNAL: ErrMsgTbl out of order";
	    sprintf(logbuf, "INTERNAL: ErrMsgs out of order for errcode %d\n",
	    	errcode);
	    log_err(logbuf);
	}
    }
    else {
        emsg = "INTERNAL: Unknown error";
        sprintf(logbuf, "INTERNAL: Unknown error code %d\n", errcode);
	log_err(logbuf);
    }
    return emsg;
}


#if defined(SAFEWORD)
/************************************************************************/
/*									*/
/*		Enigma Safeword Interface routines			*/
/*									*/
/************************************************************************/


	/* simple debug output */
void dbgPblk(pb)
struct pblk	*pb;
{
    if( debug_flag ) {
	printf("PBLK: uport    = <%s>\r\n", pb->uport);
	printf("PBLK: id       = <%s>\r\n", pb->id);
	printf("PBLK: chal     = <%s>\r\n", pb->chal);
	printf("PBLK: dynpwd   = <%s>\r\n", pb->dynpwd);
	printf("PBLK: fixpwd   = <%s>\r\n", pb->fixpwd);
	printf("PBLK: nfixpwd  = <%s>\r\n", pb->nfixpwd);
	printf("PBLK: errcode  = <%d>\r\n", pb->errcode);
	printf("PBLK: lastmode = <%d>\r\n", pb->lastmode);
	printf("PBLK: mode     = <%d>\r\n", pb->mode);
	printf("PBLK: fixmin   = <%d>\r\n", pb->fixmin);
	printf("PBLK: dynpwdf  = <%d>\r\n", pb->dynpwdf);
	printf("PBLK: fixpwdf  = <%d>\r\n", pb->fixpwdf);
	printf("PBLK: echodyn  = <%d>\r\n", pb->echodyn);
	printf("PBLK: echofix  = <%d>\r\n", pb->echofix);
	printf("PBLK: status   = <%d>\r\n", pb->status);
    }
}


/*************************************************************************
 *
 *	Function: safeword_chall
 *
 *	Purpose: Obtain a (possibly NUL) challenge
 *
 *************************************************************************/

int
safeword_chall(authInfo, pb)
AuthInfo	*authInfo;
struct pblk	*pb;
{
	DEBUG("safeword_chall()\r\n");

	/*
	 *
	 *  Fill in param block id field for use by the external
	 *  authentication server:
	 *
	 */
	initpb (pb);
       	strncpy(pb->id, authInfo->authName->strvalue, sizeof(pb->id));
	pb->id[sizeof(pb->id)-1] = 0;	/* ensure NUL-termimation */

	/*
	 * Invoke the SafeWord API
	 */
	pb->mode = CHALLENGE;
	pbmain(pb);

	/* diagnostic */
	DEBUG("after safeword {challenge}\r\n");
	dbgPblk(pb);

	switch( pb->status ) {
	case GOOD_USER:
        	DEBUG("safeword_chall: SafeWord Challenging(%s).\r\n",
			pb->chal);
		return SAFEWORD_CHALLENGING;
	default:
        	DEBUG("safeword_chall: SafeWord failed = %d.\r\n", pb->status);
		return SAFEWORD_FAILED;
	}
}

/*************************************************************************
 *
 *	Function: safeword_eval
 *
 *	Purpose: Check the users password against the Safeword
 *		 password info.
 *
 *************************************************************************/
int
safeword_eval(authInfo, pb)
AuthInfo	*authInfo;
struct pblk	*pb;
{
	VALUE_PAIR	*authPwd = authInfo->authPwd;
	VALUE_PAIR	*authState = authInfo->authState;
	char		*pw_digest = authInfo->pw_digest;
	char		string[AUTH_PASS_LEN + 1];
	int		pwdlen;
	int		i;
	char		*fixed;

	DEBUG("safeword_eval()\r\n");


	memset(string, 0, sizeof(string));
	pwdlen = authPwd->size;
	if( pwdlen > sizeof(string) ) {
		pwdlen = sizeof(string);
	}
        strncpy(string, authPwd->strvalue, pwdlen);
	string[sizeof(string)-1] = 0;

        for( i = 0; i < AUTH_PASS_LEN; i++ ) {
                /*
                 * xor each char of string with corresponding char of pw_digest
                 */
		string[i] ^= pw_digest[i];
        }
	string[AUTH_PASS_LEN] = '\0';

	/* see if we have the <dynamic><,><fixed> format given to us */
	fixed = strchr(string, ',');
	if( fixed ) {
		*fixed = '\0';	/* NUL-terminate dynamic password */
		fixed++;
		DEBUG("received fixed pwd %s\r\n", fixed);
	}

	memcpy(BEG_PB_1ST(pb), authState->strvalue, LEN_PB_1ST(pb));
	memcpy(BEG_PB_2ND(pb),
		authState->strvalue + LEN_PB_1ST(pb),
		LEN_PB_2ND(pb));
	for( i = 0; i < AUTH_PASS_LEN; i++ ) {
		if( (pb->chal[i] < ' ') || (pb->chal[i] > 0x7e) ) {
			pb->chal[i] = 0;
			break;
		}
	}
	DEBUG("Old challenge is <%s>\r\n", pb->chal);

	/* copy new dynamic password to the SafeWord param block */
	pb->dynpwd[0] = 0;
	if( pb->dynpwdf ) {
    		strncpy(pb->dynpwd, string, sizeof(pb->dynpwd));
		pb->dynpwd[sizeof(pb->dynpwd)-1] = 0;
	}
	else {
		char *dynfMsg = "safeword_eval(): dynamic pwd flag not set!\n";
		DEBUG("%s\r\n", dynfMsg);
		log_err(dynfMsg);
		return SAFEWORD_FAILED;
	}

	pb->fixpwd[0] = 0;
	if( fixed ) {
		/* copy fixed pwd *always* but bitch if the flag is clear */
    		strncpy(pb->fixpwd, fixed, sizeof(pb->fixpwd));
		pb->fixpwd[sizeof(pb->fixpwd)-1] = 0;
		if( !pb->fixpwdf ) {
			char	buf[128];

			sprintf(buf,
				"user %s, fixed pwd w/o fixpwdf, NAS %s\n",
					authInfo->authName->strvalue,
					ip_hostname(authInfo->authreq->ipaddr));
			DEBUG("%s\r\n", buf);
			log_err(buf);
		}
	}

	pb->mode = EVALUATE_ALL;
	pbmain(pb);

	/* diagnostic */
	DEBUG("after safeword {eval}\r\n");
	dbgPblk(pb);

	switch( pb->status ) {
	case PASS:
        	DEBUG("safeword_eval: SafeWord PASSED.\r\n");
		return SAFEWORD_PASSED;
	default:
        	DEBUG("safeword_eval: SafeWord FAILED, = %d.\r\n", pb->status);
		return SAFEWORD_FAILED;
	}
}

/*************************************************************************
 *
 *	Function: safeword_pass
 *
 *	Purpose: Check the users password against the Safeword
 *		 password info.
 *
 *************************************************************************/
	/*
	 * Since the Radius database has detected a default keyword as the
	 * special "SAFEWORD" flag, it is now appropriate to ask SafeWord
	 * if it has any data on him (or her). We check the given STATE;
	 * if NUL, then this is a challenge request, else it is
	 * an evaluation request
	 */
int
safeword_pass(authInfo, pb)
AuthInfo	*authInfo;
struct pblk	*pb;
{
	DEBUG("safeword_pass()\r\n");
	if( authInfo->authState != (VALUE_PAIR *)NULL ) {
		if( authInfo->authState->attribute == PW_STATE ) {
			if( authInfo->authState->size ) {
				return safeword_eval(authInfo, pb);
			}
			else {
				return safeword_chall(authInfo, pb);
			}
		}
	}
	DEBUG("safeword_pass(): FAILED: no state attribute\r\n");
	return SAFEWORD_FAILED;
}


/* ---------------------------- initpb () ---------------------------- */

	/* initialize parameter block */
void
initpb(pb)
struct pblk	*pb;
{
	memset(pb, 0, sizeof(*pb));

	strcpy (pb->uport, "custpb");
	pb->status = NO_STATUS;
}
#endif

#if defined(ACE)
/************************************************************************/
/*									*/
/*		Security Dynamics ACE/Server Interface routines		*/
/*									*/
/************************************************************************/


	/* simple debug output */
void dbgSdClient(sd)
struct SD_CLIENT	*sd;
{
    if( debug_flag ) {
	printf("SD_CLIENT: appId    = <%d>\r\n", sd->application_id);
	printf("SD_CLIENT: name     = <%s>\r\n", sd->username);

#if defined(UINT4_IS_UINT)
	printf("SD_CLIENT: pc_time  = <%d>\r\n", sd->passcode_time);
#else
	printf("SD_CLIENT: pc_time  = <%ld>\r\n", sd->passcode_time);
#endif
	printf("SD_CLIENT: passcode = <%s>\r\n", sd->validated_passcode);
    }
}


/*************************************************************************
 *
 *	Function: ace_eval
 *
 *	Purpose: Check the users password against the ACE
 *		 password info.
 *
 *************************************************************************/
int
ace_eval(authInfo, sd)
AuthInfo		*authInfo;
struct SD_CLIENT	*sd;
{
	VALUE_PAIR	*authName = authInfo->authName;
	VALUE_PAIR	*authPwd = authInfo->authPwd;
	char		*pw_digest = authInfo->pw_digest;
	int		status;
	char		acePwd[AUTH_PASS_LEN + 1];
	int		i;
	int		pwdlen;
	char		*user;
	char		username[LENACMNAME];
				/* N.B.: sdi_ath.h uses LENACMNAME although
				   one would think that LENUSERNAME would
				   be more appropriate */

	DEBUG("ace_eval()\r\n");

	/*
	 * Establish communications with the server
	 */
	memset(sd, 0, sizeof(*sd));	/* clear shared param block */
	creadcfg();			/* accesses sdconf.rec */
	if( sd_init(sd) ) {
		DEBUG("ace_eval(): Comm Server init failed!\r\n");
		return ACE_FAILED;
	}

	DEBUG("ace_eval(): ace_username: %s\r\n", authName->strvalue);
        strncpy(username, authName->strvalue, sizeof(username));
	username[sizeof(username)-1] = 0;	/* ensure final NUL */
	user = (char *)NULL;

	memset(acePwd, 0, sizeof(acePwd));
	pwdlen = authPwd->size;
	    if( pwdlen > LENPRNST ) {
		pwdlen = LENPRNST;
	    }
	strncpy(acePwd, authPwd->strvalue, pwdlen);

	for( i = 0; i < AUTH_PASS_LEN; i++ ) {
		/*
		 * xor each char of string with corresponding char of pw_digest
		 */
		acePwd[i] ^= pw_digest[i];
	}
	acePwd[AUTH_PASS_LEN] = '\0';
#if defined( PWD_DEBUG )
	DEBUG("ace_eval(): rad_pwd: %s\r\n", acePwd);
#endif

	/* see if we have the <password><.><username> format given to us */
	user = strchr(acePwd, '.');
	if( user ) {
		*user = '\0';	/* NUL-terminate dynamic password */
		user++;
		DEBUG("received username %s\r\n", user);
	}

	/* copy the username to the ACE param block */
	sd->username[0] = 0;
	if( !user ) {
		strcpy(sd->username, username);
	} else {
		strcpy(sd->username, user);
	}

#if defined( PWD_DEBUG )
	DEBUG("Ready to try user %s with pwd %s\r\n", sd->username, acePwd);
#endif
	status = sd_check(acePwd, sd->username, sd);
	switch( status ) {
	case ACM_OK:
		DEBUG("user %s authenticated with pwd %s\r\n",
			sd->username, acePwd);
		status = ACE_PASSED;
		break;
	case ACM_ACCESS_DENIED:
		DEBUG("user %s with pwd %s NOT AUTHENTICATED!\r\n",
			sd->username, acePwd);
		status = ACE_FAILED;
		break;
	case ACM_NEXT_CODE_REQUIRED:
		DEBUG("user %s with pwd %s requires next code\r\n",
			sd->username, acePwd);
		status = ACE_NEXT_PASSCODE;
		break;
	case ACM_NEW_PIN_REQUIRED:
		DEBUG("user %s with pwd %s requires new pin\r\n",
			sd->username, acePwd);
		status = ACE_NEW_PIN;
		break;
	default:
		DEBUG("user %s with pwd %s : ERROR %d\r\n",
			sd->username, acePwd, status);
		status = ACE_FAILED;
		break;
	}

	/* diagnostic */
	DEBUG("after ace {eval}\r\n");
	dbgSdClient(sd);

	return status;
}

/*************************************************************************
 *
 *	Function: ace_next
 *
 *	Purpose: Check the users password against the ACE
 *		 password info.
 *
 *************************************************************************/
int
ace_next(authInfo, sd)
AuthInfo		*authInfo;
struct SD_CLIENT	*sd;
{
	VALUE_PAIR	*authName = authInfo->authName;
	VALUE_PAIR	*authPwd = authInfo->authPwd;
	VALUE_PAIR	*authState = authInfo->authState;
	char		*pw_digest = authInfo->pw_digest;
	int		status;
	char		acePwd[AUTH_PASS_LEN + 1];
	int		i;
	int		pwdlen;
	char		*src;
	char		*user;
	char		username[LENACMNAME];
				/* N.B.: sdi_ath.h uses LENACMNAME although
				   one would think that LENUSERNAME would
				   be more appropriate */

	DEBUG("ace_next()\r\n");

	DEBUG("ace_next(): ace_username: %s\r\n", authName->strvalue);
        strncpy(username, authName->strvalue, sizeof(username));
	username[sizeof(username)-1] = 0;	/* ensure final NUL */
	user = (char *)NULL;

	memset(acePwd, 0, sizeof(acePwd));
	pwdlen = strlen(authPwd->strvalue);
	if( pwdlen > LENPRNST ) {
		pwdlen = LENPRNST;
	}
        strncpy(acePwd, authPwd->strvalue, pwdlen);

	for( i = 0; i < AUTH_PASS_LEN; i++ ) {
		/*
		 * xor each char of string with corresponding char of pw_digest
		 */
		acePwd[i] ^= pw_digest[i];
	}
	acePwd[AUTH_PASS_LEN] = '\0';
	DEBUG("ace_next(): rad_pwd: %s\r\n", acePwd);

	/* see if we have the <password>.<username> format given to us */
	user = strchr(acePwd, '.');
	if( user ) {
		*user = '\0';	/* NUL-terminate dynamic password */
		user++;
		DEBUG("received username %s\r\n", user);
	}

	src = &authState->strvalue[1];	/* skip length field */
	memcpy(BEG_SD_1ST(sd), src, LEN_SD_1ST(sd));
	memcpy(BEG_SD_2ND(sd), src + LEN_SD_1ST(sd), LEN_SD_2ND(sd));
	memcpy(BEG_SD_3RD(sd), src + LEN_SD_1ST(sd) + LEN_SD_2ND(sd),
			LEN_SD_3RD(sd));

	/* copy the username to the ACE param block */
	sd->username[0] = 0;
	if( !user ) {
		strcpy(sd->username, username);
	} else {
		strcpy(sd->username, user);
	}

	DEBUG("Ready to try user %s with pwd %s\r\n", sd->username, acePwd);
	status = sd_next(acePwd, sd);
	switch( status ) {
	case ACM_OK:
		DEBUG("user %s authenticated with pwd %s\r\n",
			sd->username, acePwd);
		status = ACE_PASSED;
		break;
	case ACM_ACCESS_DENIED:
		DEBUG("user %s with pwd %s NOT AUTHENTICATED!\r\n",
			sd->username, acePwd);
		status = ACE_FAILED;
		break;
	default:
		DEBUG("user %s with pwd %s : ERROR %d\r\n",
			sd->username, acePwd, status);
		break;
	}

	/* diagnostic */
	DEBUG("after ace {nextcode}\r\n");
	dbgSdClient(sd);

	return status;
}

/*************************************************************************
 *
 *	Function: ace_pass
 *
 *	Purpose: Check the users password against the ACE
 *		 password info.
 *
 *************************************************************************/
	/*
	 * Since the Radius database has detected a default keyword as the
	 * special "ACE" flag, it is now appropriate to ask Ace
	 * if it has any data on him (or her). We check the STATE;
	 * if STATE == DUMMY,
	 * then this is a challenge request,
	 * else if the contained STATE length == NEXTCODE STATE length
	 *	then this is a response to a NEXTCODE request
	 * else it is an evaluation request
	 */
int
ace_pass(authInfo, sd)
AuthInfo		*authInfo;
struct SD_CLIENT	*sd;
{
	VALUE_PAIR	*authState = authInfo->authState;

	DEBUG("ace_pass()\r\n");
	if( authState != (VALUE_PAIR *)NULL ) {
		if( !strcmp(authState->strvalue, ACE_DUMMY_STATE) ) {
			DEBUG("ace_pass(eval): state len = %d\r\n",
				(u_char)authState->strvalue[0]);
			return ace_eval(authInfo, sd);
		}
		else if( (u_char)(authState->strvalue[0])
			== (u_char)(LEN_SD_TOTAL(sd) + 1) )
		{
			DEBUG("ace_pass(next): state len = %u\r\n",
				(u_char)authState->strvalue[0]);
	        	return ace_next(authInfo, sd);
		}
		else {
			DEBUG("ace_pass(chall): state len = %u\r\n",
				(u_char)authState->strvalue[0]);
			return ACE_CHALLENGING;
		}
	}

	DEBUG("ace_pass(): FAILED: no state attribute\r\n");
	return ACE_FAILED;
}

#endif	/* ACE */

