diff -c -N radiusd/Makefile ascendd/Makefile
*** radiusd/Makefile	Thu Jan 11 17:26:16 1996
--- ascendd/Makefile	Thu May 23 20:02:56 1996
***************
*** 2,39 ****
  #	Makefile for RADIUS - 
  #		Remote Authentication Dial In User Service
  #
  #
- # Add -DNOSHADOW to CFLAGS if you don't have Shadow Passwords
  #
! # aix:	add -Daix to CFLAGS
! # SCO:	add LIBS= -lsocket
! # Solaris:
! #	CFLAGS= -O -Dsys5
! #	CC=	gcc -traditional
! #	LIBS= -lnsl -lsocket
! # unixware: add -Dunixware to CFLAGS, use LIBS= -lucb -lnsl -lsocket
  #
  SRCDIR=.
- CFLAGS= -O -DNOSHADOW
- LDFLAGS=
- CC=	cc
- LIBS=
- SERVER_OBJS=radiusd.o dict.o users.o util.o md5.o attrprint.o acct.o version.o
- SERVERDBM_OBJS=radiusd.o dict.o usersdbm.o util.o md5.o attrprint.o acct.o versiondbm.o
- SERVER_SRCS=radiusd.c dict.c users.c util.c md5.c attrprint.c acct.c version.c
- INCLUDES=$(SRCDIR)/radius.h $(SRCDIR)/conf.h
  
! ALL:	radiusd radpass
  
  dbm:	radiusd.dbm builddbm
  
  radiusd: $(SERVER_OBJS)
  	$(CC) $(CFLAGS) -o radiusd $(SERVER_OBJS) $(LIBS)
  
  radiusd.dbm: $(SERVERDBM_OBJS)
! 	$(CC) $(CFLAGS) -o radiusd.dbm $(SERVERDBM_OBJS) -ldbm $(LIBS)
  
! radiusd.o: $(SRCDIR)/radiusd.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/radiusd.c
  
  acct.o: $(SRCDIR)/acct.c $(INCLUDES)
--- 2,170 ----
  #	Makefile for RADIUS - 
  #		Remote Authentication Dial In User Service
  #
+ # ASCEND: @(#)Makefile	1.3 (95/07/25 00:55:22)
  #
  #
! # Add -DNOSHADOW to CFLAGS if you don't have Shadow Passwords.  As is,
! # the Makefile compiles a RADIUS daemon with Ascend extensions on a SunOs
! # box using GCC.
  #
+ #
+ #Host OS:
+ # aix:
+ #	add -Daix to CFLAGS
+ #
+ # HP-UX:
+ #	CFLAGS=
+ #	CC= gcc -traditional
+ #
+ # SCO:
+ #	add LIBS= -lsocket
+ #
+ # SunOs:
+ #	CFLAGS= -DOSUN
+ #	CC= gcc -traditional
+ #
+ # Solaris with gcc:
+ #	CFLAGS= -O -Dsys5 -DSOLARIS
+ #	(Add -D_SVID_GETTOD to CFLAGS for Solaris 2.5)
+ #	CC= gcc -ansi
+ #	add LIBS= -lnsl -lsocket
+ #
+ # Solaris with cc:
+ #	CFLAGS= -O -Dsys5 -DSOLARIS
+ #	(Add -D_SVID_GETTOD to CFLAGS for Solaris 2.5)
+ #	CC= cc
+ #	add LIBS= -lnsl -lsocket
+ #
+ # unixware:
+ #	add -Dunixware to CFLAGS
+ #	add LIBS= -lucb -lnsl -lsocket
+ #
+ # BSDI:
+ #	CC= gcc		# Note: cannot use -ansi on some versions
+ #
+ # Linux:
+ #	add -Dsys5 to CFLAGS
+ #
+ #
+ #External Authentication Servers:
+ # Security Dynamics (ACE):
+ #	add -DACE to AUTH_SERVERS
+ #	add SD_INCLUDES to AUTH_INCLUDES
+ #	add -lsdiclient to AUTH_LIBS
+ # Enigma Logic (SAFEWORD):
+ #	add -DSAFEWORD to AUTH_SERVERS
+ #	add SW_INCLUDES to AUTH_INCLUDES
+ #	add -lidp to AUTH_LIBS
+ #
+ 
  SRCDIR=.
  
! # Pick the combination of external authentication servers to use.  Requires
! # the authentication source and libraries provided by the server vendors.
! #
! AUTH_SERVERS=
! #AUTH_SERVERS= -DACE
! #AUTH_SERVERS= -DSAFEWORD
! #AUTH_SERVERS= -DACE -DSAFEWORD
! 
! # List of extra files for external authentication servers
! #  SD_INCLUDES          Source files provided by Security Dynamics (ACE)
! #  SW_INCLUDES          Source files provided by Enigma Logic (SafeWord)
! #
! SD_INCLUDES= $(SRCDIR)/sdi_athd.h $(SRCDIR)/sdi_size.h $(SRCDIR)/sdi_type.h \
! 		$(SRCDIR)/sdacmvls.h $(SRCDIR)/sdconf.h
! SW_INCLUDES= $(SRCDIR)/custpb.h $(SRCDIR)/custfail.h
! 
! # Specify the authentication server include list to use
! #
! AUTH_INCLUDES=
! #AUTH_INCLUDES= $(SD_INCLUDES)
! #AUTH_INCLUDES= $(SW_INCLUDES)
! #AUTH_INCLUDES= $(SW_INCLUDES) $(SD_INCLUDES)
! 
! # List of external authentication libraries
! #  -lidpb               For Enigma Logic (SafeWord)
! #  -lsdiclient          For Security Dynamics (ACE)
! #
! AUTH_LIBS=
! #AUTH_LIBS= -L. -lsdiclient
! #AUTH_LIBS= -L. -lidpb
! #AUTH_LIBS= -L. -lsdiclient -lidpb
! 
! # Pick your compiler
! #
! #CC=	gcc -ansi -Wall	# set the GCC compiler to ANSI mode and picky
! CC=	gcc -ansi	# set the GCC compiler to ANSI mode
! #CC=	gcc		# set the GCC compiler for BSDI and friends
! #CC=	cc -Xc -v -fd	# set the SUN compiler to full-whine ANSI mode
! #CC=	cc -Xc -v	# set the SUN compiler to partial-whine ANSI mode
! #CC=	cc -Xc		# set the SUN compiler to ANSI mode
! #CC=	cc
! 
! # Pick your compiler flags
! #  -Dsys5               System V
! #  -DSOLARIS            Solaris (use sys5 also)
! #  -DBINARY_FILTERS     Ascend specific feature to store filters in RADIUS
! #  -DASCEND_SECRET	Ascend specific feature to send secrets from RADIUS
! #  -DASCEND_LOGOUT      Ascend specific feature to handle logout notification
! #  -DOSUN               SunOS 4.1.x
! #  -DNOSHADOW           No shadow password file support
! #  $(AUTH_SERVERS)      See above
! #
! 
! #	use this for BSDI
! #CFLAGS= -O -DNOSHADOW -DBSDI -DBINARY_FILTERS -DASCEND_SECRET -DASCEND_LOGOUT $(AUTH_SERVERS)
! #	use this for SunOS
! CFLAGS= -O -DNOSHADOW -DOSUN -DBINARY_FILTERS -DASCEND_SECRET -DASCEND_LOGOUT $(AUTH_SERVERS)
! #	use this for Solaris 2.x (Add -D_SVID_GETTOD to CFLAGS for Solaris 2.5)
! #CFLAGS= -O -Dsys5 -DSOLARIS -DBINARY_FILTERS -DASCEND_SECRET -DASCEND_LOGOUT $(AUTH_SERVERS)
! #	use this for Linux
! #CFLAGS= -O -Dsys5 -DNOSHADOW -DBINARY_FILTERS -DASCEND_SECRET -DASCEND_LOGOUT $(AUTH_SERVERS)
! 
! # Pick any load options and the load libraries.  The AUTH_LIBS
! # are selected above
! #
! LDFLAGS= 
! 
! # See above for the things you may need to add here
! #
! #LIBS= $(AUTH_LIBS)			# use this for Linux, BSDI and friends
! LIBS= -lnsl $(AUTH_LIBS)		# use this for SunOS
! #LIBS= -lnsl -lsocket $(AUTH_LIBS)	# use this for Solaris 2.x
! 
! #       use this for SunOs. Linux, BSDI
! DBMLIBS= -ldbm
! #       use this for Solaris
! #DBMLIBS=
! 
! CMN_OBJS=radiusd.o dict.o util.o md5.o attrprint.o acct.o filters.o \
! 	 usr_read.o cache.o des.o
! SERVER_OBJS=$(CMN_OBJS) users.o version.o
! SERVERDBM_OBJS=$(CMN_OBJS) usersdbm.o versiondbm.o
! 
! SERVER_SRCS=$(SERVER_OBJS:%.o:%.c)
! SRVR_SOURCES=$(SERVER_OBJS:.o=.c)
! SOURCES=$(SRVR_SOURCES) $(SRCDIR)/radpass.c $(SRCDIR)/md5.c \
! 	$(SRCDIR)/util.c $(SRCDIR)/cexample.c 
! 
! INCLUDES=$(SRCDIR)/radius.h $(SRCDIR)/conf.h $(SRCDIR)/protos.h \
! 	 $(SRCDIR)/cache.h $(SRCDIR)/des.h
! ALL_INCLUDES=$(INCLUDES) $(SRCDIR)/md5.h
! 
! all:	radiusd
  
  dbm:	radiusd.dbm builddbm
  
  radiusd: $(SERVER_OBJS)
  	$(CC) $(CFLAGS) -o radiusd $(SERVER_OBJS) $(LIBS)
+ #	chmod 4111 radiusd
  
  radiusd.dbm: $(SERVERDBM_OBJS)
! 	$(CC) $(CFLAGS) -o radiusd.dbm $(SERVERDBM_OBJS) $(DBMLIBS) $(LIBS)
  
! radiusd.o: $(SRCDIR)/radiusd.c $(INCLUDES) $(AUTH_INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/radiusd.c
  
  acct.o: $(SRCDIR)/acct.c $(INCLUDES)
***************
*** 42,47 ****
--- 173,181 ----
  attrprint.o: $(SRCDIR)/attrprint.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/attrprint.c
  
+ des.o: $(SRCDIR)/des.c $(INCLUDES)
+ 	$(CC) $(CFLAGS) -c $(SRCDIR)/des.c 
+ 
  dict.o: $(SRCDIR)/dict.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/dict.c
  
***************
*** 49,55 ****
  	$(CC) $(CFLAGS) -c $(SRCDIR)/users.c
  
  usersdbm.o: $(SRCDIR)/users.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -DDBM -o usersdbm.o -c $(SRCDIR)/users.c
  
  util.o: $(SRCDIR)/util.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/util.c
--- 183,189 ----
  	$(CC) $(CFLAGS) -c $(SRCDIR)/users.c
  
  usersdbm.o: $(SRCDIR)/users.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -DDBM_MODE -o usersdbm.o -c $(SRCDIR)/users.c
  
  util.o: $(SRCDIR)/util.c $(INCLUDES)
  	$(CC) $(CFLAGS) -c $(SRCDIR)/util.c
***************
*** 58,64 ****
  	$(CC) $(CFLAGS) -o version.o -c $(SRCDIR)/version.c
  
  versiondbm.o: $(SRCDIR)/version.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -DDBM -o versiondbm.o -c $(SRCDIR)/version.c
  
  radpass: radpass.o md5.o util.o
  	$(CC) $(CFLAGS) -o radpass radpass.o md5.o util.o $(LIBS)
--- 192,198 ----
  	$(CC) $(CFLAGS) -o version.o -c $(SRCDIR)/version.c
  
  versiondbm.o: $(SRCDIR)/version.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -DDBM_MODE -o versiondbm.o -c $(SRCDIR)/version.c
  
  radpass: radpass.o md5.o util.o
  	$(CC) $(CFLAGS) -o radpass radpass.o md5.o util.o $(LIBS)
***************
*** 69,83 ****
  md5.o: $(SRCDIR)/md5.c $(SRCDIR)/md5.h
  	$(CC) $(CFLAGS) -c $(SRCDIR)/md5.c
  
! builddbm: builddbm.o
! 	$(CC) $(CFLAGS) -o builddbm builddbm.o -ldbm $(LIBS)
  
  builddbm.o: $(SRCDIR)/builddbm.c
  	$(CC) $(CFLAGS) -c $(SRCDIR)/builddbm.c
  
  lint:
! 	-lint -hbacvx -DLINT $(SERVER_SRCS)
! 	-lint -hbacvx -DLINT ../radpass.c ../md5.c ../util.c
  
  clean:
! 	rm -f *.o radiusd radpass builddbm radiusd.dbm
--- 203,240 ----
  md5.o: $(SRCDIR)/md5.c $(SRCDIR)/md5.h
  	$(CC) $(CFLAGS) -c $(SRCDIR)/md5.c
  
! usr_read.o: $(SRCDIR)/usr_read.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -c $(SRCDIR)/usr_read.c
! 
! cexample: cexample.o md5.o util.o filters.o
! 	$(CC) $(CFLAGS) -o cexample cexample.o md5.o util.o filters.o $(LIBS)
! 
! cexample.o: $(SRCDIR)/cexample.c $(INCLUDES)
! 	$(CC) $(CFLAGS) -c $(SRCDIR)/cexample.c
! 
! builddbm: builddbm.o usr_read.o
! 	$(CC) $(CFLAGS) -o builddbm builddbm.o usr_read.o $(DBMLIBS) $(LIBS)
  
  builddbm.o: $(SRCDIR)/builddbm.c
  	$(CC) $(CFLAGS) -c $(SRCDIR)/builddbm.c
  
+ filters.o: $(SRCDIR)/filters.c $(SRCDIR)/radius.h
+ 	$(CC) $(CFLAGS) -c $(SRCDIR)/filters.c
+ 
+ cache.o: $(SRCDIR)/cache.c $(SRCDIR)/cache.h
+ 	$(CC) $(CFLAGS) -c $(SRCDIR)/cache.c
+ 
+ cache: $(SRCDIR)/cache.c $(SRCDIR)/cache.h
+ 	$(CC) $(CFLAGS) -DAUTOTEST -o ./cache $(SRCDIR)/cache.c
+ 
  lint:
! 	-lint -hbacvx -DLINT $(SOURCES)
  
  clean:
! 	rm -f *.o radiusd radpass builddbm radiusd.dbm cache \
! 	cexample
! 
! tar:
! 	@-rm radius.tar
! 	tar cvf radius.tar Makefile *h *c
! 
diff -c -N radiusd/acct.c ascendd/acct.c
*** radiusd/acct.c	Thu Jan 11 17:26:16 1996
--- ascendd/acct.c	Wed May 22 13:24:20 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS Accounting
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)acct.c	1.2 (95/07/25 00:55:24)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 43,53 ****
  #include	<signal.h>
  #include	<errno.h>
  #include	<sys/wait.h>
  
  #include	"radius.h"
  
! extern char		recv_buffer[4096];
! extern char		send_buffer[4096];
  extern char		*progname;
  extern int		debug_flag;
  extern char		*radacct_dir;
--- 45,57 ----
  #include	<signal.h>
  #include	<errno.h>
  #include	<sys/wait.h>
+ #include	<sys/stat.h>
  
  #include	"radius.h"
+ #include	"protos.h"
  
! extern char		recv_buffer[];
! extern char		send_buffer[];
  extern char		*progname;
  extern int		debug_flag;
  extern char		*radacct_dir;
***************
*** 56,69 ****
  extern UINT4		warning_seconds;
  extern int		errno;
  
! UINT4			calctime();
! 
  rad_accounting(authreq, activefd)
  AUTH_REQ	*authreq;
  int		activefd;
  {
  	FILE		*outfd;
- 	char		*ip_hostname();
  	char		clientname[128];
  	char		buffer[512];
  	VALUE_PAIR	*pair;
--- 60,71 ----
  extern UINT4		warning_seconds;
  extern int		errno;
  
! void
  rad_accounting(authreq, activefd)
  AUTH_REQ	*authreq;
  int		activefd;
  {
  	FILE		*outfd;
  	char		clientname[128];
  	char		buffer[512];
  	VALUE_PAIR	*pair;
***************
*** 82,91 ****
  	 */
  	sprintf(buffer, "%s/%s/detail", radacct_dir, clientname);
  	if((outfd = fopen(buffer, "a")) == (FILE *)NULL) {
! 		sprintf(buffer,
! 			"Acct: Couldn't open file %s/%s/detail\n",
  			radacct_dir, clientname);
- 		log_err(buffer);
  		/* don't respond if we can't save record */
  	} else {
  
--- 84,91 ----
  	 */
  	sprintf(buffer, "%s/%s/detail", radacct_dir, clientname);
  	if((outfd = fopen(buffer, "a")) == (FILE *)NULL) {
! 		log_err( "Acct: Couldn't open file %s/%s/detail\n",
  			radacct_dir, clientname);
  		/* don't respond if we can't save record */
  	} else {
  
***************
*** 123,128 ****
--- 123,129 ----
   *
   *************************************************************************/
  
+ void
  send_acct_reply(authreq, reply, msg, activefd)
  AUTH_REQ	*authreq;
  VALUE_PAIR	*reply;
***************
*** 138,144 ****
  	UINT4			lvalue;
  	u_char			digest[16];
  	int			secretlen;
- 	char			*ip_hostname();
  
  	auth = (AUTH_HDR *)send_buffer;
  
--- 139,144 ----
***************
*** 163,169 ****
  		case PW_TYPE_STRING:
  			len = strlen(reply->strvalue);
  			*ptr++ = len + 2;
! 			strcpy(ptr, reply->strvalue);
  			ptr += len;
  			total_length += len + 2;
  			break;
--- 163,169 ----
  		case PW_TYPE_STRING:
  			len = strlen(reply->strvalue);
  			*ptr++ = len + 2;
! 			strcpy((char *)ptr, reply->strvalue);
  			ptr += len;
  			total_length += len + 2;
  			break;
***************
*** 199,207 ****
  	auth->length = htons(total_length);
  
  	/* Calculate the response digest */
! 	secretlen = strlen(authreq->secret);
  	memcpy(send_buffer + total_length, authreq->secret, secretlen);
! 	md5_calc(digest, (char *)auth, total_length + secretlen);
  	memcpy(auth->vector, digest, AUTH_VECTOR_LEN);
  	memset(send_buffer + total_length, 0, secretlen);
  
--- 199,207 ----
  	auth->length = htons(total_length);
  
  	/* Calculate 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);
  
diff -c -N radiusd/attrprint.c ascendd/attrprint.c
*** radiusd/attrprint.c	Thu Jan 11 17:26:16 1996
--- ascendd/attrprint.c	Thu Jan 11 17:26:11 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)attrprint.c	1.2 (95/07/25 00:55:25)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 40,45 ****
--- 42,48 ----
  #include	<ctype.h>
  
  #include	"radius.h"
+ #include	"protos.h"
  
  /*************************************************************************
   *
***************
*** 50,60 ****
   *
   *************************************************************************/
  
  fprint_attr_val(fd, pair)
  FILE		*fd;
  VALUE_PAIR	*pair;
  {
- 	DICT_VALUE	*dict_valget();
  	DICT_VALUE	*dval;
  	char		buffer[32];
  	u_char		*ptr;
--- 53,63 ----
   *
   *************************************************************************/
  
+ void
  fprint_attr_val(fd, pair)
  FILE		*fd;
  VALUE_PAIR	*pair;
  {
  	DICT_VALUE	*dval;
  	char		buffer[32];
  	u_char		*ptr;
***************
*** 96,101 ****
--- 99,118 ----
  					gmtime((time_t *)&pair->lvalue));
  		fprintf(fd, "%s = \"%s\"", pair->name, buffer);
  		break;
+ 
+ #if defined( BINARY_FILTERS )
+ 	case PW_TYPE_FILTER_BINARY:
+ 		{
+ 		    int		ix;
+ 
+ 		    fprintf( fd, "%s =", pair->name );
+ 		    for ( ix = 0; ix < pair->lvalue; ix += 1 ) {
+ 			fprintf( fd, " %02x", (u_char)(pair->strvalue[ ix ]) );
+ 		    }
+ 		    printf( "\n" );
+ 		}
+ 		break;
+ #endif /* BINARY_FILTERS */
  
  	default:
  		fprintf(fd, "Unknown type %d", pair->type);
diff -c -N radiusd/builddbm.c ascendd/builddbm.c
*** radiusd/builddbm.c	Thu Jan 11 17:26:16 1996
--- ascendd/builddbm.c	Wed May 22 13:30:36 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)builddbm.c	1.2 (95/07/25 00:55:26)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 28,33 ****
--- 30,45 ----
  static char sccsid[] =
  "@(#)builddbm.c	1.4 Copyright 1992 Livingston Enterprises Inc";
  
+ #if defined(SOLARIS)
+ #       include <sys/uio.h>
+ #       include <unistd.h>
+ #       include <sys/stat.h>
+ #       include <fcntl.h>
+ #       include </usr/ucbinclude/dbm.h>
+ #else
+ #       include <dbm.h>
+ #endif  /* SOLARIS */
+ 
  #include	<sys/types.h>
  #include	<sys/socket.h>
  #include	<sys/time.h>
***************
*** 36,97 ****
  
  #include	<stdio.h>
  #include	<netdb.h>
! #include	<strings.h>
  #include	<pwd.h>
  #include	<time.h>
  #include	<ctype.h>
- #include	<dbm.h>
  
  #include	"radius.h"
  
  char		*progname;
- int		debug_flag;
  char		*radius_dir;
! 
! #define FIND_MODE_NAME	0
! #define FIND_MODE_REPLY	1
! #define FIND_MODE_SKIP	2
! #define FIND_MODE_FLUSH	3
! 
  FILE		*userfd;
! 
! main(argc,argv)
! int argc;
! char **argv;
  {
  	char	name[128];
! 	char	content[1024];
! 	char 	*progname;
  	int	fd;
  	datum	named;
  	datum	contentd;
  
  	progname = *argv;
  
! 	if((fd = open("users.pag", O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
! 		fprintf(stderr, "%s: Couldn't open users.pag for writing\n",progname);
  		exit(-1);
  	}
  	close(fd);
! 	if((fd = open("users.dir", O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
! 		fprintf(stderr, "%s: Couldn't open users.dir for writing\n",progname);
  		exit(-1);
  	}
  	close(fd);
! 	radius_dir = ".";
! 	if(dbminit("users") != 0) {
! 		fprintf(stderr, "%: Couldn't init dbm\n",progname);
  		exit(-1);
  	}
  
! 	while(user_read(name, content) == 0) {
  		named.dptr = name;
! 		named.dsize = strlen(name);
  		contentd.dptr = content;
! 		contentd.dsize = strlen(content);
  		if(store(named, contentd) != 0) {
! 			fprintf(stderr, "%s: Couldn't store datum for %s\n",
! 				progname,name);
  			exit(-1);
  		}
  	}
--- 48,170 ----
  
  #include	<stdio.h>
  #include	<netdb.h>
! #include	<string.h>
  #include	<pwd.h>
  #include	<time.h>
  #include	<ctype.h>
  
  #include	"radius.h"
+ #include	"protos.h"
  
  char		*progname;
  char		*radius_dir;
! char		*radius_users;
! char		pagfile[256];
! char		dirfile[256];
! char		dbmname[256];
  FILE		*userfd;
! FILE		*errf;
! extern int	curParseLine;
! int		verbose;
! extern int	warning;
! 
! #if !defined(FALSE)
! #  define FALSE	0
! #  define TRUE	1
! #endif
! 
! int main P__((int argc, char **argv));
! 
! int
! main(argc, argv)
! int	argc;
! char	**argv;
  {
  	char	name[128];
! 	char	content[4 * 1024];
  	int	fd;
  	datum	named;
  	datum	contentd;
+ 	int	c;
+ 	extern char	*optarg;
+ 	extern int	optind, opterr;
  
  	progname = *argv;
+ 	radius_dir = RADIUS_DIR;
+ 	radius_users = RADIUS_USERS;
+ 	errf = stderr;
+ 	verbose = FALSE;
+ 	warning = TRUE;
+ 
+ 	while( (c = getopt(argc, argv, "d:ehu:v")) != -1 ) {
+ 		switch( c ) {
+ 		case 'h':
+ 		case '?':
+ 		default:
+ 			printf("\n%s options are:\n%s%s%s%s%s\n", progname,
+ 				"\t-d dir\t\tradius directory\n",
+ 				"\t-e\t\tuse stdout rather than stderr\n",
+ 				"\t-h\t\trequest help\n",
+ 				"\t-u file\t\tradius users file\n",
+ 				"\t-v\t\tverbose mode\n"
+ 			);
+ 			return -1;
+ 		case 'd':
+ 			radius_dir = optarg;
+ 			break;
+ 		case 'e':
+ 			errf = stdout;
+ 			break;
+ 		case 'u':
+ 			radius_users = optarg;
+ 			break;
+ 		case 'v':
+ 			verbose = TRUE;
+ 			break;
+ 		}
+ 	}
  
! 	sprintf(pagfile, "%s/%s.pag", radius_dir, radius_users);
! 	if((fd = open(pagfile, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
! 		fprintf(errf, "%s: Couldn't open %s for writing\n",
! 			progname, pagfile);
  		exit(-1);
  	}
  	close(fd);
! 	sprintf(dirfile, "%s/%s.dir", radius_dir, radius_users);
! 	if((fd = open(dirfile, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
! 		fprintf(errf, "%s: Couldn't open %s for writing\n",
! 			progname, dirfile);
  		exit(-1);
  	}
  	close(fd);
! 	sprintf(dbmname, "%s/%s", radius_dir, radius_users);
! 	if(dbminit(dbmname) != 0) {
! 		fprintf(errf, "%s: Couldn't dbminit(%s)\n", progname, dbmname);
  		exit(-1);
  	}
  
! 	userfd = (FILE *)NULL;
! 	curParseLine = 0;
! 	while(user_read(&userfd, name, content) == 0) {
! 		if( verbose ) {
! 			fprintf(stdout, "\nRECORD: {'%s', '%s'}\n",
! 				name, content);
! 		}
! 		named.dptr = name;
! 		named.dsize = strlen(name)+1;
! 		contentd = fetch(named);
! 		if(contentd.dsize != 0) {	/* name found */
! 			warn("Skipping duplicate record\n\tfor user '%s'", name);
! 			continue;
! 		}
  		named.dptr = name;
! 		named.dsize = strlen(name)+1;
  		contentd.dptr = content;
! 		contentd.dsize = strlen(content)+1;
  		if(store(named, contentd) != 0) {
! 			fprintf(errf, "%s: Couldn't store datum for %s\n",
! 				progname, name);
  			exit(-1);
  		}
  	}
***************
*** 99,187 ****
  	exit(0);
  }
  
- /*************************************************************************
-  *
-  *	Function: user_read
-  *
-  *	Purpose: Return each user in the database - name is key content
-  *		 is 2 strings - check values, and reply values seperated
-  *		 by a newline.
-  *
-  *************************************************************************/
- 
- user_read(name, content)
- char	*name;
- char	*content;
- {
- 	char		buffer[256];
- 	char		*ptr;
- 	int		namelen;
- 	int		mode;
- 	VALUE_PAIR	*check_first;
- 	VALUE_PAIR	*reply_first;
- 
- 	/*
- 	 * Open the user table
- 	 */
- 	if(userfd == (FILE *)NULL) {
- 		sprintf(buffer, "%s/%s", radius_dir, RADIUS_USERS);
- 		if((userfd = fopen(buffer, "r")) == (FILE *)NULL) {
- 			fprintf(stderr, "%s:Couldn't open %s for reading\n",
- 					progname, buffer);
- 			exit(-1);
- 		}
- 	}
- 
- 	mode = FIND_MODE_NAME;
- 
- 	while(fgets(buffer, sizeof(buffer), userfd) != (char *)NULL) {
- 		if(mode == FIND_MODE_NAME) {
- 			/*
- 			 * Find the entry starting with the users name
- 			 */
- 			if(*buffer != '#' && *buffer != '\t') {
- 				ptr = buffer;
- 				while(*ptr != ' ' && *ptr != '\t' &&
- 								*ptr != '\0') {
- 					*name++ = *ptr++;
- 				}
- 				*name = '\0';
- 				if(*ptr == '\0') {
- 					continue;
- 				}
- 				ptr++;
- 				while(*ptr == ' ' || *ptr == '\t') {
- 					ptr++;
- 				}
- 				strcpy(content, ptr);
- 				content += strlen(content);
- 				mode = FIND_MODE_REPLY;
- 			}
- 		}
- 		else {
- 			if(*buffer == ' ' || *buffer == '\t') {
- 				ptr = buffer;
- 				while(*ptr == ' ' || *ptr == '\t') {
- 					ptr++;
- 				}
- 				strcpy(content, ptr);
- 				content += strlen(content);
- 				content -= 2;
- 				while(*content == ' ' || *content == '\t' ) {
- 					content--;
- 				}
- 				content++;
- 				*content = '\0';
- 				if(*(content - 1) != ',') {
- 					return(0);
- 				}
- 			}
- 			else {
- 				/* We are done */
- 				return(0);
- 			}
- 		}
- 	}
- 	fclose(userfd);
- 	return(-1);
- }
--- 172,174 ----
diff -c -N radiusd/cache.c ascendd/cache.c
*** radiusd/cache.c	Wed Dec 31 16:00:00 1969
--- ascendd/cache.c	Thu Jan 11 17:26:11 1996
***************
*** 0 ****
--- 1,596 ----
+ /*
+  * ASCEND: @(#)cache.c	1.0 (95/10/05 00:55:30)
+  *
+  *      Copyright (c) 1994 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *	Permission to copy all or part of this material for any purpose is
+  *	granted provided that the above copyright notice and this paragraph
+  *	are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *	AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *	FOR A PARTICULAR PURPOSE.
+  */
+ 
+ #include	<sys/types.h>
+ #include	<ctype.h>
+ #if defined(BSDI)
+ #	include	<memory.h>
+ #	include	<stdlib.h>
+ #else
+ #	include	<malloc.h>
+ #endif
+ #include	<string.h>
+ #include	<sys/time.h>
+ 
+ #if defined(AUTOTEST)
+ #	include	<stdio.h>
+ #	include	<unistd.h>
+ #endif
+ 
+ #include	"cache.h"
+ 
+ static CONST HASHVAL *
+ 	_hlist_search P__((HASHLIST **list, CONST HASHKEY *key, int len));
+ static HASHLIST *
+ 	_hlist_locate P__((HASHLIST **list, CONST HASHKEY *key, int len));
+ static void _hlist_expire P__((HASHLIST **list));
+ static int _key_expired P__((HASHLIST *elem));
+ static int _idle_expired P__((HASHLIST *elem));
+ static time_t _calc_expiration P__((time_t duration));
+ 
+ #if defined(AUTOTEST)
+ 	static CACHE	cache;	/* allocate storage for the cache */
+ 	int	count[BUCKETS];	/* allocater storage for statistics */
+ #	define log_err	printf	/* debug output */
+ #else
+ 	extern int log_err P__((char *msg));
+ #endif
+ 
+ 
+ 	/*
+ 	 * Initialise the given cache.
+ 	 */
+ void
+ cache_init(cache, buckets)
+ 	CACHE	*cache;
+ 	int		buckets;
+ {
+ 	int	i;
+ 
+ 	cache->buckets = buckets;
+ 	for( i = 0; i < buckets; i++ ) {
+ 		cache->table[i] = (HASHLIST *)0;
+ #if defined(AUTOTEST)
+ 		count[i] = 0;
+ #endif
+ 	}
+ }
+ 
+ 	/*
+ 	 * Search the given cache for the indicated key.
+ 	 *
+ 	 * Return a pointer to the associated value or (HASHVAL *)0
+ 	 * if the key is not found
+ 	 */
+ CONST HASHVAL *
+ cache_search(cache, key, len)
+ 	CACHE		*cache;
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ {
+ 	int bucket;
+ 
+ 	if( len > MAX_KEY_LEN ) {
+ 		return (CONST HASHVAL *)0;
+ 	}
+ 
+ 	bucket = hash(key, len, cache->buckets);
+ 	if( cache->table[bucket] ) {
+ 		return _hlist_search(&cache->table[bucket], key, len);
+ 	}
+ 	else {
+ 		return (CONST HASHVAL *)0;
+ 	}
+ }
+ 
+ 	/*
+ 	 * Insert the given info into the given cache.
+ 	 *
+ 	 * Checks for duplicate records.
+ 	 *
+ 	 * Return TRUE if inserted, else FALSE.
+ 	 */
+ int
+ cache_insert(cache, key, key_len, val, val_len, duration, idle)
+ 	CACHE		*cache;
+ 	CONST HASHKEY	*key;
+ 	int		key_len;
+ 	CONST HASHVAL	*val;
+ 	int		val_len;
+ 	time_t		duration;
+ 	time_t		idle;
+ {
+ 	HASHLIST *elem;
+ 	int bucket;
+ 	CONST HASHVAL *res;
+ 
+ 	if( (key_len > MAX_KEY_LEN) || (val_len > MAX_VAL_LEN) ) {
+ 		return FALSE;
+ 	}
+ 
+ 	bucket = hash(key, key_len, cache->buckets);
+ 	res = _hlist_search(&cache->table[bucket], key, key_len);
+ 
+ 	if( res && (res != ELEM_DELETED) ) {
+ 		return FALSE;	/* already in table, delete first */
+ 	}
+ 
+ 	elem = (HASHLIST *)malloc(sizeof(*elem));
+ 	if( !elem ) {
+ 		return FALSE;
+ 	}
+ 	else {
+ 		elem->next = cache->table[bucket];
+ 		memcpy(elem->key, key, (size_t)key_len);
+ 		memcpy(elem->val, val, (size_t)val_len);
+ 		elem->time = _calc_expiration(duration);
+ 		if( idle ) {
+ 			elem->idle = _calc_expiration(idle);
+ 			elem->idling = TRUE;
+ 		}
+ 		else {
+ 			elem->idling = FALSE;
+ 		}
+ 		cache->table[bucket] = elem;
+ #if defined(AUTOTEST)
+ 		count[bucket]++;
+ #endif
+ #if(0)
+ 		printf("cache_insert(key<%d><%s>, val<%d><%s>)\n",
+ 			key_len, elem->key, val_len, elem->val);
+ #endif
+ 		return TRUE;
+ 	}
+ }
+ 
+ 	/*
+ 	 * Delete the given info from the given cache.
+ 	 *
+ 	 * Return TRUE if deleted, else FALSE.
+ 	 */
+ int
+ cache_delete(cache, key, len)
+ 	CACHE		*cache;
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ {
+ 	int bucket;
+ 	HASHLIST *cur;
+ 	HASHLIST *prev = (HASHLIST *)0;
+ 	HASHLIST **list;
+ 	int result = FALSE;
+ 
+ 	if( len > MAX_KEY_LEN ) {
+ 		return FALSE;
+ 	}
+ 
+ 	bucket = hash(key, len, cache->buckets);
+ 	list = &cache->table[bucket];
+ 
+ 	for( cur = *list; cur; prev = cur, cur = cur->next ) {
+ 		if( memcmp(key, cur->key, len) == 0 ) {
+ 			if( prev ) {
+ 				prev->next = cur->next;
+ 			}
+ 			else {
+ 				*list = cur->next;
+ 			}
+ 			free(cur);
+ 			result = TRUE;
+ 			break;
+ 		}
+ 	}
+ 	return result;
+ }
+ 
+ 	/*
+ 	 * Update the idle time for this entry
+ 	 *
+ 	 * Return TRUE if elem has idling enabled, else FALSE.
+ 	 */
+ int
+ cache_idle_update(cache, key, len, idle)
+ 	CACHE		*cache;
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ 	time_t		idle;
+ {
+ 	int bucket;
+ 	HASHLIST *elem;
+ 
+ 	if( len > MAX_KEY_LEN ) {
+ 		return FALSE;
+ 	}
+ 
+ 	bucket = hash(key, len, cache->buckets);
+ 	elem = _hlist_locate(&cache->table[bucket], key, len);
+ 	elem->idle = _calc_expiration(idle);
+ 	return elem->idling;
+ }
+ 
+ 	/*
+ 	 * Search the given cache for expired keys and delete them.
+ 	 */
+ void
+ cache_expire(cache)
+ 	CACHE		*cache;
+ {
+ 	int ix;
+ 
+ 	for(ix = 0; ix < cache->buckets; ix++ ) {
+ 		_hlist_expire(&cache->table[ix]);
+ 	}
+ }
+ 
+ 	/*
+ 	 * Search the given hash list for the indicated key.
+ 	 *
+ 	 * Return a pointer to the associated elem or (HASHLIST *)0
+ 	 * if the key is not found.
+ 	 */
+ static HASHLIST *
+ _hlist_locate(list, key, len)
+ 	HASHLIST	**list;
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ {
+ 	HASHLIST *cur;
+ 	HASHLIST *prev;
+ 	HASHLIST *retval = (HASHLIST *)0;
+ 	prev = (HASHLIST *)0;
+ 	for( cur = *list; cur; prev = cur, cur = cur->next ) {
+ 		if( memcmp(key, cur->key, len) == 0 ) {
+ 			retval = cur;
+ 			break;
+ 		}
+ 	}
+ 
+ 	return retval;
+ }
+ 
+ 	/*
+ 	 * Search the given hash list for the indicated key.
+ 	 *
+ 	 * Return a pointer to the associated value or (HASHVAL *)0
+ 	 * if the key is not found.
+ 	 *
+ 	 * For efficiency, delete the search entry if it has expired
+ 	 * and return ELEM_DELETED.
+ 	 *
+ 	 * Does NOT update the idle timer.
+ 	 */
+ static CONST HASHVAL *
+ _hlist_search(list, key, len)
+ 	HASHLIST	**list;
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ {
+ 	HASHLIST *cur;
+ 	HASHLIST *prev;
+ 	HASHVAL *retval = (HASHVAL *)0;
+ 	prev = (HASHLIST *)0;
+ 	for( cur = *list; cur; prev = cur, cur = cur->next ) {
+ 		if( memcmp(key, cur->key, len) == 0 ) {
+ 			retval = cur->val;
+ 			break;
+ 		}
+ 	}
+ 	if( retval ) {
+ 		if( (_key_expired(cur) == TRUE)
+ 		 || ((cur->idling == TRUE) && (_idle_expired(cur) == TRUE)) ) {
+ 			/* delete it now */
+ 			if( prev ) {
+ 				prev->next = cur->next;
+ 			}
+ 			else {
+ 				*list = cur->next;
+ 			}
+ 			retval = ELEM_DELETED;
+ 			free(cur);
+ 		}
+ 	}
+ 	return (CONST HASHVAL *)retval;
+ }
+ 
+ 	/*
+ 	 * Search for and delete all expired keys
+ 	 */
+ static void
+ _hlist_expire(list)
+ 	HASHLIST	**list;
+ {
+ 	HASHLIST *cur;
+ 	HASHLIST *prev;
+ 	prev = (HASHLIST *)0;
+ 	for( cur = *list; cur; prev = cur, cur = cur->next ) {
+ 		if( _key_expired(cur) ) {
+ 			if( prev ) {
+ 				prev->next = cur->next;
+ 			}
+ 			else {
+ 				*list = cur->next;
+ 			}
+ 			free(cur);
+ 		}
+ 	}
+ }
+ 
+ 	/*
+ 	 * Return TRUE if the key has expired
+ 	 */
+ static int
+ _key_expired(elem)
+ 	HASHLIST	*elem;
+ {
+ 	time_t curtime;
+ 
+ 	curtime = time((time_t *)0);
+ 	if( curtime == -1 ) {
+ 		log_err("_key_expired(): time() err\n");
+ 		return TRUE;
+ 	}
+ 	if( curtime > elem->time ) {
+ 		return TRUE;
+ 	}
+ 	else {
+ 		return FALSE;
+ 	}
+ }
+ 
+ 	/*
+ 	 * Return TRUE if the idle timer has expired
+ 	 */
+ static int
+ _idle_expired(elem)
+ 	HASHLIST	*elem;
+ {
+ 	time_t curtime;
+ 
+ 	curtime = time((time_t *)0);
+ 	if( curtime == -1 ) {
+ 		log_err("_key_expired(): time() err\n");
+ 		return TRUE;
+ 	}
+ 	if( curtime > elem->idle ) {
+ 		return TRUE;
+ 	}
+ 	else {
+ 		return FALSE;
+ 	}
+ }
+ 
+ 	/*
+ 	 * Calculate the time of expiration
+ 	 */
+ static time_t
+ _calc_expiration(duration)
+ 	time_t duration;
+ {
+ 	time_t curtime;
+ 
+ 	curtime = time((time_t *)0);
+ 	if( curtime == -1 ) {
+ 		log_err("_calc_expiration(): time() err\n");
+ 		return duration;
+ 	}
+ 	curtime += duration;
+ 	return curtime;
+ }
+ 
+ #define	CRC_16_POLY	(0xa001)
+ /*--------------------------------------------------------------------
+ 		NOTE: 17 (_not_ 16) terms [x^16 ... x^0]
+ 
+ 		X^16 + X^15 + X^2 + 1
+ 			= 1 1000 0000 0000 0101
+ 			= 18005
+ 			  ^ implicit value (17th bit)
+ 
+ 		Inverting the bit order yields
+ 			= 1010 0000 0000 0001 1
+ 			= a0011
+ 			      ^ implicit value (17th  bit)
+ --------------------------------------------------------------------*/
+ 
+ /**********************************************************
+ 
+ 
+ 	CRC-16 Byte Look-up Table <initial crc=0>
+ 
+ 		P(x) = a001 = 1 + x^2 + x^15 + x^16
+ 
+ 	This table was automatically generated by 'gencrc'
+ 
+ 	-- Do _NOT_ modify this table manually ! --
+ 
+ 	Table generated on Thu Oct  5 16:45:34 1995
+ 
+ 
+ **********************************************************/
+ 
+ unsigned short crc_16_lookup[256] = {
+ 	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 
+ 	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 
+ 	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 
+ 	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 
+ 	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 
+ 	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 
+ 	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 
+ 	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 
+ 	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 
+ 	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 
+ 	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 
+ 	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 
+ 	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 
+ 	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 
+ 	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 
+ 	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 
+ 	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 
+ 	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 
+ 	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 
+ 	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 
+ 	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 
+ 	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 
+ 	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 
+ 	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 
+ 	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 
+ 	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 
+ 	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 
+ 	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 
+ 	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 
+ 	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 
+ 	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 
+ 	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+ };
+ 
+ /*-----------------------------------------------------------------------
+ 	unsigned short hash(key, len, buckets)
+ 
+ 
+ 	Calculate the CRC-16 of a key, modulo MODULUS
+ 
+ 		uses the x^16 + x^15 + x^2 + 1 CRC-16 algorithm
+ 
+ 	Return the 16 bit crc number for a key, mod BUCKETS
+ 	Uses a 256 entry table lookup
+ -----------------------------------------------------------------------*/
+ u_short
+ hash(key, len, buckets)
+ 	CONST HASHKEY	*key;
+ 	int		len;
+ 	int		buckets;
+ {
+ 	u_short	n, crc;
+ 	int	i;
+   
+ 	crc = 0;
+ 	for( i = len; i; i-- ) {
+ 		n = *key++;	
+ 		n ^= crc;
+ 		crc = crc_16_lookup[n & 0xff] ^ (crc >> 8);
+ 	}
+ 	return (unsigned int)crc % (unsigned int)buckets;
+ }
+ 
+ /**********************************************************************
+ 
+ 			Test code
+ 
+ **********************************************************************/
+ 
+ #if defined(AUTOTEST)
+ int
+ main(argc, argv)
+ 	int	argc;
+ 	char	**argv;
+ {
+ 	FILE	*fd;
+ 	HASHVAL	*val;
+ 	int	stat;
+ 	char	buf[101];
+ 	int	least, most;
+ 	int	i;
+ 	char	*words;		/* typically, /usr/dict/words */
+ 	int	len;
+ 
+ 	if( argc < 2 ) {
+ 		words = "/usr/dict/words";
+ 	}
+ 	else {
+ 		words = argv[1];
+ 	}
+ 
+ 	cache_init(&cache, BUCKETS);
+ 	fd = fopen(words, "r");
+ 	if( !fd ) {
+ 		printf("Error opening '%s'\n", words);
+ 		exit(-1);
+ 	}
+ 
+ 	while( fgets(buf, 100, fd) != (char *)0 ) {
+ 		len = strlen(buf);
+ 		if( buf[len-1] == '\n' ) {
+ 			buf[len-1] = '\0';
+ 			len--;
+ 		}
+ 		len = MIN((size_t)MAX_KEY_LEN, strlen(buf));
+ 		buf[len+1] = '\0';
+ 		stat = cache_insert(&cache, buf, len+1, buf, len+1, 1000);
+ 		printf("Insert key <%s>: %s\n",
+ 				buf, (stat == TRUE) ? "GOOD" : "BAD");
+ 	}
+ 
+ 	least = most = 0;
+ 	for( i = 0; i < BUCKETS; i++ ) {
+ 		if( count[i] < least ) {
+ 			least = count[i];
+ 		}
+ 		else if( count[i] > most ) {
+ 			most = count[i];
+ 		}
+ 	}
+ 	printf("\n\nShortest List Len=%d, Longest List Len=%d\n\n",
+ 		least, most);
+ 	for( i = 0; i < BUCKETS; i++ ) {
+ 		if( (i % 10) == 0 ) {
+ 			printf("\ncount[%4x]= ", i);
+ 		}
+ 		printf("%4d ", count[i]);
+ 	}
+ 	printf("\n\n");
+ 
+ 	rewind(fd);
+ 	while( fgets(buf, 100, fd) != (char *)0 ) {
+ 		char	*msg;
+ 
+ 		len = strlen(buf);
+ 		if( buf[len-1] == '\n' ) {
+ 			buf[len-1] = '\0';
+ 			len--;
+ 		}
+ 		len = MIN((size_t)MAX_KEY_LEN, strlen(buf));
+ 		buf[len+1] = '\0';
+ 		val = (HASHVAL *)cache_search(&cache, buf, len+1);
+ 		if( val && (val != ELEM_DELETED) ) {
+ 			msg = "GOOD";
+ 		}
+ 		else if( val == ELEM_DELETED ) {
+ 			msg = "DELETED";
+ 		}
+ 		else {
+ 			msg = "BAD";
+ 		}
+ 		printf("Search key <%s, %s>: %s\n", buf, val, msg);
+ 	}
+ 
+ 	rewind(fd);
+ 	while( fgets(buf, 100, fd) != (char *)0 ) {
+ 		len = strlen(buf);
+ 		if( buf[len-1] == '\n' ) {
+ 			buf[len-1] = '\0';
+ 			len--;
+ 		}
+ 		len = MIN((size_t)MAX_KEY_LEN, strlen(buf));
+ 		buf[len+1] = '\0';
+ 		stat = cache_delete(&cache, buf, len+1);
+ 		printf("Delete key <%s>: %s\n",
+ 				buf, (stat == TRUE) ? "GOOD" : "BAD");
+ 	}
+ 
+ 	fclose(fd);
+ 	return 0;
+ }
+ 
+ #endif
+ 
diff -c -N radiusd/cache.h ascendd/cache.h
*** radiusd/cache.h	Wed Dec 31 16:00:00 1969
--- ascendd/cache.h	Thu Jan 11 17:26:11 1996
***************
*** 0 ****
--- 1,91 ----
+ /*
+  * ASCEND: @(#)cache.h	1.0 (95/10/05 00:55:30)
+  *
+  *      Copyright (c) 1994 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *	Permission to copy all or part of this material for any purpose is
+  *	granted provided that the above copyright notice and this paragraph
+  *	are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *	AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *	FOR A PARTICULAR PURPOSE.
+  */
+ 
+ #if defined(__STDC__) && (__STDC__ == 1)
+ #	define	P__(s)	s
+ #	define	CONST	const
+ #else
+ #	define	P__(s)	(/* s */)
+ #	define	CONST	/* const */
+ #endif
+ 
+ #if !defined(TRUE)
+ #	define TRUE	1
+ #endif
+ #if !defined(FALSE)
+ #	define FALSE	0
+ #endif
+ 
+ #if !defined(MIN)
+ #	define MIN(x, y)	(((x) < (y)) ? (x) : (y))
+ #endif
+ 
+ #if !defined(MAX)
+ #	define MAX(x, y)	(((x) > (y)) ? (x) : (y))
+ #endif
+ 
+ #define MAX_KEY_LEN	16
+ #define MAX_VAL_LEN	16
+ 
+ 	/*
+ 	 * BUCKETS should be prime; try 1021, 2039, 4093, 8191, 16381.
+ 	 * More buckets use more statically allocated memory but decrease
+ 	 * the length of the hash lists, which will improve performance.
+ 	 */
+ #define BUCKETS	4093
+ 
+ typedef char HASHKEY;
+ typedef char HASHVAL;
+ 
+ #define ELEM_DELETED	((HASHVAL *)(-1))
+ 
+ typedef struct HASHLIST {
+ 	struct HASHLIST	*next;
+ 	HASHKEY		key[MAX_KEY_LEN + 1];
+ 	HASHVAL		val[MAX_VAL_LEN + 1];
+ 	time_t		time;
+ 	int		idling;		/* Boolean: use idle time */
+ 	time_t		idle;
+ } HASHLIST;
+ 
+ typedef struct CACHE {
+ 	int		buckets;
+ 	HASHLIST	*table[BUCKETS];
+ } CACHE;
+ 
+ typedef enum CacheMsgType {
+ 	CACHE_INSERT	= 1,
+ 	CACHE_DELETE,
+ 	CACHE_IDLE_UPDATE
+ } CacheMsgType;
+ 
+ typedef struct CacheMsg {
+ 	CacheMsgType	type;
+ 	HASHKEY		key[MAX_KEY_LEN + 1];
+ 	HASHVAL		val[MAX_VAL_LEN + 1];
+ 	time_t		time;
+ 	time_t		idle;
+ } CacheMsg;
+ 
+ 	/***********************/
+ 	/* function prototypes */
+ 	/***********************/
+ 
+ u_short hash P__((CONST HASHKEY *key, int len, int buckets));
+ void cache_init P__((CACHE *dict, int buckets));
+ CONST HASHVAL *cache_search P__((CACHE *dict, CONST HASHKEY *key, int len));
+ int cache_insert P__((CACHE *dict, CONST HASHKEY *key, int key_len, CONST HASHVAL *val, int val_len, time_t duration, time_t idle));
+ int cache_delete P__((CACHE *dict, CONST HASHKEY *key, int len));
+ void cache_expire P__((CACHE *dict));
+ int cache_idle_update P__((CACHE *cache, CONST HASHKEY *key, int len, time_t idle));
diff -c -N radiusd/cexample.c ascendd/cexample.c
*** radiusd/cexample.c	Wed Dec 31 16:00:00 1969
--- ascendd/cexample.c	Fri Feb 16 14:44:24 1996
***************
*** 0 ****
--- 1,584 ----
+ /*
+  * ASCEND: %W% (%E% %U%)
+  *
+  *      Copyright (c) 1993 Ascend Communications, Inc.
+  *      All rights reserved.
+  *      Use of copyright notice does not imply publication.
+  *
+  *
+  *                      CONFIDENTIAL INFORMATION
+  *                      -------------------------
+  *	This Document contains Confidential Information or Trade Secrets,
+  *	or both, which are the property of Ascend Communications, Inc.
+  *	This document may not be copied, reproduced, reduced to any
+  *	electronic medium or machine readable form or otherwise duplicated
+  *	and the information herein may not be used, disseminated or
+  *	otherwise disclosed, except with the prior written consent of
+  *	Ascend Communications, Inc.
+  *
+  *	Ascend Communications, Inc. makes no representations about
+  *	the suitability of this software for any purpose.  It is
+  *	provided "as is" without express or implied warranty.
+  */
+ 
+ #include	<sys/types.h>
+ #include	<sys/socket.h>
+ #include	<netinet/in.h>
+ 
+ #include	<stdio.h>
+ #include	<unistd.h>
+ #include	<netdb.h>
+ #include	<pwd.h>
+ #include	<stdlib.h>
+ #include	<sys/time.h>	/* gettimeofday() */
+ 
+ #include	"radius.h"
+ #include	"protos.h"
+ 
+ 
+ u_char		recv_buffer[4096];
+ u_char		send_buffer[4096];
+ u_char		*progname;
+ int		sockfd;
+ u_char		debug_flag;
+ 
+ static void usage P__((void));
+         /* udp_port promoted due to old-style function definition */
+ static int result_recv P__((u_char *buffer, int length, u_char	*vector,
+   				char	*secret));
+ static int make_pkt P__(( u_char *buffer, int maxlen, UINT4 sessionId,
+ 		char *userName, UINT4 ipAddr,
+ 		int newMsgId, char *secret,
+ 		int sendChgFilterCmd,
+ 		VALUE_PAIR *filterHdr ));
+ static int get_client_secret P__(( UINT4 rqstIpAddr, char *secret ));
+ int filterBinary P__((VALUE_PAIR *pair, char *valStr ));
+ 
+ int main P__((int argc, u_char **argv));
+ 
+ int
+ main( argc, argv )
+   int		argc;
+   u_char	**argv;
+ {
+ 	int			salen;
+ 	int			result;
+ 	struct	sockaddr	salocal;
+ 	struct	sockaddr	saremote;
+ 	struct	sockaddr_in	*sin;
+ 	struct	servent		*svp;
+         u_short                 svc_port;
+ 	UINT4			serv_ipaddr;
+ 	int			total_length;
+ 	int			i;
+ 	char			argval;
+ 	char			anotherArg;
+ 
+ 	UINT4			sessionId;
+ 	char			*userName;
+ 	UINT4			ipAddr;
+ 	char			*psecret;
+ 	AUTH_HDR		*auth;
+ 	VALUE_PAIR		*pair;
+ 	VALUE_PAIR		*filterPairHdr;
+ 	VALUE_PAIR		*filterPairTail;
+ 	int			sendChgFilterCmd;
+ 	char			*pchar;
+ 	char			secret[256];
+ 
+ 
+ 	progname = *argv++;
+ 	argc--;
+ 
+ 	filterPairHdr = filterPairTail = NULL;
+ 	sessionId = 0;
+ 	userName = NULL;
+ 	psecret = NULL;
+ 	ipAddr = 0;
+ 	debug_flag = 0;
+ 	svc_port = 0;
+ 	serv_ipaddr = 0;
+ 	sendChgFilterCmd = 0;
+ 
+ 
+ 	while(argc) {
+ 
+ 	    if( **argv != '-' ) {
+ 		printf("missing minus sign, got %c\n", **argv);
+ 		usage();
+ 	    }
+ 
+ 	    argval = *(*argv + 1);
+ 	    argc--;
+ 	    argv++;
+ 
+ 	    anotherArg = 1;
+ 
+ 	    switch(argval) {
+ 
+ 		case 'P':		/* UDP port of server */
+ 		    if (argc == 0) {
+ 			printf("missing port#\n");
+ 			usage();
+ 		    }
+ 		    svc_port = atoi((char *)*argv);
+ 		    break;
+ 
+ 		case 'H':		/* IP addr of server */
+ 		    if (argc == 0) {
+ 			printf("missing server addr\n");
+ 			usage();
+ 		    }
+ 		    serv_ipaddr = get_ipaddr( (char *)*argv );
+ 		    break;
+ 
+ 		case 'K':		/* shared secret */
+ 		    if (argc == 0) {
+ 			printf("missing secret\n");
+ 			usage();
+ 		    }
+ 		    psecret = (char *) *argv;
+ 		    break;
+ 
+ 		case 's':		/* session ID# assoc w session */
+ 		    if (argc == 0) {
+ 			printf("missing session ID#\n");
+ 			usage();
+ 		    }
+ 		    sessionId = atol((char *)*argv);
+ 		    break;
+ 		
+ 		case 'u':		/* name of user assoc w session */
+ 		    if (argc == 0) {
+ 			printf("missing user name\n");
+ 			usage();
+ 		    }
+ 		    userName = (char *) *argv;
+ 		    break;
+ 		
+ 		case 'i':		/* IP addr assoc w session */
+ 		    if (argc == 0) {
+ 			printf("missing IP addr\n");
+ 			usage();
+ 		    }
+ 		    ipAddr = get_ipaddr( (char *)*argv );
+ 		    break;
+ 
+ 		case 'c':		/* call filter */
+ 		case 'd':		/* data filter */
+ 		    if (argc == 0) {
+ 			printf("missing filter string\n");
+ 			usage();
+ 		    }
+ 		    sendChgFilterCmd = 1;
+ 		    pchar = (char *)*argv;
+ 		    if ( *pchar == 0 ) {	/* ...NULL filter??? */
+ 			break;
+ 		    }
+  		    if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
+ 						(VALUE_PAIR *)NULL) {
+ 			printf("%s: no memory\n", progname);
+ 			exit(MEMORY_ERR);
+ 		    }
+ 		    if ( argval == 'd' ) {
+ 			strcpy(pair->name, "Ascend-Data-Filter");
+ 			pair->attribute = 242;
+ 		    } else {
+ 			strcpy(pair->name, "Ascend-Call-Filter");
+ 			pair->attribute = 243;
+ 		    }
+ 		    pair->type = PW_TYPE_FILTER_BINARY;
+ 
+ 		    if ( filterBinary( pair, (char *)*argv ) != -1 ) {
+ 			pair->size = pair->lvalue;
+ 			pair->next = NULL;
+ 			if ( filterPairTail ) {
+ 			    filterPairTail->next = pair;
+ 			    filterPairTail = pair;
+ 			} else {
+ 			    filterPairHdr = filterPairTail = pair;
+ 			}
+ 		    }
+ 		    break;
+ 
+ 		case 'x':		/* debugging flag */
+ 		    debug_flag = 1;
+ 		    anotherArg = 0;
+ 		    break;
+ 		
+ 		default:
+ 		    printf("unknown argval = %c\n", argval);
+ 		    usage();
+ 	    }
+ 	    if ( anotherArg ) {
+ 		argc--;
+ 		argv++;
+ 	    }
+ 	}/*while*/
+ 
+ 	if ( serv_ipaddr && !psecret ) {
+ 	    if ( get_client_secret( serv_ipaddr, secret ) ) {
+ 		psecret = &secret[0];
+ 	    }
+ 	}
+ 
+ 	if ( !psecret ||
+ 	     !svc_port ||
+ 	     !serv_ipaddr ) {
+ 	    (void) perror ("missing server addr, port, or secret");
+ 	    exit(-1);
+ 	}
+ 
+ 	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
+ 	if (sockfd < 0) {
+ 	    (void) perror ("socket");
+ 	    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 = htons(svc_port);
+ 
+ 	if ( bind( sockfd, &salocal, sizeof (struct sockaddr_in) ) < 0 ) {
+ 	    close(sockfd);
+ 	    (void) perror ("bind");
+ 	    exit(-1);
+ 	}
+ 
+ 	/* Build a session disconnect OR change filter request pkt... */
+ 	total_length = make_pkt( send_buffer,
+ 					sizeof(send_buffer),
+ 					sessionId,
+ 					userName,
+ 					ipAddr,
+ 					1,
+ 					psecret,
+ 					sendChgFilterCmd,
+ 					filterPairHdr );
+ 
+ 	if ( debug_flag ) {
+ 	    printf( "sessionId = %lu, name=<%s>, IpAddr=%lx, len=%d\n",
+ 			sessionId, userName, ipAddr, total_length );
+ 	    pair = filterPairHdr;
+ 	    while ( pair ) {
+ 		printf( "filter len = %d\n", pair->size );
+ 		pair = pair->next;
+ 	    }
+ 	}
+ 
+ 
+ 	auth = (AUTH_HDR *) send_buffer;
+ 
+ 	sin = (struct sockaddr_in *) & saremote;
+         memset ((char *) sin, '\0', sizeof (saremote));
+ 	sin->sin_family = AF_INET;
+ 	/*** sin->sin_addr.s_addr = serv_ipaddr; ***/
+ 	sin->sin_addr.s_addr = htonl(serv_ipaddr);
+ 	sin->sin_port = htons(svc_port);
+ 
+ 	if ( debug_flag ) {
+ 	    printf( "sending rqst to %lx at port=%d\n",
+ 			serv_ipaddr, svc_port );
+ 	}
+ 	sendto(sockfd, (char *)send_buffer, (int)total_length, (int)0,
+ 			&saremote, sizeof(struct sockaddr_in));
+ 
+ 	salen = sizeof (saremote);
+ 	result = recvfrom (sockfd, (char *) recv_buffer,
+ 			(int) sizeof(recv_buffer),
+ 			(int) 0, & saremote, & salen);
+ 
+ 	if(result > 0) {
+ 	    result_recv( recv_buffer, result, auth->vector, psecret );
+ 	    exit(0);
+ 	}
+ 	(void) perror ("recv");
+ 	close(sockfd);
+ 	return 0;
+ }
+ 
+ int
+ result_recv( buffer, length, vector, secret )
+   u_char	*buffer;
+   int		length;
+   u_char	*vector;
+   char		*secret;
+ {
+ 	AUTH_HDR	*auth;
+ 	int		totallen;
+ 	u_char		reply_digest[AUTH_VECTOR_LEN];
+ 	u_char		calc_digest[AUTH_VECTOR_LEN];
+ 	int		secretlen;
+ 
+ 	auth = (AUTH_HDR *)buffer;
+ 	totallen = ntohs(auth->length);
+ 
+ 	if (totallen > length) {
+ 	    printf("invalid reply length from server!\n");
+ 	    return(-1);
+ 	}
+ 
+ 	/* check the reply digest... */
+ 	memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN);
+ 	memcpy(auth->vector, vector, AUTH_VECTOR_LEN);
+ 	secretlen = strlen(secret);
+ 	memcpy(buffer + totallen, secret, secretlen);
+ 	md5_calc( calc_digest, (u_char *)auth, totallen + secretlen );
+ 
+ 	if(memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) {
+ 	    printf("invalid reply digest from server!\n");
+ 	    return(-2);
+ 	}
+ 
+ 	switch (auth->code) {
+ 	    case PW_ASCEND_DISCONNECT_ACK:
+ 		printf("Session disconnect accepted!\n");
+ 		break;
+ 	    case PW_ASCEND_DISCONNECT_NAK:
+ 		printf("Session disconnect rejected!\n");
+ 		break;
+ 	    case PW_ASCEND_CHG_FILTERS_ACK:
+ 		printf("Filter change accepted!\n");
+ 		break;
+ 	    case PW_ASCEND_CHG_FILTERS_NAK:
+ 		printf("Filter change rejected!\n");
+ 		break;
+ 	    default:
+ 		printf("Received unknown code: %u!\n", auth->code);
+ 		break;
+ 	}
+ 	return( 0 );
+ }
+ 
+ void
+ usage()
+ {
+ 	printf("Usage: %s -H servAddr -P port -K secret "
+ 		"[-s sessID] [-u name] [-i ipAddr] [-x]\n",
+ 				progname);
+ 	exit(-1);
+ }
+ 
+ 
+     /*
+      * make_pkt
+      *
+      *	Build an Ascend RADIUS disconnect request OR
+      *	change filter packet.
+      *	Function returns length of packet if successfully
+      *	built otherwise returns 0.
+      *
+      *	buffer		Buffer to hold request packet.
+      *
+      *	maxlen		Max length of 'buffer'.
+      *
+      *	sessionId	Reference number of the session to
+      *			disconnect or change filters for.
+      *			Sent only if NOT 0.
+      *
+      *	userName	User name associated with the session
+      *			to disconnect or change filters for.
+      *			Sent only if NOT NULL AND NOT the NULL
+      *			string.
+      *
+      *	ipAddr		IP address of the session to disconnect
+      *			or change filters for.
+      *			Sent only if NOT 0.
+      *
+      *	newMsgId	The message ID value to load.
+      *
+      *	secret		Pointer to the shared secret used in
+      *			doing MD5 calculations.
+      *
+      *	filterPairHdr	List of filters to send.  If NULL then
+      *			this is a disconnect request packet.
+      */
+ int
+ make_pkt( buffer, maxlen, sessionId,
+ 		userName, ipAddr, newMsgId, secret,
+ 		sendChgFilterCmd, filterPairHdr )
+   u_char	*buffer;
+   int		maxlen;
+   UINT4		sessionId;
+   char		*userName;
+   UINT4		ipAddr;
+   int		newMsgId;
+   char		*secret;
+   int		sendChgFilterCmd;
+   VALUE_PAIR	*filterPairHdr;
+ {
+     AUTH_HDR	*auth;			/* ptr to packet header fields */
+     int		pktLen;			/* packet length */
+     int		md5_len;		/* #bytes for MD5 calculations */
+     u_char	*ptr;			/* working ptr */
+ 
+     char	sessBuf[12];		/* to convert sessionId to a str */
+     int		sessBufLen;		/* length of 'sessBuf' */
+     int		userNameLen;		/* length of 'userName' */
+     int		secretLen;		/* length of 'secret' */
+     UINT4	lvalue;
+     u_char	digest[ AUTH_VECTOR_LEN ];	/* to hold results of the */
+ 						/*   MD5 calculations */
+     VALUE_PAIR	*p;			/* for traversing filter list */
+ 
+ 
+ 	/* determine the length of the packet... */
+ 	pktLen = AUTH_HDR_LEN;
+ 
+ 	if ( sessionId ) {
+ 	    sprintf( sessBuf, "%lu", sessionId );
+ 	    sessBufLen = strlen( sessBuf );
+ 	    pktLen += 2 + sessBufLen;
+ 	}
+ 
+ 	if ( userName && *userName ) {
+ 	    userNameLen = strlen( userName );
+ 	    pktLen += 2 + userNameLen;
+ 	} else {
+ 	    userNameLen = 0;
+ 	}
+ 
+ 	if ( ipAddr ) {
+ 	    pktLen += 2 + sizeof(UINT4);
+ 	}
+ 
+ 	/* make sure caller provided at least 1 session specifier... */
+ 	/* quit if none were given... */
+ 	if ( pktLen == AUTH_HDR_LEN ) {
+ 	    fprintf(stderr, "make_pkt: no session specifier!\n");
+ 	    return(0);
+ 	}
+ 
+ 	if ( sendChgFilterCmd ) {
+ 	    for( p = filterPairHdr; p; p=p->next ) {
+ 		pktLen += 2 + p->lvalue;
+ 	    }
+ 	}
+ 
+ 	secretLen = strlen(secret);
+ 
+ 	/* we calculate the MD5 digest over the entire packet */
+ 	/* with the shared secret appended so make sure buffer */
+ 	/* is large enough... */
+ 	md5_len = pktLen + secretLen;
+ 	if ( md5_len > maxlen ) {
+ 	    fprintf(stderr, "make_pkt: buffer too short!\n");
+ 	    return(0);
+ 	}
+ 
+ 	memset( buffer, 0, pktLen );
+ 
+ 	/* load packet header EXCEPT authenticator field... */
+ 	auth = (AUTH_HDR *)send_buffer;
+ 	auth->code   = (sendChgFilterCmd ? PW_ASCEND_CHG_FILTERS_REQUEST :
+ 					   PW_ASCEND_DISCONNECT_REQUEST);
+ 	auth->id     = (u_char) newMsgId;
+ 	auth->length = htons(pktLen);
+ 
+ 	/* now load the attributes... */
+ 
+ 	ptr = auth->data;
+ 
+ 	/* add the session ID if it was given... */
+ 	if ( sessionId ) {
+ 	    *ptr++ = PW_ACCT_SESSION_ID;
+ 	    *ptr++ = 2 + sessBufLen;
+ 	    memcpy( ptr, sessBuf, sessBufLen );
+ 	    ptr += sessBufLen;
+ 	}
+ 
+ 	/* add the user name if it was given... */
+ 	if ( userNameLen ) {
+ 	    *ptr++ = PW_USER_NAME;
+ 	    *ptr++ = 2 + userNameLen;
+ 	    memcpy( ptr, userName, userNameLen );
+ 	    ptr += userNameLen;
+ 	}
+ 
+ 	/* add the IP address if it was given... */
+ 	if ( ipAddr ) {
+ 	    *ptr++ = PW_FRAMED_ADDRESS;
+ 	    *ptr++ = 2 + sizeof(UINT4);
+ 	    lvalue = htonl(ipAddr);
+ 	    memcpy( ptr, (u_char *)&lvalue, sizeof(UINT4) );
+ 	    ptr += sizeof(UINT4);
+ 	}
+ 
+ 	if ( sendChgFilterCmd ) {
+ 	    for( p = filterPairHdr; p; p=p->next ) {
+ 		*ptr++ = p->attribute;
+ 		*ptr++ = 2 + p->lvalue;
+ 		memcpy( ptr, p->strvalue, p->lvalue );
+ 		ptr += p->lvalue;
+ 	    }
+ 	}
+ 
+ 	/* append the shared secret... */
+ 	memcpy( ptr, secret, secretLen );
+ 
+ 	/* calculate the MD5 digest over the entire packet */
+ 	/* with the shared secret appended...note that the */
+ 	/* authenticator field is all 0's at this point... */
+ 	md5_calc( digest, buffer, md5_len );
+ 
+ 	/* copy resulting digest into authenticator field... */
+ 	memcpy( auth->vector, digest, AUTH_VECTOR_LEN );
+ 
+ 	/* for security reasons, zero out the secret from buffer... */
+ 	memset( ptr, 0, secretLen );
+ 
+ 	return( pktLen );				/* A-OK */
+ }
+ 
+     /* Dummy stub module needed by filterBinary() */
+     /* or one of its subordinates. */
+ int
+ log_err(msg)
+   char *msg;
+ {
+ }
+ 
+ int
+ get_client_secret( rqstIpAddr, secret )
+   UINT4   rqstIpAddr;
+   char    *secret;
+ {
+ 	FILE    *clientfd;
+ 	u_char  buf[1024];
+ 	UINT4   ipaddr;
+ 	char	hostnm[256];
+ 
+ 	secret[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) {
+ 		printf( "%s: Couldn't open %s to find client\n",
+ 				progname, buf);
+ 		return( 0 );
+ 	}
+ 	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;
+ 		}
+ 		secret[0] = '\0';
+ 	}
+ 	fclose(clientfd);
+ 
+ 	return ( ipaddr ? 1 : 0 );
+ }
+ 
diff -c -N radiusd/conf.h ascendd/conf.h
*** radiusd/conf.h	Thu Jan 11 17:26:16 1996
--- ascendd/conf.h	Thu Jan 11 17:26:11 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)conf.h	1.2 (95/07/25 00:55:28)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 30,52 ****
   */
  
  #if defined(__alpha) && defined(__osf__)
  typedef unsigned int	UINT4;
  #else
  typedef unsigned long	UINT4;
  #endif
  
  #if defined(unixware) || defined(sys5) || defined(M_UNIX)
  #include        <string.h>
  #else   /* unixware */
  #include        <strings.h>
  #endif  /* unixware */
  
! #if defined(bsdi)
  #include        <machine/inline.h>
  #include        <machine/endian.h>
! #else	/* bsdi */
  #include        <malloc.h>
! #endif	/* bsdi */
  
  #if defined(aix)
  #include	<sys/select.h>
--- 32,61 ----
   */
  
  #if defined(__alpha) && defined(__osf__)
+ #define UINT4_IS_UINT
  typedef unsigned int	UINT4;
  #else
+ #define UINT4_IS_ULONG
  typedef unsigned long	UINT4;
  #endif
  
  #if defined(unixware) || defined(sys5) || defined(M_UNIX)
  #include        <string.h>
  #else   /* unixware */
+ #if defined(SOLARIS)
+ #include	<string.h>
+ #else
  #include        <strings.h>
+ #endif
  #endif  /* unixware */
  
! #if defined(BSDI)
  #include        <machine/inline.h>
  #include        <machine/endian.h>
! #include        <stdlib.h>
! #else	/* BSDI */
  #include        <malloc.h>
! #endif	/* BSDI */
  
  #if defined(aix)
  #include	<sys/select.h>
diff -c -N radiusd/des.c ascendd/des.c
*** radiusd/des.c	Wed Dec 31 16:00:00 1969
--- ascendd/des.c	Thu Jan 11 17:26:13 1996
***************
*** 0 ****
--- 1,586 ----
+ /*** NOTICE *** NOTICE *** NOTICE *** NOTICE *** NOTICE *** NOTICE ***
+  ***                                                               ***
+  ***                  CONFIDENTIAL INFORMATION                     ***
+  ***                  ------------------------                     ***
+  ***    This Document contains Confidential Information or         ***
+  ***    Trade Secrets, or both, which are the property of Ascend   ***
+  ***    Communications, Inc.  This document may not be  copied,    ***
+  ***    reproduced, reduced to any electronic medium or machine    ***
+  ***    readable form or otherwise duplicated and the information  ***
+  ***    herein may not be used, disseminated or otherwise          ***
+  ***    disclosed, except with the prior written consent of Ascend ***
+  ***    Communications, Inc.                                       ***
+  ***                                                               ***
+  *** NOTICE *** NOTICE *** NOTICE *** NOTICE *** NOTICE *** NOTICE ***/
+ 
+ /*
+  *  @(#)des.c	26.3 (96/01/02 15:21:37)
+     Copyright 1995 Ascend Communications, Inc.
+     All rights reserved.
+     Use of copyright notice does not imply publication.
+  */
+ 
+ /* des.c,  /appletalk/source,  Garth Conboy,  10/05/88  */
+ /* Public domain code.  */
+ 
+ /* FILE: DES.C
+  * Overview:  This file contains routines that implement software
+  * 	DES functions.
+  *
+  */
+ 
+ #include <assert.h>
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include "radius.h"
+ #include "des.h"
+ 
+ /* Static routines */
+ 
+ static void spinit P__((void));
+ static void perminit P__((char perm[16][16][8], char p[64]));
+ static void permute P__((char *inblock, char perm[16][16][8],char *outblock));
+ static void round P__((int num, long unsigned *block));
+ static long unsigned f P__((long unsigned r, unsigned char subkey[8]));
+ 
+ 
+ /* Tables defined in the Data Encryption Standard documents */
+ 
+ /* initial permutation IP */
+ static char ip[] = {
+   58, 50, 42, 34, 26, 18, 10, 2,
+   60, 52, 44, 36, 28, 20, 12, 4,
+   62, 54, 46, 38, 30, 22, 14, 6,
+   64, 56, 48, 40, 32, 24, 16, 8,
+   57, 49, 41, 33, 25, 17, 9, 1,
+   59, 51, 43, 35, 27, 19, 11, 3,
+   61, 53, 45, 37, 29, 21, 13, 5,
+   63, 55, 47, 39, 31, 23, 15, 7
+ };
+ 
+ /* final permutation IP^-1 */
+ static char fp[] = {
+   40, 8, 48, 16, 56, 24, 64, 32,
+   39, 7, 47, 15, 55, 23, 63, 31,
+   38, 6, 46, 14, 54, 22, 62, 30,
+   37, 5, 45, 13, 53, 21, 61, 29,
+   36, 4, 44, 12, 52, 20, 60, 28,
+   35, 3, 43, 11, 51, 19, 59, 27,
+   34, 2, 42, 10, 50, 18, 58, 26,
+   33, 1, 41, 9, 49, 17, 57, 25
+ };
+ 
+ /*
+  * expansion operation matrix This is for reference only; it is unused in the
+  * code as the f() function performs it implicitly for speed
+  */
+ 
+ #if 0
+ static char ei[] = {
+   32, 1, 2, 3, 4, 5,
+   4, 5, 6, 7, 8, 9,
+   8, 9, 10, 11, 12, 13,
+   12, 13, 14, 15, 16, 17,
+   16, 17, 18, 19, 20, 21,
+   20, 21, 22, 23, 24, 25,
+   24, 25, 26, 27, 28, 29,
+   28, 29, 30, 31, 32, 1
+ };
+ 
+ #endif
+ 
+ /* permuted choice table (key) */
+ static char pc1[] = {
+   57, 49, 41, 33, 25, 17, 9,
+   1, 58, 50, 42, 34, 26, 18,
+   10, 2, 59, 51, 43, 35, 27,
+   19, 11, 3, 60, 52, 44, 36,
+ 
+   63, 55, 47, 39, 31, 23, 15,
+   7, 62, 54, 46, 38, 30, 22,
+   14, 6, 61, 53, 45, 37, 29,
+   21, 13, 5, 28, 20, 12, 4
+ };
+ 
+ /* number left rotations of pc1 */
+ static char totrot[] = {
+   1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
+ };
+ 
+ /* permuted choice key (table) */
+ static char pc2[] = {
+   14, 17, 11, 24, 1, 5,
+   3, 28, 15, 6, 21, 10,
+   23, 19, 12, 4, 26, 8,
+   16, 7, 27, 20, 13, 2,
+   41, 52, 31, 37, 47, 55,
+   30, 40, 51, 45, 33, 48,
+   44, 49, 39, 56, 34, 53,
+   46, 42, 50, 36, 29, 32
+ };
+ 
+ /* The (in)famous S-boxes */
+ static char si[8][64] = {
+   /* S1 */ {
+   14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+   0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+   4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+   15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
+   },
+ 
+   /* S2 */ {
+   15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+   3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+   0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+   13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
+   },
+ 
+   /* S3 */ {
+   10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+   13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+   13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+   1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
+   },
+ 
+   /* S4 */ {
+   7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+   13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+   10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+   3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
+   },
+ 
+   /* S5 */ {
+   2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+   14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+   4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+   11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
+   },
+ 
+   /* S6 */ {
+   12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+   10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+   9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+   4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
+   },
+ 
+   /* S7 */ {
+   4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+   13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+   1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+   6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
+   },
+ 
+   /* S8 */ {
+   13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+   1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+   7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+   2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+   }
+ };
+ 
+ /* 32-bit permutation function P used on the output of the S-boxes */
+ static char p32i[] = {
+   16, 7, 20, 21,
+   29, 12, 28, 17,
+   1, 15, 23, 26,
+   5, 18, 31, 10,
+   2, 8, 24, 14,
+   32, 27, 3, 9,
+   19, 13, 30, 6,
+   22, 11, 4, 25
+ };
+ 
+ /* End of DES-defined tables */
+ 
+ /* Lookup tables initialized once only at startup by desinit() */
+ static long (*sp)[64];		/* Combined S and P boxes */
+ 
+ static char (*iperm)[16][8];	/* Initial and final permutations */
+ static char (*fperm)[16][8];
+ 
+ /* 8 6-bit subkeys for each of 16 rounds, initialized by setkey() */
+ static unsigned char (*kn)[8];
+ 
+ /* bit 0 is left-most in byte */
+ static int bytebit[] = {
+   0200, 0100, 040, 020, 010, 04, 02, 01
+ };
+ 
+ static int nibblebit[] = {
+   010, 04, 02, 01
+ };
+ static int desmode;
+ 
+ /*
+  * Allocate space and initialize DES lookup arrays mode == 0: standard Data
+  * Encryption Algorithm mode == 1: DEA without initial and final permutations
+  * for speed mode == 2: DEA without permutations and with 128-byte key
+  * (completely independent subkeys for each round)
+  * for now I assume that this is called with mode 0.
+  */
+ 
+ int 
+ des_init()
+ {
+   int mode   = 0;
+ 
+ 
+   if (sp != 0) {		/* Already inited? */
+     /* If the tables are already allocated don't alloca again. */
+     return( MEMORY_ERR );
+   }
+ 
+   desmode = mode;
+ 
+   if ((sp = (long (*)[64]) malloc(sizeof(long) * 8 * 64)) == 0) {
+     return( MEMORY_ERR );
+   }
+ 
+   spinit();
+   if ((kn = (unsigned char (*)[8]) malloc(sizeof(char) * 8 * 16)) == 0) {
+     free(sp);
+     return( MEMORY_ERR );
+   }
+   if (mode == 1 || mode == 2)	/* No permutations */
+     return( MEMORY_ERR ) ;
+ 
+   if ((iperm = (char (*)[16][8]) malloc(sizeof(char) * 16 * 16 * 8)) == 0) {
+     free(sp);
+     free(kn);
+     return( MEMORY_ERR );
+   }
+   perminit(iperm, ip);
+ 
+   if ((fperm = (char (*)[16][8]) malloc(sizeof(char) * 16 * 16 * 8)) == 0) {
+     free(sp);
+     free(kn);
+     free(iperm);
+     return( MEMORY_ERR );
+   }
+   perminit(fperm, fp);
+   return(0);
+ }				/* desinit */
+ 
+ /* Free up storage used by DES */
+ void 
+ desdone()
+ {
+   if (sp == 0)
+     return;			/* Already done */
+ 
+   free(sp);
+   free(kn);
+   if (iperm != 0)
+     free(iperm);
+   if (fperm != 0)
+     free(fperm);
+ 
+   sp = 0;
+   iperm = 0;
+   fperm = 0;
+   kn = 0;
+ 
+ }				/* desdone */
+ 
+ /* Set key (initialize key schedule array) */
+ 
+ void 
+ dessetkey(key)
+ char *key;
+ {				/* 64 bits (will use only 56) */
+   char pc1m[56];		/* place to modify pc1 into */
+   char pcr[56];			/* place to rotate pc1 into */
+ 
+   register int i, j, l;
+   int m;
+ 
+   /*
+    * In mode 2, the 128 bytes of subkey are set directly from the user's key,
+    * allowing him to use completely independent subkeys for each round. Note
+    * that the user MUST specify a full 128 bytes.
+    * 
+    * I would like to think that this technique gives the NSA a real headache,
+    * but I'm not THAT naive.
+    */
+ 
+   if (desmode == 2) {
+     for (i = 0; i < 16; i++)
+       for (j = 0; j < 8; j++)
+ 	kn[i][j] = *key++;
+     return;
+   }
+   /* Clear key schedule */
+ 
+   for (i = 0; i < 16; i++)
+     for (j = 0; j < 8; j++)
+       kn[i][j] = 0;
+ 
+   for (j = 0; j < 56; j++) {	/* convert pc1 to bits of key */
+     l = pc1[j] - 1;		/* integer bit location  */
+     m = l & 07;			/* find bit              */
+     pc1m[j] = (char) ((key[l >> 3] &	/* find which key byte l is in */
+ 		       bytebit[m])	/* and which bit of that byte */
+ 		      ? 1 : 0);	/* and store 1-bit result */
+   }
+ 
+   for (i = 0; i < 16; i++) {	/* key chunk for each iteration */
+     for (j = 0; j < 56; j++)	/* rotate pc1 the right amount */
+       pcr[j] = pc1m[(l = j + totrot[i]) < (j < 28 ? 28 : 56) ? l : l - 28];
+ 
+     /* rotate left and right halves independently */
+ 
+     for (j = 0; j < 48; j++) {	/* select bits individually */
+       /* check bit that goes to kn[j] */
+ 
+       if (pcr[pc2[j] - 1]) {
+ 	/* mask it in if it's there */
+ 
+ 	l = j % 6;
+ 	kn[i][j / 6] |= (unsigned char) (bytebit[l] >> 2);
+       }
+     }
+   }
+ 
+ }				/* setkey */
+ 
+ /* In-place encryption of 64-bit block */
+ 
+ void 
+ endes(block)
+ char *block;
+ {
+   register int i;
+   unsigned long work[2];	/* Working data storage */
+   long unsigned tmp;
+ 
+   permute(block, iperm, (char *) work);	/* Initial Permutation */
+ 
+   work[0] = ntohl(work[0]);
+   work[1] = ntohl(work[1]);
+ 
+   /* Do the 16 rounds */
+ 
+   for (i = 0; i < 16; i++)
+     round(i, work);
+ 
+   /* Left/right half swap */
+ 
+   tmp = work[0];
+   work[0] = work[1];
+   work[1] = tmp;
+ 
+   work[0] = ntohl(work[0]);
+   work[1] = ntohl(work[1]);
+ 
+   permute((char *) work, fperm, block);	/* Inverse initial permutation */
+ 
+ }				/* endes */
+ 
+ /* In-place decryption of 64-bit block */
+ 
+ void 
+ dedes(block)
+ char *block;
+ {
+   register int i;
+   unsigned long work[2];	/* Working data storage */
+   long unsigned tmp;
+ 
+   permute(block, iperm, (char *) work);	/* Initial permutation */
+ 
+   work[0] = ntohl(work[0]);
+   work[1] = ntohl(work[1]);
+ 
+   /* Left/right half swap */
+ 
+   tmp = work[0];
+   work[0] = work[1];
+   work[1] = tmp;
+ 
+   /* Do the 16 rounds in reverse order */
+ 
+   for (i = 15; i >= 0; i--)
+     round(i, work);
+ 
+   work[0] = ntohl(work[0]);
+   work[1] = ntohl(work[1]);
+ 
+   permute((char *) work, fperm, block);	/* Inverse initial permutation */
+ 
+ }				/* dedes */
+ 
+ /* Permute inblock with perm */
+ 
+ static void 
+ permute(inblock,
+ 	perm,	/* 2K bytes defining perm. */
+ 	outblock)
+ char *inblock;
+ char perm[16][16][8];
+ char *outblock;
+ {				/* result into outblock,64 bits */
+   register int i, j;
+   register char *ib, *ob;	/* ptr to input or output block */
+   register char *p, *q;
+ 
+   if (perm == 0) {
+     /* No permutation, just copy */
+ 
+     for (i = 8; i != 0; i--)
+       *outblock++ = *inblock++;
+     return;
+   }
+   /* Clear output block */
+ 
+   for (i = 8, ob = outblock; i != 0; i--)
+     *ob++ = 0;
+ 
+   ib = inblock;
+   for (j = 0; j < 16; j += 2, ib++) {	/* for each input nibble */
+     ob = outblock;
+     p = perm[j][(*ib >> 4) & 017];
+     q = perm[j + 1][*ib & 017];
+     for (i = 8; i != 0; i--)	/* and each output byte */
+       *ob++ |= (char) (*p++ | *q++);	/* OR the masks together */
+   }
+ 
+ }				/* permute */
+ 
+ /* Do one DES cipher round */
+ 
+ static void 
+ round(num,			/* i.e. the num-th one   */
+       block)
+ int num;
+ long unsigned *block;
+ {
+ 
+   /*
+    * The rounds are numbered from 0 to 15. On even rounds the right half is
+    * fed to f() and the result exclusive-ORs the left half; on odd rounds the
+    * reverse is done.
+    */
+ 
+   if (num & 1)
+     block[1] ^= f(block[0], kn[num]);
+   else
+     block[0] ^= f(block[1], kn[num]);
+ 
+ }				/* round */
+ 
+ /* The nonlinear function f(r,k), the heart of DES */
+ 
+ static long unsigned 
+ f(r,		/* 32 bits */
+   subkey)
+ long unsigned r;
+ unsigned char subkey[8];
+ {				/* 48-bit key for this round */
+   register unsigned long rval, rt;
+ 
+ #ifdef  TRACE
+   unsigned char *cp;
+   int i;
+ 
+   NU_printf("f(%08lx, %02x %02x %02x %02x %02x %02x %02x %02x) = ",
+ 	    r,
+ 	    subkey[0], subkey[1], subkey[2],
+ 	    subkey[3], subkey[4], subkey[5],
+ 	    subkey[6], subkey[7]);
+ #endif
+ 
+   /*
+    * Run E(R) ^ K through the combined S & P boxes This code takes advantage
+    * of a convenient regularity in E, namely that each group of 6 bits in
+    * E(R) feeding a single S-box is a contiguous segment of R.
+    */
+ 
+   rt = (r >> 1) | ((r & 1) ? (long unsigned) 0x80000000 : (long unsigned) 0);
+   rval = 0;
+   rval |= (long unsigned) sp[0][((rt >> 26) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[1][((rt >> 22) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[2][((rt >> 18) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[3][((rt >> 14) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[4][((rt >> 10) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[5][((rt >> 6) ^ *subkey++) & 0x3f];
+   rval |= (long unsigned) sp[6][((rt >> 2) ^ *subkey++) & 0x3f];
+   rt = (r << 1) | ((r & 0x80000000) ? (long unsigned) 1 : (long unsigned) 0);
+   rval |= (long unsigned) sp[7][(rt ^ *subkey) & 0x3f];
+ 
+ #ifdef  TRACE
+   NU_printf(" %08lx\n", rval);
+ #endif
+ 
+   return (rval);
+ 
+ }				/* f */
+ 
+ /* Initialize a perm array */
+ 
+ static void 
+ perminit(perm,	/* 64-bit, either init or final */
+ 	 p)
+ char perm[16][16][8];
+ char p[64];
+ {
+   register int l, j, k;
+   int i, m;
+ 
+   /* Clear the permutation array */
+ 
+   for (i = 0; i < 16; i++)
+     for (j = 0; j < 16; j++)
+       for (k = 0; k < 8; k++)
+ 	perm[i][j][k] = 0;
+ 
+   for (i = 0; i < 16; i++)	/* each input nibble position */
+     for (j = 0; j < 16; j++)	/* each possible input nibble */
+       for (k = 0; k < 64; k++) {/* each output bit position */
+ 	l = p[k] - 1;		/* where does this bit come from */
+ 	if ((l >> 2) != i)	/* does it come from input position? */
+ 	  continue;		/* if not, bit k is 0 */
+ 	if (!(j & nibblebit[l & 3]))
+ 	  continue;		/* any such bit in input? */
+ 	m = k & 07;		/* which bit is this in the byte */
+ 	perm[i][j][k >> 3] |= (char) bytebit[m];
+       }
+ 
+ }				/* perminit */
+ 
+ /* Initialize the lookup table for the combined S and P boxes */
+ static void 
+ spinit()
+ {
+   char pbox[32];
+ 
+   int p, i, s, j, rowcol;
+   long val;
+ 
+   /* Compute pbox, the inverse of p32i.  This is easier to work with. */
+ 
+   for (p = 0; p < 32; p++)
+     for (i = 0; i < 32; i++)
+       if (p32i[i] - 1 == p) {
+ 	pbox[p] = (char) i;
+ 	break;
+       }
+   for (s = 0; s < 8; s++)	/* For each S-box */
+     for (i = 0; i < 64; i++) {	/* For each possible input */
+       val = 0;
+ 
+       /*
+        * The row number is formed from the first and last bits; the column
+        * number is from the middle 4
+        */
+ 
+       rowcol = (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & 0xf);
+       for (j = 0; j < 4; j++)	/* For each output bit */
+ 	if (si[s][rowcol] & (8 >> (short) j))
+ 	  val |= 1L << (short) (31 - pbox[4 * s + j]);
+ 
+       sp[s][i] = val;
+ 
+     }
+ 
+ }				/* spinit */
+ 
diff -c -N radiusd/des.h ascendd/des.h
*** radiusd/des.h	Wed Dec 31 16:00:00 1969
--- ascendd/des.h	Thu Jan 11 17:26:13 1996
***************
*** 0 ****
--- 1,44 ----
+ 
+ /*
+  * ASCEND: DES.H
+  *
+  *      Copyright (c) 1995 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *      Permission to copy all or part of this material for any purpose is
+  *      granted provided that the above copyright notice and this paragraph
+  *      are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *      AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *      LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *      FOR A PARTICULAR PURPOSE.
+  *
+  *  - This file contains the prototypes for the externally visible
+  *    functions that exist in the des.c file.
+  */
+ 
+ 
+ #if !defined( DES_H)
+ #define DES_H
+ 
+ #if defined(__STDC__) && (__STDC__ == 1)
+ #       define  P__(s)  s
+ #       define  CONST   const
+ #else
+ #       define  P__(s)  (/* s */)
+ #       define  CONST   /* const */
+ #endif
+ 
+ /* ARA - DES defines */
+ #define 	ARA_PASS_LEN	8
+ 
+ 
+ /* External entries */
+ 
+ extern int  des_init P__((void));     
+ extern void des_done P__((void));   
+ extern void dessetkey P__((char *key));
+ extern void endes P__((char *block)); 
+ extern void dedes P__((char *block));
+ 
+ #endif
+ 
diff -c -N radiusd/dict.c ascendd/dict.c
*** radiusd/dict.c	Thu Jan 11 17:26:16 1996
--- ascendd/dict.c	Thu Jan 11 17:26:11 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)dict.c	1.2 (95/07/25 00:55:29)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 32,39 ****
--- 34,44 ----
  #include	<sys/types.h>
  #include	<pwd.h>
  #include	<ctype.h>
+ #include	<stdlib.h>
+ #include	<sys/time.h>	/* gettimeofday() */
  
  #include	"radius.h"
+ #include	"protos.h"
  
  extern char		*progname;
  extern int		debug_flag;
***************
*** 72,80 ****
  	if((dictfd = fopen(buffer, "r")) == (FILE *)NULL) {
  		fprintf (stderr, "%s: Couldn't open dictionary: %s\n",
  			progname, buffer);
! 		return(-1);
  	}
  
  	line_no = 0;
  	while(fgets(buffer, sizeof(buffer), dictfd) != (char *)NULL) {
  		line_no++;
--- 77,88 ----
  	if((dictfd = fopen(buffer, "r")) == (FILE *)NULL) {
  		fprintf (stderr, "%s: Couldn't open dictionary: %s\n",
  			progname, buffer);
! 		return DICTFILE_READ_ERR;
  	}
  
+ 	dictionary_attributes = (DICT_ATTR *)0;
+ 	dictionary_values = (DICT_VALUE *)0;
+ 
  	line_no = 0;
  	while(fgets(buffer, sizeof(buffer), dictfd) != (char *)NULL) {
  		line_no++;
***************
*** 92,115 ****
  				fprintf(stderr,
  			"%s: Invalid attribute on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
  			/*
  			 * Validate all entries
  			 */
! 			if(strlen(namestr) > 31) {
  				fprintf(stderr,
  			"%s: Invalid name length on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
  			if(!isdigit(*valstr)) {
  				fprintf(stderr,
  			"%s: Invalid value on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  			value = atoi(valstr);
  
--- 100,123 ----
  				fprintf(stderr,
  			"%s: Invalid attribute on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_ATTRLINE_ERR;
  			}
  
  			/*
  			 * Validate all entries
  			 */
! 			if(strlen(namestr) > (size_t)31) {
  				fprintf(stderr,
  			"%s: Invalid name length on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_ATTRNAME_ERR;
  			}
  
  			if(!isdigit(*valstr)) {
  				fprintf(stderr,
  			"%s: Invalid value on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_ATTRVALUE_ERR;
  			}
  			value = atoi(valstr);
  
***************
*** 125,135 ****
  			else if(strcmp(typestr, "date") == 0) {
  				type = PW_TYPE_DATE;
  			}
  			else {
  				fprintf(stderr,
  			"%s: Invalid type on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
  			/* Create a new attribute for the list */
--- 133,148 ----
  			else if(strcmp(typestr, "date") == 0) {
  				type = PW_TYPE_DATE;
  			}
+ #if defined( BINARY_FILTERS )
+                         else if(strcmp(typestr, "abinary") == 0) {
+                                 type = PW_TYPE_FILTER_BINARY;
+                         }
+ #endif /* BINARY_FILTERS */
  			else {
  				fprintf(stderr,
  			"%s: Invalid type on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_ATTRTYPE_ERR;
  			}
  
  			/* Create a new attribute for the list */
***************
*** 137,143 ****
  					(DICT_ATTR *)NULL) {
  				fprintf(stderr, "%s: out of memory\n",
  							progname);
! 				return(-1);
  			}
  			strcpy(attr->name, namestr);
  			attr->value = value;
--- 150,156 ----
  					(DICT_ATTR *)NULL) {
  				fprintf(stderr, "%s: out of memory\n",
  							progname);
! 				return MEMORY_ERR;
  			}
  			strcpy(attr->name, namestr);
  			attr->value = value;
***************
*** 155,185 ****
  				fprintf(stderr,
  			"%s: Invalid value entry on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
  			/*
  			 * Validate all entries
  			 */
! 			if(strlen(attrstr) > 31) {
  				fprintf(stderr,
  			"%s: Invalid attribute length on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
! 			if(strlen(namestr) > 31) {
  				fprintf(stderr,
  			"%s: Invalid name length on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  
  			if(!isdigit(*valstr)) {
  				fprintf(stderr,
  			"%s: Invalid value on line %d of dictionary\n",
  					progname, line_no);
! 				return(-1);
  			}
  			value = atoi(valstr);
  
--- 168,198 ----
  				fprintf(stderr,
  			"%s: Invalid value entry on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_VALLINE_ERR;
  			}
  
  			/*
  			 * Validate all entries
  			 */
! 			if(strlen(attrstr) > (size_t)31) {
  				fprintf(stderr,
  			"%s: Invalid attribute length on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_VALATTR_ERR;
  			}
  
! 			if(strlen(namestr) > (size_t)31) {
  				fprintf(stderr,
  			"%s: Invalid name length on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_VALNAME_ERR;
  			}
  
  			if(!isdigit(*valstr)) {
  				fprintf(stderr,
  			"%s: Invalid value on line %d of dictionary\n",
  					progname, line_no);
! 				return DICTFILE_VALVALUE_ERR;
  			}
  			value = atoi(valstr);
  
***************
*** 188,194 ****
  					(DICT_VALUE *)NULL) {
  				fprintf(stderr, "%s: out of memory\n",
  							progname);
! 				return(-1);
  			}
  			strcpy(dval->attrname, attrstr);
  			strcpy(dval->name, namestr);
--- 201,207 ----
  					(DICT_VALUE *)NULL) {
  				fprintf(stderr, "%s: out of memory\n",
  							progname);
! 				return MEMORY_ERR;
  			}
  			strcpy(dval->attrname, attrstr);
  			strcpy(dval->name, namestr);
diff -c -N radiusd/filters.c ascendd/filters.c
*** radiusd/filters.c	Wed Dec 31 16:00:00 1969
--- ascendd/filters.c	Wed May 22 13:22:46 1996
***************
*** 0 ****
--- 1,831 ----
+ /*
+  * ASCEND: @(#)filters.c	1.3 (95/07/25 00:55:30)
+  *
+  *      Copyright (c) 1994 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *	Permission to copy all or part of this material for any purpose is
+  *	granted provided that the above copyright notice and this paragraph
+  *	are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *	AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *	FOR A PARTICULAR PURPOSE.
+  */
+ 
+ #if defined( BINARY_FILTERS )
+ 
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <netinet/in.h>
+ #include <stdlib.h>
+ #include <sys/time.h>	/* gettimeofday() */
+ 
+ #include "radius.h"
+ #include "protos.h"
+ 
+ VALUE_PAIR*	prevRadPair = 0;
+ 
+ #undef DEBUG
+ #if defined( DEBUG )
+ #define PRINTF(x)	printf x
+ #else
+ #define PRINTF( x )
+ #endif
+ 
+ #define NO_TOKEN -1
+ 
+ typedef struct {
+     char*	name;
+     int 	value;
+ } KeywordStruct;
+ 
+     /*
+      * FilterPortType:
+      *
+      * Ascii names of some well known tcp/udp services.
+      * Used for filtering on a port type.
+      *
+      */
+ 
+ static KeywordStruct _filterPortType[] = {
+     { "ftp-data", 20 },
+     { "ftp", 21 },
+     { "telnet", 23 },
+     { "smtp", 25 },
+     { "nameserver", 42 },
+     { "domain", 53 },
+     { "tftp", 69 },
+     { "gopher", 70 },
+     { "finger", 79 },
+     { "www", 80 },
+     { "kerberos", 88 },
+     { "hostname", 101 },
+     { "nntp", 119 },
+     { "ntp", 123 },
+     { "exec", 512 },
+     { "login", 513 },
+     { "cmd", 514 },
+     { "talk", 517 },
+     {  NULL , NO_TOKEN },
+ };
+ 
+ typedef enum {
+     FILTER_IP_TYPE,
+     FILTER_GENERIC_TYPE,
+     FILTER_IN,
+     FILTER_OUT,
+     FILTER_FORWARD,
+     FILTER_DROP,
+     FILTER_GENERIC_OFFSET,
+     FILTER_GENERIC_MASK,
+     FILTER_GENERIC_VALUE,
+     FILTER_GENERIC_COMPNEQ,
+     FILTER_GENERIC_COMPEQ,
+     FILTER_MORE,
+     FILTER_IP_DST,
+     FILTER_IP_SRC,
+     FILTER_IP_PROTO,
+     FILTER_IP_DST_PORT,
+     FILTER_IP_SRC_PORT,
+     FILTER_EST
+ } FilterTokens;
+ 
+ 
+ static KeywordStruct _filterKeywords[] = {
+     { "ip", 	FILTER_IP_TYPE },
+     { "generic",FILTER_GENERIC_TYPE },
+     { "in", 	FILTER_IN },
+     { "out",	FILTER_OUT },
+     { "forward",FILTER_FORWARD },
+     { "drop",	FILTER_DROP },
+     { "dstip",  FILTER_IP_DST },
+     { "srcip",  FILTER_IP_SRC },
+     { "dstport",FILTER_IP_DST_PORT },
+     { "srcport",FILTER_IP_SRC_PORT },
+     { "est",	FILTER_EST },
+     { "more",	FILTER_MORE },
+     { "!=",	FILTER_GENERIC_COMPNEQ },
+     { "==",	FILTER_GENERIC_COMPEQ  },
+     {  NULL , NO_TOKEN },
+ };
+ 
+ #define FILTER_DIRECTION 	0
+ #define FILTER_DISPOSITION	1
+ #define IP_FILTER_COMPLETE  	0x3	/* bits shifted by FILTER_DIRECTION */
+ 					/* FILTER_DISPOSITION */
+ #define GENERIC_FILTER_COMPLETE 0x1c3	/* bits shifted for FILTER_DIRECTION */
+ 					/* FILTER_DISPOSITION, FILTER_GENERIC_OFFSET*/
+ 					/* FILTER_GENERIC_MASK, FILTER_GENERIC_VALUE*/
+ 
+     /*
+      * FilterProtoName:
+      *
+      * Ascii name of protocols used for filtering.
+      *
+      */
+ static KeywordStruct _filterProtoName[] = {
+     { "tcp",  6 },
+     { "udp",  17 },
+     { "ospf", 89 },
+     { "icmp", 1 },
+     {  NULL , NO_TOKEN },
+ };
+ 
+ static KeywordStruct _filterCompare[] = {
+     { ">", RAD_COMPARE_GREATER },
+     { "=", RAD_COMPARE_EQUAL },
+     { "<", RAD_COMPARE_LESS },
+     { "!=", RAD_COMPARE_NOT_EQUAL },
+     {  NULL , NO_TOKEN },
+ };
+ 
+ static char	_curString[512];
+ 
+ int _findKey P__(( char *string, KeywordStruct *list ));
+ static int _isAllDigit P__(( char *token ));
+ 
+     /*
+      * _findKey:
+      *
+      * Given a table of keywords, it will try and match string to an
+      * entry. If it does it returns that keyword value. if no NO_TOKEN is
+      * returned. A sanity check is made for upper case characters.
+      *
+      *	string:			Pointer to the token to match.
+      *
+      *	list:			Point to the list of keywords.
+      *
+      *	returns:		Keyword value on a match or NO_TOKEN.
+      */
+ int 
+ _findKey(string, list)
+ char		*string;
+ KeywordStruct	*list;
+ {
+     short 	len;
+     KeywordStruct*  entry;
+     char	buf[80], *ptr;
+ 
+     len = strlen( (char *) string );
+     for( ptr = buf ; len; len--, string++ ) {
+ 	if( isupper( *string ) ) {
+ 	    *ptr++ = tolower( *string );
+ 	} else {
+ 	    *ptr++ = *string;
+ 	}
+     }
+     *ptr = 0;
+     entry = list;
+     while( entry->name ) {
+    	if( strcmp( (char*) entry->name, (char*) buf ) == 0 ) {
+ 	    break;
+ 	}
+ 	entry++;
+     }
+     return( entry->value );
+ }
+ 
+     /*
+      * _isAllDigit:
+      *
+      * Routine checks a string to make sure all values are digits.
+      *
+      *	token:			Pointer to sting to check.
+      *
+      * 	returns:		TRUE if all digits, or FALSE.
+      *
+      */
+ 
+ static int
+ _isAllDigit(token)
+ char	*token;
+ {
+     int i;
+ 
+     i = strlen( (char *) token );
+     while( i-- ) {
+ 	if( isdigit( *token ) ) {
+ 	    token++;
+ 	} else {
+ 	    break;
+ 	}
+     }
+     if( i > 0 ) {
+ 	return( FALSE );
+     } 
+ 
+     return( TRUE );
+ }
+ 
+     /*
+      * _a2octet:
+      *
+      * Converts the ascii mask and value for generic filters into octets.
+      * It also does a sanity check to see if the string is greater than
+      * MAX_FILTER_LEN. It assumes the sting is hex with NO leading "0x"
+      *
+      *	tok:			Pointer to the string.
+      *
+      *  retBuf:			Pointer to place the octets.
+      *
+      *	returns:		Number of octects or -1 for error.
+      * 
+      */
+ static short
+ _a2octet(tok, retBuf)
+ char	*tok;
+ char	*retBuf;
+ {
+     short	rc, len, val, retLen, i;
+     char	buf[ RAD_MAX_FILTER_LEN *2 ];
+     char	*octet = buf;
+ 
+     rc = -1;
+     retLen = 0;
+ 
+     if( ( len = strlen( (char*) tok ) ) <= ( RAD_MAX_FILTER_LEN*2 ) ) {
+ 	retLen = len/2;
+ 	if( len % 2 ) {
+ 	    retLen++;
+ 	}
+ 	memset( buf, '\0', RAD_MAX_FILTER_LEN * 2 );
+ 	for( ; len; len-- ) {
+ 	    if( *tok <= '9' && *tok >= '0' ) {
+ 		val = '0';
+ 	        *octet++ = *tok++ - val;
+ 	    } else if( isxdigit( *tok ) ) {
+ 		if( *tok > 'Z' ) {
+ 		    val = 'a';
+ 		} else {
+ 		    val = 'A';
+ 		}
+ 	        *octet++ = ( *tok++ - val ) + 10;
+ 	    } else {
+ 		break;	
+ 	    }
+ 	}
+ 	if( !len ) {
+ 	    /* merge the values */
+ 	    for( i = 0; i < RAD_MAX_FILTER_LEN*2; i+=2 ) {
+ 		*retBuf++ = (buf[i] << 4) | buf[i+1];
+ 	    }
+ 	}
+     }
+ 
+     if( len ) {
+ 	rc = -1;
+     } else {
+ 	rc = retLen;
+     }
+     return( rc );
+ }
+ 
+ 
+ 
+     /*
+      * _defaultNetMask:
+      *
+      *	Given an ip address this routine calculate a default netmask.
+      *
+      *	address:		Ip address.
+      *
+      *	returns:		Number of bits for the netmask
+      *
+      */
+ static char
+ _defaultNetmask(address)
+ unsigned long	address;
+ {
+     char netmask;
+ 
+     if ( ! address ) {
+ 	netmask = 0;
+     } else if (( address & htonl( 0x80000000 ) ) == 0 ) {
+ 	netmask = 8;
+     } else if (( address & htonl( 0xc0000000 ) ) == htonl( 0x80000000 ) ) {
+ 	netmask = 16;
+     } else if (( address & htonl( 0xe0000000 ) ) == htonl( 0xc0000000 ) ) {
+ 	netmask = 24;
+     } else {
+ 	netmask = 32;
+     }
+     return netmask;
+ }
+ 
+ 		
+ static char ipAddressDigits[] = "1234567890./";
+     /*
+      * This functions attempts to convert an IP address in ASCII dot
+      * with an optional netmask part to a pair of IpAddress.  Note:
+      * An IpAddress is always stored in network byte order.
+      *
+      * Parameters:
+      *
+      *  string:		Pointer to a NULL terminated IP address in dot 
+      *			notation followed by an optional /nn to indicate
+      *			the number leading of bits in the netmask.
+      * 
+      *  ipAddress:	Pointer to an IpAddress where the converted
+      *			address will be stored.
+      *
+      *	netmask:	Pointer to an IpAddress where the netmask
+      *			will be stored.  If no netmask is passed as
+      *			as part of the address the default netmask will
+      *			be stored here.
+      *
+      * Returns:
+      *	<>		TRUE if valid conversion, FALSE otherwise.
+      *
+      *	*ipAddress:	If function returns TRUE, the IP address in NBO.
+      *	*netmask:	If function returns TRUE, the netmask in NBO.
+      */
+ 
+ static int
+ _ipAddressStringToValue(string, ipAddress, netmask)
+ char		*string;
+ unsigned long	*ipAddress;
+ char		*netmask;
+ {
+     u_char*	dst;
+     char*	cp;
+     int		numDots;
+     int		i;
+     long	value;
+ 
+     if ( ! string ) {
+     	return(FALSE);
+     }
+ 
+     /* Allow an IP address to be blanked instead of forcing entry of
+        0.0.0.0 -- the user will like it. */
+ 
+     if ( *string == 0 ) {
+ 	*ipAddress = 0;
+ 	*netmask = 0;
+ 	return TRUE;
+     }
+ 
+     /* First just count the number of dots in the address.  If there
+        are more or less than three the address is invalid. */
+ 
+     cp = string;
+     numDots = 0;
+     while( *cp ) {
+ 	if( !strchr( ipAddressDigits, *cp) ) {
+ 	    return( FALSE );
+ 	}
+ 	if ( *cp == '.') {
+ 	    ++numDots;
+ 	}
+ 	++cp;
+     }
+     if ( numDots != 3 ) {
+ 	return( FALSE );
+     }
+ 
+     dst = (u_char *) ipAddress;
+     cp = string;
+ 
+     for ( i = 0; i < sizeof( *ipAddress ); i++ ) {
+ 	value = strtol( cp, (char**) &cp, 10 );
+ 	if (( value < 0 ) || ( value > 255 )) {
+ 	    return( FALSE );
+ 	}
+ 	*dst++ = (u_char) value;
+ 	if ( *cp == '.' ) {
+ 	    cp += 1;
+ 	}
+     }
+ 
+     /* If there is a netmask part, parse it, otherwise figure out the
+        default netmask for this class of address. */
+ 
+     if ( *cp == '/' ) {
+ 	value = strtol( cp + 1, (char**) &cp, 10 );
+ 	if (( *cp != 0 ) || ( value < 0 ) || ( value > 32 )) {
+ 	    return FALSE;
+ 	}
+ 	*netmask = (char) value;
+     } else {
+ 	*netmask = _defaultNetmask( *ipAddress );
+     }
+     return TRUE;
+ }
+ 
+     /*
+      * _parseIpFilter:
+      *
+      * This routine parses an IP filter string from a RADIUS
+      * reply. The format of the string is:
+      *
+      *	ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
+      *	    [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ] 
+      *
+      * Fields in [...] are optional.
+      *	where:
+      *
+      *  ip:		Keyword to designate an IP filter. Actually this
+      *			has been determined by _parseFilter.
+      *
+      *	dir:		Filter direction. "IN" or "OUT"
+      *
+      *	action:		Filter action. "FORWARD" or "DROP"
+      *
+      *	dstip:		Keyword for destination IP address.
+      *			n.n.n.n = IP address. /nn - netmask. 
+      *			
+      *	srcip:		Keyword for source IP address.
+      *			n.n.n.n = IP address. /nn - netmask. 
+      *			
+      *	proto:		Optional protocol field. Either a name or
+      *			number. Known names are in FilterProtoName[].
+      *			
+      *	dstpost:	Keyword for destination port. Only valid with tcp
+      *			or udp. 'cmp' are in FilterPortType[]. 'value' can be
+      *			a name or number.
+      *
+      *	srcpost:	Keyword for source port. Only valid with tcp
+      *			or udp. 'cmp' are in FilterPortType[]. 'value' can be
+      *			a name or number.
+      *			
+      *	est:		Keyword for TCP established. Valid only for tcp.
+      *			
+      * expects:
+      *
+      *	curEntry:	Pointer to place the filter structure
+      *
+      *	returns:	-1 for error or 0 for OK
+      *	
+      */
+ 
+ static int 
+ _parseIpFilter(curEntry)
+ RadFilter	*curEntry;
+ {
+  
+     unsigned long	elements = 0l;
+     int			tok; 
+     char*		token;
+     RadIpFilter*	ip;
+ 
+     token = (char *) strtok( NULL, " " ); 
+ 
+     PRINTF((" in ip  filter \n")); 
+ 
+     memset( curEntry, '\0', sizeof( RadFilter ) );
+     curEntry->type = RAD_FILTER_IP; 
+     ip = &curEntry->u.ip;
+     ip->established = FALSE;
+  
+     while( token ) {
+ 	PRINTF((" token %s ", token ));
+   	tok = _findKey( token, _filterKeywords );
+ 	switch( tok ) {
+ 	    case FILTER_IN:
+ 	    case FILTER_OUT:
+ 		curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
+ 		PRINTF((" got %s ", tok == FILTER_IN?"FILTER_IN":"FILTER_OUT"));
+ 	        elements |= (1 << FILTER_DIRECTION );
+ 		break;
+ 	    case FILTER_FORWARD:
+ 	    case FILTER_DROP:
+ 		PRINTF((" got %s ", tok == FILTER_DROP?
+ 			"FILTER_DROP":"FILTER_FORWARD"));
+ 	        elements |= (1 << FILTER_DISPOSITION );
+ 		if( tok == FILTER_FORWARD ) {
+ 		    curEntry->forward = TRUE;
+ 		} else {
+ 		    curEntry->forward = FALSE;
+ 		}
+ 		break;
+ 	    case FILTER_IP_DST:
+ 	    case FILTER_IP_SRC:
+ 		PRINTF((" got %s ", tok == FILTER_IP_DST?
+ 			"FILTER_IP_DST":"FILTER_IP_SRC"));
+ 		token = (char *) strtok( NULL, " " );
+ 		if ( token ) {
+ 		    if( tok == FILTER_IP_DST ) {
+ 			
+ 		        if( _ipAddressStringToValue( (char*)token, 
+ 				 &ip->dstip, (char *)&ip->dstmask ) ) {
+ 			    PRINTF((" ip %lx netmask %lx \n", ip->dstip, 
+ 				     ip->dstmask ));
+ 			    break;
+ 			}
+ 		    } else {
+ 		        if( _ipAddressStringToValue( (char *)token, 
+ 				&ip->srcip, (char *)&ip->srcmask ) ) {
+ 			    PRINTF((" ip %lx netmask %lx \n", ip->srcip,
+ 				     ip->srcmask ));
+ 			    break;
+ 			}
+ 		    }
+ 		} 
+ 
+ 		PRINTF(( "RADIF: IP Filter syntax error %s \n", token ));
+ 		log_err( "ip filter error: do not recognize %s in %s \n",
+ 			  token, _curString );
+ 		goto doneErr; 
+ 	    case FILTER_IP_DST_PORT:
+ 	    case FILTER_IP_SRC_PORT:
+ 	    {
+ 		RadFilterComparison cmp;
+ 		short		 port;
+ 
+ 		PRINTF((" got %s ", tok == FILTER_IP_DST_PORT?
+ 			"FILTER_IP_DST_PORT":"FILTER_IP_SRC_PORT"));
+ 		token = (char *) strtok( NULL, " " );
+ 		if ( token ) {
+   		    cmp = _findKey( token, _filterCompare );
+ 		    PRINTF((" cmp value = %d \n", cmp ));
+ 		    if( cmp != NO_TOKEN ) {
+ 			token = (char *) strtok( NULL, " " );
+ 			if ( token ) {
+ 			    if( _isAllDigit( token ) ) {
+ 				port = atoi( (char *) token );
+ 			    } else {
+   		    	        port = _findKey( token, _filterPortType );
+ 			    }
+ 			    if( port != (short) NO_TOKEN ) {
+ 		    	    	PRINTF((" port = %d \n", port ));
+ 				if( tok == FILTER_IP_DST_PORT ) {
+ 				    ip->dstPortComp = cmp;
+ 				    ip->dstport = htons( port );
+ 				} else {
+ 				    ip->srcPortComp = cmp;
+ 				    ip->srcport = htons( port );
+ 				}
+ 				break;
+ 			    }
+ 			}
+ 		    }
+ 		}
+ 		log_err( "ip filter error: do not recognize %s in %s \n",
+ 			  token, _curString );
+ 		PRINTF(( "RADIF: IP Filter syntax error %s \n", token ));
+ 		goto doneErr;
+ 		break;
+ 	    }
+ 	    case FILTER_EST:
+ 		PRINTF((" got est %s ", token ));
+ 		ip->established = TRUE;
+ 		break;
+ 	    default:
+ 		/* no keyword match but may match a protocol list */
+ 		if( _isAllDigit( token ) ) {
+ 		    tok = atoi( (char *) token );
+ 		} else {
+ 		    tok = _findKey( token, _filterProtoName );
+ 
+ 		    if( tok == NO_TOKEN ) {
+ 			PRINTF(( "RADIF: IP proto error %s \n", token ));
+ 			log_err( "ip filter error: do not recognize %s in %s \n",
+ 			     token, _curString );
+ 			goto doneErr;
+ 		    }
+ 		}
+ 		ip->proto = tok;
+ 		PRINTF(("ip proto cmd = %d ", tok));
+ 	}
+         token = (char *) strtok( NULL, " " ); 
+     } 
+ 
+     if( elements == IP_FILTER_COMPLETE ) {
+ 	return( 0 );
+     }
+ 
+ doneErr:
+     PRINTF((" done err \n"));
+     return( -1 );
+ }
+ 
+     /*
+      * _parseGenericFilter:
+      *
+      * This routine parses a Generic filter string from a RADIUS
+      * reply. The format of the string is:
+      *
+      *	GENERIC dir action offset mask value [== or != ] [more]
+      *
+      * Fields in [...] are optional.
+      *	where:
+      *
+      * 	generic:	Keyword to indicate a generic filter. This
+      *			has been determined by _parseFilter.
+      *
+      *	dir:		Filter direction. "IN" or "OUT"
+      *
+      *	action:		Filter action. "FORWARD" or "DROP"
+      *
+      *	offset:		A Number. Specifies an offset into a frame 
+      *			to start comparing.
+      *			
+      *	mask:		A hexadecimal mask of bits to compare.
+      *			
+      *	value:		A value to compare with the masked data.
+      *
+      *	compNeq:	Defines type of comparison. ( "==" or "!=")
+      *			Default is "==".
+      *			
+      *	more:		Optional keyword MORE, to represent the attachment
+      *			to the next entry.
+      *
+      * expects:
+      *
+      *	curEntry:	Pointer to place the filter structure
+      *
+      *	returns:	-1 for error or 0 for OK
+      *	
+      */
+ 
+ static int
+ _parseGenericFilter(curEntry)
+ RadFilter	*curEntry;
+ {
+     unsigned long	elements = 0l; 
+     int			tok; 
+     int			gstate = FILTER_GENERIC_OFFSET;
+     char*		token;
+     short		valLen, maskLen;
+     RadGenericFilter*	gen;
+ 
+     token = (char *) strtok( NULL, " " ); 
+ 
+     PRINTF((" in parse generic filter \n")); 
+ 
+     maskLen = 0;
+     memset( (char *)curEntry, '\0', sizeof( RadFilter ) );
+     curEntry->type = RAD_FILTER_GENERIC;
+     gen = &curEntry->u.generic;
+     gen->more = FALSE; 
+     gen->compNeq = FALSE;	
+ 
+     while( token ) {
+ 	PRINTF((" token %s ", token ));
+   	tok = _findKey( token, _filterKeywords );
+    	PRINTF(("tok %d ", tok));
+ 	switch( tok ) {
+ 	    case FILTER_IN:
+ 	    case FILTER_OUT:
+ 		curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
+ 	        elements |= (1 << FILTER_DIRECTION );
+ 		PRINTF((" got %s ", tok == FILTER_IN?"FILTER_IN":"FILTER_OUT"));
+ 		break;
+ 	    case FILTER_FORWARD:
+ 	    case FILTER_DROP:
+ 	        elements |= (1 << FILTER_DISPOSITION );
+ 		PRINTF((" got %s ", tok == FILTER_DROP?
+ 			"FILTER_DROP":"FILTER_FORWARD"));
+ 		if( tok == FILTER_FORWARD ) {
+ 		    curEntry->forward = TRUE;
+ 		} else {
+ 		    curEntry->forward = FALSE;
+ 		}
+ 		break;
+ 	    case FILTER_GENERIC_COMPNEQ:
+ 		gen->compNeq = TRUE;
+ 		PRINTF((" got compare %s ", token));
+ 		break;
+ 	    case FILTER_GENERIC_COMPEQ:
+ 		gen->compNeq = FALSE;
+ 		PRINTF((" got compare %s ", token));
+ 		break;
+ 	    case FILTER_MORE:
+ 		gen->more = htons( TRUE );
+ 		PRINTF((" got more %s ", token ));
+ 		break;
+ 	    default:
+ 	        elements |= ( 1 << gstate );
+ 		switch( gstate ) {
+ 		    case FILTER_GENERIC_OFFSET:
+ 			gstate = FILTER_GENERIC_MASK;
+ 			gen->offset = htons( atoi( (char *) token ) );
+ 			break;
+ 		    case FILTER_GENERIC_MASK:
+ 			gstate = FILTER_GENERIC_VALUE;
+ 			maskLen = _a2octet( token, (char *)gen->mask );
+ 			if( maskLen == (short) -1 ) {
+ 			    log_err( "filter mask error: %s \n", _curString );
+ 			    goto doneErr;
+ 			}
+ #ifdef DEBUG
+ 			PRINTF((" octet retlen = %d ", maskLen ));
+ 			for( tok = 0; tok < maskLen; tok++) {
+         		    PRINTF(("%2x", gen->mask[tok]));
+ 		        }
+ 			PRINTF(("\n"));
+ #endif
+ 
+ 			break;
+ 		    case FILTER_GENERIC_VALUE:
+ 			gstate ++;
+ 			valLen = _a2octet( token, (char *)gen->value );
+ 			if( valLen != maskLen ) {
+ 			    log_err( "filter value size is not the same size as the filter mask: %s \n", 
+ 				     _curString );
+ 			    goto doneErr;
+ 			}
+ 			gen->len = htons( valLen );
+ #ifdef DEBUG
+ 			PRINTF((" octet retlen = %d ", maskLen ));
+ 			for( tok = 0; tok < maskLen; tok++) {
+         		    PRINTF(("%2x", gen->value[tok]));
+ 		        }
+ 			PRINTF(("\n"));
+ #endif
+ 
+ 			break;
+ 		    default:
+ 			log_err( "filter: do not know %s in %s \n",
+ 				 token, _curString );
+ 			PRINTF(( "RADIF: Filter syntax error %s \n", token ));
+ 			goto doneErr;    
+ 		}
+ 	}
+         token = (char *) strtok( NULL, " " ); 
+     }
+ 
+     if( elements == GENERIC_FILTER_COMPLETE ) {
+ 	return( 0 );
+     }
+ 
+ doneErr:
+     PRINTF((" done err \n"));
+     return( -1 );
+ }
+ 		       
+     /*
+      * filterBinary:
+      *
+      * This routine will call routines to parse entries from an ASCII format
+      * to a binary format recognized by the Ascend boxes.
+      *
+      *	pair:			Pointer to value_pair to place return.
+      *
+      *	valstr:			The string to parse	
+      *
+      *	return:			-1 for error or 0.
+      */
+ int 
+ filterBinary(pair, valstr)
+ VALUE_PAIR	*pair;
+ char		*valstr;
+ {
+ 
+     char*		token;
+     unsigned long	tok;
+     int			rc;
+     RadFilter		radFil, *filt;
+     RadGenericFilter*	gen;
+ 
+     rc = -1;
+     strcpy( _curString, valstr );
+ 
+     token = (char *) strtok( (char *)valstr, " " );
+     tok = _findKey( token, _filterKeywords );
+     pair->lvalue = sizeof( RadFilter );
+     switch( tok ) {
+       case FILTER_IP_TYPE:
+ 	rc = _parseIpFilter( &radFil );
+ 	break;
+       case FILTER_GENERIC_TYPE:
+ 	rc = _parseGenericFilter( &radFil );
+ 	break;
+     }
+ 
+     /*
+      * if more is set then this new entry must exist, be a 
+      * FILTER_GENERIC_TYPE, direction and disposition must match for 
+      * the previous more to be valid. If any should fail then TURN OFF 
+      * previos more
+      */
+     if( prevRadPair ) {
+ 	filt = ( RadFilter * )prevRadPair->strvalue;
+ 	if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
+ 	   ( prevRadPair->attribute != pair->attribute ) || 
+ 	   ( filt->indirection != radFil.indirection ) || 
+ 	   ( filt->forward != radFil.forward ) ) {
+ 	    gen = &filt->u.generic;
+ 	    gen->more = FALSE;
+ 	    log_err( "filterBinary:  'more' for previous entry doesn't match: %s.\n",
+ 		     _curString );
+ 	}
+     }
+     prevRadPair = NULL;
+     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
+ 	if( radFil.u.generic.more ) {
+ 	    prevRadPair = pair;
+ 	} 
+     }
+ 
+     if( rc != -1 ) {
+ 	memcpy( pair->strvalue, (char *) &radFil, pair->lvalue );
+     }
+     return(rc);
+ }
+ 
+ #endif /* BINARY_FILTERS */
+ 
diff -c -N radiusd/md5.c ascendd/md5.c
*** radiusd/md5.c	Thu Jan 11 17:26:16 1996
--- ascendd/md5.c	Wed May 22 13:17:51 1996
***************
*** 1,6 ****
--- 1,10 ----
  /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
   */
  
+ /*
+  * ASCEND: @(#)md5.c	1.2 (95/07/25 00:55:46)
+  */
+ 
  /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
  rights reserved.
  
***************
*** 93,102 ****
   (a) += (b); \
    }
  
  md5_calc(output, input, inlen)
! unsigned char *output;
! unsigned char *input;                                /* input block */
! unsigned int inlen;                     /* length of input block */
  {
  	MD5_CTX	context;
  
--- 97,107 ----
   (a) += (b); \
    }
  
+ void
  md5_calc(output, input, inlen)
! 	unsigned char	*output;	/* output block */
! 	unsigned char	*input;		/* input block */
! 	unsigned int	inlen;		/* length of input block */
  {
  	MD5_CTX	context;
  
***************
*** 107,114 ****
  
  /* MD5 initialization. Begins an MD5 operation, writing a new context.
   */
! void MD5Init (context)
! MD5_CTX *context;                                        /* context */
  {
    context->count[0] = context->count[1] = 0;
    /* Load magic initialization constants.
--- 112,120 ----
  
  /* MD5 initialization. Begins an MD5 operation, writing a new context.
   */
! void 
! MD5Init (context)
! 	MD5_CTX	*context;		/* context */
  {
    context->count[0] = context->count[1] = 0;
    /* Load magic initialization constants.
***************
*** 123,132 ****
    operation, processing another message block, and updating the
    context.
   */
! void MD5Update (context, input, inputLen)
! MD5_CTX *context;                                        /* context */
! unsigned char *input;                                /* input block */
! unsigned int inputLen;                     /* length of input block */
  {
    unsigned int i, index, partLen;
  
--- 129,139 ----
    operation, processing another message block, and updating the
    context.
   */
! void 
! MD5Update (context, input, inputLen)
! 	MD5_CTX		*context;	/* context */
! 	unsigned char	*input;		/* input block */
! 	unsigned int	inputLen;	/* length of input block */
  {
    unsigned int i, index, partLen;
  
***************
*** 141,148 ****
  
    partLen = 64 - index;
  
!   /* Transform as many times as possible.
! */
    if (inputLen >= partLen) {
   MD5_memcpy
     ((POINTER)&context->buffer[index], (POINTER)input, partLen);
--- 148,154 ----
  
    partLen = 64 - index;
  
!   /* Transform as many times as possible. */
    if (inputLen >= partLen) {
   MD5_memcpy
     ((POINTER)&context->buffer[index], (POINTER)input, partLen);
***************
*** 165,173 ****
  /* MD5 finalization. Ends an MD5 message-digest operation, writing the
    the message digest and zeroizing the context.
   */
! void MD5Final (digest, context)
! unsigned char digest[16];                         /* message digest */
! MD5_CTX *context;                                       /* context */
  {
    unsigned char bits[8];
    unsigned int index, padLen;
--- 171,180 ----
  /* MD5 finalization. Ends an MD5 message-digest operation, writing the
    the message digest and zeroizing the context.
   */
! void 
! MD5Final (digest, context)
! 	unsigned char	digest[16];	/* message digest */
! 	MD5_CTX		*context;	/* context */
  {
    unsigned char bits[8];
    unsigned int index, padLen;
***************
*** 194,202 ****
  
  /* MD5 basic transformation. Transforms state based on block.
   */
! static void MD5Transform (state, block)
! UINT4 state[4];
! unsigned char block[64];
  {
    UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
  
--- 201,210 ----
  
  /* MD5 basic transformation. Transforms state based on block.
   */
! static void 
! MD5Transform (state, block)
! 	UINT4		state[4];
! 	unsigned char	block[64];
  {
    UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
  
***************
*** 287,296 ****
  /* Encodes input (UINT4) into output (unsigned char). Assumes len is
    a multiple of 4.
   */
! static void Encode (output, input, len)
! unsigned char *output;
! UINT4 *input;
! unsigned int len;
  {
    unsigned int i, j;
  
--- 295,305 ----
  /* Encodes input (UINT4) into output (unsigned char). Assumes len is
    a multiple of 4.
   */
! static void 
! Encode (output, input, len)
! 	unsigned char	*output;
! 	UINT4		*input;
! 	unsigned int	len;
  {
    unsigned int i, j;
  
***************
*** 305,314 ****
  /* Decodes input (unsigned char) into output (UINT4). Assumes len is
    a multiple of 4.
   */
! static void Decode (output, input, len)
! UINT4 *output;
! unsigned char *input;
! unsigned int len;
  {
    unsigned int i, j;
  
--- 314,324 ----
  /* Decodes input (unsigned char) into output (UINT4). Assumes len is
    a multiple of 4.
   */
! static void 
! Decode (output, input, len)
! 	UINT4		*output;
! 	unsigned char	*input;
! 	unsigned int	len;
  {
    unsigned int i, j;
  
***************
*** 320,345 ****
  /* Note: Replace "for loop" with standard memcpy if possible.
   */
  
! static void MD5_memcpy (output, input, len)
! POINTER output;
! POINTER input;
! unsigned int len;
  {
    unsigned int i;
  
    for (i = 0; i < len; i++)
!  output[i] = input[i];
  }
  
  /* Note: Replace "for loop" with standard memset if possible.
   */
! static void MD5_memset (output, value, len)
! POINTER output;
! int value;
! unsigned int len;
  {
    unsigned int i;
  
    for (i = 0; i < len; i++)
!  ((char *)output)[i] = (char)value;
  }
--- 330,365 ----
  /* Note: Replace "for loop" with standard memcpy if possible.
   */
  
! static void 
! MD5_memcpy ( output, input, len)
! 	POINTER		output;
! 	POINTER		input;
! 	unsigned int	len;
  {
+ #if( 0 )
    unsigned int i;
  
    for (i = 0; i < len; i++)
! 	output[i] = input[i];
! #else
! 	memcpy(output, input, len);
! #endif
  }
  
  /* Note: Replace "for loop" with standard memset if possible.
   */
! static void 
! MD5_memset (output, value, len)
! 	POINTER		output;
! 	int		value;
! 	unsigned int	len;
  {
    unsigned int i;
  
+ #if( 1 )
    for (i = 0; i < len; i++)
! 	((char *)output)[i] = (char)value;
! #else
! 	memset(output, value, len);
! #endif
  }
diff -c -N radiusd/md5.h ascendd/md5.h
*** radiusd/md5.h	Thu Jan 11 17:26:17 1996
--- ascendd/md5.h	Thu Jan 11 17:26:12 1996
***************
*** 1,13 ****
  /* GLOBAL.H - RSAREF types and constants
   */
  
  /* PROTOTYPES should be set to one if and only if the compiler supports
    function argument prototyping.
    The following makes PROTOTYPES default to 0 if it has not already
!   been defined with C compiler flags.
   */
! #ifndef PROTOTYPES
! #define PROTOTYPES 0
  #endif
  
  /* POINTER defines a generic pointer type */
--- 1,21 ----
  /* GLOBAL.H - RSAREF types and constants
   */
  
+ /*
+  * ASCEND: @(#)md5.h	1.2 (95/07/25 00:55:48)
+  */
+ 
  /* PROTOTYPES should be set to one if and only if the compiler supports
    function argument prototyping.
    The following makes PROTOTYPES default to 0 if it has not already
!   been defined explictly or with C compiler flags.
   */
! #if !defined(PROTOTYPES)
! #	if defined(__STDC__) && (__STDC__ == 1)
! #		define PROTOTYPES 1
! #	else
! #		define PROTOTYPES 0
! #	endif
  #endif
  
  /* POINTER defines a generic pointer type */
***************
*** 18,26 ****
  
  /* UINT4 defines a four byte word */
  #if defined(__alpha) && defined(__osf__)
! typedef unsigned int UINT4;
  #else
! typedef unsigned long int UINT4;
  #endif
  
  /* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
--- 26,34 ----
  
  /* UINT4 defines a four byte word */
  #if defined(__alpha) && defined(__osf__)
! 	typedef unsigned int UINT4;
  #else
! 	typedef unsigned long int UINT4;
  #endif
  
  /* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
***************
*** 28,36 ****
    returns an empty list.
   */
  #if PROTOTYPES
! #define PROTO_LIST(list) list
  #else
! #define PROTO_LIST(list) ()
  #endif
  
  /* MD5.H - header file for MD5C.C
--- 36,44 ----
    returns an empty list.
   */
  #if PROTOTYPES
! #	define PROTO_LIST(list) list
  #else
! #	define PROTO_LIST(list) ()
  #endif
  
  /* MD5.H - header file for MD5C.C
diff -c -N radiusd/protos.h ascendd/protos.h
*** radiusd/protos.h	Wed Dec 31 16:00:00 1969
--- ascendd/protos.h	Fri May 24 13:26:41 1996
***************
*** 0 ****
--- 1,97 ----
+ /*
+  * ASCEND: @(#)protos.h	1.3 (95/07/25 00:55:32)
+  *
+  *      Copyright (c) 1995 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *	Permission to copy all or part of this material for any purpose is
+  *	granted provided that the above copyright notice and this paragraph
+  *	are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *	AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *	FOR A PARTICULAR PURPOSE.
+  */
+ 
+ #if defined(__STDC__) && (__STDC__ == 1)
+ #	define	P__(s)	s
+ #	define	CONST	const
+ #else
+ #	define	P__(s)	(/* s */)
+ #	define	CONST	/* const */
+ #endif
+ 
+ 	/* function prototypes */
+ #if defined(SOLARIS)
+ 	extern int getopt P__((int, char *CONST *, CONST char *));
+ 	extern int gettimeofday P__((struct timeval *tp));
+ #else
+ 	extern int gettimeofday P__((struct timeval *tp, struct timezone *tzp));
+ #endif
+ extern void radversion P__((void));
+ extern char *get_errmsg P__((int errcode));
+ extern AUTH_REQ *authenticateAcctReq P__((AUTH_REQ *authreq, AUTH_HDR *auth));
+ #if(0)	/* parameter promotion due to old-style function definition */
+ extern AUTH_REQ *radrecv P__((UINT4 host, u_short udp_port, u_char *buffer, int length, char *isAcctPkt));
+ #else
+ extern AUTH_REQ *radrecv P__((UINT4 host, int udp_port, u_char *buffer, int length, char *isAcctPkt));
+ #endif
+ extern DICT_ATTR *dict_attrfind P__((char *attrname));
+ extern DICT_ATTR *dict_attrget P__((int attribute));
+ extern DICT_VALUE *dict_valfind P__((char *valname));
+ extern DICT_VALUE *dict_valget P__((UINT4 value, char *attrname));
+ extern UINT4 get_ipaddr P__((char *host));
+ extern UINT4 ipstr2long P__((char *ip_str));
+ extern char *ip_hostname P__((UINT4 ipaddr));
+ extern int get_client_info P__((UINT4 rqstIpAddr, UINT4 *ipaddr, u_char *secret, char *hostnm));
+ extern int calc_digest P__((u_char *digest, AUTH_REQ *authreq));
+ extern int config_init P__((void));
+ extern int dict_init P__((void));
+ extern int filterBinary P__((VALUE_PAIR *pair, char *valstr));
+ extern int good_ipaddr P__((char *addr));
+ extern int kill P__((pid_t pid, int sig));
+ /* Can't declare prototypes with old varargs */
+ extern int log_err ();
+ extern void debugf ();
+ extern int pw_expired P__((UINT4 exptime, UINT4 usr_exp_days, UINT4 usr_warn_days));
+ extern int rad_spawn_child P__((AUTH_REQ *authreq, int activefd));
+ #if(0)	/* parameter promotion due to old-style function definition */
+ extern int radrespond P__((AUTH_REQ *authreq, int activefd, char isAcctPkt));
+ #else
+ extern int radrespond P__((AUTH_REQ *authreq, int activefd, int isAcctPkt));
+ #endif
+ extern int set_expiration P__((VALUE_PAIR *user_check, UINT4 expiration));
+ extern int unix_pass P__((char *name, char *passwd));
+ extern int user_find P__((char *name, VALUE_PAIR **check_pairs, VALUE_PAIR **reply_pairs));
+ extern int user_update P__((char *name, VALUE_PAIR *user_check, VALUE_PAIR *user_reply));
+ extern void debug_pair P__((FILE *fd, VALUE_PAIR *pair));
+ extern void fprint_attr_val P__((FILE *fd, VALUE_PAIR *pair));
+ extern void ipaddr2str P__((char *buffer, UINT4 ipaddr));
+ extern void md5_calc P__((unsigned char *output, unsigned char *input, unsigned int inlen));
+ extern void pairfree P__((VALUE_PAIR *pair));
+ extern void rad_accounting P__((AUTH_REQ *authreq, int activefd));
+ extern int rad_authenticate P__((AUTH_REQ *authreq, int activefd));
+ extern void rad_passchange P__((AUTH_REQ *authreq, int activefd));
+ extern void send_accept P__((AUTH_REQ *authreq, VALUE_PAIR *reply, char *msg, int activefd));
+ extern void send_accounting_response P__((AUTH_REQ *authreq, int activefd, char *msg));
+ extern PasswordType getPasswordType P__((char *pwd));
+ extern UINT4 get_attr_lvalue P__((VALUE_PAIR *list, int attribute));
+ extern char *get_attr_strvalue P__((VALUE_PAIR *list, int attribute));
+ extern void send_acct_reply P__((AUTH_REQ *authreq, VALUE_PAIR *reply, char *msg, int activefd));
+ extern void send_challenge P__((AUTH_REQ *authreq, char *msg, int msgLen, char *state, int stateLen, int activefd));
+ extern void send_newpin P__((AUTH_REQ *authreq, char *state, int stateLen, int activefd));
+ extern void send_nextcode P__((AUTH_REQ *authreq, char *msg, int msgLen, char *state, int stateLen, int activefd));
+ extern void send_pwack P__((AUTH_REQ *authreq, int activefd));
+ extern void send_pwexpired P__((AUTH_REQ *authreq, char *msg, int activefd));
+ extern void make_secret P__((u_char *digest, u_char *vector, u_char *secret, char *value));
+ extern void send_reject P__((AUTH_REQ *authreq, char *msg, int activefd));
+ extern void sig_cleanup P__((int sig));
+ extern void sig_fatal P__((int sig));
+ extern void sig_hup P__((int sig));
+ extern int user_read P__((FILE **userfd, char *name, char *content));
+ extern int ace_auth_request P__((int activefd, struct sockaddr_in *sin, char *buffer, int buf_length));
+ extern int ace_forward P__((char *buffer, int length, int indx));
+ extern int ace_find_child_index P__((pid_t pid));
+ extern struct ace_child *ace_find_free_child P__((void));
+ extern int get_NFILE P__((void));
+ extern int ace_fork P__((int activefd, struct sockaddr_in *sin, int id, char *buffer, int length));
+ extern int user_wants_ace P__((char *user_name, int *immediate));
diff -c -N radiusd/radius.h ascendd/radius.h
*** radiusd/radius.h	Thu Jan 11 17:26:17 1996
--- ascendd/radius.h	Wed May 15 17:19:00 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)radius.h	1.3 (95/07/25 00:55:33)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 32,39 ****
  #include "conf.h"
  
  #define AUTH_VECTOR_LEN		16
! #define AUTH_PASS_LEN		16
  #define AUTH_STRING_LEN		128	/* maximum of 254 */
  
  
  typedef struct pw_auth_hdr {
--- 34,45 ----
  #include "conf.h"
  
  #define AUTH_VECTOR_LEN		16
! #define AUTH_PASS_LEN		254
! #ifdef ORIG
  #define AUTH_STRING_LEN		128	/* maximum of 254 */
+ #else
+ #define AUTH_STRING_LEN		254	/* need 256 ???? */ 
+ #endif
  
  
  typedef struct pw_auth_hdr {
***************
*** 44,49 ****
--- 50,62 ----
  	u_char		data[2];
  } AUTH_HDR;
  
+     /* types of passwords */
+ typedef enum PasswordType {
+ 	PWD_UNKNOWN	= 1,	/* not yet determined */
+ 	PWD_SPECIAL,		/* triggers for external authentication */
+ 	PWD_NORMAL		/* plain jane */
+ } PasswordType;
+ 
  #define AUTH_HDR_LEN			20
  #define CHAP_VALUE_LENGTH		16
  
***************
*** 54,73 ****
  #define PW_TYPE_INTEGER			1
  #define PW_TYPE_IPADDR			2
  #define PW_TYPE_DATE			3
  
  
  #define	PW_AUTHENTICATION_REQUEST	1
  #define	PW_AUTHENTICATION_ACK		2
  #define	PW_AUTHENTICATION_REJECT	3
  #define	PW_ACCOUNTING_REQUEST		4
  #define	PW_ACCOUNTING_RESPONSE		5
  #define	PW_ACCOUNTING_STATUS		6
! #define PW_PASSWORD_REQUEST		7
! #define PW_PASSWORD_ACK			8
! #define PW_PASSWORD_REJECT		9
  #define	PW_ACCOUNTING_MESSAGE		10
  #define PW_ACCESS_CHALLENGE		11
  
  #define	PW_USER_NAME			1
  #define	PW_PASSWORD			2
  #define	PW_CHAP_PASSWORD		3
--- 67,103 ----
  #define PW_TYPE_INTEGER			1
  #define PW_TYPE_IPADDR			2
  #define PW_TYPE_DATE			3
+ #if defined( BINARY_FILTERS )
+ #define PW_TYPE_FILTER_BINARY           4
+ #endif /* BINARY_FILTERS */
  
  
+ 	/* Types */
  #define	PW_AUTHENTICATION_REQUEST	1
  #define	PW_AUTHENTICATION_ACK		2
  #define	PW_AUTHENTICATION_REJECT	3
  #define	PW_ACCOUNTING_REQUEST		4
  #define	PW_ACCOUNTING_RESPONSE		5
  #define	PW_ACCOUNTING_STATUS		6
! #define PW_PASSWORD_REQUEST		7	/* Livingston-specific code */
! #define PW_PASSWORD_ACK			8	/* Livingston-specific code */
! #define PW_PASSWORD_REJECT		9	/* Livingston-specific code */
  #define	PW_ACCOUNTING_MESSAGE		10
  #define PW_ACCESS_CHALLENGE		11
+ #define PW_NEXT_PASSCODE		29	/* Ascend-specific code */
+ #define PW_NEW_PIN			30	/* Ascend-specific code */
+ #define PW_TERMINATE_SESSION		31	/* Ascend-specific code */
+ #define PW_PASSWORD_EXPIRED		32	/* Ascend-spcific code */
+ 
+ #define PW_ASCEND_DISCONNECT_REQUEST	40	/* Ascend-specific code */
+ #define PW_ASCEND_DISCONNECT_ACK	41	/* Ascend-specific code */
+ #define PW_ASCEND_DISCONNECT_NAK	42	/* Ascend-specific code */
+ #define PW_ASCEND_CHG_FILTERS_REQUEST	43	/* Ascend-specific code */
+ #define PW_ASCEND_CHG_FILTERS_ACK	44	/* Ascend-specific code */
+ #define PW_ASCEND_CHG_FILTERS_NAK	45	/* Ascend-specific code */
  
+ 
+ 	/* Attributes */
  #define	PW_USER_NAME			1
  #define	PW_PASSWORD			2
  #define	PW_CHAP_PASSWORD		3
***************
*** 88,94 ****
  #define PW_PORT_MESSAGE			18
  #define PW_DIALBACK_NO			19
  #define PW_DIALBACK_NAME		20
! #define PW_EXPIRATION			21
  #define PW_FRAMED_ROUTE			22
  #define PW_FRAMED_IPXNET		23
  #define PW_STATE			24
--- 118,124 ----
  #define PW_PORT_MESSAGE			18
  #define PW_DIALBACK_NO			19
  #define PW_DIALBACK_NAME		20
! #define ASCEND_PW_EXPIRATION		21	/* Officially Un-assigned */
  #define PW_FRAMED_ROUTE			22
  #define PW_FRAMED_IPXNET		23
  #define PW_STATE			24
***************
*** 101,106 ****
--- 131,151 ----
  #define PW_ACCT_AUTHENTIC		45
  #define PW_ACCT_SESSION_TIME		46
  
+ 
+ #define PW_ASCEND_ARADES		181	
+ #define	ASCEND_TOKEN_IDLE		199
+ #define	ASCEND_TOKEN_IMMEDIATE		200	
+ 	/* 201 - 203 allocated but not used in this implementation */
+ #define ASCEND_REQUIRE_AUTH		201
+ #define ASCEND_NUMBER_SESSIONS		202
+ #define ASCEND_AUTHEN_ALIAS		203
+ #define ASCEND_TOKEN_EXPIRY		204	/* internal */
+ 	/* 205, 206 allocated but not used in this implementation */
+ #define ASCEND_MENU_SELECTOR		205
+ #define ASCEND_MENU_ITEM		206
+ #define	ASCEND_PW_WARNTIME		207
+ #define	ASCEND_PW_LIFETIME		208
+ 
  /*
   *	INTEGER TRANSLATIONS
   */
***************
*** 147,152 ****
--- 192,207 ----
  #define PW_STATUS_STOP			2
  #define PW_STATUS_ALIVE			3
  
+ /*	Token Immediate Values	*/
+ #define TOK_IMM_NO			0
+ #define TOK_IMM_YES			1
+ 
+ /*	Password Aging Attribute	*/
+ #define PW_EXPIRATION_NAME	"Ascend-PW-Expiration"	/* attribute name */
+ /*	Password Aging Value Names for Attribute Ascend-Expiration	*/
+ #define PW_LIFETIME		"Lifetime-In-Days"	/* value name */
+ #define PW_WARNTIME		"Days-Of-Warning"	/* value name */
+ 
  /* Default Database File Names */
  
  #define RADIUS_DIR		"/etc/raddb"
***************
*** 179,184 ****
--- 234,240 ----
  	int			attribute;
  	int			type;
  	UINT4			lvalue;
+ 	int			size;
  	char			strvalue[AUTH_STRING_LEN];
  	struct value_pair	*next;
  } VALUE_PAIR;
***************
*** 196,204 ****
  	struct auth_req		*next;		/* Next active request */
  } AUTH_REQ;
  
! #define DEBUG	if(debug_flag)printf
  
- #define SECONDS_PER_DAY		86400
  #define MAX_REQUEST_TIME	30
  #define CLEANUP_DELAY		5
  #define MAX_REQUESTS		100
--- 252,505 ----
  	struct auth_req		*next;		/* Next active request */
  } AUTH_REQ;
  
!     /*
!      * Struct to pass information around to password authentication
!      * subroutines
!      */
! typedef struct AuthInfo {
! 	int		fd;		/* file descriptor to use */
! 	AUTH_REQ	*authreq;	/* original auth request */
! 	UINT4		ipaddr;		/* from authreq, for convenience */
! 	u_char		*vector;	/* ditto */
! 	int		cacheAuth;	/* Boolean, cache hit */
! 	VALUE_PAIR	*cutList;	/* list of cut attributes */
! 	VALUE_PAIR	*authName;	/* PW_USER_NAME from req */
! 	VALUE_PAIR	*authPwd;	/* PW_[CHAP]_PASSWORD from req */
! 	VALUE_PAIR	*authState;	/* STATE from req */
! 	VALUE_PAIR	*chkPwd;	/* PW_PASSWORD from 'users' */
! 	VALUE_PAIR	*chkSecret;	/* ASCEND_RECEIVE_SECRET from 'users' */
! 	VALUE_PAIR	*chkImmediate;	/* Boolean: true skips challenge */
! 	VALUE_PAIR	*chkIdle;	/* idle timer value */
! 	VALUE_PAIR	*tokExp;	/* ASCEND_TOKEN_EXPIRY from 'users' */
! 	time_t		tokExpSecs;	/* Seconds till token expires */
! 	char		*pw_digest;	/* as calculated */
! 	char		*user_msg;	/* return message */
! } AuthInfo;
! 
! #define DEBUG	if(debug_flag)debugf
! 
! 	/*
! 	 *	try to regularize the error status handling
! 	 *
! 	 *	make *SURE* you update radiusd.c "db_errmsgs"
! 	 *	when you update this list!
! 	 */
! typedef enum DBASE_ERRS {
! 	FIRST_DBASE_ERROR	= -100,
! 	DEFAULT_PARSE_ERR	= -99,
! 	DICT_VALFIND_ERR	= -98,
! 	BINARY_FILTER_ERR	= -97,
! 	MISSING_EQUALS		= -96,
! 	DICT_ATTRFIND_ERR	= -95,
! 	REPLY_FIRST_IS_NULL	= -94,
! 	MISSING_NEWLINE		= -93,
! 	NO_USER_OR_DEFAULT_NAME	= -92,
! 	ZERO_LENGTH_NAME	= -91,
! 	NO_USER_FILE		= -90,
! 	NO_WHITESPACE		= -89,
! 	USERFILE_RENAME_FAILED	= -88,
! 	USERFILE_READ_ERR	= -87,
! 	USERFILE_WRITE_ERR	= -86,
! 	END_OF_USERS_LIST	= -85,
! 	MEMORY_ERR		= -84,
! 	DICTFILE_READ_ERR	= -83,
! 	DICTFILE_ATTRLINE_ERR	= -82,
! 	DICTFILE_ATTRNAME_ERR	= -81,
! 	DICTFILE_ATTRVALUE_ERR	= -80,
! 	DICTFILE_ATTRTYPE_ERR	= -79,
! 	DICTFILE_VALLINE_ERR	= -78,
! 	DICTFILE_VALATTR_ERR	= -77,
! 	DICTFILE_VALNAME_ERR	= -76,
! 	DICTFILE_VALVALUE_ERR	= -75,
! 	UNIX_GETPWNAME_ERR	= -74,
! 	UNIX_GETSHDWNAME_ERR	= -73,
! 	UNIX_BAD_PASSWORD	= -72,
! 	CLIENTFILE_READ_ERR	= -71,
! 	WRONG_NAS_ADDR		= -70,
! 	LOGFILE_APPEND_ERR	= -69,
! 	NULL_VALUEPAIR		= -68,
! 	PWD_EXPIRED		= -67,
! 	PIPE_CREATE_ERR		= -66,
! 	LAST_DBASE_ERROR
! } DBASE_ERRS;
! #define	NUM_DBASE_ERRORS	((LAST_DBASE_ERROR - FIRST_DBASE_ERROR) - 1)
! 
! 	/* associate strings with error codes */
! typedef struct DBASE_ERRMSG {
! 	DBASE_ERRS	ecode;
! 	char	*emsg;
! } DBASE_ERRMSG;
! 
! 
! #define SECONDS_PER_MINUTE	(60)
! #define SECONDS_PER_HOUR	(60 * SECONDS_PER_MINUTE)
! #define SECONDS_PER_DAY		(24 * SECONDS_PER_HOUR)
! 
! #if defined( BINARY_FILTERS )
! 
!     /*
!      * TRUE and FALSE in case they are needed
!      */
! #if ! defined( FALSE )
! # define FALSE			0
! # define TRUE			(! FALSE)
! #endif
! 
!     /*
!      * Two types of filters are supported, GENERIC and IP.  The identifiers
!      * are:
!      */
! #define RAD_FILTER_GENERIC	0
! #define RAD_FILTER_IP		1
! 
!     /*
!      * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
!      * starting at some offset.  The length is:
!      */
! #define RAD_MAX_FILTER_LEN	6
! 
! 
!     /*
!      * RadFilterComparison:
!      *
!      * An enumerated values for the IP filter port comparisons.
!      */
! typedef enum {
!     RAD_NO_COMPARE,
!     RAD_COMPARE_LESS,
!     RAD_COMPARE_EQUAL,
!     RAD_COMPARE_GREATER,
!     RAD_COMPARE_NOT_EQUAL
! } RadFilterComparison;
! 
!     /*
!      * RadIpFilter:
!      *
!      * The binary format of an IP filter.  ALL fields are stored in
!      * network byte order.
!      *
!      *	srcip:		The source IP address.
!      *
!      *	dstip:		The destination IP address.
!      *
!      *	srcmask:	The number of leading one bits in the source address
!      *			mask.  Specifies the bits of interest.
!      *
!      *	dstmask:	The number of leading one bits in the destination
!      *			address mask. Specifies the bits of interest.
!      *
!      *	proto:		The IP protocol number
!      *
!      *	establised:	A boolean value.  TRUE when we care about the
!      *			established state of a TCP connection.  FALSE when
!      *			we dont care.
!      *
!      *	srcport:	TCP or UDP source port number.
!      *
!      *	dstport:	TCP or UDP destination port number.
!      *
!      *	srcPortCmp:	One of the values of the RadFilterComparison enumeration
!      *			specifying how to compare the srcport value.
!      *
!      *	dstPortCmp:	One of the values of the RadFilterComparison enumeration
!      *			specifying how to compare the dstport value.
!      *
!      *	fill:		Round things out to a dword boundary.
!      */
! typedef struct radip {
!     UINT4  		srcip;
!     UINT4  		dstip;
!     unsigned char 	srcmask;
!     unsigned char 	dstmask;
!     unsigned char	proto;
!     unsigned char	established;
!     unsigned short	srcport;
!     unsigned short	dstport;
!     unsigned char	srcPortComp;
!     unsigned char	dstPortComp;
!     unsigned char	fill[2];
! } RadIpFilter;
! 
!     /*
!      * RadGenericFilter:
!      *
!      * The binary format of a GENERIC filter.  ALL fields are stored in
!      * network byte order.
!      *
!      *	offset:		Number of bytes into packet to start comparison.
!      *
!      *	len:		Number of bytes to mask and compare.  May not
!      *			exceed RAD_MAX_FILTER_LEN.
!      *
!      *	more:		Boolean.  If non-zero the next filter entry is
!      *			also to be applied to a packet.
!      *
!      *	mask:		A bit mask specifying the bits to compare.
!      *
!      *	value:		A value to compare against the masked bits at
!      *			offset in a users packet.
!      *			
!      *	compNeq:	Defines type of comarison (Equal or Notequal)
!      *			default is Equal.
!      *
!      *	fill:		Round things out to a dword boundary
!      */
! typedef struct radgeneric {
!     unsigned short	offset;
!     unsigned short	len;
!     unsigned short	more;
!     unsigned char	mask[ RAD_MAX_FILTER_LEN ];
!     unsigned char	value[ RAD_MAX_FILTER_LEN ];
!     unsigned char	compNeq;
!     unsigned char	fill;
! } RadGenericFilter;
! 
!     /*
!      * RadFilter:
!      *
!      * A binary filter element.  Contains either a RadIpFilter or a
!      * RadGenericFilter.  All fields are stored in network byte order.
!      *
!      *	type:		Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
!      *
!      *	forward:	TRUE if we should forward packets that match this
!      *			filter, FALSE if we should drop packets that match
!      *			this filter.
!      *
!      *	indirection:	TRUE if this is an input filter, FALSE if this is
!      *			an output filter.
!      *
!      *	fill:		Round things out to a dword boundary.
!      *
!      *	u:		A union of
!      *			ip:		An ip filter entry
!      *			generic:	A generic filter entry
!      */
! typedef struct filter {
!     unsigned char 	type;
!     unsigned char	forward;
!     unsigned char	indirection;
!     unsigned char	fill;
!     union {
! 	RadIpFilter   	 ip;
! 	RadGenericFilter generic;
!     } u;
! } RadFilter;
! 
! #endif /* BINARY_FILTERS */
! 
! #if defined( ASCEND_SECRET )
! 
!     /*
!      * The name/number of the string attribute that will be encoded
!      * in response packets.  The encoding is similar to the way PAP
!      * secrets are encoded in request packets.
!      */
! #define ASCEND_SEND_SECRET	214
! #define ASCEND_RECV_SECRET	215
! 
! #endif /* ASCEND_SECRET */
  
  #define MAX_REQUEST_TIME	30
  #define CLEANUP_DELAY		5
  #define MAX_REQUESTS		100
diff -c -N radiusd/radiusd.c ascendd/radiusd.c
*** radiusd/radiusd.c	Thu Jan 11 17:26:17 1996
--- ascendd/radiusd.c	Fri May 24 12:53:40 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	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
***************
*** 17,28 ****
   *	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.
   *
   */
  
  /* don't look here for the version, run radiusd -v or look in version.c */
--- 19,39 ----
   *	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 */
***************
*** 31,37 ****
  
  #include	<sys/types.h>
  #include	<sys/socket.h>
- #include	<sys/time.h>
  #include	<sys/file.h>
  #include	<netinet/in.h>
  
--- 42,47 ----
***************
*** 46,77 ****
  #include	<errno.h>
  #include	<sys/wait.h>
  
  #if !defined(NOSHADOW)
  #include	<shadow.h>
  #endif /* !NOSHADOW */
  
  #include	"radius.h"
  
! char		recv_buffer[4096];
! char		send_buffer[4096];
  char		*progname;
  int		sockfd;
  int		acctfd;
  int		debug_flag;
  int		spawn_flag;
  int		acct_pid;
  char		*radius_dir;
  char		*radacct_dir;
  UINT4		expiration_seconds;
  UINT4		warning_seconds;
  extern int	errno;
  static AUTH_REQ	*first_request;
  
! void		sig_fatal();
! void		sig_hup();
! void		sig_cleanup();
! void		rad_passchange();
  
  main(argc, argv)
  int	argc;
  char	**argv;
--- 56,294 ----
  #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"
+ 
+ #include	<varargs.h>
+ void vdebugf P__((char *fmt, va_list ap));
+ 
+ #define	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));
+ void ace_init_children P__((void));
+ #if defined(sys5) || defined(BSDI)
+ extern char *crypt P__((CONST char *, CONST char *));
+ #else
+ extern char *crypt P__((u_char *, u_char *));
+ #endif
+ static void insertValuePair P__((VALUE_PAIR** list, VALUE_PAIR* pair));
+ static VALUE_PAIR *copyValuePair P__((VALUE_PAIR* source));
+ 
+ /*------------------------------------------------------------*/
+ 
+ 
+ 
+ #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	(sizeof(ACE_DUMMY_STATE)-1)
+ 
+ #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) + sizeof(pid_t))
! 
! #define ACE_CHILDREN_ALLOC_CHUNK 16
! #define ACE_CHILD_ALARM_PERIOD 300 /* 5 minutes */
! 
! /*-------------- Ace Interface End --------------*/
! 
! /* Kludge to work around the fact that the SDI library is stateful. */
! 
! typedef struct ace_child
! {
! 	pid_t		pid;
! 	int		pipe_fd;
! 	time_t		expiration;
! } ACE_CHILD;
! 
! ACE_CHILD *ace_children;
! int ace_child_max;
! int ace_child_count;
! 
! #endif /* ACE */
! 
! #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;
***************
*** 82,96 ****
  	struct	sockaddr	saremote;
  	struct	sockaddr_in	*sin;
  	struct	servent		*svp;
!         u_short                 lport;
  	AUTH_REQ		*authreq;
- 	AUTH_REQ		*radrecv();
  	char			argval;
  	int			t;
  	int			pid;
  	int			cons;
  	fd_set			readfds;
  	int			status;
  
  	progname = *argv++;
  	argc--;
--- 299,314 ----
  	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--;
***************
*** 99,104 ****
--- 317,335 ----
  	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;
+ #if defined(ACE)
+ 	ace_init_children();
+ #endif
  
  	signal(SIGHUP, sig_hup);
  	signal(SIGINT, sig_fatal);
***************
*** 112,118 ****
  
  	while(argc) {
  
! 		if(**argv != '-') {
  			usage();
  		}
  
--- 343,349 ----
  
  	while(argc) {
  
! 		if( **argv != '-' ) {
  			usage();
  		}
  
***************
*** 122,127 ****
--- 353,378 ----
  
  		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();
***************
*** 130,136 ****
  			argc--;
  			argv++;
  			break;
! 		
  		case 'd':
  			if(argc == 0) {
  				usage();
--- 381,391 ----
  			argc--;
  			argv++;
  			break;
! 
! 		case 'c':
! 			allow_token_caching = 1;
! 			break;
! 
  		case 'd':
  			if(argc == 0) {
  				usage();
***************
*** 139,157 ****
  			argc--;
  			argv++;
  			break;
! 		
  		case 's':	/* Single process mode */
  			spawn_flag = 0;
  			break;
  
  		case 'v':
! 			version();
  			break;
  
  		case 'x':
  			debug_flag = 1;
  			break;
! 		
  		default:
  			usage();
  			break;
--- 394,430 ----
  			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;
+ 			debugf ("Debugging enabled\n");
  			break;
! 
  		default:
  			usage();
  			break;
***************
*** 159,176 ****
  	}
  
  	/* Initialize the dictionary */
! 	if(dict_init() != 0) {
! 		exit(-1);
  	}
  
  	/* Initialize Configuration Values */
! 	if(config_init() != 0) {
! 		exit(-1);
  	}
  
  	svp = getservbyname ("radius", "udp");
  	if (svp == (struct servent *) 0) {
! 		fprintf (stderr, "%s: No such service: radius/udp\n",
  			progname);
  		exit(-1);
  	}
--- 432,479 ----
  	}
  
  	/* 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 ) {
! 			log_err("Cache Creation Failed - memory exhausted\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);
  	}
***************
*** 182,189 ****
  		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;
--- 485,499 ----
  		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;
***************
*** 197,225 ****
  	/*
  	 * Open Accounting Socket.
  	 */
! 	/* svp = getservbyname ("radacct", "udp");
! 	if (svp == (struct servent *) 0) {
! 		fprintf (stderr, "%s: No such service: %s/%s\n",
! 			progname, "radacct", "udp");
! 		exit(-1);
! 	} */
! 	lport = htons(ntohs(lport) +1);
! 	acctfd = socket (AF_INET, SOCK_DGRAM, 0);
! 	if (acctfd < 0) {
! 		(void) perror ("acct socket");
! 		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);
  	}
  
  	/*
--- 507,555 ----
  	/*
  	 * 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);
! 		}
  	}
  
  	/*
***************
*** 228,234 ****
  	if(debug_flag == 0) {
  		pid = fork();
  		if(pid < 0) {
! 			fprintf(stderr, "%s: Couldn't fork\n",
  						progname);
  			exit(-1);
  		}
--- 558,564 ----
  	if(debug_flag == 0) {
  		pid = fork();
  		if(pid < 0) {
! 			fprintf(errf, "%s: Couldn't fork\n",
  						progname);
  			exit(-1);
  		}
***************
*** 241,247 ****
  	 *	Disconnect from tty
  	 */
  	for (t = 32; t >= 3; t--) {
! 		if(t != sockfd && t != acctfd) {
  			close(t);
  		}
  	}
--- 571,578 ----
  	 *	Disconnect from tty
  	 */
  	for (t = 32; t >= 3; t--) {
! 		if(t != sockfd && t != acctfd
! 		&& t != rdCachefd && t != wrCachefd ) {
  			close(t);
  		}
  	}
***************
*** 258,270 ****
  #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) {
  		acct_pid = fork();
  		if(acct_pid < 0) {
! 			fprintf(stderr, "%s: Couldn't fork\n",
  						progname);
  			exit(-1);
  		}
--- 589,601 ----
  #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);
  		}
***************
*** 275,283 ****
  		else {
  			close(sockfd);
  			sockfd = -1;
  		}
  	}
! 		
  
  	/*
  	 *	Receive user requests
--- 606,621 ----
  		else {
  			close(sockfd);
  			sockfd = -1;
+ 			if( allow_token_caching ) {
+ 				free(cache);
+ 				close(rdCachefd);
+ 				rdCachefd = -1;
+ 				close(wrCachefd);
+ 				wrCachefd = -1;
+ 			}
  		}
  	}
! 
  
  	/*
  	 *	Receive user requests
***************
*** 294,305 ****
--- 632,691 ----
  			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;
+ 			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);
+ 				if (!stat)
+ 					log_err("Insert cache <%s> FAILED\n",
+ 						msg.key);
+ 				break;
+ 			case CACHE_DELETE:
+ 				stat = cache_delete(cache,
+ 						msg.key, strlen(msg.key)+1);
+ 				if (!stat)
+ 					log_err("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);
+ 				if (!stat)
+ 					log_err("Idle Update cache <%s> FAILED\n",
+ 						msg.key);
+ 				break;
+ 			default:
+ 				stat = FALSE;
+ 				log_err("Unknown cache msg type %d\n",
+ 					msg.type);
+ 				break;
+ 			}
+ 		}
  		if(sockfd >= 0 && FD_ISSET(sockfd, &readfds)) {
  			salen = sizeof (saremote);
  			result = recvfrom (sockfd, (char *) recv_buffer,
***************
*** 307,321 ****
  				(int) 0, & saremote, & salen);
  
  			if(result > 0) {
  				authreq = radrecv(
  					ntohl(sin->sin_addr.s_addr),
  					ntohs(sin->sin_port),
! 					recv_buffer, result);
! 				radrespond(authreq, sockfd);
  			}
  			else if(result < 0 && errno == EINTR) {
  				result = 0;
  			}
  		}
  		if(acctfd >=0 && FD_ISSET(acctfd, &readfds)) {
  			salen = sizeof (saremote);
--- 693,719 ----
  				(int) 0, & saremote, & salen);
  
  			if(result > 0) {
+ #if defined(ACE)
+ 				if (ace_auth_request(sockfd, sin, recv_buffer, result))
+ 					authreq = 0;
+ 				else
+ #endif
  				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);
***************
*** 324,340 ****
  				(int) 0, & saremote, & salen);
  
  			if(result > 0) {
  				authreq = radrecv(
  					ntohl(sin->sin_addr.s_addr),
  					ntohs(sin->sin_port),
! 					recv_buffer, result);
! 				radrespond(authreq, acctfd);
  			}
  			else if(result < 0 && errno == EINTR) {
  				result = 0;
  			}
  		}
  	}
  }
  
  /*************************************************************************
--- 722,747 ----
  				(int) 0, & saremote, & salen);
  
  			if(result > 0) {
+ #if defined(ACE)
+ 				if (ace_auth_request(acctfd, sin, recv_buffer, result))
+ 					authreq = 0;
+ 				else
+ #endif
  				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 */
  }
  
  /*************************************************************************
***************
*** 347,358 ****
   *
   *************************************************************************/
  
! AUTH_REQ	*
! radrecv(host, udp_port, buffer, length)
  UINT4	host;
  u_short	udp_port;
  u_char	*buffer;
  int	length;
  {
  	u_char		*ptr;
  	AUTH_HDR	*auth;
--- 754,766 ----
   *
   *************************************************************************/
  
! 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;
***************
*** 360,389 ****
  	int		attribute;
  	int		attrlen;
  	DICT_ATTR	*attr;
- 	DICT_ATTR	*dict_attrget();
- 	char		string[64];
  	UINT4		lvalue;
- 	char		*ip_hostname();
  	VALUE_PAIR	*first_pair;
  	VALUE_PAIR	*prev;
  	VALUE_PAIR	*pair;
  	AUTH_REQ	*authreq;
  
  	/*
  	 * Pre-allocate the new request data structure
  	 */
  
  	if((authreq = (AUTH_REQ *)malloc(sizeof(AUTH_REQ))) ==
  						(AUTH_REQ *)NULL) {
! 		fprintf(stderr, "%s: no memory\n", progname);
! 		exit(-1);
  	}
  
  	auth = (AUTH_HDR *)buffer;
  	totallen = ntohs(auth->length);
  
  	DEBUG("radrecv: Request from host %lx code=%d, id=%d, length=%d\n",
! 				(u_long)host, auth->code, auth->id, totallen);
  
  	/*
  	 * Fill header fields
--- 768,796 ----
  	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\n",
! 		(u_long)host, auth->code, auth->id, totallen);
  
  	/*
  	 * Fill header fields
***************
*** 393,398 ****
--- 800,806 ----
  	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
***************
*** 412,433 ****
  		}
  		attrlen -= 2;
  		if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) {
! 			DEBUG("Received unknown attribute %d\n", attribute);
  		}
  		else if ( attrlen >= AUTH_STRING_LEN ) {
! 			DEBUG("attribute %d too long, %d >= %d\n", attribute,
! 				attrlen, AUTH_STRING_LEN);
  		}
  		else {
  			if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
  						(VALUE_PAIR *)NULL) {
! 				fprintf(stderr, "%s: no memory\n",
  						progname);
  				exit(-1);
  			}
  			strcpy(pair->name, attr->name);
  			pair->attribute = attr->value;
  			pair->type = attr->type;
  			pair->next = (VALUE_PAIR *)NULL;
  
  			switch(attr->type) {
--- 820,846 ----
  		}
  		attrlen -= 2;
  		if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) {
! 		    DEBUG("Received unknown attribute %d\n", attribute);
  		}
  		else if ( attrlen >= AUTH_STRING_LEN ) {
! 		    DEBUG("attribute %d too long, %d >= %d\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) {
***************
*** 444,450 ****
  				}
  				prev = pair;
  				break;
! 			
  			case PW_TYPE_INTEGER:
  			case PW_TYPE_IPADDR:
  				memcpy(&lvalue, ptr, sizeof(UINT4));
--- 857,863 ----
  				}
  				prev = pair;
  				break;
! 
  			case PW_TYPE_INTEGER:
  			case PW_TYPE_IPADDR:
  				memcpy(&lvalue, ptr, sizeof(UINT4));
***************
*** 458,466 ****
  				}
  				prev = pair;
  				break;
! 			
  			default:
! 				DEBUG("    %s (Unknown Type %d)\n", attr->name,attr->type);
  				free(pair);
  				break;
  			}
--- 871,889 ----
  				}
  				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)\n",
! 					attr->name,attr->type);
  				free(pair);
  				break;
  			}
***************
*** 470,480 ****
--- 893,970 ----
  		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 ) {
+ 		log_err("authenticateAcctReq: from %s, ID %d : %s\n",
+ 			ip_hostname(authreq->ipaddr),
+ 			authreq->id, get_errmsg(stat));
+ 		memset(secret, 0, sizeof(secret));
+ 		error = TRUE;
+ 	}
+ 	else if( totallen + secretlen > MAX_RCVBUF_SIZE ) {
+ 		log_err("rcv req from %s plus secret : too long: s - ID: %d\n",
+ 				ip_hostname(authreq->ipaddr), authreq->id);
+ 		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) ) {
+ 			log_err( "Bad authenticator: from %s - ID: %d\n",
+ 				ip_hostname(authreq->ipaddr), authreq->id);
+ 			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:
***************
*** 489,497 ****
   *
   *************************************************************************/
  
! radrespond(authreq, activefd)
  AUTH_REQ	*authreq;
  int		activefd;
  {
  	switch(authreq->code) {
  
--- 979,989 ----
   *
   *************************************************************************/
  
! int
! radrespond(authreq, activefd, isAcctPkt)
  AUTH_REQ	*authreq;
  int		activefd;
+ char		isAcctPkt;
  {
  	switch(authreq->code) {
  
***************
*** 503,517 ****
  			rad_authenticate(authreq, activefd);
  		}
  		break;
! 	
  	case PW_ACCOUNTING_REQUEST:
! 		rad_accounting(authreq, activefd);
  		break;
! 	
  	case PW_PASSWORD_REQUEST:
  		rad_passchange(authreq, activefd);
  		break;
! 	
  	default:
  		break;
  	}
--- 995,1017 ----
  			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;
  	}
***************
*** 532,537 ****
--- 1032,1038 ----
   *
   *************************************************************************/
  
+ int
  rad_spawn_child(authreq, activefd)
  AUTH_REQ	*authreq;
  int		activefd;
***************
*** 540,547 ****
  	AUTH_REQ	*prevreq;
  	UINT4		curtime;
  	int		request_count;
- 	char		msg[128];
- 	char		*ip_hostname();
  	int		child_pid;
  
  	curtime = (UINT4)time(0);
--- 1041,1046 ----
***************
*** 568,576 ****
  		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);
--- 1067,1074 ----
  		else if(curreq->ipaddr == authreq->ipaddr &&
  					curreq->id == authreq->id) {
  			/* This is a duplicate request - just drop it */
! 			log_err("Dropping duplicate: from %s - ID: %d\n",
  				ip_hostname(authreq->ipaddr), authreq->id);
  			pairfree(authreq->request);
  			free(authreq);
  			return(0);
***************
*** 580,589 ****
  						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);
  			}
--- 1078,1085 ----
  						curreq->child_pid != -1) {
  				/* This request seems to have hung - kill it */
  				child_pid = curreq->child_pid;
! 				log_err("Killing unresponsive child pid %d\n",
  								child_pid);
  				curreq->child_pid = -1;
  				kill(child_pid, SIGHUP);
  			}
***************
*** 595,603 ****
  
  	/* 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);
--- 1091,1098 ----
  
  	/* This is a new request */
  	if(request_count > MAX_REQUESTS) {
! 		log_err("Dropping request (too many): from %s - ID: %d\n",
  				ip_hostname(authreq->ipaddr), authreq->id);
  		pairfree(authreq->request);
  		free(authreq);
  		return(0);
***************
*** 618,629 ****
  	/* 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 */
  		rad_authenticate(authreq, activefd);
  		exit(0);
  	}
--- 1113,1126 ----
  	/* fork our child */
  	child_pid = fork();
  	if(child_pid < 0) {
! 		log_err("Fork failed for request from %s - ID: %d\n",
  				ip_hostname(authreq->ipaddr), authreq->id);
! 		return(1);
  	}
+ 	DEBUG("fork in rad_spawn_child\n");
  	if(child_pid == 0) {
  		/* This is the child, it should go ahead and respond */
+ 		close(rdCachefd);
  		rad_authenticate(authreq, activefd);
  		exit(0);
  	}
***************
*** 634,650 ****
  }
  
  void
! sig_cleanup()
  {
  	int		status;
!         pid_t		pid;
  	AUTH_REQ	*curreq;
!  
!         for (;;) {
  		pid = waitpid((pid_t)-1,&status,WNOHANG);
  		signal(SIGCHLD, sig_cleanup);
!                 if (pid <= 0)
!                         return;
  
  #if defined (aix)
  		kill(pid, SIGKILL);
--- 1131,1154 ----
  }
  
  void
! sig_cleanup(sig)
! int	sig;
  {
  	int		status;
! 	pid_t		pid;
  	AUTH_REQ	*curreq;
! #if defined(ACE)
! 	ACE_CHILD	*child;
! 	ACE_CHILD	*end = &ace_children[ace_child_count];
! #endif
! 
! 	(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);
***************
*** 662,766 ****
  			}
  			curreq = curreq->next;
  		}
!         }
  }
  
  /*************************************************************************
   *
!  *	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	*oldpasspair;
! 	VALUE_PAIR	*curpass;
! 	VALUE_PAIR	*user_check;
! 	VALUE_PAIR	*user_reply;
! 	char		pw_digest[16];
! 	char		string[64];
! 	char		passbuf[AUTH_PASS_LEN];
! 	int		i;
! 	int		secretlen;
! 	char		msg[128];
! 	char		*ip_hostname();
  
! 	/* 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);
! 		pairfree(authreq->request);
! 		memset(authreq, 0, sizeof(AUTH_REQ));
! 		free(authreq);
! 		return;
! 	}
  
! 	/*
! 	 * Look the user up in the database
! 	 */
! 	if(user_find(namepair->strvalue, &user_check, &user_reply) != 0) {
! 		sprintf(msg,
! 			"Passchange: from %s - Invalid User: %s\n",
! 			ip_hostname(authreq->ipaddr), 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);
--- 1166,1589 ----
  			}
  			curreq = curreq->next;
  		}
! #if defined(ACE)
! 		/* reap dead ACE children */
! 		for (child = ace_children; child < end; child++) {
! 			if (child->pid != pid)
! 				continue;
! 			close(child->pipe_fd);
! 			*child-- = *--end;
! 			ace_child_count--;
! 		}
! #endif
! 	}
  }
  
+ #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)\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\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\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;
! 	int		vecLen;
! 	int		retval;
! 	UINT4		user_expiration_days;
! 	UINT4		user_expiration_seconds;
! 
! 	if( allow_passchange == 0 ) {
! 		log_err("Passchange: from %s: Password Changing NOT Allowed\n",
! 				ip_hostname(authreq->ipaddr));
! 		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) {
! 		log_err("Passchange: from %s - No User name supplied\n",
! 			ip_hostname(authreq->ipaddr));
! 		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) {
! 		log_err("Passchange: from %s - %s: %s\n",
! 			ip_hostname(authreq->ipaddr), get_errmsg(retval),
! 			namepair->strvalue);
! 		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 */
! 		log_err("Passchange: from %s - Missing Password: %s\n",
! 			ip_hostname(authreq->ipaddr), namepair->strvalue);
! 		send_reject(authreq, (char *)NULL, activefd);
! 		pairfree(authreq->request);
! 		pairfree(user_check);
  		pairfree(user_reply);
  		memset(authreq, 0, sizeof(AUTH_REQ));
  		free(authreq);
***************
*** 777,786 ****
  	}
  	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);
--- 1600,1607 ----
  	}
  	if((curpass == (VALUE_PAIR *)NULL) || curpass->strvalue == (char *)NULL) {
  		/* Missing our local copy of the password */
! 		log_err("Passchange: from %s - Missing Local Password: %s\n",
  			ip_hostname(authreq->ipaddr), namepair->strvalue);
  		send_reject(authreq, (char *)NULL, activefd);
  		pairfree(authreq->request);
  		pairfree(user_check);
***************
*** 789,800 ****
  		free(authreq);
  		return;
  	}
! 	if(strcmp(curpass->strvalue,"UNIX") == 0) {
  		/* 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);
--- 1610,1620 ----
  		free(authreq);
  		return;
  	}
! 	if( getPasswordType(curpass->strvalue) != PWD_NORMAL ) {
  		/* Can't change passwords that aren't in users file */
! 		log_err("Passchange: from %s - system password change not allowed: %s\n",
! 				ip_hostname(authreq->ipaddr),
! 				namepair->strvalue);
  		send_reject(authreq, (char *)NULL, activefd);
  		pairfree(authreq->request);
  		pairfree(user_check);
***************
*** 806,825 ****
  
  	/* Decrypt the old password */
  	secretlen = strlen(curpass->strvalue);
  	memcpy(string, curpass->strvalue, secretlen);
! 	memcpy(string + secretlen, newpasspair->strvalue, AUTH_VECTOR_LEN);
! 	md5_calc(pw_digest, string, AUTH_VECTOR_LEN + secretlen);
! 	memcpy(passbuf, oldpasspair->strvalue, AUTH_PASS_LEN);
! 	for(i = 0;i < AUTH_PASS_LEN;i++) {
! 		passbuf[i] ^= pw_digest[i];
  	}
  
  	/* Did they supply the correct password ??? */
! 	if(strncmp(passbuf, curpass->strvalue, AUTH_PASS_LEN) != 0) {
! 		sprintf(msg,
! 			"Passchange: from %s - Incorrect Password: %s\n",
! 			ip_hostname(authreq->ipaddr), namepair->strvalue);
! 		log_err(msg);
  		send_reject(authreq, (char *)NULL, activefd);
  		pairfree(authreq->request);
  		pairfree(user_check);
--- 1626,1655 ----
  
  	/* Decrypt the old password */
  	secretlen = strlen(curpass->strvalue);
+ 	vecLen = newpasspair->size;
+ #if( 0 )
+ 	DEBUG("strlen(newpasspair->strvalue): %d, newpasspair->size: %d\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) {
! 		log_err("Passchange: from %s - Bad Pwd for %s:\n",
! 				ip_hostname(authreq->ipaddr),
! 				namepair->strvalue);
! #if defined( PWD_DEBUG )
! 		log_err("Passchange (cont'd): Exp Pwd(%d, %.*s); User Exp Pwd(%d, %.*s)\n",
! 				curpass->size, curpass->size, curpass->strvalue,
! 				newpasspair->size, newpasspair->size, passbuf);
! #endif
  		send_reject(authreq, (char *)NULL, activefd);
  		pairfree(authreq->request);
  		pairfree(user_check);
***************
*** 832,859 ****
  	/* Decrypt the new password */
  	memcpy(string, curpass->strvalue, secretlen);
  	memcpy(string + secretlen, authreq->vector, AUTH_VECTOR_LEN);
! 	md5_calc(pw_digest, string, AUTH_VECTOR_LEN + secretlen);
! 	memcpy(passbuf, newpasspair->strvalue, AUTH_PASS_LEN);
! 	for(i = 0;i < AUTH_PASS_LEN;i++) {
! 		passbuf[i] ^= pw_digest[i];
  	}
  
  	/* Update the users password */
! 	strncpy(curpass->strvalue, passbuf, AUTH_PASS_LEN);
  
  	/* Add a new expiration date if we are aging passwords */
! 	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);
--- 1662,1700 ----
  	/* 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);
! 		log_err("Passchange: unable to update password for %s\n",
  			namepair->strvalue);
  	}
  	else {
  		send_pwack(authreq, activefd);
***************
*** 868,1735 ****
  
  /*************************************************************************
   *
!  *	Function: rad_authenticate
   *
!  *	Purpose: Process and reply to an authentication request
   *
   *************************************************************************/
  
! rad_authenticate(authreq, activefd)
! AUTH_REQ	*authreq;
! int		activefd;
  {
! 	VALUE_PAIR	*namepair;
! 	VALUE_PAIR	*check_item;
! 	VALUE_PAIR	*auth_item;
! 	VALUE_PAIR	*user_check;
! 	VALUE_PAIR	*user_reply;
! 	int		result;
! 	char		pw_digest[16];
! 	char		string[128];
! 	int		i;
! 	char		msg[128];
! 	char		umsg[128];
! 	char		*user_msg;
! 	char		*ip_hostname();
! 	int		retval;
! 	char		*ptr;
  
! 	/* Get the username from the request */
! 	namepair = authreq->request;
! 	while(namepair != (VALUE_PAIR *)NULL) {
! 		if(namepair->attribute == PW_USER_NAME) {
! 			break;
! 		}
! 		namepair = namepair->next;
! 	}
! 	if((namepair == (VALUE_PAIR *)NULL) || 
! 	   (strlen(namepair->strvalue) <= 0)) {
! 		sprintf(msg, "Authenticate: from %s - No User Name\n",
! 			ip_hostname(authreq->ipaddr));
! 		log_err(msg);
! 		pairfree(authreq->request);
! 		memset(authreq, 0, sizeof(AUTH_REQ));
! 		free(authreq);
! 		return;
  	}
  
! 	/* Verify the client and Calculate the MD5 Password Digest */
! 	if(calc_digest(pw_digest, authreq) != 0) {
! 		/* We dont respond when this fails */
! 		sprintf(msg, "Authenticate: from %s - Security Breach: %s\n",
! 			ip_hostname(authreq->ipaddr), namepair->strvalue);
! 		log_err(msg);
! 		pairfree(authreq->request);
! 		memset(authreq, 0, sizeof(AUTH_REQ));
! 		free(authreq);
! 		return;
  	}
  
! 	/* Get the user from the database */
! 	if(user_find(namepair->strvalue, &user_check, &user_reply) != 0) {
! 		sprintf(msg, "Authenticate: from %s - Invalid User: %s\n",
! 			ip_hostname(authreq->ipaddr), 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 */
  
! 	/* Look for matching check items */
! 	result = 0;
! 	user_msg = (char *)NULL;
! 	check_item = user_check;
! 	while(result == 0 && check_item != (VALUE_PAIR *)NULL) {
  
! 		/*
! 		 * Check expiration date if we are doing password aging.
! 		 */
! 		if(check_item->attribute == PW_EXPIRATION) {
! 			/* Has this user's password expired */
! 			retval = pw_expired(check_item->lvalue);
! 			if(retval < 0) {
! 				result = -1;
! 				user_msg = "Password Has Expired\r\n";
  			}
! 			else {
! 				if(retval > 0) {
! 					sprintf(umsg,
! 					  "Password Will Expire in %d Days\r\n",
! 					  retval);
! 					user_msg = umsg;
! 				}
! 				check_item = check_item->next;
  			}
- 			continue;
- 		}
  
! 		/*
! 		 * Look for the matching attribute in the request.
! 		 */
! 		auth_item = authreq->request;
! 		while(auth_item != (VALUE_PAIR *)NULL) {
! 			if(check_item->attribute == auth_item->attribute) {
! 				break;
  			}
! 			if(check_item->attribute == PW_PASSWORD &&
! 				     auth_item->attribute == PW_CHAP_PASSWORD) {
! 				break;
  			}
  
! 			auth_item = auth_item->next;
  		}
! 		if(auth_item == (VALUE_PAIR *)NULL) {
! 			result = -1;
! 			continue;
  		}
  
! 		/*
! 		 * Special handling for passwords which are encrypted,
! 		 * and sometimes authenticated against the UNIX passwd database.
! 		 * Also they can come using the Three-Way CHAP.
! 		 *
! 		 */
! 		if(check_item->attribute == PW_PASSWORD) {
! 			if(auth_item->attribute == PW_CHAP_PASSWORD) {
! 				/* Use MD5 to verify */
! 				ptr = string;
! 				*ptr++ = *auth_item->strvalue;
! 				strcpy(ptr, check_item->strvalue);
! 				ptr += strlen(check_item->strvalue);
! 				memcpy(ptr, authreq->vector, AUTH_VECTOR_LEN);
! 				md5_calc(pw_digest, string,
! 					1 + CHAP_VALUE_LENGTH +
! 					strlen(check_item->strvalue));
! 				/* Compare them */
! 				if(memcmp(pw_digest, auth_item->strvalue + 1,
! 						CHAP_VALUE_LENGTH) != 0) {
! 					result = -1;
! 				}
  			}
  			else {
! 				/* Decrypt the password */
! 				memcpy(string,
! 					auth_item->strvalue, AUTH_PASS_LEN);
! 				for(i = 0;i < AUTH_PASS_LEN;i++) {
! 					string[i] ^= pw_digest[i];
  				}
! 				string[AUTH_PASS_LEN] = '\0';
! 				/* Test Code for Challenge */
! 				if(strcmp(string, "challenge") == 0) {
! 					send_challenge(authreq, 
! 				"You want me to challenge you??\r\nOkay I will",
! 						"1",activefd);
! 					pairfree(authreq->request);
! 					memset(authreq, 0, sizeof(AUTH_REQ));
! 					free(authreq);
! 					return;
  				}
! 				if(strcmp(check_item->strvalue, "UNIX") == 0) {
! 					if(unix_pass(namepair->strvalue,
! 								string) != 0) {
! 						result = -1;
! 						user_msg = (char *)NULL;
  					}
  				}
! 				else if(strcmp(check_item->strvalue,
! 								string) != 0) {
! 					result = -1;
! 					user_msg = (char *)NULL;
  				}
  			}
  		}
  		else {
! 			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 {
! 		send_accept(authreq, user_reply, user_msg, activefd);
  	}
! 	pairfree(authreq->request);
! 	memset(authreq, 0, sizeof(AUTH_REQ));
! 	free(authreq);
! 	pairfree(user_check);
! 	pairfree(user_reply);
! 	return;
  }
  
  /*************************************************************************
   *
!  *	Function: send_reject
   *
!  *	Purpose: Reply to the request with a REJECT.  Also attach
!  *		 any user message provided.
   *
   *************************************************************************/
  
! send_reject(authreq, msg, activefd)
  AUTH_REQ	*authreq;
- char		*msg;
  int		activefd;
  {
! 	AUTH_HDR		*auth;
! 	struct	sockaddr	saremote;
! 	struct	sockaddr_in	*sin;
! 	char			*ip_hostname();
! 	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(authreq->secret);
! 	memcpy(send_buffer + total_length, authreq->secret, secretlen);
! 	md5_calc(digest, (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)\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.
   *
   *************************************************************************/
  
! send_challenge(authreq, msg, state, activefd)
  AUTH_REQ	*authreq;
  char		*msg;
  char		*state;
  int		activefd;
  {
  	AUTH_HDR		*auth;
  	struct	sockaddr_in	saremote;
  	struct	sockaddr_in	*sin;
- 	char			*ip_hostname();
  	char			digest[AUTH_VECTOR_LEN];
  	int			secretlen;
  	int			total_length;
  	u_char			*ptr;
- 	int			len;
  
  	auth = (AUTH_HDR *)send_buffer;
  
  	/* 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;
  
! 	/* 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;
  		}
  	}
  
  	/* Append the state info */
! 	if((state != (char *)NULL) && (strlen(state) > 0)) {
! 		len = strlen(state);
! 		*ptr++ = PW_STATE;
! 		*ptr++ = len + 2;
! 		memcpy(ptr, state, len);
! 		ptr += len;
! 		total_length += len + 2;
  	}
  
  	/* Set total length in the header */
  	auth->length = htons(total_length);
  
  	/* Calculate the response digest */
! 	secretlen = strlen(authreq->secret);
  	memcpy(send_buffer + total_length, authreq->secret, secretlen);
! 	md5_calc(digest, (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)\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.
   *
   *************************************************************************/
  
! send_pwack(authreq, activefd)
  AUTH_REQ	*authreq;
  int		activefd;
  {
  	AUTH_HDR		*auth;
! 	struct	sockaddr	saremote;
  	struct	sockaddr_in	*sin;
- 	char			*ip_hostname();
  	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(authreq->secret);
! 	memcpy(send_buffer + AUTH_HDR_LEN, authreq->secret, secretlen);
! 	md5_calc(digest, (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)\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_accept
!  *
!  *	Purpose: Reply to the request with an ACKNOWLEDGE.  Also attach
!  *		 reply attribute value pairs and any user message provided.
!  *
!  *************************************************************************/
! 
! 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[16];
! 	int			secretlen;
! 	char			*ip_hostname();
! 
! 	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)\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:
! 			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;
! 
! 		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(authreq->secret);
! 	memcpy(send_buffer + total_length, authreq->secret, secretlen);
! 	md5_calc(digest, (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.
!  *
!  *************************************************************************/
! 
! unix_pass(name, passwd)
! char	*name;
! char	*passwd;
  {
! 	struct passwd	*pwd;
! 	struct passwd	*getpwnam();
! 	char		*encpw;
! 	char		*crypt();
! 	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(-1);
! 	}
  
- 	encrypted_pass = pwd->pw_passwd;
  
! #if !defined(NOSHADOW)
! 	if(strcmp(pwd->pw_passwd, "x") == 0) {
! 		if((spwd = getspnam(name)) == NULL) {
! 			return(-1);
! 		}
! #if defined(M_UNIX)
! 		encrypted_pass = spwd->pw_passwd;
! #else
! 		encrypted_pass = spwd->sp_pwdp;
! #endif	/* M_UNIX */
! 	}
! #endif	/* !NOSHADOW */
  
- 	/* Run encryption algorythm */
- 	encpw = crypt(passwd, encrypted_pass);
  
! 	/* Check it */
! 	if(strcmp(encpw, encrypted_pass)) {
! 		return(-1);
! 	}
! 	return(0);
  }
  
  /*************************************************************************
   *
!  *	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.
   *
   *************************************************************************/
  
! calc_digest(digest, authreq)
! u_char		*digest;
! AUTH_REQ	*authreq;
  {
! 	FILE	*clientfd;
! 	u_char	buffer[128];
! 	u_char	secret[64];
! 	char	hostnm[256];
! 	char	msg[128];
! 	char	*ip_hostname();
! 	int	secretlen;
! 	UINT4	ipaddr;
! 	UINT4	get_ipaddr();
  
! 	/* Find the client in the database */
! 	sprintf(buffer, "%s/%s", radius_dir, RADIUS_CLIENTS);
! 	if((clientfd = fopen(buffer, "r")) == (FILE *)NULL) {
! 		fprintf(stderr, "%s: couldn't open %s to find clients\n",
! 				progname, buffer);
! 		return(-1);
! 	}
! 	ipaddr = (UINT4)0;
! 	while(fgets(buffer, sizeof(buffer), clientfd) != (char *)NULL) {
! 		if(*buffer == '#') {
! 			continue;
! 		}
! 		if(sscanf(buffer, "%s%s", hostnm, secret) != 2) {
! 			continue;
! 		}
! 		ipaddr = get_ipaddr(hostnm);
! 		if(ipaddr == authreq->ipaddr) {
! 			break;
! 		}
! 	}
! 	fclose(clientfd);
! 	memset(buffer, 0, sizeof(buffer));
  
  	/*
! 	 * Validate the requesting IP address -
! 	 * Not secure, but worth the check for accidental requests
  	 */
! 	if(ipaddr != authreq->ipaddr) {
! 		strcpy(hostnm,ip_hostname(ipaddr));
! 		sprintf(msg, "requester address mismatch: %s != %s\n",
! 			hostnm,
! 			ip_hostname(authreq->ipaddr));
! 		log_err(msg);
! 		memset(secret, 0, sizeof(secret));
! 		return(-1);
! 	}
  
! 	/* Use the secret to setup the decryption digest */
! 	secretlen = strlen(secret);
! 	strcpy(buffer, secret);
! 	memcpy(buffer + secretlen, authreq->vector, AUTH_VECTOR_LEN);
! 	md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN);
! 	strcpy(authreq->secret, 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.
   *
   *************************************************************************/
! 
! 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.
   *
   *************************************************************************/
  
! usage()
  {
! 	fprintf(stderr, "Usage: %s [ -a acct_dir ] [ -s ] [ -x ] [ -d db_dir ]\n",progname);
! 	exit(-1);
  }
  
- /*************************************************************************
-  *
-  *	Function: log_err
-  *
-  *	Purpose: Log the error message provided to the error log with
- 		 a time stamp.
-  *
-  *************************************************************************/
  
! 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) {
! 		fprintf(stderr, "%s:Couldn't open %s for logging\n",
! 				progname, buffer);
! 		return(-1);
! 	}
! 	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 Password-Expiration
!  *			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.
   *
   *************************************************************************/
! 
! config_init()
  {
! 	DICT_VALUE	*dval;
! 	DICT_VALUE	*dict_valfind();
  
! 	if((dval = dict_valfind("Password-Expiration")) == (DICT_VALUE *)NULL) {
! 		expiration_seconds = (UINT4)0;
! 	}
! 	else {
! 		expiration_seconds = dval->value * (UINT4)SECONDS_PER_DAY;
  	}
! 	if((dval = dict_valfind("Password-Warning")) == (DICT_VALUE *)NULL) {
! 		warning_seconds = (UINT4)0;
  	}
! 	else {
! 		warning_seconds = dval->value * (UINT4)SECONDS_PER_DAY;
  	}
! 	return(0);
  }
  
  /*************************************************************************
   *
!  *	Function: set_expiration
   *
!  *	Purpose: Set the new expiration time by updating or adding
! 		 the Expiration attribute-value pair.
   *
   *************************************************************************/
! 
! set_expiration(user_check, expiration)
! VALUE_PAIR	*user_check;
! UINT4		expiration;
  {
! 	VALUE_PAIR	*exppair;
! 	VALUE_PAIR	*prev;
! 	struct timeval	tp;
! 	struct timezone	tzp;
! 
! 	if(user_check == (VALUE_PAIR *)NULL) {
! 		return(-1);
  	}
  
! 	/* Look for an existing expiration entry */
! 	exppair = user_check;
! 	prev = (VALUE_PAIR *)NULL;
! 	while(exppair != (VALUE_PAIR *)NULL) {
! 		if(exppair->attribute == 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(stderr, "%s: no memory\n", progname);
! 			exit(-1);
! 		}
! 		/* Initialize it */
! 		strcpy(exppair->name, "Expiration");
! 		exppair->attribute = 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 */
! 	gettimeofday(&tp, &tzp);
! 	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
!  8		otherwise 0 for success and -1 for failure.
   *
   *************************************************************************/
  
! pw_expired(exptime)
! UINT4	exptime;
  {
! 	struct timeval	tp;
! 	struct timezone	tzp;
! 	UINT4		exp_remain;
! 	int		exp_remain_int;
  
! 	if(expiration_seconds == (UINT4)0) {
! 		return(0);
  	}
  
! 	gettimeofday(&tp, &tzp);
! 	if(tp.tv_sec > exptime) {
! 		return(-1);
  	}
! 	if(warning_seconds != (UINT4)0) {
! 		if(tp.tv_sec > exptime - warning_seconds) {
! 			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(stderr, "%s: exit on signal (%d)\n", progname, sig);
! 	fflush(stderr);
! 	exit(1);
  }
  
  void
! sig_hup(sig)
  int	sig;
  {
! 	return;
  }
--- 1709,4668 ----
  
  /*************************************************************************
   *
!  *	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;
! 
! 	DEBUG("authSafewordPwd\n");
!     	authInfo->user_msg = (char *)NULL;
! 	pb = (struct pblk *)malloc(sizeof(*pb));
! 	if( !pb ) {
! 		log_err("authSafewordPwd: malloc err for user %s\n",
! 			authInfo->authName->strvalue);
! 		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;
! 	int result;
! 
! 	result = safeword_pass(authInfo, pb);
! 	switch( result ) {
! 	default:
! 		log_err("safeword_pass BIZARRE result %d, user %s, NAS %s\n",
! 			result, authInfo->authName->strvalue,
! 			ip_hostname(authInfo->authreq->ipaddr));
! 		authInfo->user_msg = (char *)NULL;
! 		result = SAFEWORD_FAILED;
! 		break;
! 
! 	case SAFEWORD_FAILED:
! 		log_err("safeword_pass FAILED, user %s, NAS %s\n",
! 			authInfo->authName->strvalue,
! 			ip_hostname(authInfo->authreq->ipaddr));
! 		authInfo->user_msg = (char *)NULL;
! 		result = SAFEWORD_FAILED;
! 		break;
! 
! 	case SAFEWORD_CHALLENGING:
! 		DEBUG("safeword_pass CHALLENGING\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\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;
! {
! 	u_char stateInfo[253];
! 	u_char *si;
! 	struct SD_CLIENT *sd = (struct SD_CLIENT *)malloc(sizeof(*sd));
! 	char *nextMsg = "(without PIN)";
! 	char *ncMsg;
! 	int result;
! 
!     	authInfo->user_msg=(char *)NULL;
! 	result = ace_pass(authInfo, sd);
! 	switch( result ) {
! 	default:
! 		log_err("ace_pass BIZARRE result %d for user %s, NAS %s\n",
! 			result, authInfo->authName->strvalue,
! 			ip_hostname(authInfo->authreq->ipaddr));
! 		authInfo->user_msg = (char *)NULL;
! 		result = ACE_FAILED;
! 		break;
! 
! 	case ACE_FAILED:
! 		log_err("ace_pass FAILED for user %s, NAS %s\n",
! 			authInfo->authName->strvalue,
! 			ip_hostname(authInfo->authreq->ipaddr));
! 		authInfo->user_msg = (char *)NULL;
! 		result = ACE_FAILED;
! 		break;
! 
! 	case ACE_CHALLENGING:
! 		DEBUG("ace_pass CHALLENGING\n");
! 		send_challenge( authInfo->authreq, NULL, 0,
! 				ACE_DUMMY_STATE, LEN_ACE_DUMMY_STATE, authInfo->fd);
! 		result = -ACE_CHALLENGING;
! 		break;
! 
! 	case ACE_PASSED:
! 		DEBUG("ace_pass PASSED\n");
! 		result = 0;
! 		break;
! 
! 	case ACE_NEXT_PASSCODE:
! 		DEBUG("ace_pass NEXT_CODE\n");
! 		ncMsg = nextMsg;
! 		si = stateInfo;
! 
! 		*si = (u_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));
! 		si += LEN_SD_3RD(sd);
! 		if (spawn_flag) {
! 			pid_t pid = getpid();
! 			memcpy(si, &pid, sizeof(pid_t));
! 		}
! 		send_nextcode(	authInfo->authreq, ncMsg, strlen(ncMsg),
! 				stateInfo, LEN_SD_TOTAL(sd) + 1, authInfo->fd);
! 		result = -ACE_NEXT_PASSCODE;
! 		break;
! 
! 	case ACE_NEW_PIN:
! 		DEBUG("ace_pass NEXT_PIN\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	string[AUTH_PASS_LEN + 64];
! 	char	*ptr;
! 	u_char	size;
! 	int	result;
! 	u_char	*pw_digest = (u_char *)authInfo->pw_digest;
! 
! 	DEBUG("authChapToken\n");
! 
! 	size = authInfo->authPwd->size;
! 	if( size != AUTH_VECTOR_LEN + 1 ) {
! 		log_err("CHAP Token - Bad Pwd Size(%d): user %s, NAS %s\n",
! 				size, authInfo->authName->strvalue,
! 				ip_hostname(authInfo->ipaddr));
! 		return -1;
! 	}
! 	ptr = string;
! 	if( authInfo->authState && authInfo->chkSecret ) {
! #if defined( PWD_DEBUG )
! 		DEBUG("chkSecret(%s)\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>\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\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\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 ) {
! 					log_err("Err: deleting <%s>\n",
! 						authInfo->authName->strvalue);
! 				}
! #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\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 */
+ 		log_err("CHAP Token Attempt: user %s, NAS %s\n",
+ 				authInfo->authName->strvalue,
+ 				ip_hostname(authInfo->ipaddr));
+ 		result = -1;
+ 	}
+ 	return result;
+ }
  
! /*************************************************************************
!  *
!  *	Function: authChapPwd
!  *
!  *	Purpose: Process and reply to a CHAP authentication request
!  *
!  *************************************************************************/
! 
! int
! authChapPwd(authInfo)
! AuthInfo	*authInfo;
! {
! 	char	string[AUTH_PASS_LEN + 64];
! 	char	*ptr;
! 	int	result = -1;	/* init to failure */
! 
! 	DEBUG("authChapPwd\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\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 */
+ 		log_err("CHAP Unix Attempt: user %s, NAS %s\n",
+ 				authInfo->authName->strvalue,
+ 				ip_hostname(authInfo->ipaddr));
+ 		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\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\n");
! 	authInfo->user_msg = (char *)NULL;
! 
! 	/* Decrypt the password */
! 	len = authInfo->authPwd->size;
! 	if( len > AUTH_PASS_LEN ) {
! 		log_err("authPapPwd: from %s, user %s: pwd too long (%d)\n",
! 			ip_hostname(authInfo->authreq->ipaddr),
! 			authInfo->authName->strvalue, len);
! 		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' decrypted pwd is `%s'\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);
! 			authInfo->authState->size = LEN_ACE_DUMMY_STATE;
! 		}
! 		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
   *
   *************************************************************************/
  
! int
! 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		umsg[128];
! 	char		*user_msg;
! 	int		retval;
! 	PasswordType	passwordType;
! 	int		passwordExpired;
! 	int		tokenExpired;
! 	AuthInfo	authInfo;
! 
! 	DEBUG("rad_authenticate\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)) {
! 		log_err("Authenticate: from %s - No User Name\n",
! 			ip_hostname(authreq->ipaddr));
! 		authCleanup(&authInfo);
! 		return -1;
  	}
  	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\n");
! 		log_err("Authenticate: from %s - Security Breach: %s\n",
! 			ip_hostname(authreq->ipaddr), authName->strvalue);
! 		authCleanup(&authInfo);
! 		return -1;
! 	}
! 
! 	/* Get the user from the database */
! 	if( (retval = user_find(authName->strvalue, &checkList, &replyList))
! 	  != 0) {
! 		log_err("Authenticate: from %s - %s: %s\n",
! 			ip_hostname(authreq->ipaddr), get_errmsg(retval),
! 			authName->strvalue);
! 		send_reject(authreq, (char *)NULL, activefd);
! 		authCleanup(&authInfo);
! 		return -1;
! 	}
! 
! 	/* 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! */
! 		log_err("Authenticate: from %s - No pwd in record!\n",
! 			ip_hostname(authreq->ipaddr));
! 		send_reject(authreq, (char *)NULL, activefd);
! 		authCleanup(&authInfo);
! 		return -1;
! 	}
! 	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;
! 
! 		VALUE_PAIR *expiryCopy = copyValuePair(pwdExp);
! 		insertValuePair(&replyList,expiryCopy);
! 
! 		DEBUG("rad_auth: check_item: ASCEND_PW_EXPIRATION\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\n");
! 		}
! 		else if( retval > 0 ) {
! 			DEBUG("rad_auth: password WARNING: %d\n", retval);
! 			sprintf(umsg,
! 			  	"Password Will Expire in %d Days\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! */
! 		log_err("Authenticate: from %s - No pwd in request!\n",
! 			ip_hostname(authreq->ipaddr));
! 		send_reject(authreq, (char *)NULL, activefd);
! 		authCleanup(&authInfo);
! 		return -1;
! 	}
! 	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\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\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\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\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 result;
! 	}
! 	else if( result > 0 ) {	/* token stuff; neither pass nor fail */
! 		authCleanup(&authInfo);
! 		return result;
! 	}
! 
! 	/*
! 	 * 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\n";
! 				send_reject(authreq, user_msg, activefd);
! 				break;
! 			case PWD_NORMAL:
! 				user_msg = "Password Has Expired\n";
! 				send_pwexpired(authreq, user_msg, activefd);
! 				break;
! 			}
! 		    }
! 		    else {
! 			user_msg = "Password Has Expired\n";
! 			send_reject(authreq, user_msg, activefd);
! 		    }
! 		}
! 		else {
! 			send_accept(authreq, replyList, user_msg, activefd);
! 		}
! 	}
! 	authCleanup(&authInfo);
! 	pairfree(checkList);
! 	pairfree(replyList);
! 	return result;
! }
! 
! /*************************************************************************
!  *
!  *	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)\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("send_challenge:\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\n", msgLen);
! 		*ptr++ = PW_PORT_MESSAGE;
! 		*ptr++ = msgLen + 2;
! 		if (msg) {
! 			memcpy(ptr, msg, msgLen);
! 			if( debug_flag == 1 ) {
! 				debugf("challenge msg before encrypt: \"");
! 				fwrite(msg, 1, msgLen, stdout);
! 				puts("\"");
! 			}
! 			ptr += msgLen;
! 		}
! 		total_length += msgLen + 2;
! 	}
! 
! 	/* Append the state info */
! 	if( state != (char *)NULL ) {
! 		DEBUG("...Adding state, len=%d\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\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;
! 
! 	    debugf("sendBuf before encrypt:\n");
! 	    for( i = 0; i < total_length+secretlen; i++ ) {
! 		printf("%x ", (u_char)(send_buffer[i]));
! 	    }
! 	    printf("\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)\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)\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)\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)\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];
! 	int	secretlen;
! 	UINT4	ipaddr;
! 	int	stat;
! 
! 	stat = get_client_info( authreq->ipaddr, &ipaddr, secret, hostnm );
! 	memset(buffer, 0, sizeof(buffer));
! 	if( stat != 0 ) {
! 		log_err("Calc_digest: from %s, ID %d : %s\n",
! 			ip_hostname(authreq->ipaddr),
! 			authreq->id, get_errmsg(stat));
! 		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(va_alist)
! va_dcl
! {
! 	FILE	*msgfd;
! 	char	buffer[128];
! 	time_t	timeval;
! 	char	*fmt;
! 	va_list ap;
! 
! 	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: ", ctime(&timeval));
! 	va_start (ap);
! 	fmt = va_arg(ap, char *);
! 	vfprintf (msgfd, fmt, ap);
! 	vdebugf (fmt, ap);
! 	va_end (ap);
! 	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\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\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)\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("send_nextcode:\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\n", msgLen);
! 		*ptr++ = PW_PORT_MESSAGE;
! 		*ptr++ = msgLen + 2;
! 		memcpy(ptr, msg, msgLen);
! 		if( debug_flag == 1 ) {
! 			debugf("nextcode msg before encrypt: \"");
! 			fwrite(msg, 1, msgLen, stdout);
! 			puts("\"");
  		}
+ 		ptr += msgLen;
+ 		total_length += msgLen + 2;
  	}
  
  	/* Append the state info */
! 	if( state != (char *)NULL ) {
! 	    DEBUG("...Adding state <%s>, len=%d\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\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;
! 	    debugf("sendBuf before encrypt:\n");
! 	    for( i = 0; i < total_length+secretlen; i++ ) {
! 		printf("%x ", (u_char)(send_buffer[i]));
! 	    }
! 	    printf("\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)\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("send_newpin: NOT IMPLEMENTED\n");
  
  }
  
! /************************************************************************/
! /*									*/
! /*		Map error codes to messages				*/
! /*									*/
! /************************************************************************/
! 
! char *
! get_errmsg(errcode)
! int errcode;
  {
!     char *emsg = (char *)0;
  
!     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";
! 	    log_err("INTERNAL: ErrMsgs out of order for errcode %d\n",
! 		    errcode);
! 	}
!     }
!     else {
! 	emsg = "INTERNAL: Unknown error";
! 	log_err("INTERNAL: Unknown error code %d\n", errcode);
!     }
!     return emsg;
! }
  
! static VALUE_PAIR *
! copyValuePair(source)
! VALUE_PAIR* source;
! {
!     VALUE_PAIR *pair;
  
!     if ( (pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
!                                               (VALUE_PAIR *)NULL ) {
!       fprintf(errf, "%s: no memory\n",progname);
!       exit(MEMORY_ERR);
!     }
!     *pair = *source;
!     pair->next = (VALUE_PAIR*) NULL;
!     return pair;
  }
  
! static void
! insertValuePair(list,pair)
! VALUE_PAIR** list;
! VALUE_PAIR*  pair;
  {
!     pair->next = *list;
!     *list = pair;
! }
  
  
! #if defined(SAFEWORD)
! /************************************************************************/
! /*									*/
! /*		Enigma Safeword Interface routines			*/
! /*									*/
! /************************************************************************/
  
  
! 	/* simple debug output */
! void dbgPblk(pb)
! struct pblk	*pb;
! {
!     if( debug_flag ) {
! 	debugf("\n");
! 	printf("    PBLK: uport    = <%s>\n", pb->uport);
! 	printf("    PBLK: id       = <%s>\n", pb->id);
! 	printf("    PBLK: chal     = <%s>\n", pb->chal);
! 	printf("    PBLK: dynpwd   = <%s>\n", pb->dynpwd);
! 	printf("    PBLK: fixpwd   = <%s>\n", pb->fixpwd);
! 	printf("    PBLK: nfixpwd  = <%s>\n", pb->nfixpwd);
! 	printf("    PBLK: errcode  = <%d>\n", pb->errcode);
! 	printf("    PBLK: lastmode = <%d>\n", pb->lastmode);
! 	printf("    PBLK: mode     = <%d>\n", pb->mode);
! 	printf("    PBLK: fixmin   = <%d>\n", pb->fixmin);
! 	printf("    PBLK: dynpwdf  = <%d>\n", pb->dynpwdf);
! 	printf("    PBLK: fixpwdf  = <%d>\n", pb->fixpwdf);
! 	printf("    PBLK: echodyn  = <%d>\n", pb->echodyn);
! 	printf("    PBLK: echofix  = <%d>\n", pb->echofix);
! 	printf("    PBLK: status   = <%d>\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:\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}\n");
! 	dbgPblk(pb);
! 
! 	switch( pb->status ) {
! 	case GOOD_USER:
! 		DEBUG("safeword_chall: SafeWord Challenging(%s).\n",
! 			pb->chal);
! 		return SAFEWORD_CHALLENGING;
! 	default:
! 		DEBUG("safeword_chall: SafeWord failed = %d.\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:\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\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>\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 {
! 		log_err("safeword_eval(): dynamic pwd flag not set!\n");
! 		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 ) {
! 			log_err("user %s, fixed pwd w/o fixpwdf, NAS %s\n",
! 					authInfo->authName->strvalue,
! 					ip_hostname(authInfo->authreq->ipaddr));
! 		}
! 	}
! 
! 	pb->mode = EVALUATE_ALL;
! 	pbmain(pb);
! 
! 	/* diagnostic */
! 	DEBUG("after safeword {eval}\n");
! 	dbgPblk(pb);
! 
! 	switch( pb->status ) {
! 	case PASS:
! 		DEBUG("safeword_eval: SafeWord PASSED.\n");
! 		return SAFEWORD_PASSED;
! 	default:
! 		DEBUG("safeword_eval: SafeWord FAILED, = %d.\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:\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\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 ) {
! 	debugf("\n");
! 	printf("    SD_CLIENT: appId    = <%d>\n", sd->application_id);
! 	printf("    SD_CLIENT: name     = <%s>\n", sd->username);
  
! #if defined(UINT4_IS_UINT)
! 	printf("    SD_CLIENT: pc_time  = <%d>\n", sd->passcode_time);
! #else
! 	printf("    SD_CLIENT: pc_time  = <%ld>\n", sd->passcode_time);
! #endif
! 	printf("    SD_CLIENT: passcode = <%s>\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:\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!\n");
! 		return ACE_FAILED;
! 	}
! 
! 	DEBUG("ace_eval: ace_username: %s\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\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\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\n", sd->username, acePwd);
! #endif
! 	status = sd_check(acePwd, sd->username, sd);
! 	switch( status ) {
! 	case ACM_OK:
! 		DEBUG("user %s authenticated with pwd %s\n",
! 			sd->username, acePwd);
! 		status = ACE_PASSED;
! 		break;
! 	case ACM_ACCESS_DENIED:
! 		DEBUG("user %s with pwd %s NOT AUTHENTICATED!\n",
! 			sd->username, acePwd);
! 		status = ACE_FAILED;
! 		break;
! 	case ACM_NEXT_CODE_REQUIRED:
! 		DEBUG("user %s with pwd %s requires next code\n",
! 			sd->username, acePwd);
! 		status = ACE_NEXT_PASSCODE;
! 		break;
! 	case ACM_NEW_PIN_REQUIRED:
! 		DEBUG("user %s with pwd %s requires new pin\n",
! 			sd->username, acePwd);
! 		status = ACE_NEW_PIN;
! 		break;
! 	default:
! 		DEBUG("user %s with pwd %s : ERROR %d\n",
! 			sd->username, acePwd, status);
! 		status = ACE_FAILED;
! 		break;
  	}
! 	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: ace_username: %s\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\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\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\n", sd->username, acePwd);
! 	status = sd_next(acePwd, sd);
! 	switch( status ) {
! 	case ACM_OK:
! 		DEBUG("user %s authenticated with pwd %s\n",
! 			sd->username, acePwd);
! 		status = ACE_PASSED;
! 		break;
! 	case ACM_ACCESS_DENIED:
! 		DEBUG("user %s with pwd %s NOT AUTHENTICATED!\n",
! 			sd->username, acePwd);
! 		status = ACE_FAILED;
! 		break;
! 	default:
! 		DEBUG("user %s with pwd %s : ERROR %d\n",
! 			sd->username, acePwd, status);
! 		break;
  	}
  
! 	/* diagnostic */
! 	DEBUG("after ace {nextcode}\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;
  
! 	if( authState != (VALUE_PAIR *)NULL ) {
! 		if( !memcmp(authState->strvalue, ACE_DUMMY_STATE, LEN_ACE_DUMMY_STATE) ) {
! 			DEBUG("ace_pass(eval): state len = %d\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\n",
! 				(u_char)authState->strvalue[0]);
! 			return ace_next(authInfo, sd);
! 		}
! 		else {
! 			DEBUG("ace_pass(chall): state len = %u\n",
! 				(u_char)authState->strvalue[0]);
! 			return ACE_CHALLENGING;
! 		}
! 	}
! 
! 	DEBUG("ace_pass: FAILED: no state attribute\n");
! 	return ACE_FAILED;
! }
! 
! /* Quickly scan recv_buffer far enough to determine whether or not
!    this is an ACE authentication request.  If so, handle it here and
!    return TRUE. If it's not for ACE, return FALSE and our caller will
!    handle it.  The goal is to scan recv_buffer as quickly as possible
!    to avoid burdening the parent process any more than necessary.
!    Heavyweight attribute/value parsing is left for the child process. */
! 
! int
! ace_auth_request(activefd, sin, buffer, length)
! int			activefd;
! struct sockaddr_in	*sin;
! char			*buffer;
! int			length;
  {
! 	AUTH_HDR	*auth;
! 	u_char		*data;
! 	u_char		*end;
! 	int		save_char;
! 	int		auth_chap = 0;
! 	int		auth_pass = 0;
! 	int		immediate = 0;
! 	u_char		*user_name = NULL;
! 	u_char		*state = NULL;
! 	int		state_length = 0;
! 	int		result;
! 	struct SD_CLIENT *dummy_sd = 0;
  
! 	if (!spawn_flag) {
! 		DEBUG("ace_auth_request: not spawning\n");
! 		return FALSE;
  	}
  
! 	auth = (AUTH_HDR *)buffer;
! 	if (auth->code != PW_AUTHENTICATION_REQUEST) {
! 		DEBUG("ace_auth_request: not auth request\n");
! 		return FALSE;
  	}
! 
! 	/* Extract only those few attributes we care about */
! 	data = auth->data;
! 	end = &data[length - AUTH_HDR_LEN];
! 	while (data < end) {
! 		int attr = *data++;
! 		int size = *data++;
! 		if (size < 2)
! 			continue;
! 		size -= 2;
! 		switch (attr) {
! 		case PW_USER_NAME:
! 			user_name = data;
! 			break;
! 		case PW_STATE:
! 			state = data;
! 			state_length = size;
! 			break;
! 		case PW_PASSWORD:
! 			auth_pass++;
! 			break;
! 		case PW_CHAP_PASSWORD:
! 			auth_chap++;
! 			break;
  		}
+ 		data += size;
+ 	}
+ 	if (debug_flag) {
+ 		if (!user_name)
+ 			debugf("ace_auth_request: no user name\n");
+ 		if (!state)
+ 			debugf("ace_auth_request: no state\n");
+ 		if (!(auth_pass || auth_chap))
+ 			debugf("ace_auth_request: not PAP or CHAP\n");
+ 	}
+ 	if (!user_name || !state || !(auth_pass || auth_chap))
+ 		return FALSE;
+ 
+ 	result = user_wants_ace(user_name, &immediate);
+ 	if (!result) {
+ 		DEBUG("ace_auth_request: user doesn't want ACE\n");
+ 		return FALSE;
+ 	}
+ 	if (state_length == 0 && !immediate) {
+ 		DEBUG("ace_auth_request: state-len = 0, not immediate\n");
+ 		return FALSE;
+ 	}
+ 	/* We now know that this request is destined for ACE.
+ 	   Create a new child, or if this is a next-passcode
+ 	   request, find an existing child to handle it.  */
+ 	DEBUG("ace_auth_request: state_length == %d\n", state_length);
+ 	if (state_length == 0)
+ 		return FALSE;
+ 	else if (state_length == LEN_ACE_DUMMY_STATE
+ 		 && memcmp(state, ACE_DUMMY_STATE, LEN_ACE_DUMMY_STATE) == 0)
+ 		return ace_fork(activefd, sin, auth->id, buffer, length);
+ 	else if (state_length == LEN_SD_TOTAL(dummy_sd) + 1) {
+ 		pid_t pid;
+ 		memcpy(&pid, &state[state_length - sizeof(pid_t)], sizeof(pid_t));
+ 		return ace_forward(buffer, length, pid);
+ 	} else
+ 		DEBUG("ace_auth_request: state_length != %d\n",
+ 		      LEN_SD_TOTAL(dummy_sd) + 1);
+ 	return FALSE;
+ }
+ 
+ /* Return an ACE_CHILD descriptor for the given pid.  The important
+    thing is to get access to the write-end of the pipe so we can
+    forward a next-passcode response to the child.  */
+ 
+ ACE_CHILD *
+ ace_find_child(pid)
+ pid_t			pid;
+ {
+ 	ACE_CHILD	*child = ace_children;
+ 	ACE_CHILD	*end = &ace_children[ace_child_count];
+ 
+ 	while (child < end) {
+ 		if (child->pid == pid)
+ 			return child;
+ 	}
+ 	return NULL;
+ }
+ 
+ /* Forward a next-passcode response to a child.  This is necessary
+    because the child's SDI-library data space contains critical
+    state-information for this authentication.  */
+ 
+ int
+ ace_forward(buffer, length, pid)
+ char			*buffer;
+ int			length;
+ pid_t			pid;
+ {
+ 	ACE_CHILD *child = ace_find_child(pid);
+ 
+ 	if (child) {
+ 		DEBUG("ace_forward: send %d bytes to child pid %ld\n", length, pid);
+ 		if (write(child->pipe_fd, buffer, length) < 0) {
+ 			DEBUG("ace_forward: can't write (err=%d)\n", errno);
+ 			return FALSE;
+ 		}
+ 		return TRUE;
+ 	} else {
+ 		DEBUG("ace_forward: pid %ld died!\n", pid);
+ 		return FALSE;
  	}
  }
  
+ /* Setup the ACE_CHILD table.  */
+ 
  void
! ace_init_children()
  {
! 	ace_child_max = ACE_CHILDREN_ALLOC_CHUNK;
! 	ace_child_count = 0;
! 	ace_children = (ACE_CHILD *)malloc(sizeof(ACE_CHILD) * ace_child_max);
! }
! 
! /* Allocate an ACE_CHILD descriptor prior to forking */
! 
! ACE_CHILD *
! ace_alloc_child()
! {
! 	ACE_CHILD	*child = &ace_children[ace_child_count++];
! 
! 	if (ace_child_count == ace_child_max) {
! 		ace_child_max += ACE_CHILDREN_ALLOC_CHUNK;
! 		ace_children = (ACE_CHILD *)realloc(ace_children,
! 						    sizeof(ACE_CHILD) * ace_child_max);
! 		child = &ace_children[ace_child_count - 1];
  	}
+ 	return child;
+ }
  
! #ifndef NFILE
! 
! #include <sys/resource.h>
! 
! int
! get_NFILE()
! {
! 	struct rlimit rl;
! 	if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
! 		return 256;
! 	else
! 		return rl.rlim_max;
  }
  
+ #endif /* ! NFILE */
+ 
+ /* Commit suicide if we've lingered too long. */
+ 
  void
! sig_expire(sig)
  int	sig;
  {
! 	pid_t		pid = getpid();
! 
! 	(void)sig;	/* ignore param */
! 
! 	kill(SIGTERM, pid);
! 	kill(SIGKILL, pid);
! }
! 
! /* Fork a child to handle ACE authentication.  A single child handles
!    an entire authentication transaction, including all next-passcodes.
!    The parent must keep track of its ACE children and forward
!    next-passcode responses to the appropriate child.  */
! 
! int
! ace_fork(activefd, sin, id, buffer, length)
! int			activefd;
! struct sockaddr_in	*sin;
! int			id;
! char			*buffer;
! int			length;
! {
! 	ACE_CHILD	*child = ace_alloc_child();
! 	pid_t		pid;
! 	int		fds[2];
! 	int		fd;
! 	int		nfile;
! 
! 	if (pipe(fds) < 0) {
! 		log_err("Pipe Creation Failed\n");
! 		return FALSE;
! 	}
! 	pid = fork();
! 	if (pid < 0) {
! 		log_err("Fork failed for request from %s - ID: %d\n",
! 			ip_hostname(ntohl(sin->sin_addr.s_addr)), id);
! 		return FALSE;
! 	}
! 	DEBUG("fork in ace_fork\n");
! 	if (pid != 0) {
! 		child->pid = pid;
! 		child->pipe_fd = fds[1];
! 		close(fds[0]);
! 		return TRUE;
! 	}
! 	/* We're running as the child from now on... */
! 	signal(SIGALRM, sig_expire);
! 	alarm(ACE_CHILD_ALARM_PERIOD);
! 
! #ifdef NFILE
! 	nfile = NFILE;
! #else
! 	nfile = get_NFILE();
! #endif
! 	for (fd = 3; fd < nfile; fd++) {
! 		if (fd != fds[0] && fd != activefd)
! 			close(fd);
! 	}
! 
! 	DEBUG("ace_fork: child handling %d bytes from client\n", length);
! 	for (;;) {
! 		int	result;
! 		char	dummy;
! 		AUTH_REQ *authreq = radrecv(ntohl(sin->sin_addr.s_addr),
! 					    ntohs(sin->sin_port),
! 					    (u_char *)buffer, length, &dummy);
! 		if (!authreq)
! 			exit(1);
! 		result = rad_authenticate(authreq, activefd);
! 		if (result != -ACE_NEXT_PASSCODE)
! 			exit(0);
! 		length = read(fds[0], buffer, MAX_RCVBUF_SIZE);
! 		if (length < 0) {
! 			log_err("ACE Child can't read from pipe: err=%d\n", errno);
! 			exit(1);
! 		}
! 		DEBUG("ace_fork: child read %d bytes from pipe\n", length);
! 	}
! }
! 
! #endif	/* ACE */
! 
! /* printf-like interface to the debug trace.  */
! 
! void
! debugf(va_alist)
! va_dcl
! {
! 	va_list ap;
! 	char *fmt;
! 	va_start (ap);
! 	fmt = va_arg(ap, char *);
! 	vdebugf (fmt, ap);
! 	va_end (ap);
! }
! 
! /* vprintf-like interface to the debug trace.
!    Prepends timestamp and pid to each message.  */
! 
! void
! vdebugf(fmt, ap)
! char *fmt;
! va_list ap;
! {
! 	struct timeval tv;
! 	struct tm *tm;
! 
! 	gettimeofday (&tv, 0);
! 	tm = localtime (&tv.tv_sec);
! 	printf ("%02d:%02d:%02d.%03d [%d] ",
! 		tm->tm_hour, tm->tm_min, tm->tm_sec,
! 		tv.tv_usec / 1000, getpid());
! 	vprintf (fmt, ap);
! 	fflush (stdout);
  }
diff -c -N radiusd/radpass.c ascendd/radpass.c
*** radiusd/radpass.c	Thu Jan 11 17:26:17 1996
--- ascendd/radpass.c	Thu Jan 11 17:26:12 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)radpass.c	1.2 (95/07/25 00:55:38)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 36,43 ****
--- 38,48 ----
  #include	<unistd.h>
  #include	<netdb.h>
  #include	<pwd.h>
+ #include	<stdlib.h>
+ #include	<sys/time.h>	/* gettimeofday() */
  
  #include	"radius.h"
+ #include	"protos.h"
  
  #define MAXPWNAM	8
  #define MAXPASS		16
***************
*** 49,57 ****
  u_char		vector[AUTH_VECTOR_LEN];
  u_char		oldpass[AUTH_PASS_LEN];
  
  main(argc, argv)
  int	argc;
! u_char	*argv[];
  {
  	int			salen;
  	int			result;
--- 54,70 ----
  u_char		vector[AUTH_VECTOR_LEN];
  u_char		oldpass[AUTH_PASS_LEN];
  
+ static void random_vector P__((u_char *vector));
+ static void usage P__((void));
+         /* udp_port promoted due to old-style function definition */
+ static void result_recv P__((UINT4 host, int udp_port, u_char *buffer, int length));
+ static void usage P__((void));
+ int main P__((int argc, u_char **argv));
+ 
+ int
  main(argc, argv)
  int	argc;
! u_char	**argv;
  {
  	int			salen;
  	int			result;
***************
*** 67,73 ****
  	u_char			passbuf[AUTH_PASS_LEN];
  	u_char			md5buf[256];
  	u_char			*oldvector;
- 	UINT4			get_ipaddr();
  	UINT4			auth_ipaddr;
  	u_short			local_port;
  	int			total_length;
--- 80,85 ----
***************
*** 75,81 ****
  	int			length;
  	int			secretlen;
  	int			i;
! 	char			*getpass();
  
  	progname = argv[0];
  
--- 87,93 ----
  	int			length;
  	int			secretlen;
  	int			i;
! 	char			*getpass(CONST char *);
  
  	progname = argv[0];
  
***************
*** 137,143 ****
  
  	/* Get their new password again */
  	strcpy((char *)newpass2, getpass("Re-type New Password:"));
! 	if(strcmp(newpass1, newpass2) != 0) {
  		printf("New Passwords didn't match\n");
  		exit(-1);
  	}
--- 149,155 ----
  
  	/* Get their new password again */
  	strcpy((char *)newpass2, getpass("Re-type New Password:"));
! 	if(strcmp((char *)newpass1, (char *)newpass2) != 0) {
  		printf("New Passwords didn't match\n");
  		exit(-1);
  	}
***************
*** 153,159 ****
  
  	/* User Name */
  	*ptr++ = PW_USER_NAME;
! 	length = strlen(username);
  	if(length > MAXPWNAM) {
  		length = MAXPWNAM;
  	}
--- 165,171 ----
  
  	/* User Name */
  	*ptr++ = PW_USER_NAME;
! 	length = strlen((char *)username);
  	if(length > MAXPWNAM) {
  		length = MAXPWNAM;
  	}
***************
*** 167,181 ****
  	*ptr++ = AUTH_PASS_LEN + 2;
  
  	/* Encrypt the Password */
! 	length = strlen(newpass1);
  	if(length > MAXPASS) {
  		length = MAXPASS;
  	}
  	memset(passbuf, 0, AUTH_PASS_LEN);
  	memcpy(passbuf, newpass1, length);
  	/* Calculate the MD5 Digest */
! 	secretlen = strlen(oldpass);
! 	strcpy(md5buf, oldpass);
  	memcpy(md5buf + secretlen, auth->vector, AUTH_VECTOR_LEN);
  	md5_calc(ptr, md5buf, secretlen + AUTH_VECTOR_LEN);
  	oldvector = ptr;
--- 179,193 ----
  	*ptr++ = AUTH_PASS_LEN + 2;
  
  	/* Encrypt the Password */
! 	length = strlen((char *)newpass1);
  	if(length > MAXPASS) {
  		length = MAXPASS;
  	}
  	memset(passbuf, 0, AUTH_PASS_LEN);
  	memcpy(passbuf, newpass1, length);
  	/* Calculate the MD5 Digest */
! 	secretlen = strlen((char *)oldpass);
! 	strcpy((char *)md5buf, (char *)oldpass);
  	memcpy(md5buf + secretlen, auth->vector, AUTH_VECTOR_LEN);
  	md5_calc(ptr, md5buf, secretlen + AUTH_VECTOR_LEN);
  	oldvector = ptr;
***************
*** 190,204 ****
  	*ptr++ = AUTH_PASS_LEN + 2;
  
  	/* Encrypt the Password */
! 	length = strlen(oldpass);
  	if(length > MAXPASS) {
  		length = MAXPASS;
  	}
  	memset(passbuf, 0, AUTH_PASS_LEN);
  	memcpy(passbuf, oldpass, length);
  	/* Calculate the MD5 Digest */
! 	secretlen = strlen(oldpass);
! 	strcpy(md5buf, oldpass);
  	memcpy(md5buf + secretlen, oldvector, AUTH_VECTOR_LEN);
  	md5_calc(ptr, md5buf, secretlen + AUTH_VECTOR_LEN);
  
--- 202,216 ----
  	*ptr++ = AUTH_PASS_LEN + 2;
  
  	/* Encrypt the Password */
! 	length = strlen((char *)oldpass);
  	if(length > MAXPASS) {
  		length = MAXPASS;
  	}
  	memset(passbuf, 0, AUTH_PASS_LEN);
  	memcpy(passbuf, oldpass, length);
  	/* Calculate the MD5 Digest */
! 	secretlen = strlen((char *)oldpass);
! 	strcpy((char *)md5buf, (char *)oldpass);
  	memcpy(md5buf + secretlen, oldvector, AUTH_VECTOR_LEN);
  	md5_calc(ptr, md5buf, secretlen + AUTH_VECTOR_LEN);
  
***************
*** 231,239 ****
  	}
  	(void) perror ("recv");
  	close(sockfd);
! 	exit(0);
  }
  
  result_recv(host, udp_port, buffer, length)
  UINT4	host;
  u_short	udp_port;
--- 243,252 ----
  	}
  	(void) perror ("recv");
  	close(sockfd);
! 	return 0;
  }
  
+ void
  result_recv(host, udp_port, buffer, length)
  UINT4	host;
  u_short	udp_port;
***************
*** 242,248 ****
  {
  	AUTH_HDR	*auth;
  	int		totallen;
- 	char		*ip_hostname();
  	u_char		reply_digest[AUTH_VECTOR_LEN];
  	u_char		calc_digest[AUTH_VECTOR_LEN];
  	int		secretlen;
--- 255,260 ----
***************
*** 258,266 ****
  	/* Verify the reply digest */
  	memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN);
  	memcpy(auth->vector, vector, AUTH_VECTOR_LEN);
! 	secretlen = strlen(oldpass);
  	memcpy(buffer + AUTH_HDR_LEN, oldpass, secretlen);
! 	md5_calc(calc_digest, (char *)auth, AUTH_HDR_LEN);
  
  	if(memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) {
  		printf("Warning: Received invalid reply digest from server\n");
--- 270,278 ----
  	/* Verify the reply digest */
  	memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN);
  	memcpy(auth->vector, vector, AUTH_VECTOR_LEN);
! 	secretlen = strlen((char *)oldpass);
  	memcpy(buffer + AUTH_HDR_LEN, oldpass, secretlen);
! 	md5_calc(calc_digest, (u_char *)auth, AUTH_HDR_LEN);
  
  	if(memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) {
  		printf("Warning: Received invalid reply digest from server\n");
***************
*** 274,285 ****
--- 286,299 ----
  	}
  }
  
+ void
  usage()
  {
  	printf("Usage: %s username\n", progname);
  	exit(-1);
  }
  
+ void
  random_vector(vector)
  u_char	*vector;
  {
diff -c -N radiusd/users.c ascendd/users.c
*** radiusd/users.c	Thu Jan 11 17:26:17 1996
--- ascendd/users.c	Fri May 24 13:21:09 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)users.c	1.2 (95/07/25 00:55:40)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 38,58 ****
  #include	<pwd.h>
  #include	<time.h>
  #include	<ctype.h>
! 
! #ifdef DBM
! 
! #include	<dbm.h>
! 
! #endif /* DBM */
  
  #include	"radius.h"
  
  extern char		*progname;
  extern int		debug_flag;
  extern char		*radius_dir;
  
- static	void fieldcpy();
- static	int  userparse();
  
  #define FIND_MODE_NAME	0
  #define FIND_MODE_REPLY	1
--- 40,77 ----
  #include	<pwd.h>
  #include	<time.h>
  #include	<ctype.h>
! #include	<string.h>
! #include	<unistd.h>
! #include	<stdlib.h>
! 
! #if defined(DBM_MODE)
! #	if defined(SOLARIS)
! #		include	</usr/ucbinclude/dbm.h>
! #	else
! #		include	<dbm.h>
! #	endif	/* SOLARIS */
! #endif /* DBM_MODE */
  
  #include	"radius.h"
+ #include	"protos.h"
  
  extern char		*progname;
  extern int		debug_flag;
  extern char		*radius_dir;
+ extern char		*radius_users;
+ extern FILE		*errf;
+ 
+ static int parse_record P__((char *name, char *linep,
+ 	VALUE_PAIR **check_first, VALUE_PAIR **reply_first));
+ extern int user_read P__((FILE **userfd, char *name, char *content));
+ static int userparse P__((char **buf, VALUE_PAIR **first_pair));
+ static void fieldcpy P__((char *string, char **uptr));
+ static int fieldequ P__((char *string, char *ptr));
+ static void fieldskip P__((char **uptr));
+ static void user_gettime P__((char *valstr, struct tm *tm));
+ static int fetch_user_data P__((char *user_name, char **user_data));
+ extern int curParseLine;	/* -- current line in file being parsed */
  
  
  #define FIND_MODE_NAME	0
  #define FIND_MODE_REPLY	1
***************
*** 61,66 ****
--- 80,253 ----
  
  /*************************************************************************
   *
+  *	Function: userinfo_open
+  *
+  *	Purpose: Opens the users information file. Do different things
+  *		 different ways depending on whether we are using
+  *		 a flat file or a dbm database.
+  *
+  *	Returns: zero upon success, non-zero upon failure
+  *
+  *************************************************************************/
+ 
+ static int
+ userinfo_open(userfd, infoname)
+ FILE	**userfd;
+ char	*infoname;
+ {
+ #if defined(DBM_MODE)
+ 	(void)userfd;
+ 	return dbminit(infoname);
+ #else
+ 	*userfd = fopen(infoname, "r");
+ 	return *userfd == (FILE *)NULL;
+ #endif
+ }
+ 
+ /*************************************************************************
+  *
+  *	Function: userinfo_close
+  *
+  *	Purpose: Close the users information file. Do different things
+  *		 different ways depending on whether we are using
+  *		 a flat file or a dbm database.
+  *
+  *************************************************************************/
+ 
+ static void
+ userinfo_close(userfd)
+ FILE	*userfd;
+ {
+ #if defined(DBM_MODE)
+ 	(void)userfd;
+ 	dbmclose();
+ #else
+ 	fclose(userfd);
+ #endif
+ }
+ 
+ /*************************************************************************
+  *
+  *	Function: parse_record
+  *
+  *	Purpose: Given a record for the requested user, parse the record
+  *		 into the required attributes (check_first) and the
+  *		 attributes to return (reply_first).
+  *		 Ensure that all is fine with the record.
+  *
+  *		 Perform parsing identically for flat-file and for
+  *		 dbm databases.
+  *
+  *************************************************************************/
+ 
+ static int
+ parse_record(name, linep, check_first, reply_first)
+ char		*name;
+ char		*linep;
+ VALUE_PAIR	**check_first;
+ VALUE_PAIR	**reply_first;
+ {
+ 	int status;
+ 	char		msg[128];
+ 
+ 	/*
+ 	 * Parse the check values
+ 	 */
+ 	if( (status = userparse(&linep, check_first)) != 0) {
+ 		sprintf(msg, "%s: Parse.check error %d for user %s\n",
+ 				progname, status, name);
+ 		fprintf(errf, msg);
+ 		log_err(msg);
+ 		pairfree(*check_first);
+ 		return(status);
+ 	}
+ 	while(*linep != '\n' && *linep != '\0') {
+ 		linep++;
+ 	}
+ 	if(*linep != '\n') {
+ 		pairfree(*check_first);
+ 		fprintf(errf, "%s: Missing NL for user %s\n",
+ 				progname, name);
+ 		return(MISSING_NEWLINE);
+ 	}
+ 	linep++;
+ 	/*
+ 	 * Parse the reply values
+ 	 */
+ 	if( (status = userparse(&linep, reply_first)) != 0) {
+ 		fprintf(errf, "%s: Parse.reply error %d for user %s\n",
+ 			progname, status, name);
+ 		pairfree(*check_first);
+ 		pairfree(*reply_first);
+ 		return(status);
+ 	}
+ 	return 0;
+ }
+ 
+ static int
+ fetch_user_data(name, user_data)
+ char		*name;
+ char		**user_data;
+ {
+ 	FILE		*userfd;
+ 	char		buf[256];
+ 	int		status;
+ #ifdef DBM_MODE
+ 	datum		named;
+ 	datum		contentd;
+ #else
+ 	char		curname[512];
+ 	static char	line[4 * 1024];
+ #endif /* DBM_MODE */
+ 
+ 	*user_data = NULL;
+ 	/*
+ 	 * Open the user table
+ 	 */
+ 	sprintf(buf, "%s/%s", radius_dir, radius_users);
+ 	if( userinfo_open(&userfd, buf) != 0 ) {
+ 		fprintf(errf, "%s:Couldn't open %s for reading\n",
+ 				progname, buf);
+ 		return NO_USER_FILE;
+ 	}
+ 
+ #ifdef DBM_MODE
+ 	named.dptr = name;
+ 	named.dsize = strlen(name)+1;
+ 	contentd = fetch(named);
+ 	if(contentd.dsize != 0) {	/* name found */
+ 		*user_data = contentd.dptr;
+ 		userinfo_close(userfd);
+ 		return 0;
+ 	}
+ 
+ 	named.dptr = "DEFAULT";
+ 	named.dsize = sizeof("DEFAULT");
+ 	contentd = fetch(named);
+ 	if(contentd.dsize != 0) {	/* DEFAULT found */
+ 		*user_data = contentd.dptr;
+ 		userinfo_close(userfd);
+ 		return 0;
+ 	}
+ #else /* DBM_MODE */
+ 	curParseLine = 0;
+ 	while( (status = user_read(&userfd, curname, line)) == 0 ) {
+ 		if( (strcmp(name, curname) == 0)
+ 		    || (strcmp("DEFAULT", curname) == 0) ) {
+ 			*user_data = line;
+ 			userinfo_close(userfd);
+ 			return 0;
+ 		}
+ 	}
+ #endif /* DBM_MODE */
+ 	fprintf(errf, "%s:users %s and %s not found\n",
+ 		progname, name, "DEFAULT");
+ 	userinfo_close(userfd);
+ 	return NO_USER_OR_DEFAULT_NAME;
+ }
+ 
+ /*************************************************************************
+  *
   *	Function: user_find
   *
   *	Purpose: Find the named user in the database.  Create the
***************
*** 69,91 ****
   *
   *************************************************************************/
  
  user_find(name, check_pairs, reply_pairs)
! char	*name;
  VALUE_PAIR	**check_pairs;
  VALUE_PAIR	**reply_pairs;
  {
- 	FILE		*userfd;
- 	char		buffer[256];
- 	char		msg[128];
  	char		*ptr;
  	int		namelen;
  	int		mode;
  	VALUE_PAIR	*check_first;
  	VALUE_PAIR	*reply_first;
! #ifdef DBM
! 	datum		named;
! 	datum		contentd;
! #endif /* DBM */
  
  	/* 
  	 * Check for valid input, zero length names not permitted 
--- 256,274 ----
   *
   *************************************************************************/
  
+ int
  user_find(name, check_pairs, reply_pairs)
! char		*name;
  VALUE_PAIR	**check_pairs;
  VALUE_PAIR	**reply_pairs;
  {
  	char		*ptr;
  	int		namelen;
  	int		mode;
+ 	char		*user_data;
  	VALUE_PAIR	*check_first;
  	VALUE_PAIR	*reply_first;
! 	int		status;
  
  	/* 
  	 * Check for valid input, zero length names not permitted 
***************
*** 105,242 ****
  	namelen=strlen(name);
  
  	if (namelen < 1) {
! 		fprintf(stderr, "%s: zero length username not permitted\n",progname);
! 		return(-1);
  	}
  
! 
! 	/*
! 	 * Open the user table
! 	 */
! 	sprintf(buffer, "%s/%s", radius_dir, RADIUS_USERS);
! #ifdef DBM
! 	if(dbminit(buffer) != 0) {
! #else /* DBM */
! 	if((userfd = fopen(buffer, "r")) == (FILE *)NULL) {
! #endif /* DBM */
! 		fprintf(stderr, "%s:Couldn't open %s for reading\n",
! 				progname, buffer);
! 		return(-1);
! 	}
  
  	check_first = (VALUE_PAIR *)NULL;
  	reply_first = (VALUE_PAIR *)NULL;
! 
! 
! #ifdef DBM
! 	named.dptr = name;
! 	named.dsize = strlen(name);
! 	contentd = fetch(named);
! 
! 	if(contentd.dsize == 0) {
! 		named.dptr = "DEFAULT";
! 		named.dsize = strlen("DEFAULT");
! 		contentd = fetch(named);
! 		if(contentd.dsize == 0) {
! 			dbmclose();
! 			return(-1);
! 		}
! 	}
! 
! 	/*
! 	 * Parse the check values
! 	 */
! 	ptr = contentd.dptr;
! 	contentd.dptr[contentd.dsize] = '\0';
! 
! 	if(userparse(ptr, &check_first) != 0) {
! 		sprintf(msg, "%s: Parse error for user %s\n",
! 				progname, name);
! 		fprintf(stderr, msg);
! 		log_err(msg);
! 		pairfree(check_first);
! 		dbmclose();
! 		return(-1);
! 	}
! 	while(*ptr != '\n' && *ptr != '\0') {
! 		ptr++;
! 	}
! 	if(*ptr != '\n') {
! 		pairfree(check_first);
! 		dbmclose();
! 		return(-1);
! 	}
! 	ptr++;
! 	/*
! 	 * Parse the reply values
! 	 */
! 	if(userparse(ptr, &reply_first) != 0) {
! 		fprintf(stderr, "%s: Parse error for user %s\n",
! 			progname, name);
! 		pairfree(check_first);
! 		pairfree(reply_first);
! 		dbmclose();
! 		return(-1);
! 	}
! 	dbmclose();
! 
! #else /* DBM */
! 
! 	while(fgets(buffer, sizeof(buffer), userfd) != (char *)NULL) {
! 		if(mode == FIND_MODE_NAME) {
! 			/*
! 			 * Find the entry starting with the users name
! 			 */
! 			if((strncmp(buffer, name, namelen) == 0 &&
! 		 	 (buffer[namelen] == ' ' || buffer[namelen] == '\t')) ||
! 					 strncmp(buffer, "DEFAULT", 7) == 0) {
! 				if(strncmp(buffer, "DEFAULT", 7) == 0) {
! 					ptr = &buffer[7];
! 				}
! 				else {
! 					ptr = &buffer[namelen];
! 				}
! 				/*
! 				 * Parse the check values
! 				 */
! 				if(userparse(ptr, &check_first) != 0) {
! 					sprintf(msg,"%s: Parse error for user %s\n",
! 						progname, name);
! 					fprintf(stderr,msg);
! 					log_err(msg);
! 					pairfree(check_first);
! 					fclose(userfd);
! 					return(-1);
! 				}
! 				mode = FIND_MODE_REPLY;
! 			}
! 		}
! 		else {
! 			if(*buffer == ' ' || *buffer == '\t') {
! 				/*
! 				 * Parse the reply values
! 				 */
! 				if(userparse(buffer, &reply_first) != 0) {
! 					fprintf(stderr,
! 						"%s: Parse error for user %s\n",
! 						progname, name);
! 					pairfree(check_first);
! 					pairfree(reply_first);
! 					fclose(userfd);
! 					return(-1);
! 				}
! 			}
! 			else {
! 				/* We are done */
! 				fclose(userfd);
! 				*check_pairs = check_first;
! 				*reply_pairs = reply_first;
! 				return(0);
! 			}
! 		}
  	}
- 	fclose(userfd);
- #endif /* DBM */
  
  	/* Update the callers pointers */
  	if(reply_first != (VALUE_PAIR *)NULL) {
--- 288,308 ----
  	namelen=strlen(name);
  
  	if (namelen < 1) {
! 		fprintf(errf, "%s: zero length username not permitted\n",
! 			progname);
! 		return(ZERO_LENGTH_NAME);
  	}
  
! 	status = fetch_user_data(name, &user_data);
! 	if( status )
! 		return status;
  
  	check_first = (VALUE_PAIR *)NULL;
  	reply_first = (VALUE_PAIR *)NULL;
! 	status = parse_record(name, user_data, &check_first, &reply_first);
! 	if( status ) {
! 		return status;
  	}
  
  	/* Update the callers pointers */
  	if(reply_first != (VALUE_PAIR *)NULL) {
***************
*** 244,250 ****
  		*reply_pairs = reply_first;
  		return(0);
  	}
! 	return(-1);
  }
  
  #define PARSE_MODE_NAME		0
--- 310,316 ----
  		*reply_pairs = reply_first;
  		return(0);
  	}
! 	return(REPLY_FIRST_IS_NULL);
  }
  
  #define PARSE_MODE_NAME		0
***************
*** 261,284 ****
   *************************************************************************/
  
  static int
! userparse(buffer, first_pair)
! char		*buffer;
  VALUE_PAIR	**first_pair;
  {
  	int		mode;
  	char		attrstr[64];
  	char		valstr[64];
  	DICT_ATTR	*attr;
- 	DICT_ATTR	*dict_attrfind();
  	DICT_VALUE	*dval;
- 	DICT_VALUE	*dict_valfind();
  	VALUE_PAIR	*pair;
  	VALUE_PAIR	*link;
- 	UINT4		ipstr2long();
- 	UINT4		get_ipaddr();
  	struct tm	*tm;
  	time_t		timeval;
  
  	mode = PARSE_MODE_NAME;
  	while(*buffer != '\n' && *buffer != '\0') {
  
--- 327,352 ----
   *************************************************************************/
  
  static int
! userparse(buf, first_pair)
! char		**buf;
  VALUE_PAIR	**first_pair;
  {
  	int		mode;
  	char		attrstr[64];
+ #ifdef ORIG
  	char		valstr[64];
+ #else
+ 	char		valstr[256];
+ #endif
  	DICT_ATTR	*attr;
  	DICT_VALUE	*dval;
  	VALUE_PAIR	*pair;
  	VALUE_PAIR	*link;
  	struct tm	*tm;
  	time_t		timeval;
+ 	char		*buffer;
  
+ 	buffer = *buf;
  	mode = PARSE_MODE_NAME;
  	while(*buffer != '\n' && *buffer != '\0') {
  
***************
*** 294,300 ****
  			fieldcpy(attrstr, &buffer);
  			if((attr = dict_attrfind(attrstr)) ==
  						(DICT_ATTR *)NULL) {
! 				return(-1);
  			}
  			mode = PARSE_MODE_EQUAL;
  			break;
--- 362,371 ----
  			fieldcpy(attrstr, &buffer);
  			if((attr = dict_attrfind(attrstr)) ==
  						(DICT_ATTR *)NULL) {
! 				*buf = buffer;
! 			fprintf(errf, "%s: attribute name %s not found\n",
! 					progname, attrstr);
! 				return(DICT_ATTRFIND_ERR);
  			}
  			mode = PARSE_MODE_EQUAL;
  			break;
***************
*** 306,312 ****
  				buffer++;
  			}
  			else {
! 				return(-1);
  			}
  			break;
  
--- 377,384 ----
  				buffer++;
  			}
  			else {
! 				*buf = buffer;
! 				return(MISSING_EQUALS);
  			}
  			break;
  
***************
*** 316,333 ****
  
  			if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
  						(VALUE_PAIR *)NULL) {
! 				fprintf(stderr, "%s: no memory\n",
  						progname);
! 				exit(-1);
  			}
  			strcpy(pair->name, attr->name);
  			pair->attribute = attr->value;
  			pair->type = attr->type;
  
  			switch(pair->type) {
  
  			case PW_TYPE_STRING:
  				strcpy(pair->strvalue, valstr);
  				break;
  
  			case PW_TYPE_INTEGER:
--- 388,419 ----
  
  			if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) ==
  						(VALUE_PAIR *)NULL) {
! 				fprintf(errf, "%s: no memory\n",
  						progname);
! 				exit(MEMORY_ERR);
  			}
  			strcpy(pair->name, attr->name);
  			pair->attribute = attr->value;
  			pair->type = attr->type;
  
  			switch(pair->type) {
+ #if defined( BINARY_FILTERS )
+                         case PW_TYPE_FILTER_BINARY:
+                                 /*
+                                  * special case to convert filter to binary
+                                  */
+                                 if( filterBinary( pair, valstr ) == -1 ) {
+                                     free(pair);
+ 				    *buf = buffer;
+                                     return(BINARY_FILTER_ERR);
+                                 }
+ 				pair->size = pair->lvalue;
+                                 break;
+ #endif /* BINARY_FILTERS */
  
  			case PW_TYPE_STRING:
  				strcpy(pair->strvalue, valstr);
+ 				pair->size = strlen(valstr) + 1;
  				break;
  
  			case PW_TYPE_INTEGER:
***************
*** 337,367 ****
  				else if((dval = dict_valfind(valstr)) ==
  							(DICT_VALUE *)NULL) {
  					free(pair);
! 					return(-1);
  				}
  				else {
  					pair->lvalue = dval->value;
  				}
  				break;
  
  			case PW_TYPE_IPADDR:
  				pair->lvalue = get_ipaddr(valstr);
  				break;
  
  			case PW_TYPE_DATE:
! 				timeval = time(0);
  				tm = localtime(&timeval);
  				user_gettime(valstr, tm);
  #ifdef TIMELOCAL
  				pair->lvalue = (UINT4)timelocal(tm);
  #else /* TIMELOCAL */
  				pair->lvalue = (UINT4)mktime(tm);
  #endif /* TIMELOCAL */
  				break;
  
  			default:
  				free(pair);
! 				return(-1);
  			}
  			pair->next = (VALUE_PAIR *)NULL;
  			if(*first_pair == (VALUE_PAIR *)NULL) {
--- 423,464 ----
  				else if((dval = dict_valfind(valstr)) ==
  							(DICT_VALUE *)NULL) {
  					free(pair);
! 					*buf = buffer;
! 					return(DICT_VALFIND_ERR);
  				}
  				else {
  					pair->lvalue = dval->value;
  				}
+ 				pair->size = sizeof(UINT4);
  				break;
  
  			case PW_TYPE_IPADDR:
  				pair->lvalue = get_ipaddr(valstr);
+ 				pair->size = sizeof(UINT4);
  				break;
  
  			case PW_TYPE_DATE:
! 				timeval = time((time_t *)0);
! 				if( timeval == -1 ) {
! 					log_err("userparse(): time() err\n");
! 					pair->lvalue = 0;
! 					break;
! 				}
  				tm = localtime(&timeval);
+ 
  				user_gettime(valstr, tm);
  #ifdef TIMELOCAL
  				pair->lvalue = (UINT4)timelocal(tm);
  #else /* TIMELOCAL */
  				pair->lvalue = (UINT4)mktime(tm);
  #endif /* TIMELOCAL */
+ 				pair->size = sizeof(UINT4);
  				break;
  
  			default:
  				free(pair);
! 				*buf = buffer;
! 				return(DEFAULT_PARSE_ERR);
  			}
  			pair->next = (VALUE_PAIR *)NULL;
  			if(*first_pair == (VALUE_PAIR *)NULL) {
***************
*** 382,387 ****
--- 479,485 ----
  			break;
  		}
  	}
+ 	*buf = buffer;
  	return(0);
  }
  
***************
*** 394,400 ****
   *
   *************************************************************************/
  
! static	void
  fieldcpy(string, uptr)
  char	*string;
  char	**uptr;
--- 492,498 ----
   *
   *************************************************************************/
  
! static void
  fieldcpy(string, uptr)
  char	*string;
  char	**uptr;
***************
*** 433,439 ****
   *		 list of reply items which are supplied.
   *
   *************************************************************************/
! 
  user_update(name, user_check, user_reply)
  char		*name;
  VALUE_PAIR	*user_check;
--- 531,537 ----
   *		 list of reply items which are supplied.
   *
   *************************************************************************/
! int
  user_update(name, user_check, user_reply)
  char		*name;
  VALUE_PAIR	*user_check;
***************
*** 441,473 ****
  {
  	FILE		*oldfd;
  	FILE		*userfd;
! 	char		buffer[256];
  	char		buffer1[256];
  	int		namelen;
  	int		mode;
  
! 	sprintf(buffer, "%s/%s", radius_dir, RADIUS_USERS);
  	sprintf(buffer1, "%s/%s", radius_dir, RADIUS_HOLD);
  
  	/* Move the user table to a temporary location */
  	if(rename(buffer, buffer1) != 0) {
! 		fprintf(stderr, "%s: Couldn't rename %s\n",
  				progname, buffer);
! 		return(-1);
  	}
  
! 	/* Open the old user file (using the temporary name */
  	if((oldfd = fopen(buffer1, "r")) == (FILE *)NULL) {
! 		fprintf(stderr, "%s: Couldn't open %s for reading\n",
  				progname, buffer1);
! 		exit(-1);
  	}
  
  	/* Open the new user file */
  	if((userfd = fopen(buffer, "w")) == (FILE *)NULL) {
! 		fprintf(stderr, "%s: Couldn't open %s for writing\n",
  				progname, buffer);
! 		exit(-1);
  	}
  
  	mode = FIND_MODE_NAME;
--- 539,571 ----
  {
  	FILE		*oldfd;
  	FILE		*userfd;
! 	char		buffer[1024];
  	char		buffer1[256];
  	int		namelen;
  	int		mode;
  
! 	sprintf(buffer, "%s/%s", radius_dir, radius_users);
  	sprintf(buffer1, "%s/%s", radius_dir, RADIUS_HOLD);
  
  	/* Move the user table to a temporary location */
  	if(rename(buffer, buffer1) != 0) {
! 		fprintf(errf, "%s: Couldn't rename %s\n",
  				progname, buffer);
! 		return(USERFILE_RENAME_FAILED);
  	}
  
! 	/* Open the old user file (using the temporary name) */
  	if((oldfd = fopen(buffer1, "r")) == (FILE *)NULL) {
! 		fprintf(errf, "%s: Couldn't open %s for reading\n",
  				progname, buffer1);
! 		exit(USERFILE_READ_ERR);
  	}
  
  	/* Open the new user file */
  	if((userfd = fopen(buffer, "w")) == (FILE *)NULL) {
! 		fprintf(errf, "%s: Couldn't open %s for writing\n",
  				progname, buffer);
! 		exit(USERFILE_WRITE_ERR);
  	}
  
  	mode = FIND_MODE_NAME;
***************
*** 532,542 ****
  	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  
  user_gettime(valstr, tm)
  char		*valstr;
  struct tm	*tm;
  {
! 	char	buffer[48];
  	int	i;
  
  	/* Get the month */
--- 630,646 ----
  	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  
+ static void
  user_gettime(valstr, tm)
  char		*valstr;
  struct tm	*tm;
  {
! #if(0)
! 	/* Use this code only if strtok() is not available */
! 
! 	/*
! 	 * Date format: "Mmm.dd.yyyy"
! 	 */
  	int	i;
  
  	/* Get the month */
***************
*** 552,555 ****
--- 656,832 ----
  
  	/* Now the year */
  	tm->tm_year = atoi(&valstr[7]) - 1900;
+ #else
+ 	/*
+ 	 * Date format: "Mmm+[ \t,]+d+[ \t,]+yyyy"
+ 	 */
+ 	int	i;
+ 	char	datestr[80];
+ 	char	*str;
+ 
+ 	strncpy(datestr, valstr, 79);
+ 	datestr[79] = '\0';
+ 
+ 	/* Get the month */
+ 	str = strtok(datestr, " \t,");
+ 	if( !str ) {
+ 		return;
+ 	}
+ 	for(i = 0;i < 12;i++) {
+ 		if(strncmp(months[i], str, 3) == 0) {
+ 			tm->tm_mon = i;
+ 			break;
+ 		}
+ 	}
+ 
+ 	/* Get the Day */
+ 	str = strtok((char *)0, " \t,");
+ 	if( !str ) {
+ 		return;
+ 	}
+ 
+ 	tm->tm_mday = atoi(str);
+ 
+ 	/* Now the year */
+ 	str = strtok((char *)0, " \t,");
+ 	if( !str ) {
+ 		return;
+ 	}
+ 	tm->tm_year = atoi(str) - 1900;
+ #endif
+ }
+ 
+ 
+ /*************************************************************************
+  *
+  *	Function: fieldequ
+  *
+  *	Purpose: Compare a string with a field from the buffer.
+  *		 Advance the buffer past the data field.
+  *
+  *************************************************************************/
+ 
+ static int
+ fieldequ(string, ptr)
+ char	*string;
+ char	*ptr;
+ {
+ 	if(*ptr == '"') {
+ 		ptr++;
+ 		while(*ptr != '"' && *ptr != '\0' && *ptr != '\n') {
+ 			if (*string++ != *ptr++)
+ 				return FALSE;
+ 		}
+ 		return TRUE;
+ 	}
+ 
+ 	while(*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
+ 						*ptr != '=' && *ptr != ',') {
+ 		if (*string++ != *ptr++)
+ 			return FALSE;
+ 	}
+ 	return TRUE;
+ }
+ 
+ /*************************************************************************
+  *
+  *	Function: fieldskip
+  *
+  *	Purpose: Advance the buffer past a data field.
+  *
+  *************************************************************************/
+ 
+ static void
+ fieldskip(uptr)
+ char	**uptr;
+ {
+ 	char	*ptr;
+ 
+ 	ptr = *uptr;
+ 	if(*ptr == '"') {
+ 		ptr++;
+ 		while(*ptr != '"' && *ptr != '\0' && *ptr != '\n') {
+ 			ptr++;
+ 		}
+ 		if(*ptr == '"') {
+ 			ptr++;
+ 		}
+ 		*uptr = ptr;
+ 		return;
+ 	}
+ 
+ 	while(*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
+ 						*ptr != '=' && *ptr != ',') {
+ 		ptr++;
+ 	}
+ 	*uptr = ptr;
+ 	return;
+ }
+ 
+ int
+ user_wants_ace(user_name, immediate)
+ char		*user_name;
+ int		*immediate;
+ {
+ 	char	*immed_name = "Ascend-Token-Immediate";
+ 	char	*pw_name = "Password";
+ 	int	parse_pw = FALSE;
+ 	int	parse_immed = FALSE;
+ 	int	have_pw = FALSE;
+ 	int	have_immed = FALSE;
+ 	int	want_ace = FALSE;
+ 	char	*user_data;
+ 	int	status;
+ 	int	mode;
+ 
+ 	status = fetch_user_data(user_name, &user_data);
+ 	if( status )
+ 		return FALSE;
+ 
+ 	mode = PARSE_MODE_NAME;
+ 	while (*user_data && *user_data != '\n') {
+ 		if(*user_data == ' ' || *user_data == '\t' || *user_data == ',') {
+ 			user_data++;
+ 			continue;
+ 		}
+ 		switch(mode) {
+ 		case PARSE_MODE_NAME:
+ 			if (!have_pw && fieldequ(pw_name, user_data))
+ 				parse_pw = TRUE;
+ 			else if (!have_immed && fieldequ(immed_name, user_data))
+ 				parse_immed = TRUE;
+ 			fieldskip(&user_data);
+ 			mode = PARSE_MODE_EQUAL;
+ 			break;
+ 
+ 		case PARSE_MODE_EQUAL:
+ 			if(*user_data == '=') {
+ 				mode = PARSE_MODE_VALUE;
+ 				user_data++;
+ 			} else {
+ 				return MISSING_EQUALS;
+ 			}
+ 			break;
+ 
+ 		case PARSE_MODE_VALUE:
+ 			if (parse_pw) {
+ 				want_ace = fieldequ("ACE", user_data);
+ 				have_pw = TRUE;
+ 				parse_pw = FALSE;
+ 			} else if (parse_immed) {
+ 				*immediate = (fieldequ("1", user_data)
+ 					      || fieldequ("Tok-Imm-Yes", user_data));
+ 				have_immed = TRUE;
+ 				parse_immed = FALSE;
+ 			}
+ 			fieldskip(&user_data);
+ 			mode = PARSE_MODE_NAME;
+ 			break;
+ 
+ 		default:
+ 			mode = PARSE_MODE_NAME;
+ 			break;
+ 		}
+ 	}
+ 	return want_ace;
  }
diff -c -N radiusd/usr_read.c ascendd/usr_read.c
*** radiusd/usr_read.c	Wed Dec 31 16:00:00 1969
--- ascendd/usr_read.c	Thu May 23 20:08:57 1996
***************
*** 0 ****
--- 1,370 ----
+ /*
+  *
+  *	RADIUS
+  *	Remote Authentication Dial In User Service
+  *
+  * ASCEND: @(#)usr_read.c	1.5 (95/07/25 00:55:42)
+  *
+  *	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.
+  *
+  *      Copyright (c) 1995 Ascend Communications, Inc.
+  *      All rights reserved.
+  *
+  *	Permission to copy all or part of this material for any purpose is
+  *	granted provided that the above copyright notice and this paragraph
+  *	are duplicated in all copies.  THIS SOFTWARE IS PROVIDED ``AS IS''
+  *	AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+  *	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  *	FOR A PARTICULAR PURPOSE.
+  */
+ 
+ #include	<sys/types.h>
+ #include	<sys/socket.h>
+ #include	<sys/time.h>
+ #include	<sys/file.h>
+ #include	<netinet/in.h>
+ 
+ #include	<stdio.h>
+ #include	<unistd.h>
+ #include	<netdb.h>
+ #include	<string.h>
+ #include	<pwd.h>
+ #include	<time.h>
+ #include	<ctype.h>
+ #include	<varargs.h>
+ 
+ #include	"radius.h"
+ 
+ extern char	*progname;
+ extern char	*radius_dir;
+ extern char	*radius_users;
+ extern FILE	*errf;
+ int		curParseLine;	/* current line being parsed */
+ 
+ #define FIND_MODE_NAME	0
+ #define FIND_MODE_REPLY	1
+ #define FIND_MODE_SKIP	2
+ #define FIND_MODE_FLUSH	3
+ 
+ #if defined(__STDC__) && (__STDC__ == 1)
+ #	define	P__(s)	s
+ #else
+ #	define	P__(s)	(/* s */)
+ #endif
+ 
+ extern void warn ();		/* can't declare prototype with old varargs */
+ static int test_eof P__((FILE *userfd));
+ static int probably_eor P__((FILE *userfd));
+ extern int user_read P__((FILE **userfd, char *name, char *content));
+ 
+ int	warning;
+ 
+ /*************************************************************************
+  *
+  *	Function: test_eof
+  *
+  *	Purpose: See if we are at the end of file (before we actually
+  *		 get there).
+  *
+  *************************************************************************/
+ 
+ static int
+ test_eof(userfd)
+ FILE	*userfd;
+ {
+ 	int	c;
+ 	int	status;
+ 
+ 	c = fgetc(userfd);
+ 	if( c == EOF ) {
+ 		status = TRUE;
+ 	}
+ 	else {
+ 		if( ungetc(c, userfd) != c ) {
+ 			perror("test_eof");
+ 			warn("Problem with ungetc");
+ 		}
+ 		status = FALSE;
+ 	}
+ 	return status;
+ }
+ 
+ /*************************************************************************
+  *
+  *	Function: probably_eor
+  *
+  *	Purpose: Simple heuristic to see if the missing comma really
+  *		 indicates the end of the record or if the user simply
+  *		 forgot to put one in.
+  *
+  *		 If the next line starts with a space or a tab, assume
+  *		 the user forgot the comma, else it's really the end.
+  *		 If there is no next line, then it's also the end of the
+  *		 record.
+  *
+  *		NOTE: missing commas followed by comment lines, followed
+  *			by additional lines of the record will NOT be
+  *			so interpreted; the next non-comment line will be
+  *			assumed to be the start of a new record.
+  *
+  *************************************************************************/
+ 
+ static int
+ probably_eor(userfd)
+ FILE	*userfd;
+ {
+ 	int	c;
+ 	int	status;
+ 
+ 	c = fgetc(userfd);
+ 	if( c == EOF ) {
+ 		status = TRUE;
+ 	}
+ 	else if( (c == ' ') || (c == '\t') ) {
+ 		if( ungetc(c, userfd) != c ) {
+ 			perror("probably_eor");
+ 			warn("Problem with ungetc");
+ 		}
+ 		status = FALSE;
+ 	}
+ 	else {
+ 		if( ungetc(c, userfd) != c ) {
+ 			perror("probably_eor");
+ 			warn("Problem with ungetc");
+ 		}
+ 		status = TRUE;
+ 	}
+ 	return status;
+ }
+ 
+ /*************************************************************************
+  *
+  *	Function: user_read
+  *
+  *	Purpose: Return the next user in the database - name is key content
+  *		 is 2 strings - check values, and reply values seperated
+  *		 by a newline.
+  *
+  *************************************************************************/
+ 
+ int
+ user_read(userfd, name, content)
+ FILE	**userfd;
+ char	*name;
+ char	*content;
+ {
+ 	char		buf[4 * 1024];
+ 	char		*ptr;
+ 	int		mode;
+ 	int		submode;
+ 	long		fpos;
+ 	int		replyCount = 0;
+ 
+ 	/*
+ 	 * Open the user table
+ 	 */
+ 	if(*userfd == (FILE *)NULL) {
+ 		sprintf(buf, "%s/%s", radius_dir, radius_users);
+ 		if((*userfd = fopen(buf, "r")) == (FILE *)NULL) {
+ 			fprintf(errf, "%s: Couldn't open %s for reading\n",
+ 					progname, buf);
+ 			exit(USERFILE_READ_ERR);
+ 		}
+ 		curParseLine = 0;
+ 	}
+ 
+ 	mode = FIND_MODE_NAME;
+ 	submode = FIND_MODE_NAME;
+ 	fpos = ftell(*userfd);
+ 	while(fgets(buf, sizeof(buf), *userfd) != (char *)NULL) {
+ 		curParseLine++;
+ 		/* skip lines starting with '#' or newline
+ 			name is the string until the first
+ 				blank, tab, newline, or NUL
+ 			if no name, skip to next record
+ 			if name, but no password on same line,
+ 				start over on next line 
+ 			Alert the user about missing names or passwords,
+ 			and commas at the end of the first record line
+ 		*/
+ 
+ 		if(mode == FIND_MODE_NAME) {
+ 			/*
+ 			 * Find the entry starting with the users name
+ 			 */
+ 			if( (*buf != '#') && (*buf != '\n') ) {
+ 				char *origname = name;
+ 
+ 				*name = '\0';	/* name found flag */
+ 				ptr = buf;
+ 				while( (*ptr != ' ') && (*ptr != '\t')
+ 				    && (*ptr != '\n') && (*ptr != '\0') ) {
+ 					*name++ = *ptr++;
+ 				}
+ 				if( *origname == '\0' ) {
+ 					name = origname;
+ 					if( submode == FIND_MODE_SKIP ) {
+ 						continue;
+ 					}
+ 					while( *ptr == ' ' || *ptr == '\t') {
+ 						ptr++;
+ 					}
+ 					if( *ptr == '\n' || *ptr == '\0') {
+ 						warn("No name or attributes\n\t- skipping blank line");
+ 					}
+ 					else {
+ 						warn("No name found\n\t- possible missing comma on a previous line\n\t... skipping to next record");
+ 						submode = FIND_MODE_SKIP;
+ 					}
+ 					continue;	/* no name found */
+ 				}
+ 				if( (*ptr == '\n') || (*ptr == '\0') ) {
+ 					name = origname;
+ 					warn( "Missing mandatory password\n\tfor user '%s'", name);
+ 					submode = FIND_MODE_SKIP;
+ 					continue;	/* no pwd on line */
+ 				}
+ 				*name = '\0';	/* NUL-terminate name */
+ 				ptr++;
+ 				while(*ptr == ' ' || *ptr == '\t') {
+ 					ptr++;
+ 				}
+ 				*content = 0;
+ 				if( (*ptr == '\n') || (*ptr == '\0') ) {
+ 					name = origname;
+ 					warn( "Missing mandatory password\n\tfor user '%s'", name);
+ 					submode = FIND_MODE_SKIP;
+ 					continue;	/* no pwd on line */
+ 				}
+ 				submode = FIND_MODE_NAME;
+ 				strcpy(content, ptr);
+ 				content += strlen(content);
+ 
+ 				/* if line ends with ",\n" then whine */
+ 				if( content[-2] == ',' ) {
+ 					warn("Trailing comma on name-pwd line");
+ 				}
+ 				mode = FIND_MODE_REPLY;
+ 				replyCount = 0;	/* no reply attributes yet */
+ 				if( test_eof(*userfd) ) {
+ 					warn("Premature end of file");
+ 					return 0;
+ 				}
+ 			}
+ 		}
+ 		else {	/* not first line of record */
+ 			/* line must start with tab or blank
+ 			   if it starts with a '#' skip it, but warn the
+ 			     user since previously, flat file searches would
+ 			     terminate at comment lines inside a record.
+ 			   if there is a comma on the last line of the
+ 			     record warn the user.
+ 			   if there is no comma but it is not the last
+ 			     line of the record then insert one
+ 			*/
+ 			replyCount++;
+ 			if(*buf == ' ' || *buf == '\t') {
+ 				ptr = buf;
+ 				while(*ptr == ' ' || *ptr == '\t') {
+ 					ptr++;
+ 				}
+ 				if( *ptr == '\n' || *ptr == '\0' ) {
+ 					warn("No attributes on line\n\t- assuming end of record");
+ 					return 0;
+ 				}
+ 				strcpy(content, ptr);
+ 				content += strlen(content);
+ 				content -= 2;
+ 				while(*content == ' ' || *content == '\t' ) {
+ 					content--;
+ 				}
+ 				content++;
+ 				*content = '\0';
+ 
+ 				/* check for end of record */
+ 				if(*(content - 1) != ',') {
+ 					/* double-check */
+ 					if( probably_eor(*userfd) ) {
+ 						return(0);
+ 					}
+ 					else {
+ 						warn("Missing comma at eol");
+ 						/* force a comma */
+ 						*content++ = ',';
+ 						*content = '\0';
+ 					}
+ 				}
+ 				if( test_eof(*userfd) ) {
+ 					warn("Premature end of file");
+ 					return 0;
+ 				}
+ 			}
+ 			else if( *buf == '#' ) {
+ 				warn("Comment in record");
+ 				if( test_eof(*userfd) ) {
+ 					warn("Premature end of file");
+ 					return 0;
+ 				}
+ 				else {
+ 					continue;
+ 				}
+ 			}
+ 			else if( *buf == '\n' ) {
+ 				if( replyCount > 1 ) {
+ 					warn("Trailing comma on last record");
+ 				}
+ 				return(0);
+ 			}
+ 			else {
+ 				/* We are done */
+ 				warn("No tab before attribute\n\t- assuming end of record and rescanning");
+ 				fseek(*userfd, fpos, SEEK_SET);
+ 				curParseLine--;
+ 				replyCount--;
+ 				return(0);
+ 			}
+ 		}
+ 		fpos = ftell(*userfd);
+ 	}
+ 	fclose(*userfd);
+ 	*userfd = (FILE *)NULL;
+ 	return(END_OF_USERS_LIST);
+ }
+ 
+ void
+ warn(va_alist)
+ va_dcl
+ {
+ 	va_list ap;
+ 	char *fmt;
+ 
+ 	if( !warning ) {
+ 		return;
+ 	}
+ 	fprintf(errf, "%s: %s/%s: line %d: Warning: ",
+ 		progname, radius_dir, radius_users, curParseLine);
+ 	va_start(ap);
+ 	fmt = va_arg(ap, char *);
+ 	vfprintf(errf, fmt, ap);
+ 	va_end(ap);
+ 	fputc('\n', errf);
+ }
+ 
+ 
diff -c -N radiusd/util.c ascendd/util.c
*** radiusd/util.c	Thu Jan 11 17:26:17 1996
--- ascendd/util.c	Thu Jan 11 17:26:13 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)util.c	1.2 (95/07/25 00:55:43)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 38,45 ****
--- 40,49 ----
  #include	<pwd.h>
  #include	<time.h>
  #include	<ctype.h>
+ #include	<stdlib.h>
  
  #include	"radius.h"
+ #include	"protos.h"
  
  /*************************************************************************
   *
***************
*** 64,70 ****
  		ipaddr2str(hstname, ipaddr);
  		return(hstname);
  	}
! 	return(hp->h_name);
  }
  
  /*************************************************************************
--- 68,74 ----
  		ipaddr2str(hstname, ipaddr);
  		return(hstname);
  	}
! 	return (char *)(hp->h_name);
  }
  
  /*************************************************************************
***************
*** 81,88 ****
  char	*host;
  {
  	struct hostent *hp;
- 	static char	buffer[32];
- 	UINT4		ipstr2long();
  
  	if(good_ipaddr(host) == 0) {
  		return(ipstr2long(host));
--- 85,90 ----
***************
*** 101,106 ****
--- 103,109 ----
   *
   *************************************************************************/
  
+ int
  good_ipaddr(addr)
  char	*addr;
  {
***************
*** 142,147 ****
--- 145,151 ----
   *
   *************************************************************************/
  
+ void
  ipaddr2str(buffer, ipaddr)
  char	*buffer;
  UINT4	ipaddr;
***************
*** 168,173 ****
--- 172,178 ----
   *
   *************************************************************************/
  
+ void
  pairfree(pair)
  VALUE_PAIR	*pair;
  {
diff -c -N radiusd/version.c ascendd/version.c
*** radiusd/version.c	Thu Jan 11 17:26:17 1996
--- ascendd/version.c	Thu Jan 11 17:44:32 1996
***************
*** 3,8 ****
--- 3,10 ----
   *	RADIUS
   *	Remote Authentication Dial In User Service
   *
+  * ASCEND: @(#)version.c	1.4 (95/12/19 13:24:45)
+  *
   *
   *	Livingston Enterprises, Inc.
   *	6920 Koll Center Parkway
***************
*** 29,34 ****
--- 31,37 ----
  "@(#)version.c	1.2 Copyright 1992 Livingston Enterprises Inc";
  
  #include        <sys/types.h>
+ #include	<unistd.h>
  #include	<stdio.h>
  #include	"radius.h"
  
***************
*** 39,45 ****
   *  would like a range of versions allocated for your use.
   */
  
! #define		VERSION		"1.16 95/01/06"
  
  /*************************************************************************
   *
--- 42,56 ----
   *  would like a range of versions allocated for your use.
   */
  
! #define		VERSION		"1.16 (plus Ascend extensions) 96/01/12"
! 
! #if defined(__STDC__) && (__STDC__ == 1)
! #	define	P__(s)	s
! #else
! #	define	P__(s)	(/* s */)
! #endif
! 
! extern void radversion P__((void));
  
  /*************************************************************************
   *
***************
*** 49,66 ****
   *
   *************************************************************************/
  
! version()
  {
- 
  	fprintf(stderr, "%s: RADIUS version %s\n", progname, VERSION);
  
  	/* here are all the conditional feature flags */
! #if defined(DBM)
! 	fprintf(stderr," DBM");
  #endif
  #if defined(NOSHADOW)
  	fprintf(stderr," NOSHADOW");
  #endif
  
  	/* here are all the system definitions compilation uses */
  #if defined(__alpha)
--- 60,92 ----
   *
   *************************************************************************/
  
! void
! radversion()
  {
  	fprintf(stderr, "%s: RADIUS version %s\n", progname, VERSION);
  
  	/* here are all the conditional feature flags */
! #if defined(DBM_MODE)
! 	fprintf(stderr," DBM_MODE");
  #endif
  #if defined(NOSHADOW)
  	fprintf(stderr," NOSHADOW");
  #endif
+ #if defined(BINARY_FILTERS)
+ 	fprintf(stderr," BINARY_FILTERS");
+ #endif
+ #if defined( ASCEND_SECRET )
+ 	fprintf(stderr," ASCEND_SECRET");
+ #endif
+ #if defined(ASCEND_LOGOUT)
+ 	fprintf(stderr," ASCEND_LOGOUT");
+ #endif
+ #if defined(SAFEWORD)
+ 	fprintf(stderr, " SafeWord");
+ #endif
+ #if defined(ACE)
+ 	fprintf(stderr, " ACE");
+ #endif
  
  	/* here are all the system definitions compilation uses */
  #if defined(__alpha)
***************
*** 72,84 ****
  #if defined(aix)
  	fprintf(stderr," aix");
  #endif
! #if defined(bsdi)
  	fprintf(stderr," bsdi");
  #endif
! #if defined(sun)
  	fprintf(stderr," sun");
  #endif
! #if defined(sys5)
  	fprintf(stderr," sys5");
  #endif
  #if defined(unixware)
--- 98,112 ----
  #if defined(aix)
  	fprintf(stderr," aix");
  #endif
! #if defined(BSDI)
  	fprintf(stderr," bsdi");
  #endif
! #if defined(sun) || defined(OSUN)
  	fprintf(stderr," sun");
  #endif
! #if defined(SOLARIS)
! 	fprintf(stderr," SOLARIS");
! #elif defined(sys5)
  	fprintf(stderr," sys5");
  #endif
  #if defined(unixware)
