--- /dev/null
+>Changes from Release 0.0 to release 0.1
+---------------------------------------
+
+You must now say "default attribute = permit" instead of
+default authorization = permit" when configuring service
+defaults
+
+You must now say "default svc = permit" instead of "default
+authorization = permit" when configuring service defaults for a user.
+
+When authorizing enable requests, the daemon used to prompt for a
+username which it then ignored. It no longer prompts for a username.
+
+Fix recursion issues with service and command lookups. They are now
+fully recursive c.f. password lookups.
+
+Add debugging output to password verification to provide information
+about expiry processing.
+
+Keep track of longest hash chain we create, for fine tuning. Hash all
+keywords into a keyword table instead of doing linear lookup.
+
+Update users_guide to reflect the new configuration syntax.
+
+The convert.pl script now generates the new configuration file syntax.
+
+Accounting code now honours the "more" flag.
+
+
+Changes from Release 0.1 to release 0.2
+---------------------------------------
+
+You can now send a SIGHUP to the daemon to cause it to reinitialize
+itself and re-read its CONFIG file. There is a new debugging flag
+devoted to this section of the code.
+
+Node types are now pretty-printed in debug output.
+
+The conversion script "convert.pl" now will not print out an expires
+field if it doesn't think the syntax of the field is correct. It also
+now ignores blank lines in its input files.
+
+When doing authorization, the NAS supplied attribute "cmd=" is now
+correctly ignored. This would previously have caused exec
+authorization to be denied.
+
+Changes from Release 0.2 to release 0.3
+---------------------------------------
+Warn when not invoked as uid 0.
+Improved Usage message
+Add make install target
+
+Changes from Release 0.3 to release 0.4
+---------------------------------------
+Add TAC_PLUS_PIDFILE to makefile per Andy Linton's suggestion.
+Fix bug in authorization code (protocol field needs to be
+uppercase) which prevented authorization from working.
+
+Changes from Release 0.4 to release 0.5
+---------------------------------------
+Add pre and post authorization calls to shell commands.
+Minor bugfixes and code cleanup
+The "More" bit in accounting records is now honoured.
+Fix a bug in convert.pl
+Redo accounting output routines. You can now name the accounting file
+in the configuration file.
+Change "svc" to "service" and "proto" to "protocol".
+You can use any string to name a ppp protocol, even one which doesn't yet exist.
+Add PPP/LCP special case processing
+Revised authorization algorithm (see user's guide)
+Add hex debug flag to allow skipping hex in packet dumps.
+Update user's guide to reflect changes
+
+Changes from Release 0.4 to release 1.0
+---------------------------------------
+Changed format of syslog messages to make writing scripts easier
+Added ability to use cleartext passwords instead of DES passwords
+Updated man page to reflect the fact that we use SIGUSR1 to re-read
+the config file. SIGHUP is now ignored.
+Updated the users guide.
+
+Changes from Release 1.0 to release 1.1
+---------------------------------------
+Release 1.1 corresponds to RCS version 1.64 of tac_plus
+(see tac_plus -v)
+
+A typo in the Solaris section of the Makefile has been fixed.
+
+The keyword 'des' has been introduced which must be used before all
+des encrypted passwords.
+
+The keyword 'password' has been changed to 'login', so
+ password = f23sac783n
+has become
+ login = des f23sac783n
+
+The convert.pl script knows about these changes.
+
+arap and chap now require the keyword 'cleartext' in front of their
+passwords.
+
+A cleartext, per-user, global password can now be configured, which
+works for login, arap and chap.
+
+The users_guide has been updated to include a list of all A/V pairs
+recognised by IOS 10.3(3) code.
+
+Some solaris binaries have been provided as a courtesy.
+
+Changes from Release 1.1 to release 2.0
+---------------------------------------
+generate_password.pl has been removed in favour of a C program
+generate_passwd.c
+
+The version number reported by tac_plus has been changed to agree with the
+release number. This is why the version has jumped to 2.0
+
+skey was broken by changes made in 1.1. These are now fixed.
+
+Documentation has been added for the authorization AV pairs supported
+by IOS releases 10.3(3) and 11.0.
+
+Changes from Release 2.0 to release 2.1
+---------------------------------------
+There are now Makefile definitions for most of the major platforms.
+
+Minor changes to remove some spurious debugging output.
+
+A prematurely closed NAS connection will now call the authentication
+function with the abort flag set, so that it can do any clean up it
+requires.
+
+syslog messages will contain the string "unknown" for usernames and
+ports which are NULL, so that the messages always contain a fixed
+number of fields.
+
+The authentication code has been rearranged to better reflect the
+structure of the API.
+
+The "default user = permit" directive is still accepted but is now
+deprecated in favour of "default authorization = permit".
+
+A bug in the handling of substring AV pairs which caused the attribute
+"addr" to erroneously match "addr-pool" has been fixed.
+
+Added new files: enable.c generate_passwd.c skey_fn.c
+
+New #defines have been added to make it easier to port tacacs+ to new
+systems.
+
+Many more iterations are allowed before an error is declared.
+
+Changes from Release 2.1 to release 2.2
+---------------------------------------
+The expiry field in the shadow file on Solaris machines is now
+honored, if it exists.
+
+Added TAC_PLUS_AUTHEN_SVC_NASI
+
+Changes from Release 2.2 to release F3.0.13
+-------------------------------------------
+NEW REVISION OF THE PROTOCOL corresponding to tacacs+.spec.v1.63.ps
+(which see) to increase security in the case of compromised keys.
+
+Inbound pap logins and outbound pap password are now configurable as
+separate entries for each principal. Inbound pap logins are now
+declared by using a "pap = " configuration directive. Outbound PAP is
+now configured using "opap =".
+
+Substantial code rearrangement of authentication routines.
+
+Cleartext passwords can be up to 255 characters in length (previously
+only the first 8 characters were used).
+
+default service = permit is now fully recursive and now allows you to
+say default service = deny in case you belong to a group where the
+default is to permit.
+
+Include backward compatibility with old revision of the protocol
+(prior to v1.63).
+
+post_authorization scripts are now invoked for command authorization.
+
+Better sanity checking of authorization and accounting packets.
+
+The API has changed slightly. All character string fields in the
+identity structure are now allocated from the heap and can be up to
+255 bytes long (instead of being character arrays of 32 and 64 bytes,
+as specified in the API document revision 1.30 or earlier).
+
+Double quotes can now appear inside strings if they are escaped with a
+backslash.
+
+Added code which limits the number of simultaneous sessions a user can
+have (see MAXSESS in the user's guide).
+
+The accounting "more" bit is gone (It was deprecated from the spec).
+
+Hooks are now in so that if you have DES code, you can do ARAP more
+securely, per the new protocol.
+
+The packet read/write routines now handle exceptions more gracefully.
+
+Lots of stuff added to the user's guide.
+
+If you use a port number other than the default, the pidfile has the
+port number appended to it, in case you are running multiple daemons.
+We also now remove the pidfile when the daemon terminates via SIGTERM.
+
+user = DEFAULT has been added, deprecating "default authorization =
+permit". See the user's guide.
+
+Arbitrary service types can now be configured in the config file.
+
+REARMSIGNAL has been added for those systems which install one-shot
+signal handlers which need to be rearmed after use (LINUX, HPUX).
+
+A \n can now be embedded within strings.
+
+Concede defeat. Allow SIGHUP as synonym for SIGUSR1.
+
+Avoid symbol buffer overflow by checking the maximum length of a
+string or token.
+
+Make peer DNS lookup on incoming connections optional.
+
+Do not close socket when servicing a SIGHUP
+
+Fix a bad bug where service/cmd declarations which were not contiguous
+were parsed but ignored (reported by Gabor Kiss).
+
+Patch maxsessions to not count the current port on a different
+NAS. Add various other fixes to maxsession code.
+
+Add timeout to finger read routine.
+
+Changes from release F3.0.13 to F4.0.1
+-------------------------------------------
+Added MSCHAP routines
+
+CSCdi37706 exposed a bug in command authorization on the daemon.
+Change assemble_args so it returns "" if there are no command
+arguments.
+
+Changes from release F4.0.1 to F4.0.2
+-------------------------------------------
+Fix fseek problem in maxsess code
+
+Changes from release F4.0.2 to F4.0.3
+-------------------------------------------
+Add option for wtmp file logging in accounting
+Added -DGLIBC for Linux.
+Support PAP with des encrypted passwords
+Support a return code of 3 for external authorization scripts
+
--- /dev/null
+This is modified verision of tacacs+
+The installation is very basic.
+just type :
+./configure
+(if you like to learn the configure parameters try --help option with configure )
+make install
+
+Thats all.
+
+This packet consist some helper file to using tacacs+ easly.
+tac_plus.cfg: Example tacacs+ config file
+README.PAM: For PAM supported systems
+tac_plus.init: For starting and stoping tacacs+
+tac_plus.sql: table description (Mysql)
+
+Sorry for this short description.:(
+
+Please feel free to send comments and suggestion to my mail address
+
+devrim seral <devrim@tef.gazi.edu.tr>
--- /dev/null
+# Please NOTE: None of the TACACS code available here comes with any
+# warranty or support.
+# Copyright (c) 1995-1998 by Cisco systems, 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 of the software and
+# supporting documentation, the name of Cisco Systems, 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 modification, copying and distribution is by
+# permission of Cisco Systems, Inc.
+#
+# Cisco Systems, Inc. makes no representations about the suitability of this
+# software for any purpose. 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.
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = $(exec_prefix)/bin
+sbindir = $(exec_prefix)/sbin
+infodir = $(prefix)/info
+libdir = $(prefix)/lib/gnudl
+mandir = $(prefix)/man/man1
+
+CC = @CC@
+CPPFLAGS = @CPPFLAGS@
+FLAGS = $(CPPFLAGS) @CFLAGS@
+LDFLAGS = @LDFLAGS@
+OSLIBS = @LIBS@
+DEFINES = @DEFINES@
+OS = @OS@
+PIDFILE = @PIDFILE@
+DB = @DB@
+
+# For AIX
+# See /usr/lpp/bos/bsdport on your system for details of how to define bsdcc
+# CC=bsdcc
+# OS=-DAIX
+
+# For HP/UX uncomment the following line
+# OS=-DHPUX
+
+# For MIPS, uncomment the following line
+# OS=-DMIPS
+
+# For Solaris (SUNOS 5.3, 5.4, 5.5, 5.6) uncomment the following two lines
+#OS=-DSOLARIS
+#OSLIBS=-lsocket -lnsl
+
+# For FreeBSD
+# OS=-DFREEBSD
+# You may also need to add:
+# OSLIBS=-lcrypt
+# NOTE: If you want your password encryption to be compatible with
+# e.g. SunOS, you may need to instead use:
+# OSLIBS=-ldescrypt
+
+# For LINUX
+# OS=-DLINUX
+#
+# On REDHAT 5.0 systems, or systems that use the new glibc,
+# you might instead need the following:
+#OS=-DLINUX -DGLIBC
+#OSLIBS=-lcrypt
+
+# Athough invoked as root, most of the time you don't want tac_plus to
+# be running as root. If USERID and GROUPID are set, tac_plus will
+# attempt change to run as that user & group after reading the
+# configuration file and obtaining a privileged socket. If you always
+# want tac_plus to run as root, then just comment out the FLAGS line.
+
+# USERID = 1500
+# GROUPID = 25
+# FLAGS = -DTAC_PLUS_USERID=$(USERID) -DTAC_PLUS_GROUPID=$(GROUPID)
+
+# Definitions for SKEY functionality
+# DEFINES = -DSKEY
+# LIBS = ../crimelab/skey/src/libskey.a
+# INCLUDES = -I../crimelab/skey/src
+
+# Debugging flags
+DEBUG =
+
+# Opt flags
+OPT_FLAGS =
+
+# Enforce a limit on maximum sessions per user. See the user's guide
+# for more information.
+#MAXSESS = -DMAXSESS
+
+# Microsoft CHAP extension support. See the user's guide for more
+# information.
+# MSCHAP = -DMSCHAP
+# MSCHAP_DES = -DMSCHAP_DES
+# MSCHAP_MD4_SRC = md4.c
+
+# On startup, tac_plus creates the file /etc/tac_plus.pid (if
+# possible), containing its process id. Uncomment and modify the
+# following line to change this filename
+
+#PIDFILE = -DTAC_PLUS_PIDFILE=\"/var/run/tac_plus.pid\"
+
+#
+# End of customisable section of Makefile
+#
+
+CFLAGS = $(DEBUG) $(OPT_FLAGS) $(DEFINES) $(INCLUDES) $(FLAGS) $(OS) $(PIDFILE) $(LDFLAGS) $(DB)
+
+HFILES = expire.h parse.h regmagic.h md5.h regexp.h tac_plus.h
+
+SRCS = acct.c authen.c author.c choose_authen.c config.c do_acct.c \
+ do_author.c dump.c encrypt.c expire.c $(MSCHAP_MD4_SRC) md5.c \
+ packet.c report.c sendauth.c tac_plus.c utils.c pw.c hash.c \
+ parse.c regexp.c programs.c enable.c pwlib.c default_fn.c \
+ skey_fn.c default_v0_fn.c sendpass.c maxsess.c tac_pam.c \
+ db.c db_null.c db_mysql.c db_pgsql.c tcpwrap.c ldap.c time_limit.c
+
+OBJS = $(SRCS:.c=.o)
+
+all:
+ @echo "Please edit the Makefile and then make tac_plus"
+
+tac_plus: $(OBJS) $(LIBS) generate_passwd
+ $(CC) -o tac_plus $(CFLAGS) $(OBJS) $(LIBS) $(OSLIBS)
+ strip tac_plus
+
+purecov: $(OBJS) $(LIBS)
+ purecov -follow-child-processes -handle-signals=SIGTERM \
+ -append-logfile -log-file=purecov.log \
+ -cache-dir=`pwd` \
+ $(CC) -o tac_plus $(CFLAGS) $(OBJS) $(LIBS) $(OSLIBS)
+
+purify: $(OBJS) $(LIBS)
+ purify -follow-child-processes=yes -log-file=./tac_plus_purify.log \
+ -handle-signals=SIGTERM -cache-dir=. \
+ $(CC) -o tac_plus $(CFLAGS) $(OBJS) $(LIBS) $(OSLIBS)
+
+generate_passwd:
+ $(CC) $(CFLAGS) -o generate_passwd generate_passwd.c $(OSLIBS)
+
+clean:
+ -rm -f *.o *~ *.BAK core tac_plus generate_passwd
+
+distclean: clean
+ -rm -f Makefile config.h config.status config.cache config.log
+
+clean_obj:
+ -rm -f *.o *~ *.BAK
+
+make_dir:
+ mkdir -p $(bindir)
+ mkdir -p $(sbindir)
+ mkdir -p $(mandir)
+
+install: make_dir
+ cp tac_plus $(sbindir)/
+ cp generate_passwd $(bindir)/
+ cp tac_plus.1 $(mandir)/
+
+rpm_install: make_dir
+ install -c -m 0755 tac_plus $(sbindir)/
+ install -c -m 0755 generate_passwd $(sbindir)/
+ install -c -m 0644 tac_plus.1 $(mandir)/
+
+depend:
+ makedepend $(CFLAGS) $(SRCS)
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
--- /dev/null
+Tacacs+ with PAM Authentication and Authorization support.
+
+patch version 0.4 (7 February 2000)
+Now Tacacs+ PAM Authorization Working..
+I am add some function to do it and also rename tac_pam_acct.c file to tam_pam.c
+But we must do more test ..:p
+devrim(devrim@tef.gazi.edu.tr)
+
+patch version 0.3 (17 November 1999)
+tac_pam_auth is MT-safe now.
+Tacacs reports pam error strings.
+More examples into the tac_plus.conf. Corrected the version number at start-up.
+
+patch version 0.2 (31 August 1999)
+Pammified default authentication. If -DUSE_PAM used, I hope that PAM is the default
+system authentication method. The PAM service used is the name of the passwd_file (???).
+Yes, it's terrible, but I have not time to spend for changing
+default authentication = file /etc/passwd
+ in
+default authentication = pam name_of_service..........any volunteer ???
+As usual, look at the tac_plus.conf file.
+
+patch version 0.1 (August 1999)
+I introduced an optional new authetication method via PAM. This makes authentication
+modular and external to the tacacs+ code.
+I have not implemented pam-authorization because the existence of pre and post autorization
+calls.
+In order to pass tacacs+ attributes to the pam modules, I use the PAM_RUSER item
+for storing the rem_addr. Have a look to tac_pam_auth.c for the details.
+As you can see, pam-authentication only works for pap and login authetication-types:
+I would like to extend it to the default authetication case, but the code seems so
+cryptic :-(.......
+You can choose the pam service by the configuration file,for a simple example look
+into tac_plus.conf.
+
+
+
+Max Liccardo <ravel@tiscalinet.it>
+
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+/*
+ * Come here when we receive an Start Accounting packet
+ */
+
+void account();
+
+/* For DB accounting */
+#ifdef DB
+int db_acct();
+#endif /* DB */
+
+void
+accounting(pak)
+u_char *pak;
+{
+ struct acct *acct_pak;
+ u_char *p;
+ HDR *hdr;
+ u_char *read_packet();
+ int i, len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "Start accounting request");
+
+ hdr = (HDR *) pak;
+ acct_pak = (struct acct *) (pak + TAC_PLUS_HDR_SIZE);
+
+ /* Do some sanity checking on the packet */
+
+ /* arg counts start here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
+
+ /* Length checks */
+ len = TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
+ len += acct_pak->user_len + acct_pak->port_len +
+ acct_pak->rem_addr_len + acct_pak->arg_cnt;
+ for (i = 0; i < (int)acct_pak->arg_cnt; i++) {
+ len += p[i];
+ }
+
+ if (len != ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_ACCT, NULL);
+ return;
+ }
+
+ account(pak);
+
+ free(pak);
+}
+
+void
+account(pak)
+u_char *pak;
+{
+ struct acct *acct_pak;
+ u_char *p, *argsizep;
+ struct acct_rec rec;
+ struct identity identity;
+ char **cmd_argp;
+ int i, errors, status;
+
+ acct_pak = (struct acct *) (pak + TAC_PLUS_HDR_SIZE);
+
+ /* Fill out accounting record structure */
+ bzero(&rec, sizeof(struct acct_rec));
+
+ if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_WATCHDOG)
+ rec.acct_type = ACCT_TYPE_UPDATE;
+ if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_START)
+ rec.acct_type = ACCT_TYPE_START;
+ if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_STOP)
+ rec.acct_type = ACCT_TYPE_STOP;
+
+ rec.authen_method = acct_pak->authen_method;
+ rec.authen_type = acct_pak->authen_type;
+ rec.authen_service = acct_pak->authen_service;
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
+
+ /* skip arg cnts */
+ p += acct_pak->arg_cnt;
+
+ /* zero out identity struct */
+ bzero(&identity, sizeof(struct identity));
+
+ identity.username = tac_make_string(p, (int)acct_pak->user_len);
+ p += acct_pak->user_len;
+
+ identity.NAS_name = tac_strdup(session.peer);
+
+ identity.NAS_port = tac_make_string(p, (int)acct_pak->port_len);
+ p += acct_pak->port_len;
+ if (acct_pak->port_len <= 0) {
+ strcpy(session.port, "unknown-port");
+ } else {
+ strcpy(session.port, identity.NAS_port);
+ }
+
+ identity.NAC_address = tac_make_string(p, (int)acct_pak->rem_addr_len);
+ p += acct_pak->rem_addr_len;
+
+ identity.priv_lvl = acct_pak->priv_lvl;
+
+ rec.identity = &identity;
+
+ /* Now process cmd args */
+ argsizep = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
+
+ cmd_argp = (char **) tac_malloc(acct_pak->arg_cnt * sizeof(char *));
+
+ for (i = 0; i < (int)acct_pak->arg_cnt; i++) {
+ cmd_argp[i] = tac_make_string(p, *argsizep);
+ p += *argsizep++;
+ }
+
+ rec.args = cmd_argp;
+ rec.num_args = acct_pak->arg_cnt;
+
+#ifdef MAXSESS
+ /*
+ * Tally for MAXSESS counting
+ */
+ loguser(&rec);
+#endif
+
+ /*
+ * Do accounting.
+ */
+ if (wtmpfile) {
+ errors = do_wtmp(&rec);
+ } else {
+ errors = do_acct(&rec);
+#ifdef DB
+ if (session.db_acct && rec.acct_type==ACCT_TYPE_STOP )
+ db_acct(&rec);
+#endif
+ }
+
+ if (errors) {
+ status = TAC_PLUS_ACCT_STATUS_ERROR;
+ } else {
+ status = TAC_PLUS_ACCT_STATUS_SUCCESS;
+ }
+ send_acct_reply(status, rec.msg, rec.admin_msg);
+
+ free(identity.username);
+ free(identity.NAS_name);
+ free(identity.NAS_port);
+ free(identity.NAC_address);
+
+ for (i = 0; i < (int)acct_pak->arg_cnt; i++) {
+ free(cmd_argp[i]);
+ }
+ free(cmd_argp);
+
+ if (rec.msg)
+ free(rec.msg);
+ if (rec.admin_msg)
+ free(rec.admin_msg);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+static int choose();
+static void authenticate();
+static void do_start();
+
+/*
+ * Come here when we receive an authentication START packet
+ */
+
+void
+authen(pak)
+u_char *pak;
+{
+ char msg[55];
+ struct authen_start *start;
+ HDR *hdr;
+
+ hdr = (HDR *) pak;
+ start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
+
+ if ((hdr->seq_no != 1) ||
+ (ntohl(hdr->datalength) != TAC_AUTHEN_START_FIXED_FIELDS_SIZE +
+ start->user_len + start->port_len + start->rem_addr_len +
+ start->data_len)) {
+ send_authen_error("Invalid AUTHEN/START packet (check keys)");
+ return;
+ }
+
+ switch (start->action) {
+ case TAC_PLUS_AUTHEN_LOGIN:
+ case TAC_PLUS_AUTHEN_SENDAUTH:
+ case TAC_PLUS_AUTHEN_SENDPASS:
+ do_start(pak);
+ return;
+
+ default:
+ sprintf(msg, "Invalid AUTHEN/START action=%d", start->action);
+ send_authen_error(msg);
+ return;
+ }
+}
+
+/*
+ * We have a valid AUTHEN/START packet. Fill out data structures and
+ * attempt to authenticate.
+ */
+
+static void
+do_start(pak)
+u_char *pak;
+{
+ struct identity identity;
+ struct authen_data authen_data;
+ struct authen_type authen_type;
+ struct authen_start *start;
+ u_char *p;
+ int ret;
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Authen Start request");
+
+ /* fixed fields of this packet */
+ start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
+
+ /* variable length data starts here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE;
+
+ /* The identity structure */
+
+ /* zero out identity struct so that all strings can be NULL terminated */
+ bzero(&identity, sizeof(struct identity));
+
+ identity.username = tac_make_string(p, (int)start->user_len);
+ p += start->user_len;
+
+ identity.NAS_name = tac_strdup(session.peer);
+
+ identity.NAS_port = tac_make_string(p, (int)start->port_len);
+ p += start->port_len;
+
+ if (start->port_len <= 0) {
+ strcpy(session.port, "unknown-port");
+ } else {
+ strcpy(session.port, identity.NAS_port);
+ }
+
+ identity.NAC_address = tac_make_string(p, (int)start->rem_addr_len);
+ p += start->rem_addr_len;
+
+ identity.priv_lvl = start->priv_lvl;
+
+ /* The authen_data structure */
+
+ bzero(&authen_data, sizeof(struct authen_data));
+
+ authen_data.NAS_id = &identity;
+ authen_data.action = start->action;
+ authen_data.service = start->service;
+ authen_data.type = start->authen_type;
+ authen_data.client_dlen = start->data_len;
+
+ authen_data.client_data = tac_malloc(start->data_len);
+ bcopy(p, authen_data.client_data, start->data_len);
+
+ /* The authen_type structure */
+
+ bzero(&authen_type, sizeof(struct authen_type));
+
+ authen_type.authen_type = start->authen_type;
+
+ /* All data structures are now initialised. Now see if we can
+ * authenticate this puppy. Begin by choosing a suitable
+ * authentication function to call to actually do the work. */
+
+#ifdef TCPWRAPPER
+if (check_from_wrap(&identity)) {
+#endif
+ ret = choose(&authen_data, &authen_type);
+
+ switch (ret) {
+ case 1:
+ /* A successful choice. Authenticate */
+ authenticate(&authen_data, &authen_type);
+ break;
+
+ case 0:
+ /* We lost our connection, aborted, or something dreadful happened */
+ break;
+ }
+#ifdef TCPWRAPPER
+} else {
+send_authen_error("You are not allowed to access here");
+}
+#endif
+ /* free data structures */
+ if (authen_data.server_msg) {
+ free(authen_data.server_msg);
+ authen_data.server_msg = NULL;
+ }
+ if (authen_data.server_data) {
+ free(authen_data.server_data);
+ authen_data.server_data = NULL;
+ }
+ if (authen_data.client_msg) {
+ free(authen_data.client_msg);
+ authen_data.client_msg = NULL;
+ }
+ if (authen_data.client_data) {
+ free(authen_data.client_data);
+ authen_data.client_data = NULL;
+ }
+ if (authen_data.method_data) {
+ report(LOG_ERR,
+ "%s: Method data not set to NULL after authentication",
+ session.peer);
+ }
+ free(identity.username);
+ free(identity.NAS_name);
+ free(identity.NAS_port);
+ free(identity.NAC_address);
+ return;
+}
+
+/* Choose an authentication function. Return 1 if we successfully
+ chose a function. 0 if we couldn't make a choice for some reason */
+
+static int
+choose(datap, typep)
+struct authen_data *datap;
+struct authen_type *typep;
+{
+ int iterations = 0;
+ int status;
+ char *prompt;
+ struct authen_cont *cont;
+ u_char *reply;
+ u_char *p;
+ struct identity *identp;
+
+ while (1) {
+
+ /* check interation counter here */
+
+ if (++iterations >= TAC_PLUS_MAX_ITERATIONS) {
+ report(LOG_ERR, "%s: %s Too many iterations for choose_authen",
+ session.peer,
+ session.port);
+ return (0);
+ }
+ status = choose_authen(datap, typep);
+
+ if (status && (debug & DEBUG_PACKET_FLAG))
+ report(LOG_DEBUG, "choose_authen returns %d", status);
+
+ switch (status) {
+
+ case CHOOSE_BADTYPE: /* FIXME */
+ default:
+ send_authen_error("choose_authen: unexpected failure return");
+ return (0);
+
+ case CHOOSE_OK:
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "choose_authen chose %s", typep->authen_name);
+ return (1);
+
+ case CHOOSE_FAILED:
+ send_authen_error("choose_authen: unacceptable authen method");
+ return (0);
+
+ case CHOOSE_GETUSER:
+ /* respond with GETUSER containing an optional message from
+ * authen_data.server_msg. */
+
+ datap->status = TAC_PLUS_AUTHEN_STATUS_GETUSER;
+ if (datap->service == TAC_PLUS_AUTHEN_SVC_LOGIN) {
+ prompt = "\nUser Access Verification\n\nUsername: ";
+ } else {
+ prompt = "Username: ";
+ }
+ send_authen_reply(TAC_PLUS_AUTHEN_STATUS_GETUSER, /* status */
+ prompt, /* msg */
+ strlen(prompt), /* msg_len */
+ datap->server_data,
+ datap->server_dlen,
+ 0 /* flags */);
+
+ if (datap->server_data) {
+ free(datap->server_data);
+ datap->server_dlen = 0;
+ }
+ /* expect a CONT from the NAS */
+ reply = get_authen_continue();
+ if (reply == NULL) {
+ /* Typically premature close of connection */
+ report(LOG_ERR, "%s %s: Null reply packet, expecting CONTINUE",
+ session.peer, session.port);
+ return (0);
+ }
+
+ cont = (struct authen_cont *) (reply + TAC_PLUS_HDR_SIZE);
+
+ if (cont->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+ char buf[65537];
+ buf[0] = '\0';
+ session.aborted = 1;
+
+ if (cont->user_data_len) {
+ /* An abort message exists. Log it */
+ p = reply + TAC_PLUS_HDR_SIZE +
+ TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + cont->user_msg_len;
+
+ bcopy(p, buf, cont->user_data_len);
+ buf[cont->user_data_len] = '\0';
+ }
+ report(LOG_INFO, "%s %s: Login aborted by request -- msg: %s",
+ session.peer, session.port, buf);
+ free(reply);
+ return(0);
+ }
+
+ p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
+
+ identp = datap->NAS_id;
+
+ if (identp->username) {
+ free(identp->username);
+ }
+ identp->username = tac_make_string(p, cont->user_msg_len);
+ free(reply);
+ }
+ }
+ /* NOTREACHED */
+}
+
+/* Perform authentication assuming we have successfully chosen an
+ authentication method */
+static void
+authenticate(datap, typep)
+struct authen_data *datap;
+struct authen_type *typep;
+{
+ int iterations = 0;
+ u_char *reply, *p;
+ struct authen_cont *cont;
+ int (*func) ();
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Calling authentication function");
+
+ func = typep->authen_func;
+
+ if (!func) {
+ send_authen_error("authenticate: cannot find function pointer");
+ return;
+ }
+
+ while (1) {
+ if (session.aborted)
+ return;
+
+ if (++iterations >= TAC_PLUS_MAX_ITERATIONS) {
+ send_authen_error("Too many iterations while authenticating");
+ return;
+ }
+
+ if ((*func) (datap)) {
+ send_authen_error("Unexpected authentication function failure");
+ return;
+ }
+
+ switch (datap->status) {
+
+ default:
+ send_authen_error("Illegal status value from authentication function");
+ return;
+
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ /* A successful authentication */
+ send_authen_reply(TAC_PLUS_AUTHEN_STATUS_PASS,
+ datap->server_msg,
+ datap->server_msg ? strlen(datap->server_msg) : 0,
+ datap->server_data,
+ datap->server_dlen,
+ 0);
+ return;
+
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ /* never supposed to happen. reply with a server_msg if any, and
+ * bail out */
+ send_authen_error(datap->server_msg ? datap->server_msg :
+ "authentication function: unspecified failure");
+ return;
+
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+
+ /* An invalid user/password combination */
+ send_authen_reply(TAC_PLUS_AUTHEN_STATUS_FAIL,
+ datap->server_msg,
+ datap->server_msg ? strlen(datap->server_msg) : 0,
+ NULL,
+ 0,
+ 0);
+ return;
+
+ case TAC_PLUS_AUTHEN_STATUS_GETUSER:
+ case TAC_PLUS_AUTHEN_STATUS_GETPASS:
+ case TAC_PLUS_AUTHEN_STATUS_GETDATA:
+
+ /* ship GETPASS/GETDATA containing
+ * datap->server_msg to NAS. */
+
+ send_authen_reply(datap->status,
+ datap->server_msg,
+ datap->server_msg ? strlen(datap->server_msg) : 0,
+ datap->server_data,
+ datap->server_dlen,
+ datap->flags);
+
+ datap->flags = 0;
+
+ if (datap->server_msg) {
+ free(datap->server_msg);
+ datap->server_msg = NULL;
+ }
+ if (datap->server_data) {
+ free(datap->server_data);
+ datap->server_data = NULL;
+ }
+ if (datap->client_msg) {
+ free(datap->client_msg);
+ datap->client_msg = NULL;
+ }
+ reply = get_authen_continue();
+ if (!reply) {
+
+ /* Typically due to a premature connection close */
+ report(LOG_ERR, "%s %s: Null reply packet, expecting CONTINUE",
+ session.peer, session.port);
+
+ /* Tell the authentication function it should clean up
+ any private data */
+
+ datap->flags |= TAC_PLUS_CONTINUE_FLAG_ABORT;
+
+ if (datap->method_data)
+ ((*func) (datap));
+
+ datap->flags = 0;
+ return;
+ }
+
+ cont = (struct authen_cont *) (reply + TAC_PLUS_HDR_SIZE);
+
+ if (cont->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+
+ session.aborted = 1;
+
+ /* Tell the authentication function to clean up
+ its private data, if there is any */
+
+ datap->flags |= TAC_PLUS_CONTINUE_FLAG_ABORT;
+ if (datap->method_data)
+ ((*func) (datap));
+ datap->flags = 0;
+
+ if (cont->user_data_len) {
+
+ /* An abort message exists. Create a
+ null-terminated string for authen_data */
+
+ datap->client_data = (char *)
+ tac_malloc(cont->user_data_len + 1);
+
+ p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE +
+ cont->user_msg_len;
+
+ bcopy(p, datap->client_data, cont->user_data_len);
+ datap->client_data[cont->user_data_len] = '\0';
+ }
+
+ free(reply);
+ return;
+ }
+
+ p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
+
+ switch (datap->status) {
+
+ case TAC_PLUS_AUTHEN_STATUS_GETDATA:
+ case TAC_PLUS_AUTHEN_STATUS_GETPASS:
+ /* A response to our GETDATA/GETPASS request. Create a
+ * null-terminated string for authen_data */
+ datap->client_msg = (char *) tac_malloc(cont->user_msg_len + 1);
+ bcopy(p, datap->client_msg, cont->user_msg_len);
+ datap->client_msg[cont->user_msg_len] = '\0';
+ free(reply);
+ continue;
+
+ case TAC_PLUS_AUTHEN_STATUS_GETUSER:
+ default:
+ report(LOG_ERR, "%s: authenticate: cannot happen",
+ session.peer);
+ send_authen_error("authenticate: cannot happen");
+ free(reply);
+ return;
+ }
+ }
+ /* NOTREACHED */
+ }
+}
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+/*
+ * Come here when we receive an authorization START packet
+ */
+
+void
+author(pak)
+u_char *pak;
+{
+ HDR *hdr;
+ struct author *apak;
+ struct identity identity;
+ struct author_data author_data;
+ u_char *p;
+ u_char *argsizep;
+ char **cmd_argp;
+ int i, len;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Start authorization request");
+
+ hdr = (HDR *)pak;
+ apak = (struct author *) (pak + TAC_PLUS_HDR_SIZE);
+
+ /* Do some sanity checks */
+ if (hdr->seq_no != 1) {
+ send_error_reply(TAC_PLUS_AUTHOR, NULL);
+ return;
+ }
+
+ /* arg counts start here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
+
+ /* Length checks */
+ len = TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
+ len += apak->user_len + apak->port_len + apak->rem_addr_len + apak->arg_cnt;
+ for (i = 0; i < (int)apak->arg_cnt; i++) {
+ len += p[i];
+ }
+
+ if (len != ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_AUTHOR, NULL);
+ return;
+ }
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
+
+ /* arg length data starts here */
+ argsizep = p;
+
+ p += apak->arg_cnt;
+
+ bzero(&author_data, sizeof(struct author_data));
+
+ /* The identity structure */
+
+ /* zero out identity struct */
+ bzero(&identity, sizeof(struct identity));
+ identity.username = tac_make_string(p, (int) apak->user_len);
+ p += apak->user_len;
+
+ identity.NAS_name = tac_strdup(session.peer);
+
+ identity.NAS_port = tac_make_string(p, (int)apak->port_len);
+ p += apak->port_len;
+ if (apak->port_len <= 0) {
+ strcpy(session.port, "unknown-port");
+ } else {
+ strcpy(session.port, identity.NAS_port);
+ }
+
+ identity.NAC_address = tac_make_string(p, (int)apak->rem_addr_len);
+ p += apak->rem_addr_len;
+
+ identity.priv_lvl = apak->priv_lvl;
+
+ /* The author_data structure */
+
+ author_data.id = &identity; /* user id */
+
+ /* FIXME: validate these fields */
+ author_data.authen_method = apak->authen_method;
+ author_data.authen_type = apak->authen_type;
+ author_data.service = apak->service;
+ author_data.num_in_args = apak->arg_cnt;
+
+ /* Space for args + NULL */
+ cmd_argp = (char **) tac_malloc(apak->arg_cnt * sizeof(char *));
+
+ /* p points to the start of args. Step thru them making strings */
+ for (i = 0; i < (int)apak->arg_cnt; i++) {
+ cmd_argp[i] = tac_make_string(p, *argsizep);
+ p += *argsizep++;
+ }
+
+ author_data.input_args = cmd_argp; /* input command arguments */
+
+ if (do_author(&author_data)) {
+ report(LOG_ERR, "%s: do_author returned an error", session.peer);
+ send_author_reply(AUTHOR_STATUS_ERROR,
+ author_data.msg,
+ author_data.admin_msg,
+ author_data.num_out_args,
+ author_data.output_args);
+ return;
+ }
+
+ /* Send a reply packet */
+ send_author_reply(author_data.status,
+ author_data.msg,
+ author_data.admin_msg,
+ author_data.num_out_args,
+ author_data.output_args);
+
+ if (debug)
+ report(LOG_INFO, "authorization query for '%s' %s from %s %s",
+ author_data.id->username && author_data.id->username[0] ?
+ author_data.id->username : "unknown",
+ author_data.id->NAS_port && author_data.id->NAS_port[0] ?
+ author_data.id->NAS_port : "unknown",
+ session.peer,
+ (author_data.status == AUTHOR_STATUS_PASS_ADD ||
+ author_data.status == AUTHOR_STATUS_PASS_REPL) ?
+ "accepted" : "rejected");
+
+ /* free the input args */
+ if (author_data.input_args) {
+ for (i = 0; i < author_data.num_in_args; i++)
+ free(author_data.input_args[i]);
+
+ free(author_data.input_args);
+ author_data.input_args = NULL;
+ }
+
+ /* free the output args */
+ if (author_data.output_args) {
+ for (i=0; i < author_data.num_out_args; i++)
+ free(author_data.output_args[i]);
+
+ free(author_data.output_args);
+ author_data.output_args = NULL;
+ }
+
+ if (author_data.msg)
+ free(author_data.msg);
+
+ if (author_data.admin_msg)
+ free(author_data.admin_msg);
+
+ free(identity.username);
+ free(identity.NAS_name);
+ free(identity.NAS_port);
+ free(identity.NAC_address);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+
+static int choose_login();
+static int choose_sendpass();
+static int choose_sendauth();
+
+int
+get_minor_version()
+{
+ return(session.version & ~TAC_PLUS_MAJOR_VER_MASK);
+}
+
+/*
+ * Choose an authentication function. Return CHOOSE_OK if chosen,
+ * CHOOSE_GETUSER if we need a username, CHOOSE_FAILED on failure
+ */
+
+int
+choose_authen(data, type)
+struct authen_data *data;
+struct authen_type *type;
+{
+ char *name = data->NAS_id->username;
+
+ switch (data->action) {
+ case TAC_PLUS_AUTHEN_SENDPASS:
+ return(choose_sendpass(data, type));
+
+ case TAC_PLUS_AUTHEN_SENDAUTH:
+ return(choose_sendauth(data, type));
+
+ case TAC_PLUS_AUTHEN_LOGIN:
+ /* For enabling, enable_fn handles everything. Must be minor
+ * version zero
+ */
+ if (data->service == TAC_PLUS_AUTHEN_SVC_ENABLE) {
+ if (session.version != TAC_PLUS_VER_0) {
+ /* must be version 0 */
+ break;
+ }
+ type->authen_func = enable_fn;
+ strcpy(type->authen_name, "enable_fn");
+ return (CHOOSE_OK);
+ }
+ return(choose_login(data, type));
+
+ case TAC_PLUS_AUTHEN_CHPASS:
+ /* we don't support chpass */
+ return(CHOOSE_FAILED);
+
+ default:
+ break;
+ }
+
+ /* never heard of this lot */
+ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d",
+ session.peer,
+ session.port,
+ name ? name : "<unknown>",
+ session.version,
+ data->action,
+ type->authen_type);
+
+ return(CHOOSE_FAILED);
+}
+
+/* Choose an authentication function for action == LOGIN, service != enable */
+static int
+choose_login(data, type)
+struct authen_data *data;
+struct authen_type *type;
+{
+ char *name = data->NAS_id->username;
+ char *cfg_passwd;
+
+ switch(type->authen_type) {
+ case TAC_PLUS_AUTHEN_TYPE_ASCII:
+ if (session.version != TAC_PLUS_VER_0) {
+ break;
+ }
+
+ if (!name[0]) {
+ /* request a user name if not already supplied */
+ return (CHOOSE_GETUSER);
+ }
+
+ /* Does this user require s/key? */
+ cfg_passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE);
+ if (cfg_passwd && STREQ(cfg_passwd, "skey")) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "%s %s: user %s requires skey",
+ session.peer, session.port, name);
+#ifdef SKEY
+ type->authen_func = skey_fn;
+ strcpy(type->authen_name, "skey_fn");
+ return (CHOOSE_OK);
+#else /* SKEY */
+ report(LOG_ERR,
+ "%s %s: user %s s/key support has not been compiled in",
+ name ? name : "<unknown>",
+ session.peer, session.port);
+ return(CHOOSE_FAILED);
+#endif /* SKEY */
+ }
+
+ /* Not an skey user. Must be none, des, cleartext or file password */
+ type->authen_func = default_fn;
+ strcpy(type->authen_name, "default_fn");
+ return (CHOOSE_OK);
+
+ case TAC_PLUS_AUTHEN_TYPE_ARAP:
+#ifndef ARAP_DES
+ /*
+ * If we have no des code we can't do ARAP via SENDAUTH. We'll
+ * have to do it via SENDPASS. Return a down-rev reply
+ * packet and hope the NAS is smart enough to deal with it.
+ */
+ session.version = TAC_PLUS_VER_0;
+ report(LOG_ERR, "%s %s: user %s DES is unavailable",
+ name ? name : "<unknown>", session.peer, session.port);
+ return (CHOOSE_FAILED);
+#endif /* ARAP_DES */
+ /* FALLTHROUGH */
+
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+#ifndef MSCHAP_DES
+ /*
+ * If we have no des code we can't do MSCHAP via LOGIN. We'll
+ * have to do it via SENDPASS. Return a down-rev reply
+ * packet and hope the NAS is smart enough to deal with it.
+ */
+ session.version = TAC_PLUS_VER_0;
+ report(LOG_ERR, "%s %s: user %s DES is unavailable",
+ name ? name : "<unknown>", session.peer, session.port);
+ return (CHOOSE_FAILED);
+#endif /* MSCHAP_DES */
+ /* FALLTHROUGH */
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ if (session.version == TAC_PLUS_VER_0) {
+ type->authen_func = default_v0_fn;
+ strcpy(type->authen_name, "default_v0_fn");
+ return (CHOOSE_OK);
+ }
+
+ /* Version 1 login/[pap|chap|arap].
+ * The username must in the initial START packet
+ */
+ if (!name[0]) {
+ report(LOG_ERR, "%s %s: No user in START packet for PAP/CHAP/ARAP",
+ session.peer, session.port);
+ return (CHOOSE_FAILED);
+ }
+ type->authen_func = default_fn;
+ strcpy(type->authen_name, "default_fn");
+ return (CHOOSE_OK);
+
+ default:
+ break;
+ }
+
+ /* Illegal value combination */
+ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d",
+ session.peer,
+ session.port,
+ name ? name : "<unknown>",
+ session.version,
+ data->action,
+ type->authen_type);
+ return(CHOOSE_FAILED);
+}
+
+static int
+choose_sendauth(data, type)
+struct authen_data *data;
+struct authen_type *type;
+{
+ char *name = data->NAS_id->username;
+
+ switch (type->authen_type) {
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+#ifndef MSCHAP_DES
+ /*
+ * If we have no des code we can't do MSCHAP via SENDAUTH. We'll
+ * have to do it via SENDPASS. Return a down-rev reply
+ * packet and hope the NAS is smart enough to deal with it.
+ */
+ session.version = TAC_PLUS_VER_0;
+ report(LOG_ERR, "%s %s: user %s DES is unavailable",
+ name ? name : "<unknown>", session.peer, session.port);
+ return (CHOOSE_FAILED);
+#endif /* MSCHAP_DES */
+ /* FALLTHROUGH */
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ /* Must be minor version 1 */
+ if (session.version != TAC_PLUS_VER_1) {
+ break;
+ }
+
+ /* The start packet must contain the username */
+ if (!name[0]) {
+ return (CHOOSE_FAILED);
+ }
+ type->authen_func = sendauth_fn;
+ strcpy(type->authen_name, "sendauth_fn");
+ return (CHOOSE_OK);
+
+ default:
+ break;
+ }
+ /* Illegal value combination */
+ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d",
+ session.peer,
+ session.port,
+ name ? name : "<unknown>",
+ session.version,
+ data->action,
+ type->authen_type);
+
+ return(CHOOSE_FAILED);
+}
+
+/* Compatibility routine for (obsolete) minor version == 0 */
+static int
+choose_sendpass(data, type)
+struct authen_data *data;
+struct authen_type *type;
+{
+ char *name = data->NAS_id->username;
+
+ switch (type->authen_type) {
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+#endif /* MSCHAP */
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ case TAC_PLUS_AUTHEN_TYPE_ARAP:
+ /* must be minor version 0 */
+ if (TAC_PLUS_VER_0 != session.version) {
+ break;
+ }
+
+ /* We need a username */
+ if (!name[0]) {
+ return (CHOOSE_GETUSER);
+ }
+
+ type->authen_func = sendpass_fn;
+ strcpy(type->authen_name, "sendpass_fn");
+ return (CHOOSE_OK);
+
+ default:
+ break;
+ }
+
+ /* Illegal value combination */
+ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d",
+ session.peer,
+ session.port,
+ name ? name : "<unknown>",
+ session.version,
+ data->action,
+ type->authen_type);
+
+ return(CHOOSE_FAILED);
+}
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include <stdio.h>
+#include <errno.h>
+#include "regexp.h"
+
+/*
+ <config> := <decl>*
+
+ <decl> := <top_level_decl> | <user_decl>
+
+ <top_level_decl> := <authen_default> |
+ accounting file = <string>
+ default authorization = permit |
+ key = <string>
+
+ <authen_default> := default authentication = file <filename>
+#if defined(DB)
+ | db <string> )
+#endif
+
+<permission> := permit | deny
+
+ <filename> := <string>
+
+ <password> := <string>
+
+ <user_decl> := user = <string> {
+ [ default service = [ permit | deny ] ]
+ <user_attr>*
+ <svc>*
+ }
+
+ <password_spec> := file <filename> |
+ skey |
+ cleartext <password> |
+ des <password> |
+
+#ifdef USE_PAM
+ pam <pam_service> |
+#endif
+#if defined(DB)
+ db <string>
+#endif
+ nopassword
+
+ <user_attr> := name = <string> |
+ login = <password_spec> |
+ member = <string> |
+ expires = <string> |
+ arap = cleartext <string> |
+ chap = cleartext <string> |
+#ifdef MSCHAP
+ ms-chap = cleartext <string> |
+#endif
+ pap = cleartext <string> |
+ pap = des <string> |
+#ifdef USE_PAM
+ pap = pam <pam_service> |
+#endif
+ opap = cleartext <string> |
+ global = cleartext <string> |
+ msg = <string>
+ before authorization = <string> |
+ after authorization = <string>
+
+ <svc> := <svc_auth> | <cmd_auth>
+
+ <cmd_auth> := cmd = <string> {
+ <cmd-match>*
+ }
+
+ <cmd-match> := <permission> <string>
+
+ <svc_auth> := service = ( exec | arap | slip | ppp protocol = <string> {
+ [ default attribute = permit ]
+ <attr_value_pair>*
+ }
+
+ <attr_value_pair> := [ optional ] <string> = <string>
+
+*/
+
+static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */
+static int sym_pos=0; /* current place in sym_buf */
+static int sym_ch; /* current parse character */
+static int sym_code; /* parser output */
+static int sym_line = 1; /* current line number for parsing */
+static FILE *cf = NULL; /* config file pointer */
+static int sym_error = 0; /* a parsing error has occurred */
+static int no_user_dflt = 0; /* default if user doesn't exist */
+static char *authen_default = NULL; /* top level authentication default */
+static int authen_default_method = 0; /*For method check */
+static char *nopasswd_str = "nopassword";
+
+/* A host definition structure. Currently unused, but when we start
+ configuring host-specific information e.g. per-host keys, this is
+ where it should be kept.
+
+ The first 2 fields (name and hash) are used by the hash table
+ routines to hash this structure into a table. Do not (re)move them */
+
+struct host {
+ char *name; /* host name */
+ void *hash; /* hash table next pointer */
+ int line; /* line number defined on */
+ char *key; /* host spesific key */
+ char *type; /* host type */
+};
+
+/* A user or group definition
+
+ The first 2 fields (name and hash) are used by the hash table
+ routines to hash this structure into a table. Move them at your
+ peril */
+
+struct user {
+ char *name; /* username */
+ void *hash; /* hash table next pointer */
+ int line; /* line number defined on */
+ long flags; /* flags field */
+
+#define FLAG_ISUSER 1 /* this structure represents a user */
+#define FLAG_ISGROUP 2 /* this structure represents a group */
+#define FLAG_SEEN 4 /* for circular definition detection */
+
+ char *full_name; /* users full name */
+ char *login; /* Login password */
+ int nopasswd; /* user requires no password */
+ char *global; /* password to use if none set */
+ char *member; /* group we are a member of */
+ char *expires; /* expiration date */
+ char *arap; /* our arap secret */
+ char *pap; /* our pap secret */
+ char *opap; /* our outbound pap secret */
+ char *chap; /* our chap secret */
+#ifdef MSCHAP
+ char *mschap; /* our mschap secret */
+#endif /* MSCHAP */
+ char *msg; /* a message for this user */
+ char *before_author; /* command to run before authorization */
+ char *after_author; /* command to run after authorization */
+ int svc_dflt; /* default authorization behaviour for svc or
+ * cmd */
+ NODE *svcs; /* pointer to svc nodes */
+#ifdef MAXSESS
+ int maxsess; /* Max sessions/user */
+#endif /* MAXSESS */
+ char *time; /* Timestamp */
+};
+
+typedef struct host HOST;
+typedef struct user USER;
+
+/* Only the first 2 fields (name and hash) are used by the hash table
+ routines to hashh structures into a table.
+*/
+
+union hash {
+ struct user u;
+ struct host h;
+};
+
+typedef union hash HASH;
+
+void *grouptable[HASH_TAB_SIZE];/* Table of group declarations */
+void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
+void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
+
+static void
+ sym_get();
+
+
+#ifdef __STDC__
+#include <stdarg.h> /* ANSI C, variable length args */
+static void
+parse_error(char *fmt,...)
+#else
+#include <varargs.h> /* has 'vararg' definitions */
+/* VARARGS2 */
+static void
+parse_error(fmt, va_alist)
+char *fmt;
+
+va_dcl /* no terminating semi-colon */
+#endif
+{
+ char msg[256]; /* temporary string */
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+
+ report(LOG_ERR, "%s", msg);
+ fprintf(stderr, "Error: %s\n", msg);
+ tac_exit(1);
+}
+
+char *
+cfg_nodestring(type)
+ int type;
+{
+ switch (type) {
+ default:
+ return ("unknown node type");
+ case N_arg:
+ return ("N_arg");
+ case N_optarg:
+ return ("N_optarg");
+ case N_svc:
+ return ("N_svc");
+ case N_svc_exec:
+ return ("N_svc_exec");
+ case N_svc_slip:
+ return ("N_svc_slip");
+ case N_svc_ppp:
+ return ("N_svc_ppp");
+ case N_svc_arap:
+ return ("N_svc_arap");
+ case N_svc_cmd:
+ return ("N_svc_cmd");
+ case N_permit:
+ return ("N_permit");
+ case N_deny:
+ return ("N_deny");
+ }
+}
+
+static void
+free_attrs(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+ switch (node->type) {
+ case N_optarg:
+ case N_arg:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+ node->value);
+ break;
+ default:
+ report(LOG_ERR, "Illegal node type %s for free_attrs",
+ cfg_nodestring(node->type));
+ return;
+ }
+
+ free(node->value);
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void
+free_cmd_matches(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+ node->value);
+
+ free(node->value); /* text */
+ free(node->value1); /* regexp compiled text */
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void
+free_svcs(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+
+ switch (node->type) {
+ case N_svc_cmd:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+ cfg_nodestring(node->type), node->value);
+ free(node->value); /* cmd name */
+ free_cmd_matches(node->value1);
+ next = node->next;
+ free(node);
+ node = next;
+ continue;
+
+ case N_svc:
+ case N_svc_ppp:
+ free(node->value1);
+ /* FALL-THROUGH */
+ case N_svc_exec:
+ case N_svc_arap:
+ case N_svc_slip:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s", cfg_nodestring(node->type));
+ free_attrs(node->value);
+ next = node->next;
+ free(node);
+ node = next;
+ continue;
+
+ default:
+ report(LOG_ERR, "Illegal node type %d for free_svcs", node->type);
+ return;
+ }
+ }
+}
+
+static void
+free_userstruct(user)
+USER *user;
+{
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+ (user->flags & FLAG_ISUSER) ? "user" : "group",
+ user->name);
+
+ if (user->name)
+ free(user->name);
+ if (user->full_name)
+ free(user->full_name);
+ if (user->login)
+ free(user->login);
+ if (user->member)
+ free(user->member);
+ if (user->expires)
+ free(user->expires);
+ if (user->time)
+ free(user->time);
+ if (user->arap)
+ free(user->arap);
+ if (user->chap)
+ free(user->chap);
+#ifdef MSCHAP
+ if (user->mschap)
+ free(user->mschap);
+#endif /* MSCHAP */
+ if (user->pap)
+ free(user->pap);
+ if (user->opap)
+ free(user->opap);
+ if (user->global)
+ free(user->global);
+ if (user->msg)
+ free(user->msg);
+ if (user->before_author)
+ free(user->before_author);
+ if (user->after_author)
+ free(user->after_author);
+ free_svcs(user->svcs);
+}
+
+static void
+free_hoststruct(host)
+HOST *host;
+{
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s",
+ host->name);
+
+ if (host->name)
+ free(host->name);
+
+ if (host->key)
+ free(host->key);
+
+ if (host->type)
+ free(host->type);
+}
+
+/*
+ * Exported routines
+ */
+
+/* Free all allocated structures preparatory to re-reading the config file */
+void
+cfg_clean_config()
+{
+ int i;
+ USER *entry, *next;
+ HOST *host_entry,*hn;
+
+ if (authen_default) {
+ free(authen_default);
+ authen_default = NULL;
+ }
+
+ if (authen_default_method) {
+ authen_default_method = 0;
+ }
+
+ if (session.key) {
+ free(session.key);
+ session.key = NULL;
+ }
+
+ if (session.acctfile) {
+ free(session.acctfile);
+ session.acctfile = NULL;
+ }
+
+ if (session.db_acct) {
+ free(session.db_acct);
+ session.db_acct = NULL;
+ }
+
+ /* clean the hosttable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ host_entry = (HOST *) hosttable[i];
+ while (host_entry) {
+ hn = host_entry->hash;
+ free_hoststruct(host_entry);
+ free(host_entry);
+ host_entry = hn;
+ }
+ hosttable[i] = NULL;
+ }
+
+ /* the grouptable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = (USER *) grouptable[i];
+ while (entry) {
+ next = entry->hash;
+ free_userstruct(entry);
+ free(entry);
+ entry = next;
+ }
+ grouptable[i] = NULL;
+ }
+
+ /* the usertable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = (USER *) usertable[i];
+ while (entry) {
+ next = entry->hash;
+ free_userstruct(entry);
+ free(entry);
+ entry = next;
+ }
+ usertable[i] = NULL;
+ }
+}
+
+static int
+parse_permission()
+{
+ int symbol = sym_code;
+
+ if (sym_code != S_permit && sym_code != S_deny) {
+ parse_error("expecting permit or deny but found '%s' on line %d",
+ sym_buf, sym_line);
+ return (0);
+ }
+ sym_get();
+
+ return (symbol);
+}
+
+static int
+parse(symbol)
+int symbol;
+
+{
+ if (sym_code != symbol) {
+ parse_error("expecting '%s' but found '%s' on line %d",
+ (symbol == S_string ? "string" : codestring(symbol)),
+ sym_buf, sym_line);
+ return (1);
+ }
+ sym_get();
+ return (0);
+}
+
+static int
+parse_opt_svc_default()
+{
+ if (sym_code != S_default) {
+ return (0);
+ }
+
+ parse(S_default);
+ parse(S_svc);
+ parse(S_separator);
+ if (sym_code == S_permit) {
+ parse(S_permit);
+ return (S_permit);
+ }
+ parse(S_deny);
+ return (S_deny);
+}
+
+static int
+parse_opt_attr_default()
+{
+ if (sym_code != S_default)
+ return (S_deny);
+
+ parse(S_default);
+ parse(S_attr);
+ parse(S_separator);
+ parse(S_permit);
+ return (S_permit);
+}
+
+static int parse_user();
+static int parse_host();
+
+static void
+ rch();
+
+/*
+ Parse lines in the config file, creating data structures
+ Return 1 on error, otherwise 0 */
+
+static int
+parse_decls()
+{
+ no_user_dflt = 0; /* default if user doesn't exist */
+
+ sym_code = 0;
+ rch();
+
+ bzero(grouptable, sizeof(grouptable));
+ bzero(usertable, sizeof(usertable));
+ bzero(hosttable, sizeof(hosttable));
+
+ sym_get();
+
+ /* Top level of parser */
+ while (1) {
+
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+
+ case S_accounting:
+ sym_get();
+ parse(S_file);
+ parse(S_separator);
+ if (session.acctfile)
+ free(session.acctfile);
+ session.acctfile = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+#ifdef DB
+ case S_db_accounting:
+ sym_get();
+ parse(S_separator);
+ if (session.db_acct)
+ free(session.db_acct);
+ session.db_acct = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+#endif
+
+ case S_default:
+ sym_get();
+ switch (sym_code) {
+ default:
+ parse_error(
+ "Expecting default authorization/authentication on lines %d",
+ sym_line);
+ return (1);
+
+ case S_authentication:
+ if (authen_default) {
+ parse_error(
+ "Multiply defined authentication default on line %d",
+ sym_line);
+ return (1);
+ }
+ parse(S_authentication);
+ parse(S_separator);
+
+ switch(sym_code) {
+
+ case S_file:
+#ifdef DB
+ case S_db:
+#endif
+#ifdef USE_LDAP
+ case S_ldap;
+#endif
+#ifdef USE_PAM
+ case S_pam:
+#endif
+ authen_default_method = sym_code;
+ break;
+
+ default:
+ parse_error("expecting default_method keyword after 'default authentication = ' on line %d",sym_line);
+ return (1);
+ }
+ sym_get();
+
+ authen_default = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_authorization:
+ parse(S_authorization);
+ parse(S_separator);
+ parse(S_permit);
+ no_user_dflt = S_permit;
+ report(LOG_INFO,
+ "default authorization = permit is now deprecated. Please use user = DEFAULT instead");
+ continue;
+ }
+
+ case S_key:
+ /* Process a key declaration. */
+ sym_get();
+ parse(S_separator);
+ if (session.key) {
+ parse_error("multiply defined key on lines %d and %d",
+ session.keyline, sym_line);
+ return (1);
+ }
+ session.key = tac_strdup(sym_buf);
+ session.keyline = sym_line;
+ sym_get();
+ continue;
+
+ case S_host:
+ parse_host();
+ continue;
+
+ case S_user:
+ case S_group:
+ parse_user();
+ continue;
+
+ /* case S_host: parse_host(); continue; */
+
+ default:
+ parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
+ return (1);
+ }
+ }
+}
+
+static NODE *parse_svcs();
+
+/* Assign a value to a field. Issue an error message and return 1 if
+ it's already been assigned. This is a macro because I was sick of
+ repeating the same code fragment over and over */
+
+#define ASSIGN(field) \
+sym_get(); parse(S_separator); if (field) { \
+ parse_error("Duplicate value for %s %s and %s on line %d", \
+ codestring(sym_code), field, sym_buf, sym_line); \
+ tac_exit(1); \
+ } \
+ field = tac_strdup(sym_buf);
+
+static int
+parse_host()
+{
+ HOST *h;
+ HOST *host = (HOST *) tac_malloc(sizeof(HOST));
+ int save_sym;
+ char buf[MAX_INPUT_LINE_LEN];
+
+ bzero(host, sizeof(HOST));
+
+ sym_get();
+ parse(S_separator);
+ host->name = tac_strdup(sym_buf);
+ host->line = sym_line;
+
+ h = hash_add_entry(hosttable, (void *) host);
+
+ if (h) {
+ parse_error("multiply defined %s on lines %d and %d",
+ host->name, h->line, sym_line);
+ return (1);
+ }
+
+ sym_get();
+ parse(S_openbra);
+
+ while (1) {
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+ case S_key:
+ ASSIGN(host->key);
+ sym_get();
+ continue;
+ case S_type:
+ ASSIGN(host->type);
+ sym_get();
+ continue;
+
+ case S_closebra:
+ parse(S_closebra);
+ return (0);
+
+ default:
+ parse_error("Unrecognised keyword %s for host %s on line %d",
+ sym_buf, host->name,sym_line);
+
+ return (0);
+ }
+ } /* while */
+} /* finish parse_host */
+
+
+static int
+parse_user()
+{
+ USER *n;
+ int isuser;
+ USER *user = (USER *) tac_malloc(sizeof(USER));
+ int save_sym;
+ char **fieldp;
+ char buf[MAX_INPUT_LINE_LEN];
+
+ bzero(user, sizeof(USER));
+
+ isuser = (sym_code == S_user);
+
+ sym_get();
+ parse(S_separator);
+ user->name = tac_strdup(sym_buf);
+ user->line = sym_line;
+
+ if (isuser) {
+ user->flags |= FLAG_ISUSER;
+ n = hash_add_entry(usertable, (void *) user);
+ } else {
+ user->flags |= FLAG_ISGROUP;
+ n = hash_add_entry(grouptable, (void *) user);
+ }
+
+ if (n) {
+ parse_error("multiply defined %s %s on lines %d and %d",
+ isuser ? "user" : "group",
+ user->name, n->line, sym_line);
+ return (1);
+ }
+ sym_get();
+ parse(S_openbra);
+
+ /* Is the default deny for svcs or cmds to be overridden? */
+ user->svc_dflt = parse_opt_svc_default();
+
+ while (1) {
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+
+ case S_time:
+ ASSIGN(user->time);
+ sym_get();
+ continue;
+
+ case S_before:
+ sym_get();
+ parse(S_authorization);
+ if (user->before_author)
+ free(user->before_author);
+ user->before_author = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_after:
+ sym_get();
+ parse(S_authorization);
+ if (user->after_author)
+ free(user->after_author);
+ user->after_author = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_svc:
+ case S_cmd:
+
+ if (user->svcs) {
+ /*
+ * Already parsed some services/commands. Thanks to Gabor Kiss
+ * who found this bug.
+ */
+ NODE *p;
+ for (p=user->svcs; p->next; p=p->next)
+ /* NULL STMT */;
+ p->next = parse_svcs();
+ } else {
+ user->svcs = parse_svcs();
+ }
+ continue;
+
+ case S_login:
+ if (user->login) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(sym_code), user->login,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+ parse(S_separator);
+ switch(sym_code) {
+
+ case S_skey:
+ user->login = tac_strdup(sym_buf);
+ break;
+
+ case S_nopasswd:
+ /* set to dummy string, so that we detect a duplicate
+ * password definition attempt
+ */
+ user->login = tac_strdup(nopasswd_str);
+ user->nopasswd = 1;
+ break;
+
+ case S_file:
+ case S_cleartext:
+ case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /* USE_PAM */
+#ifdef DB
+ case S_db:
+#endif /* USE DB */
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+ user->login = tac_strdup(buf);
+ break;
+
+ default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'file', 'cleartext', 'pam'.'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
+ sym_line);
+#else
+ parse_error(
+ "expecting 'file', 'cleartext', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
+ sym_line);
+#endif /* USE_PAM */
+ }
+ sym_get();
+ continue;
+
+ case S_pap:
+ if (user->pap) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(sym_code), user->pap,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+ parse(S_separator);
+ switch(sym_code) {
+
+ case S_cleartext:
+ case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /*USE_PAM */
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+ user->pap = tac_strdup(buf);
+ break;
+
+ sprintf(buf, "%s ", sym_buf);
+ user->pap = tac_strdup(buf);
+ break;
+
+ default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'cleartext', 'pam', or 'des' keyword after 'pap =' on line %d",
+ sym_line);
+#else
+ parse_error(
+ "expecting 'cleartext', or 'des' keyword after 'pap =' on line %d",
+ sym_line);
+#endif /*USE_PAM */
+ }
+ sym_get();
+ continue;
+
+ case S_name:
+ ASSIGN(user->full_name);
+ sym_get();
+ continue;
+
+ case S_member:
+ ASSIGN(user->member);
+ sym_get();
+ continue;
+
+
+ case S_expires:
+ ASSIGN(user->expires);
+ sym_get();
+ continue;
+
+ case S_message:
+ ASSIGN(user->msg);
+ sym_get();
+ continue;
+
+ case S_arap:
+ case S_chap:
+#ifdef MSCHAP
+ case S_mschap:
+#endif /* MSCHAP */
+ case S_opap:
+ case S_global:
+ save_sym = sym_code;
+ sym_get();
+ parse(S_separator);
+ sprintf(buf, "%s ", sym_buf);
+ parse(S_cleartext);
+ strcat(buf, sym_buf);
+
+ if (save_sym == S_arap)
+ fieldp = &user->arap;
+ if (save_sym == S_chap)
+ fieldp = &user->chap;
+#ifdef MSCHAP
+ if (save_sym == S_mschap)
+ fieldp = &user->mschap;
+#endif /* MSCHAP */
+ if (save_sym == S_pap)
+ fieldp = &user->pap;
+ if (save_sym == S_opap)
+ fieldp = &user->opap;
+ if (save_sym == S_global)
+ fieldp = &user->global;
+
+ if (*fieldp) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(save_sym), *fieldp, sym_buf, sym_line);
+ tac_exit(1);
+ }
+ *fieldp = tac_strdup(buf);
+ sym_get();
+ continue;
+
+ case S_closebra:
+ parse(S_closebra);
+ return (0);
+
+#ifdef MAXSESS
+ case S_maxsess:
+ sym_get();
+ parse(S_separator);
+ if (sscanf(sym_buf, "%d", &user->maxsess) != 1) {
+ parse_error("expecting integer, found '%s' on line %d",
+ sym_buf, sym_line);
+ }
+ sym_get();
+ continue;
+#endif /* MAXSESS */
+
+ default:
+ if (STREQ(sym_buf, "password")) {
+ fprintf(stderr,
+ "\npassword = <string> is obsolete. Use login = des <string>\n");
+ }
+ parse_error("Unrecognised keyword %s for user on line %d",
+ sym_buf, sym_line);
+
+ return (0);
+ }
+ }
+}
+
+static NODE *parse_attrs();
+static NODE *parse_cmd_matches();
+
+static NODE *
+parse_svcs()
+{
+ NODE *result;
+
+ switch (sym_code) {
+ default:
+ return (NULL);
+ case S_svc:
+ case S_cmd:
+ break;
+ }
+
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ /* cmd declaration */
+ if (sym_code == S_cmd) {
+ parse(S_cmd);
+ parse(S_separator);
+ result->value = tac_strdup(sym_buf);
+
+ sym_get();
+ parse(S_openbra);
+
+ result->value1 = parse_cmd_matches();
+ result->type = N_svc_cmd;
+
+ parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+ }
+
+ /* svc declaration */
+ parse(S_svc);
+ parse(S_separator);
+ switch (sym_code) {
+ default:
+ parse_error("expecting service type but found %s on line %d",
+ sym_buf, sym_line);
+ return (NULL);
+
+ case S_string:
+ result->type = N_svc;
+ /* should perhaps check that this is an allowable service name */
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ case S_exec:
+ result->type = N_svc_exec;
+ break;
+ case S_arap:
+ result->type = N_svc_arap;
+ break;
+ case S_slip:
+ result->type = N_svc_slip;
+ break;
+ case S_ppp:
+ result->type = N_svc_ppp;
+ parse(S_ppp);
+ parse(S_protocol);
+ parse(S_separator);
+ /* Should perhaps check that this is a known PPP protocol name */
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ }
+ sym_get();
+ parse(S_openbra);
+ result->dflt = parse_opt_attr_default();
+ result->value = parse_attrs();
+ parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+}
+
+/* <cmd-match> := <permission> <string> */
+
+static NODE *
+parse_cmd_matches()
+{
+ NODE *result;
+
+ if (sym_code != S_permit && sym_code != S_deny) {
+ return (NULL);
+ }
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ result->type = (parse_permission() == S_permit) ? N_permit : N_deny;
+ result->value = tac_strdup(sym_buf);
+
+ result->value1 = (void *) regcomp(result->value);
+ if (!result->value1) {
+ report(LOG_ERR, "in regular expression %s on line %d",
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+
+ result->next = parse_cmd_matches();
+
+ return (result);
+}
+
+static NODE *
+parse_attrs()
+{
+ NODE *result;
+ char buf[MAX_INPUT_LINE_LEN];
+ int optional = 0;
+
+ if (sym_code == S_closebra) {
+ return (NULL);
+ }
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ if (sym_code == S_optional) {
+ optional++;
+ sym_get();
+ }
+ result->type = optional ? N_optarg : N_arg;
+
+ strcpy(buf, sym_buf);
+ parse(S_string);
+ strcat(buf, sym_buf);
+ parse(S_separator);
+ strcat(buf, sym_buf);
+ parse(S_string);
+
+ result->value = tac_strdup(buf);
+ result->next = parse_attrs();
+ return (result);
+}
+
+
+static void
+ getsym();
+
+static void
+sym_get()
+{
+ getsym();
+
+ if (debug & DEBUG_PARSE_FLAG) {
+ report(LOG_DEBUG, "line=%d sym=%s code=%d buf='%s'",
+ sym_line, codestring(sym_code), sym_code, sym_buf);
+ }
+}
+
+static char *
+sym_buf_add(c)
+char c;
+{
+ if (sym_pos >= MAX_INPUT_LINE_LEN) {
+ sym_buf[MAX_INPUT_LINE_LEN-1] = '\0';
+ if (debug & DEBUG_PARSE_FLAG) {
+ report(LOG_DEBUG, "line too long: line=%d sym=%s code=%d buf='%s'",
+ sym_line, codestring(sym_code), sym_code, sym_buf);
+ }
+ return(NULL);
+ }
+
+ sym_buf[sym_pos++] = c;
+ return(sym_buf);
+}
+
+static void
+getsym()
+{
+
+next:
+ switch (sym_ch) {
+
+ case EOF:
+ sym_code = S_eof;
+ return;
+
+ case '\n':
+ sym_line++;
+ rch();
+ goto next;
+
+ case '\t':
+ case ' ':
+ while (sym_ch == ' ' || sym_ch == '\t')
+ rch();
+ goto next;
+
+ case '=':
+ strcpy(sym_buf, "=");
+ sym_code = S_separator;
+ rch();
+ return;
+
+ case '{':
+ strcpy(sym_buf, "{");
+ sym_code = S_openbra;
+ rch();
+ return;
+
+ case '}':
+ strcpy(sym_buf, "}");
+ sym_code = S_closebra;
+ rch();
+ return;
+
+ case '#':
+ while ((sym_ch != '\n') && (sym_ch != EOF))
+ rch();
+ goto next;
+
+ case '"':
+ rch();
+ sym_pos = 0;
+ while (1) {
+
+ if (sym_ch == '"') {
+ break;
+ }
+
+ /* backslash-double-quote is supported inside strings */
+ /* also allow \n */
+ if (sym_ch == '\\') {
+ rch();
+ switch (sym_ch) {
+ case 'n':
+ /* preserve the slash for \n */
+ if (!sym_buf_add('\\')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+
+ /* fall through */
+ case '"':
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ continue;
+ default:
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ }
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ }
+ rch();
+
+ if (!sym_buf_add('\0')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ sym_code = S_string;
+ return;
+
+ default:
+ sym_pos = 0;
+ while (sym_ch != '\t' && sym_ch != ' ' && sym_ch != '='
+ && sym_ch != '\n') {
+
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ }
+
+ if (!sym_buf_add('\0')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ sym_code = keycode(sym_buf);
+ if (sym_code == S_unknown)
+ sym_code = S_string;
+ return;
+ }
+}
+
+static void
+rch()
+{
+ if (sym_error) {
+ sym_ch = EOF;
+ return;
+ }
+ sym_ch = getc(cf);
+
+ if (parse_only && sym_ch != EOF)
+ fprintf(stderr, "%c", sym_ch);
+}
+
+
+/* For a user or group, find the value of a field. Does not recurse. */
+VALUE
+get_value(user, field)
+USER *user;
+int field;
+{
+ VALUE v;
+
+ v.intval = 0;
+
+ if (!user) {
+ parse_error("get_value: illegal user");
+ return (v);
+ }
+ switch (field) {
+
+ case S_name:
+ v.pval = user->name;
+ break;
+
+ case S_login:
+ v.pval = user->login;
+ break;
+
+ case S_global:
+ v.pval = user->global;
+ break;
+
+ case S_member:
+ v.pval = user->member;
+ break;
+
+ case S_expires:
+ v.pval = user->expires;
+ break;
+
+ case S_arap:
+ v.pval = user->arap;
+ break;
+
+ case S_chap:
+ v.pval = user->chap;
+ break;
+
+#ifdef MSCHAP
+ case S_mschap:
+ v.pval = user->mschap;
+ break;
+#endif /* MSCHAP */
+
+ case S_pap:
+ v.pval = user->pap;
+ break;
+
+ case S_opap:
+ v.pval = user->opap;
+ break;
+
+ case S_message:
+ v.pval = user->msg;
+ break;
+
+ case S_svc:
+ v.pval = user->svcs;
+ break;
+
+ case S_before:
+ v.pval = user->before_author;
+ break;
+
+ case S_after:
+ v.pval = user->after_author;
+ break;
+
+ case S_svc_dflt:
+ v.intval = user->svc_dflt;
+ break;
+
+#ifdef MAXSESS
+ case S_maxsess:
+ v.intval = user->maxsess;
+ break;
+#endif
+
+ case S_nopasswd:
+ v.intval = user->nopasswd;
+ break;
+
+ case S_time:
+ v.pval = user->time;
+ break;
+
+ default:
+ report(LOG_ERR, "get_value: unknown field %d", field);
+ break;
+ }
+ return (v);
+}
+
+/* For host , find value of field. Doesn't recursive */
+VALUE
+get_hvalue(host, field)
+HOST *host;
+int field;
+{
+ VALUE v;
+ v.intval = 0;
+ if(!host) {
+ parse_error("get_hvalue: illegal host");
+ return (v);
+ }
+ switch (field) {
+ case S_name:
+ v.pval = host->name;
+ break;
+
+ case S_key:
+ v.pval = host->key;
+ break;
+
+ default:
+ report(LOG_ERR, "get_value: unknown field %d", field);
+ break;
+ }
+ return (v);
+}
+
+
+/* For each user, check she doesn't circularly reference a
+ group. Return 1 if it does */
+static int
+circularity_check()
+{
+ USER *user, *entry, *group;
+ USER **users = (USER **) hash_get_entries(usertable);
+ USER **groups = (USER **) hash_get_entries(grouptable);
+ USER **p, **q;
+
+ /* users */
+ for (p = users; *p; p++) {
+ user = *p;
+
+ if (debug & DEBUG_PARSE_FLAG)
+ report(LOG_DEBUG, "circularity_check: user=%s", user->name);
+
+ /* Initialise all groups "seen" flags to zero */
+ for (q = groups; *q; q++) {
+ group = *q;
+ group->flags &= ~FLAG_SEEN;
+ }
+
+ entry = user;
+
+ while (entry) {
+ /* check groups we are a member of */
+ char *groupname = entry->member;
+
+ if (debug & DEBUG_PARSE_FLAG)
+ report(LOG_DEBUG, "\tmember of group %s",
+ groupname ? groupname : "<none>");
+
+
+ /* if not a member of any groups, go on to next user */
+ if (!groupname)
+ break;
+
+ group = (USER *) hash_lookup(grouptable, groupname);
+ if (!group) {
+ report(LOG_ERR, "%s=%s, group %s does not exist",
+ (entry->flags & FLAG_ISUSER) ? "user" : "group",
+ entry->name, groupname);
+ free(users);
+ free(groups);
+ return (1);
+ }
+ if (group->flags & FLAG_SEEN) {
+ report(LOG_ERR, "recursively defined groups");
+
+ /* print all seen "groups" */
+ for (q = groups; *q; q++) {
+ group = *q;
+ if (group->flags & FLAG_SEEN)
+ report(LOG_ERR, "%s", group->name);
+ }
+ free(users);
+ free(groups);
+ return (1);
+ }
+ group->flags |= FLAG_SEEN; /* mark group as seen */
+ entry = group;
+ }
+ }
+ free(users);
+ free(groups);
+ return (0);
+}
+
+
+/* Return a value for a group or user (isuser says if
+ this name is a group or a user name).
+
+ If no value exists, and recurse is true, also check groups we are a
+ member of, recursively.
+
+ Returns void * because it can return a string or a node pointer
+ (should really return a union pointer).
+*/
+static VALUE
+cfg_get_value(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ USER *user, *group;
+ VALUE value;
+
+ value.pval = NULL;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: name=%s isuser=%d attr=%s rec=%d",
+ name, isuser, codestring(attr), recurse);
+
+ /* find the user/group entry */
+
+ user = (USER *) hash_lookup(isuser ? usertable : grouptable, name);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name);
+ return (value);
+ }
+
+ /* found the entry. Lookup value from attr=value */
+ value = get_value(user, attr);
+
+ if (value.pval || !recurse) {
+ return (value);
+ }
+ /* no value. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: recurse group = %s",
+ group->name);
+
+ value = get_value(group, attr);
+
+ if (value.pval) {
+ return (value);
+ }
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ /* no value for this user or her containing groups */
+ value.pval = NULL;
+ return (value);
+}
+
+
+/* Wrappers for cfg_get_value */
+int
+cfg_get_intvalue(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ int val = cfg_get_value(name, isuser, attr, recurse).intval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val);
+ return(val);
+}
+
+char *
+cfg_get_pvalue(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ char *p = cfg_get_value(name, isuser, attr, recurse).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_pvalue: returns %s",
+ p ? p : "NULL");
+ return(p);
+}
+
+/* For getting host values */
+static VALUE
+cfg_get_hvalue(name, attr)
+char *name;
+int attr;
+{
+ HOST *host;
+ VALUE value;
+
+ value.pval = NULL;
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_hvalue: name=%s attr=%s ",
+ name, codestring(attr));
+
+ /* find the host entry in hash table */
+
+ host = (HOST *) hash_lookup( hosttable, name);
+
+ if (!host) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_hvalue: no host named %s", name);
+ return (value);
+ }
+
+ /* found the entry. Lookup value from attr=value */
+ value = get_hvalue(host, attr);
+
+ if (value.pval) {
+ return (value);
+ }
+ /* No any value for this host */
+ value.pval = NULL;
+ return (value);
+}
+
+/* Wrappers for cfg_get_hvalue */
+char *
+cfg_get_phvalue(name, attr)
+char *name;
+int attr;
+{
+ char *p = cfg_get_hvalue(name, attr).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_phvalue: returns %s",
+ p ? p : "NULL");
+ return(p);
+}
+
+/*
+ Read the config file and do some basic sanity checking on
+ it. Return 1 if we find any errors. */
+
+cfg_read_config(cfile)
+char *cfile;
+{
+ sym_line = 1;
+
+ if ((cf = fopen(cfile, "r")) == NULL) {
+ report(LOG_ERR, "read_config: fopen() error for file %s %s, exiting",
+ cfile, sys_errlist[errno]);
+ return (1);
+ }
+ if (parse_decls() || sym_error) {
+ fclose(cf);
+ return (1);
+ }
+
+ if (circularity_check()) {
+ fclose(cf);
+ return (1);
+ }
+
+ fclose(cf);
+ return (0);
+}
+
+/* return 1 if user exists, 0 otherwise */
+int
+cfg_user_exists(username)
+char *username;
+{
+ USER *user = (USER *) hash_lookup(usertable, username);
+
+ return (user != NULL);
+}
+
+/* return expiry string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_expires(username, recurse)
+char *username;
+
+{
+ return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse));
+}
+
+/* return time string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_timestamp(username, recurse)
+char *username;
+{
+ return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse));
+}
+
+
+/* return password string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_login_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse));
+}
+
+/* return value of the nopasswd field. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+int
+cfg_get_user_nopasswd(user, recurse)
+ char *user;
+{
+ return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse));
+}
+
+/* return user's secret. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_arap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse));
+}
+
+char *
+cfg_get_chap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse));
+}
+
+#ifdef MSCHAP
+char *
+cfg_get_mschap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse));
+}
+#endif /* MSCHAP */
+
+char *
+cfg_get_pap_secret(user, recurse)
+char *user;
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse));
+}
+
+char *
+cfg_get_opap_secret(user, recurse)
+char *user;
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse));
+}
+
+/* return the global password for the user (or the group, etc.) */
+
+char *
+cfg_get_global_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse));
+}
+
+#ifdef USE_PAM
+/* Return a pointer to a node representing a PAM Service name */
+char *
+cfg_get_pam_service(user,recurse)
+char *user;
+
+{
+ char *cfg_passwd;
+ char *p;
+
+cfg_passwd = cfg_get_pap_secret(user, recurse);
+
+if (!cfg_passwd) {
+ cfg_passwd = cfg_get_global_secret(user, recurse);
+}
+
+if (!cfg_passwd && !cfg_user_exists(user)) {
+ cfg_passwd = cfg_get_authen_default();
+ switch (cfg_get_authen_default_method()) {
+ case (S_pam):
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd);
+ return(cfg_passwd);
+ break;
+ default:
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I havent find any PAM Service!!");
+ return(NULL);/* Haven't any PAM Service!! */
+ }
+}
+
+p=tac_find_substring("pam ", cfg_passwd);
+
+if(p) { /* We find PAM services */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I get PAM sevice:%s",p);
+return (p);
+}
+
+if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "No any PAM Sevice");
+
+return(NULL);
+}
+
+#endif /* For PAM */
+
+
+
+/* Return a pointer to a node representing a given service
+ authorization, taking care of recursion issues correctly. Protocol
+ is only read if the type is N_svc_ppp. svcname is only read if type
+ is N_svc.
+*/
+
+NODE *
+cfg_get_svc_node(username, type, protocol, svcname, recurse)
+char *username;
+int type;
+char *protocol, *svcname;
+int recurse;
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d",
+ username,
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "",
+ recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, username);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username);
+ return (NULL);
+ }
+
+ /* found the user entry. Find svc node */
+ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != type)
+ continue;
+
+ if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+ continue;
+ }
+
+ if (type == N_svc && !STREQ(svc->value1, svcname)) {
+ continue;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: found %s proto=%s svcname=%s",
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "");
+
+ return(svc);
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+ return (NULL);
+ }
+
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s",
+ group->name);
+
+ for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != type)
+ continue;
+
+ if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+ continue;
+ }
+
+ if (type == N_svc && !STREQ(svc->value1, svcname)) {
+ continue;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: found %s proto=%s svcname=%s",
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "");
+
+ return(svc);
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+
+ /* no matching svc node for this user or her containing groups */
+ return (NULL);
+}
+
+/* Return a pointer to the node representing a set of command regexp
+ matches for a user and command, handling recursion issues correctly */
+NODE *
+cfg_get_cmd_node(name, cmdname, recurse)
+char *name, *cmdname;
+int recurse;
+
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: name=%s cmdname=%s rec=%d",
+ name, cmdname, recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, name);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name);
+ return (NULL);
+ }
+ /* found the user entry. Find svc node */
+ svc = (NODE *) get_value(user, S_svc).pval;
+
+ while (svc) {
+ if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s %s node",
+ cmdname, cfg_nodestring(svc->type));
+ return (svc);
+ }
+ svc = svc->next;
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
+ return (NULL);
+ }
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: recurse group = %s",
+ group->name);
+
+ svc = get_value(group, S_svc).pval;
+
+ while (svc) {
+ if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s node %s",
+ cmdname, cfg_nodestring(svc->type));
+ return (svc);
+ }
+ svc = svc->next;
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
+
+ /* no matching cmd node for this user or her containing groups */
+ return (NULL);
+}
+
+/* Return an array of character strings representing configured AV
+ * pairs, given a username and a service node.
+ *
+ * In the AV strings returned, manipulate the separator character to
+ * indicate which args are optional and which are mandatory.
+ *
+ * Lastly, indicate what default permission was configured by setting
+ * denyp */
+
+char **
+cfg_get_svc_attrs(svcnode, denyp)
+NODE *svcnode;
+int *denyp;
+{
+ int i;
+ NODE *node;
+ char **args;
+
+ *denyp = 1;
+
+ if (!svcnode)
+ return (NULL);
+
+ *denyp = (svcnode->dflt == S_deny);
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next)
+ i++;
+
+ args = (char **) tac_malloc(sizeof(char *) * (i + 1));
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next) {
+ char *arg = tac_strdup(node->value);
+ char *p = index(arg, '=');
+
+ if (p && node->type == N_optarg)
+ *p = '*';
+ args[i++] = arg;
+ }
+ args[i] = NULL;
+ return (args);
+}
+
+
+int
+cfg_user_svc_default_is_permit(user)
+char *user;
+
+{
+ int permit = cfg_get_intvalue(user, TAC_IS_USER, S_svc_dflt,
+ TAC_PLUS_RECURSE);
+
+ switch (permit) {
+ default: /* default is deny */
+ case S_deny:
+ return (0);
+ case S_permit:
+ return (1);
+ }
+}
+
+int
+cfg_no_user_permitted()
+{
+ if (no_user_dflt == S_permit)
+ return (1);
+ return (0);
+}
+
+
+char *
+cfg_get_authen_default()
+{
+ return (authen_default);
+}
+
+/* For describe authentication method(pam,file,db..etc) */
+int
+cfg_get_authen_default_method()
+{
+ return (authen_default_method);
+}
+
+
+/* Return 1 if this user has any ppp services configured. Used for
+ authorizing ppp/lcp requests */
+int
+cfg_ppp_is_configured(username, recurse)
+ char *username;
+ int recurse;
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d",
+ username, recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, username);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s",
+ username);
+ return (0);
+ }
+
+ /* found the user entry. Find svc node */
+ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != N_svc_ppp)
+ continue;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+ svc->value1);
+
+ return(1);
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+ return (0);
+ }
+
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: recurse group = %s",
+ group->name);
+
+ for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != N_svc_ppp)
+ continue;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+ svc->value1);
+
+ return(1);
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+
+ /* no PPP svc nodes for this user or her containing groups */
+ return (0);
+}
+
+/* For getting host key */
+char *
+cfg_get_host_key(host)
+char *host;
+{
+ return (cfg_get_phvalue(host, S_key));
+}
+
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
+# Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+# Please send patches to <autoconf-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# Use $HOST_CC if defined. $CC may point to a cross-compiler
+if test x"$CC_FOR_BUILD" = x; then
+ if test x"$HOST_CC" != x; then
+ CC_FOR_BUILD="$HOST_CC"
+ else
+ if test x"$CC" != x; then
+ CC_FOR_BUILD="$CC"
+ else
+ CC_FOR_BUILD=cc
+ fi
+ fi
+fi
+
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >$dummy.s
+ .globl main
+ .ent main
+main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ arm32:NetBSD:*:*)
+ echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ SR2?01:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:NetBSD:*:*)
+ echo powerpc-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ sed 's/^ //' << EOF >$dummy.c
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ rm -f $dummy.c $dummy
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i?86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ hppa*:OpenBSD:*:*)
+ echo hppa-unknown-openbsd
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alpha-cray-unicosmk${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F300:UNIX_System_V:*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ F301:UNIX_System_V:*:*)
+ echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ if test -x /usr/bin/objformat; then
+ if test "elf" = "`/usr/bin/objformat`"; then
+ echo ${UNAME_MACHINE}-unknown-freebsdelf`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+ exit 0
+ fi
+ fi
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i386-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ ld_help_string=`cd /; ld --help 2>&1`
+ ld_supported_emulations=`echo $ld_help_string \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ *ia64)
+ echo "${UNAME_MACHINE}-unknown-linux"
+ exit 0
+ ;;
+ i?86linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0
+ ;;
+ i?86coff)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0
+ ;;
+ sparclinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ armlinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ elf32arm*)
+ echo "${UNAME_MACHINE}-unknown-linux-gnu"
+ exit 0
+ ;;
+ armelf_linux*)
+ echo "${UNAME_MACHINE}-unknown-linux-gnu"
+ exit 0
+ ;;
+ m68klinux)
+ echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+ exit 0
+ ;;
+ elf32ppc)
+ # Determine Lib Version
+ cat >$dummy.c <<EOF
+#include <features.h>
+#if defined(__GLIBC__)
+extern char __libc_version[];
+extern char __libc_release[];
+#endif
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#if defined(__GLIBC__)
+ printf("%s %s\n", __libc_version, __libc_release);
+#else
+ printf("unkown\n");
+#endif
+ return 0;
+}
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy | grep 1\.99 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.c $dummy
+ echo powerpc-unknown-linux-gnu${LIBC}
+ exit 0
+ ;;
+ esac
+
+ if test "${UNAME_MACHINE}" = "alpha" ; then
+ sed 's/^ //' <<EOF >$dummy.s
+ .globl main
+ .ent main
+ main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+
+ objdump --private-headers $dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+ elif test "${UNAME_MACHINE}" = "mips" ; then
+ cat >$dummy.c <<EOF
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ else
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+ test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+ case "${UNAME_MACHINE}" in
+ i?86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i?86:*:5:7*)
+ # Fixed at (any) Pentium or better
+ UNAME_MACHINE=i586
+ if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then
+ echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:*:6*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-qnx-qnx${UNAME_VERSION}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
--- /dev/null
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function. */
+#undef HAVE_VPRINTF
+
+/* Define if you have the wait3 system call. */
+#undef HAVE_WAIT3
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define if the `setpgrp' function takes no argument. */
+#undef SETPGRP_VOID
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if you have the regcomp function. */
+#undef HAVE_REGCOMP
+
+/* Define if you have the select function. */
+#undef HAVE_SELECT
+
+/* Define if you have the socket function. */
+#undef HAVE_SOCKET
+
+/* Define if you have the strcspn function. */
+#undef HAVE_STRCSPN
+
+/* Define if you have the strdup function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the strstr function. */
+#undef HAVE_STRSTR
+
+/* Define if you have the strtol function. */
+#undef HAVE_STRTOL
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the c library (-lc). */
+#undef HAVE_LIBC
+
+/* Define if you have the crypt library (-lcrypt). */
+#undef HAVE_LIBCRYPT
+
+/* Define if you have the dl library (-ldl). */
+#undef HAVE_LIBDL
+
+/* Define if you have the og library (-log). */
+#undef HAVE_LIBOG
+
+/* Define if you have the pam library (-lpam). */
+#undef HAVE_LIBPAM
+
+/* Define this if you have shadow passwords in /etc/passwd and
+ * /etc/shadow. Note that you usually need to be root to read
+ * /etc/shadow */
+#undef SHADOW_PASSWORDS
+
--- /dev/null
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1991, 92-97, 1998, 1999 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+ | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 \
+ | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+ | alpha | alphaev[4-7] | alphaev56 | alphapca5[67] \
+ | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+ | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \
+ | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+ | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+ | mips64vr5000 | miprs64vr5000el | mcore \
+ | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \
+ | thumb | d10v | fr30)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[34567]86)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ # FIXME: clean up the formatting here.
+ vax-* | tahoe-* | i[34567]86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+ | xmp-* | ymp-* \
+ | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \
+ | alpha-* | alphaev[4-7]-* | alphaev56-* | alphapca5[67]-* \
+ | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+ | clipper-* | orion-* \
+ | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+ | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+ | mipstx39-* | mipstx39el-* | mcore-* \
+ | f301-* | armv*-* | t3e-* \
+ | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+ | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* )
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-cbm
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[34567]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i[34567]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i[34567]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i[34567]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ i386-go32 | go32)
+ basic_machine=i386-unknown
+ os=-go32
+ ;;
+ i386-mingw32 | mingw32)
+ basic_machine=i386-unknown
+ os=-mingw32
+ ;;
+ i386-qnx | qnx)
+ basic_machine=i386-qnx
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-unknown
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc | sparcv9)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -opened* | -openstep* | -oskit*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -qnx)
+ os=-qnx4
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -*MiNT)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f301-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -*MiNT)
+ vendor=atari
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-pam With PAM Support "
+ac_help="$ac_help
+ --with-ldap With LDAP Support "
+ac_help="$ac_help
+ --with-db For DB Support "
+ac_help="$ac_help
+ --with-mysql With MySQL Support "
+ac_help="$ac_help
+ --with-mysql-prefix=PREFIX Mysql prefix [default=/usr]"
+ac_help="$ac_help
+ --with-pgsql With PgSQL Support "
+ac_help="$ac_help
+ --with-pgsql-prefix=PREFIX PgSQL prefix [default=/usr]"
+ac_help="$ac_help
+ --with-tacuid=ID If you like to run tac_plus other than root user (no default value) "
+ac_help="$ac_help
+ --with-tacgid=GID If you like to run tac_plus other than root group(no default value) "
+ac_help="$ac_help
+ --enable-maxsess Enable maxsess feature "
+ac_help="$ac_help
+ --with-tacplus_pid=PREFIX Tac_plus pid file location [default=/var/run] "
+ac_help="$ac_help
+ --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support."
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:551: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:580: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:610: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:661: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:693: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 704 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:709: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:735: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:740: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:749: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:768: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:847: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:868: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:886: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+
+case $host_os in
+ *linux-gnu)
+ OS="-DLINUX -DGLIBC"
+ ;;
+ *solaris)
+ OS="-DSOLARIS"
+ ;;
+ *freebsd)
+ OS="-DFREEBSD"
+ ;;
+ *hpux)
+ OS="-DHPUX"
+ ;;
+ *aix)
+ OS="-DAIX"
+ ;;
+ *)
+ ;;
+esac
+
+echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6
+echo "configure:930: checking for main in -lnsl" >&5
+ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 938 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:945: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lnsl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -log""... $ac_c" 1>&6
+echo "configure:973: checking for main in -log" >&5
+ac_lib_var=`echo og'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-log $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 981 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:988: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo og | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-log $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -lldap""... $ac_c" 1>&6
+echo "configure:1016: checking for main in -lldap" >&5
+ac_lib_var=`echo ldap'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lldap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1024 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1031: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo ldap | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lldap $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -llber""... $ac_c" 1>&6
+echo "configure:1059: checking for main in -llber" >&5
+ac_lib_var=`echo lber'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-llber $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1067 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1074: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo lber | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-llber $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6
+echo "configure:1102: checking for main in -lsocket" >&5
+ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1110 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1117: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lsocket $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
+echo "configure:1145: checking for crypt in -lcrypt" >&5
+ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcrypt $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1153 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1164: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo crypt | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lcrypt $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for printf in -lc""... $ac_c" 1>&6
+echo "configure:1192: checking for printf in -lc" >&5
+ac_lib_var=`echo c'_'printf | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1200 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char printf();
+
+int main() {
+printf()
+; return 0; }
+EOF
+if { (eval echo configure:1211: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo c | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lc $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+
+
+
+echo $ac_n "checking for PAM support:""... $ac_c" 1>&6
+echo "configure:1243: checking for PAM support:" >&5
+echo
+# Check whether --with-pam or --without-pam was given.
+if test "${with_pam+set}" = set; then
+ withval="$with_pam"
+ :
+fi
+
+if test "x$with_pam" = "xyes";then
+ echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "configure:1253: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1261 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen();
+
+int main() {
+dlopen()
+; return 0; }
+EOF
+if { (eval echo configure:1272: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ldl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ echo $ac_n "checking for pam_start in -lpam""... $ac_c" 1>&6
+echo "configure:1300: checking for pam_start in -lpam" >&5
+ac_lib_var=`echo pam'_'pam_start | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lpam $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1308 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char pam_start();
+
+int main() {
+pam_start()
+; return 0; }
+EOF
+if { (eval echo configure:1319: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo pam | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lpam $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ DEFINES="-DUSE_PAM $DEFINES";
+ echo "$ac_t""Pam support... yes" 1>&6
+else
+ echo "$ac_t""Pam support... no" 1>&6
+fi
+
+echo $ac_n "checking for LDAP support""... $ac_c" 1>&6
+echo "configure:1353: checking for LDAP support" >&5
+echo
+# Check whether --with-ldap or --without-ldap was given.
+if test "${with_ldap+set}" = set; then
+ withval="$with_ldap"
+ :
+fi
+
+
+if test "x$with_ldap" = "xyes";then
+ echo $ac_n "checking for ldap_simple_bind_s in -lldap""... $ac_c" 1>&6
+echo "configure:1364: checking for ldap_simple_bind_s in -lldap" >&5
+ac_lib_var=`echo ldap'_'ldap_simple_bind_s | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lldap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1372 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char ldap_simple_bind_s();
+
+int main() {
+ldap_simple_bind_s()
+; return 0; }
+EOF
+if { (eval echo configure:1383: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo ldap | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lldap $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ echo $ac_n "checking for ldap_init in -lldap""... $ac_c" 1>&6
+echo "configure:1411: checking for ldap_init in -lldap" >&5
+ac_lib_var=`echo ldap'_'ldap_init | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lldap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1419 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char ldap_init();
+
+int main() {
+ldap_init()
+; return 0; }
+EOF
+if { (eval echo configure:1430: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo ldap | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lldap $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+ DEFINES="-DUSE_LDAP $DEFINES"
+ echo "$ac_t""LDAP support... yes" 1>&6
+else
+ echo "$ac_t""LDAP support... no" 1>&6
+fi
+
+echo $ac_n "checking for DB support""... $ac_c" 1>&6
+echo "configure:1465: checking for DB support" >&5
+echo
+# Check whether --with-db or --without-db was given.
+if test "${with_db+set}" = set; then
+ withval="$with_db"
+ :
+fi
+
+if test "x$with_db" = "xyes";then
+ DB="$DB -DDB -DDB_NULL"
+ echo "$ac_t""DB support... yes" 1>&6
+else
+ echo "$ac_t""DB support... no" 1>&6
+fi
+
+if test "x$with_db" = "xyes";then
+
+echo "Check for MySQL support:"
+
+# Check whether --with-mysql or --without-mysql was given.
+if test "${with_mysql+set}" = set; then
+ withval="$with_mysql"
+ :
+fi
+
+
+# Check whether --with-mysql-prefix or --without-mysql-prefix was given.
+if test "${with_mysql_prefix+set}" = set; then
+ withval="$with_mysql_prefix"
+ MYSQL_PREFIX=$withval
+else
+ MYSQL_PREFIX=/usr
+
+fi
+
+
+
+if test "x$with_mysql" = "xyes";then
+
+ LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $LDFLAGS"
+ LDFLAGS="-I$MYSQL_PREFIX/include/mysql $LDFLAGS"
+ echo $ac_n "checking for mysql_init in -lmysqlclient""... $ac_c" 1>&6
+echo "configure:1507: checking for mysql_init in -lmysqlclient" >&5
+ac_lib_var=`echo mysqlclient'_'mysql_init | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lmysqlclient -lm $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1515 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char mysql_init();
+
+int main() {
+mysql_init()
+; return 0; }
+EOF
+if { (eval echo configure:1526: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="-lmysqlclient -lm $LIBS"
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: *** couldn't find libmysqlclient" 1>&2; exit 1; }
+fi
+
+
+ DB="$DB -DDB_MYSQL";
+ echo "$ac_t""Mysql support... yes" 1>&6
+else
+ echo "$ac_t""Mysql support... no" 1>&6
+fi
+
+fi
+
+if test "x$with_db" = "xyes";then
+
+echo "Check for PgSQL support:"
+
+# Check whether --with-pgsql or --without-pgsql was given.
+if test "${with_pgsql+set}" = set; then
+ withval="$with_pgsql"
+ :
+fi
+
+
+# Check whether --with-pgsql-prefix or --without-pgsql-prefix was given.
+if test "${with_pgsql_prefix+set}" = set; then
+ withval="$with_pgsql_prefix"
+ PGSQL_PREFIX=$withval
+else
+ PGSQL_PREFIX=/usr
+
+fi
+
+
+
+if test "x$with_pgsql" = "xyes";then
+
+ LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $LDFLAGS"
+ LDFLAGS="-I$PGSQL_PREFIX/include/pgsql $LDFLAGS"
+ echo $ac_n "checking for PQconnectdb in -lpq""... $ac_c" 1>&6
+echo "configure:1583: checking for PQconnectdb in -lpq" >&5
+ac_lib_var=`echo pq'_'PQconnectdb | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lpq $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1591 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char PQconnectdb ();
+
+int main() {
+PQconnectdb ()
+; return 0; }
+EOF
+if { (eval echo configure:1602: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="-lpq $LIBS"
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: *** couldn't find libpq" 1>&2; exit 1; }
+fi
+
+
+ DB="$DB -DDB_PGSQL";
+ echo "$ac_t""Pgsql support... yes" 1>&6
+else
+ echo "$ac_t""Pgsql support... no" 1>&6
+fi
+
+fi
+
+
+# Check whether --with-tacuid or --without-tacuid was given.
+if test "${with_tacuid+set}" = set; then
+ withval="$with_tacuid"
+ :
+fi
+
+# Check whether --with-tacgid or --without-tacgid was given.
+if test "${with_tacgid+set}" = set; then
+ withval="$with_tacgid"
+ :
+fi
+
+
+
+if (test "x$with_tacuid" != "x" && test "x$with_tacgid" != "x" && test "x$with_tacuid" != "xyes" && test "x$with_tacgid" != "xyes");then
+
+ DEFINES="-DTACPLUS_USERID=$with_tacuid -DTACPLUS_GROUPID=$with_tacgid $DEFINES";
+ echo "$ac_t""tacacs+ work with given user and group id" 1>&6
+fi
+
+echo $ac_n "checking whether to enable the maxsess feature""... $ac_c" 1>&6
+echo "configure:1654: checking whether to enable the maxsess feature" >&5
+# Check whether --enable-maxsess or --disable-maxsess was given.
+if test "${enable_maxsess+set}" = set; then
+ enableval="$enable_maxsess"
+
+if test "$enableval" = "yes";then
+ DEFINES="-DMAXSESS $DEFINES";
+ echo "$ac_t""yes" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+
+ echo "$ac_t""no" 1>&6
+
+fi
+
+
+
+# Check whether --with-tacplus_pid or --without-tacplus_pid was given.
+if test "${with_tacplus_pid+set}" = set; then
+ withval="$with_tacplus_pid"
+ PIDFILE="-DTACPLUS_PIDFILE=\\\"$withval/tac_plus.pid\\\""
+else
+ PIDFILE="-DTACPLUS_PIDFILE=\\\"/var/run/tac_plus.pid\\\""
+
+fi
+
+
+echo $ac_n "checking whether to enable the libwrap feture""... $ac_c" 1>&6
+echo "configure:1685: checking whether to enable the libwrap feture" >&5
+
+# Check whether --with-libwrap or --without-libwrap was given.
+if test "${with_libwrap+set}" = set; then
+ withval="$with_libwrap"
+ case "$withval" in
+ no)
+ echo "$ac_t""no" 1>&6
+ ;;
+ yes)
+ echo "$ac_t""yes" 1>&6
+ echo $ac_n "checking for request_init in -lwrap""... $ac_c" 1>&6
+echo "configure:1697: checking for request_init in -lwrap" >&5
+ac_lib_var=`echo wrap'_'request_init | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lwrap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1705 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char request_init();
+
+int main() {
+request_init()
+; return 0; }
+EOF
+if { (eval echo configure:1716: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+
+ LIBS="-lwrap $LIBS"
+ DEFINES="-DTCPWRAPPER $DEFINES"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ ;;
+ *)
+ echo "$ac_t""yes" 1>&6
+ if test -d "$withval"; then
+ LDFLAGS="-L$withval $LDFLAGS"
+ DEFINES="-DTCPWRAPPER $DEFINES"
+ fi
+ cat > conftest.$ac_ext <<EOF
+#line 1746 "configure"
+#include "confdefs.h"
+ int allow_severity; int deny_severity;
+int main() {
+ hosts_access();
+; return 0; }
+EOF
+if { (eval echo configure:1753: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ { echo "configure: error: Could not find the $withval library. You must first install tcp_wrappers." 1>&2; exit 1; }
+fi
+rm -f conftest*
+ ;;
+ esac
+else
+ echo "$ac_t""no" 1>&6
+
+fi
+
+
+
+
+
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1776: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1791 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1797: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1808 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1814: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1825 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1831: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1856: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1861 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1869: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1886 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1904 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1925 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1936: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in fcntl.h malloc.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1963: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1968 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1973: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in shadow.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2003: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2008 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2013: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+ if test -f /etc/shadow ; then
+ cat >> confdefs.h <<\EOF
+#define SHADOW_PASSWORDS 1
+EOF
+
+ fi
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:2047: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2052 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:2101: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:2122: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2127 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:2136: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_time=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+ cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
+echo "configure:2159: checking whether ${CC-cc} needs -traditional" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 2165 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 2183 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+echo $ac_n "checking whether setpgrp takes no argument""... $ac_c" 1>&6
+echo "configure:2205: checking whether setpgrp takes no argument" >&5
+if eval "test \"`echo '$''{'ac_cv_func_setpgrp_void'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: cannot check setpgrp if cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2213 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*
+ * If this system has a BSD-style setpgrp, which takes arguments, exit
+ * successfully.
+ */
+main()
+{
+ if (setpgrp(1,1) == -1)
+ exit(0);
+ else
+ exit(1);
+}
+
+EOF
+if { (eval echo configure:2233: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_setpgrp_void=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_setpgrp_void=yes
+fi
+rm -fr conftest*
+fi
+
+
+fi
+
+echo "$ac_t""$ac_cv_func_setpgrp_void" 1>&6
+if test $ac_cv_func_setpgrp_void = yes; then
+ cat >> confdefs.h <<\EOF
+#define SETPGRP_VOID 1
+EOF
+
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:2257: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2262 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:2279: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:2298: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2303 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2326: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:2350: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2355 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2378: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for wait3 that fills in rusage""... $ac_c" 1>&6
+echo "configure:2403: checking for wait3 that fills in rusage" >&5
+if eval "test \"`echo '$''{'ac_cv_func_wait3_rusage'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_wait3_rusage=no
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2411 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdio.h>
+/* HP-UX has wait3 but does not fill in rusage at all. */
+main() {
+ struct rusage r;
+ int i;
+ /* Use a field that we can force nonzero --
+ voluntary context switches.
+ For systems like NeXT and OSF/1 that don't set it,
+ also use the system CPU time. And page faults (I/O) for Linux. */
+ r.ru_nvcsw = 0;
+ r.ru_stime.tv_sec = 0;
+ r.ru_stime.tv_usec = 0;
+ r.ru_majflt = r.ru_minflt = 0;
+ switch (fork()) {
+ case 0: /* Child. */
+ sleep(1); /* Give up the CPU. */
+ _exit(0);
+ case -1: _exit(0); /* What can we do? */
+ default: /* Parent. */
+ wait3(&i, 0, &r);
+ sleep(2); /* Avoid "text file busy" from rm on fast HP-UX machines. */
+ exit(r.ru_nvcsw == 0 && r.ru_majflt == 0 && r.ru_minflt == 0
+ && r.ru_stime.tv_sec == 0 && r.ru_stime.tv_usec == 0);
+ }
+}
+EOF
+if { (eval echo configure:2442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_wait3_rusage=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_wait3_rusage=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_func_wait3_rusage" 1>&6
+if test $ac_cv_func_wait3_rusage = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_WAIT3 1
+EOF
+
+fi
+
+for ac_func in regcomp select socket strcspn strdup strtol
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2467: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2472 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2495: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@CC@%$CC%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@DEFINES@%$DEFINES%g
+s%@PIDFILE@%$PIDFILE%g
+s%@DB@%$DB%g
+s%@OS@%$OS%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+echo timestamp > stamp-h
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+dnl This file writen by Devrim SERAL for tac_plus daemon
+
+AC_INIT()
+
+dnl Checks for programs.
+AC_PROG_MAKE_SET
+AC_PROG_CC
+
+dnl Check for Host information
+dnl AC_CANONICAL_HOST()
+AC_CANONICAL_SYSTEM()
+
+case $host_os in
+ *linux-gnu)
+ OS="-DLINUX -DGLIBC"
+ ;;
+ *solaris)
+ OS="-DSOLARIS"
+ ;;
+ *freebsd)
+ OS="-DFREEBSD"
+ ;;
+ *hpux)
+ OS="-DHPUX"
+ ;;
+ *aix)
+ OS="-DAIX"
+ ;;
+ *)
+ ;;
+esac
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lnsl:
+AC_CHECK_LIB(nsl, main)
+dnl Replace `main' with a function in -log:
+AC_CHECK_LIB(og, main)
+dnl Replace `main' with a function in -lldap:
+AC_CHECK_LIB(ldap, main)
+dnl Replace `main' with a function in -llber:
+AC_CHECK_LIB(lber, main)
+dnl Replace `main' with a function in -lsocket:
+AC_CHECK_LIB(socket, main)
+dnl Check for Crypt function
+AC_CHECK_LIB(crypt, crypt)
+AC_CHECK_LIB(c,printf)
+
+
+dnl Devrim Added
+AC_CONFIG_HEADER(config.h)
+
+dnl For PAM support
+AC_MSG_CHECKING(for PAM support:)
+echo
+AC_ARG_WITH(pam,
+ [ --with-pam With PAM Support ],,)
+if test "x$with_pam" = "xyes";then
+ AC_CHECK_LIB(dl, dlopen)
+ AC_CHECK_LIB(pam, pam_start)
+ DEFINES="-DUSE_PAM $DEFINES";
+ AC_MSG_RESULT(Pam support... yes)
+else
+ AC_MSG_RESULT(Pam support... no)
+fi
+
+dnl For LDAP Support
+AC_MSG_CHECKING(for LDAP support)
+echo
+AC_ARG_WITH(ldap,
+ [ --with-ldap With LDAP Support ],,)
+
+if test "x$with_ldap" = "xyes";then
+ AC_CHECK_LIB(ldap, ldap_simple_bind_s)
+ AC_CHECK_LIB(ldap, ldap_init)
+
+ DEFINES="-DUSE_LDAP $DEFINES"
+ AC_MSG_RESULT(LDAP support... yes)
+else
+ AC_MSG_RESULT(LDAP support... no)
+fi
+
+dnl For DB Support
+AC_MSG_CHECKING(for DB support)
+echo
+AC_ARG_WITH(db,
+ [ --with-db For DB Support ],,)
+if test "x$with_db" = "xyes";then
+ DB="$DB -DDB -DDB_NULL"
+ AC_MSG_RESULT(DB support... yes)
+else
+ AC_MSG_RESULT(DB support... no)
+fi
+
+dnl For MySQL support
+if test "x$with_db" = "xyes";then
+
+echo "Check for MySQL support:"
+
+AC_ARG_WITH(mysql,
+ [ --with-mysql With MySQL Support ],,)
+
+AC_ARG_WITH(mysql-prefix,
+ [ --with-mysql-prefix=PREFIX Mysql prefix [default=/usr]],
+ MYSQL_PREFIX=$withval,
+ MYSQL_PREFIX=/usr
+)
+
+
+if test "x$with_mysql" = "xyes";then
+
+ LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $LDFLAGS"
+ LDFLAGS="-I$MYSQL_PREFIX/include/mysql $LDFLAGS"
+ AC_CHECK_LIB(mysqlclient, mysql_init,
+ LIBS="-lmysqlclient -lm $LIBS",
+ AC_MSG_ERROR(*** couldn't find libmysqlclient),
+ -lm)
+
+ DB="$DB -DDB_MYSQL";
+ AC_MSG_RESULT(Mysql support... yes)
+else
+ AC_MSG_RESULT(Mysql support... no)
+fi
+
+fi
+
+dnl For PgSQL support
+if test "x$with_db" = "xyes";then
+
+echo "Check for PgSQL support:"
+
+AC_ARG_WITH(pgsql,
+ [ --with-pgsql With PgSQL Support ],,)
+
+AC_ARG_WITH(pgsql-prefix,
+ [ --with-pgsql-prefix=PREFIX PgSQL prefix [default=/usr]],
+ PGSQL_PREFIX=$withval,
+ PGSQL_PREFIX=/usr
+)
+
+
+if test "x$with_pgsql" = "xyes";then
+
+ LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $LDFLAGS"
+ LDFLAGS="-I$PGSQL_PREFIX/include/pgsql $LDFLAGS"
+ AC_CHECK_LIB(pq,PQconnectdb ,
+ LIBS="-lpq $LIBS",
+ AC_MSG_ERROR(*** couldn't find libpq))
+
+ DB="$DB -DDB_PGSQL";
+ AC_MSG_RESULT(Pgsql support... yes)
+else
+ AC_MSG_RESULT(Pgsql support... no)
+fi
+
+fi
+
+dnl Tacuid & tac guid
+
+AC_ARG_WITH(tacuid,
+ [ --with-tacuid=ID If you like to run tac_plus other than root user (no default value) ],,)
+AC_ARG_WITH(tacgid,
+ [ --with-tacgid=GID If you like to run tac_plus other than root group(no default value) ],,)
+
+
+if (test "x$with_tacuid" != "x" && test "x$with_tacgid" != "x" && test "x$with_tacuid" != "xyes" && test "x$with_tacgid" != "xyes");then
+
+ DEFINES="-DTACPLUS_USERID=$with_tacuid -DTACPLUS_GROUPID=$with_tacgid $DEFINES";
+ AC_MSG_RESULT(tacacs+ work with given user and group id)
+fi
+
+AC_MSG_CHECKING(whether to enable the maxsess feature)
+AC_ARG_ENABLE(maxsess,
+ [ --enable-maxsess Enable maxsess feature ],
+[
+if test "$enableval" = "yes";then
+ DEFINES="-DMAXSESS $DEFINES";
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+],
+[
+ AC_MSG_RESULT(no)
+])
+
+dnl Enable tacacs.pid file directory
+
+AC_ARG_WITH(tacplus_pid,
+ [ --with-tacplus_pid=PREFIX Tac_plus pid file location [default=/var/run] ],
+ PIDFILE="-DTACPLUS_PIDFILE=\\\"$withval/tac_plus.pid\\\"",
+ PIDFILE="-DTACPLUS_PIDFILE=\\\"/var/run/tac_plus.pid\\\""
+)
+
+dnl For libwrap check
+AC_MSG_CHECKING(whether to enable the libwrap feture)
+
+AC_ARG_WITH(libwrap,
+[ --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support.],
+[ case "$withval" in
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_CHECK_LIB(wrap, request_init, [
+ LIBS="-lwrap $LIBS"
+ DEFINES="-DTCPWRAPPER $DEFINES"])
+ ;;
+ *)
+ AC_MSG_RESULT(yes)
+ if test -d "$withval"; then
+ LDFLAGS="-L$withval $LDFLAGS"
+ DEFINES="-DTCPWRAPPER $DEFINES"
+ fi
+ AC_TRY_LINK([ int allow_severity; int deny_severity; ],
+ [ hosts_access(); ],
+ [],
+ [ AC_MSG_ERROR(Could not find the $withval library. You must first install tcp_wrappers.) ])
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+dnl insert defines to Makefile
+AC_SUBST(DEFINES)
+AC_SUBST(PIDFILE)
+AC_SUBST(DB)
+AC_SUBST(OS)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h malloc.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h)
+AC_CHECK_HEADERS(shadow.h,[
+ if test -f /etc/shadow ; then
+ AC_DEFINE(SHADOW_PASSWORDS)
+ fi
+ ],)
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_SETPGRP
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_FUNC_WAIT3
+AC_CHECK_FUNCS(regcomp select socket strcspn strdup strtol)
+
+AC_OUTPUT(Makefile,echo timestamp > stamp-h)
--- /dev/null
+#! /usr/bin/perl
+
+# convert a passwd(5) and optional supplementary file into the new
+# file format
+
+# Please NOTE: None of the TACACS code available here comes with any
+# warranty or support.
+# Copyright (c) 1995-1998 by Cisco systems, 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 of the software and
+# supporting documentation, the name of Cisco Systems, 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 modification, copying and distribution is by
+# permission of Cisco Systems, Inc.
+#
+# Cisco Systems, Inc. makes no representations about the suitability of this
+# software for any purpose. 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.
+
+die 'Usage: convert.pl <password file> [-g] [ <supplementary file> ]'
+ if $#ARGV < 0;
+
+$pwfile = '';
+$supfile = '';
+%sup = ();
+$acl_valid = 0; # is acl valid in gid field?
+
+$pwfile = shift(@ARGV);
+while ($#ARGV >= 0) {
+ local($arg) = shift(@ARGV);
+ $acl_valid++, next if ($arg eq '-g');
+ $supfile = $arg;
+}
+
+if ($supfile) {
+ open(SUP, $supfile) || die "Can't read $supfile -- $!";
+ while(<SUP>) {
+ next if /^#/;
+ chop;
+
+ local($user, $inacl, $outacl, $arap, $chap) = split(/:/);
+
+ if (defined $sup{$user,'user'}) {
+ die "User $user is multiply defined on lines $sup{$user,'user'} and $. of $supfile";
+ }
+ $users{$user} = 1;
+ $sup{$user,'user'} = $.;
+ $sup{$user,'inacl'} = $inacl;
+ $sup{$user,'outacl'} = $outacl;
+ $sup{$user,'arap'} = $arap;
+ $sup{$user,'chap'} = $chap;
+ }
+ close(SUP);
+}
+
+open(PASSWD, $pwfile) || die "Can't read $pwfile -- $!";
+
+while(<PASSWD>) {
+ chop;
+ next if ($_ eq '');
+
+ local($user, $pass, $uid, $gid, $gcos, $home, $exp) = split(/:/);
+
+ $users{$user} = 2;
+
+ print "user = $user {\n";
+ print " login = des $pass\n";
+ if (!$acl_valid) {
+ print " member = $gid\n";
+ $groups{$gid}++;
+ }
+ if ($gcos) {
+ if ($gcos =~ /[ ]/) {
+ print " name = \"$gcos\"\n";
+ } else {
+ print " name = $gcos\n";
+ }
+ }
+
+ if ($exp =~ /\S+\s+\d+\s+\d+/) {
+ print " expires = \"$exp\"\n";
+ }
+
+ if ($acl_valid) {
+ print " service = exec {\n";
+ print " acl = $gid\n";
+ print " }\n";
+ }
+
+ local($outacl) = $sup{$user,'outacl'};
+ local($inacl) = $sup{$user,'inacl'};
+ if ($inacl ne '' || $outacl ne '') {
+ print " service = slip {\n";
+ print " inacl = $inacl\n" if $inacl ne '';
+ print " outacl = $outacl\n" if $outacl ne '';
+ print " }\n";
+
+ print " service = ppp protocol = ip {\n";
+ print " inacl = $inacl\n" if $inacl ne '';
+ print " outacl = $outacl\n" if $outacl ne '';
+ print " }\n";
+ }
+
+ print " arap = $sup{$user,'arap'}\n" if $sup{$user,'arap'} ne '';
+ print " chap = $sup{$user,'chap'}\n" if $sup{$user,'chap'} ne '';
+ print "}\n";
+}
+
+close(PASSWD);
+
+foreach $user (keys %users) {
+ next if $users{$user} != 1;
+ # This user only in supfile
+ print "user = $user {\n";
+ local($outacl) = $sup{$user,'outacl'};
+ local($inacl) = $sup{$user,'inacl'};
+ if ($inacl ne '' || $outacl ne '') {
+ print " service = slip {\n";
+ print " inacl = $inacl\n" if $inacl ne '';
+ print " outacl = $outacl\n" if $outacl ne '';
+ print " }\n";
+
+ print " service = ppp protocol = ip {\n";
+ print " inacl = $inacl\n" if $inacl ne '';
+ print " outacl = $outacl\n" if $outacl ne '';
+ print " }\n";
+ }
+
+ print " arap = $sup{$user,'arap'}\n" if $sup{$user,'arap'} ne '';
+ print " chap = $sup{$user,'chap'}\n" if $sup{$user,'chap'} ne '';
+ print "}\n";
+}
+
+exit 0 if ($acl_valid);
+
+foreach $group (keys %groups) {
+ print "group = $group { }\n";
+}
+
+
+
--- /dev/null
+/*
+ Verify that this user/password is valid per a database.
+ Return 1 if verified, 0 otherwise.
+
+ Format of connection string (look like internet URL):
+
+ db://user:password@hostname/table?name&passwd
+
+ Example connect to Oracle RDBMS at user 'roger' with password
+ 'tiger', to 'host.domain.com' database server, fields name in
+ table is 'name' and 'passwd' in 'oshadow' table:
+
+ oracle://roger:tiger@host.domain.com/oshadow?name&passwd
+
+ DONE:
+ 12-nov-1998 Created
+ Add DB support to 'login = db <string>'
+ 14-nov-1998 Change Tacacs+ version from 0.95 to 3.0.9
+ 18-nov-1998 Added code for Oracle [version 8.0.5]
+ 27-nov-1998 Tested with 30'000 usernames Oracle database
+ Added DB support to global configuration
+ 'default authentication = db <string>'
+ 28-nov-1998 Added code for NULL database %)
+ 14-dec-1999 Add code for MySQL and also more check
+
+ FUTURE:
+ Make *_db_verify() the functions is reenterable
+ More security for connection to database
+ GDBM support
+ Separate debug logging
+ Perfomance testing on 10000 records in Oracle database
+ (in guide sayd about 3 auth/sec on Ultra 2 - hmm)
+
+ -------------------------------------------------------
+ fil@artelecom.ru http://twister.pp.ru
+
+ ****************************************************************************
+ PART II
+
+ I am added some extra extension. Like MySQL and PostgreSQL database support
+ And change most of lines for use dynamic memory allocation. db_accounting
+ added by me.
+
+ devrim(devrim@gazi.edu.tr)
+*/
+
+#if defined(DB)
+#include <stdio.h>
+#include "tac_plus.h"
+/* The databases recognized by this function */
+#define DEFINED_DB {"null","mysql","pgsql"}
+
+char *find_attr_value();
+
+int
+db_verify(user, users_passwd, str_conn)
+char *user, *users_passwd; /* Username and gived password */
+char *str_conn; /* String connection to database */
+{
+ char *buffer;
+ char *db_pref, *db_user, *db_password;
+ char *db_hostname, *db_table,*db_name,*dbfield_name, *dbfield_passwd;
+ int ret;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify %s by database at %s", user, str_conn);
+
+ buffer = db_pref = (char *)malloc( strlen(str_conn) + 1 );
+ if( buffer == NULL ){
+ report(LOG_DEBUG, "Error allocation memory");
+ return(0);
+ }
+
+ strcpy( buffer, str_conn );
+
+ db_user = (char *)strstr( db_pref, "://" );
+ if( db_user == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_user");
+ free(buffer);
+ return(0);
+ }
+ *db_user = '\0';
+
+ /* For recognize db authentication database */
+
+ if (check_db_type(db_pref)) {
+ report(LOG_DEBUG, "%s DB authentication scheme didn't recognize by tac_plus",db_pref);
+ free(buffer);
+ return(0);
+ }
+
+ db_user += 3;
+
+ db_password = (char *)strstr( db_user, ":" );
+ if( db_password == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_password");
+ free(buffer);
+ return(0);
+ }
+ *db_password = '\0';
+ db_password++;
+
+ db_hostname = (char *)strstr( db_password, "@" );
+ if( db_hostname == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_hostname");
+ free(buffer);
+ return(0);
+ }
+ *db_hostname = '\0';
+ db_hostname++;
+
+ db_name = (char *)strstr( db_hostname, "/" );
+ if( db_name == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_name");
+ free(buffer);
+ return(0);
+ }
+ *db_name = '\0';
+ db_name++;
+
+ db_table = (char *)strstr( db_name, "/" );
+ if( db_table == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_table");
+ free(buffer);
+ return(0);
+ }
+ *db_table = '\0';
+ db_table++;
+
+ dbfield_name = (char *)strstr( db_table, "?" );
+ if( dbfield_name == NULL){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse dbfield_name");
+ free(buffer);
+ return(0);
+ }
+ *dbfield_name = '\0';
+ dbfield_name++;
+
+
+ dbfield_passwd = (char *)strstr( dbfield_name, "&" );
+ if( dbfield_passwd == NULL){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse dbfield_passwd");
+ free(buffer);
+ return(0);
+ }
+ *dbfield_passwd = '\0';
+ dbfield_passwd++;
+
+
+ /* Parse database connection string */
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "db_verify: db_pref=%s, db_user=%s, db_password=%s db_hostname=%s, db_name=%s ,db_table=%s, dbfield_name=%s, dbfield_passwd=%s", db_pref, db_user, db_password, db_hostname, db_name,db_table, dbfield_name, dbfield_passwd);
+
+ /* Check for empty passwords */
+ if (users_passwd == NULL || *users_passwd == '\0' ||
+ db_password == NULL || *db_password == '\0' ) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "One from passwords is empty");
+ free(buffer);
+ return (0);
+ }
+
+ ret = 0;
+
+ /* Run database depend function */
+#if defined(DB_ORACLE)
+ if (!strcmp(db_pref, "oracle")) {
+ ret = oracle_db_verify(
+ user, users_passwd,
+ db_user, db_password, db_hostname, db_table,
+ dbfield_name, dbfield_passwd);
+ }
+#endif
+
+#if defined(DB_MYSQL)
+ if (!strcmp(db_pref, "mysql")) {
+ ret = mysql_db_verify(
+ user, users_passwd,
+ db_user, db_password, db_hostname, db_name, db_table,
+ dbfield_name, dbfield_passwd);
+ }
+#endif
+
+#if defined(DB_PGSQL)
+ if (!strcmp(db_pref, "pgsql")) {
+ ret = pgsql_db_verify(
+ user, users_passwd,
+ db_user, db_password, db_hostname,db_name, db_table,
+ dbfield_name, dbfield_passwd);
+ }
+#endif
+
+#if defined(DB_NULL)
+ if (!strcmp(db_pref, "null")) {
+ ret = null_db_verify(
+ user, users_passwd,
+ db_user, db_password, db_hostname ,db_table,
+ dbfield_name, dbfield_passwd);
+ }
+#endif
+
+#if defined(DB_GDBM)
+ if (!strcmp(db_pref, "gdbm")) {
+ gdb_db_verify();
+ }
+#endif
+ free(buffer); /* Free unused memory */
+ return (ret); /* error */
+}
+
+
+/* Db accounting routine */
+int
+db_acct(rec)
+struct acct_rec *rec;
+{
+ char *buffer;
+ char *db_pref, *db_user, *db_password;
+ char *db_hostname, *db_name,*db_table;
+ char *a_username,*s_name,*c_name,*elapsed_time,*bytes_in,*bytes_out;
+ int ret;
+
+ buffer = db_pref = (char *)malloc( strlen(session.db_acct) + 1 );
+
+ if( buffer == NULL ){
+ report(LOG_DEBUG, "Error allocation memory");
+ return(0);
+ }
+
+ strcpy( buffer, session.db_acct);
+
+ db_user = (char *)strstr( db_pref, "://" );
+ if( db_user == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_user");
+ free(buffer);
+ return(0);
+ }
+ *db_user = '\0';
+
+ /* For recognize db accouting database */
+
+ if( check_db_type(db_pref) ) {
+ report(LOG_DEBUG, "%s DB accounting scheme didn't recognize by tac_plus",db_pref);
+ free(buffer);
+ return(0);
+ }
+
+ db_user += 3;
+
+ db_password = (char *)strstr( db_user, ":" );
+ if( db_password == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_password");
+ free(buffer);
+ return(0);
+ }
+ *db_password = '\0';
+ db_password++;
+
+ db_hostname = (char *)strstr( db_password, "@" );
+ if( db_hostname == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_hostname");
+ free(buffer);
+ return(0);
+ }
+ *db_hostname = '\0';
+ db_hostname++;
+
+ db_name = (char *)strstr( db_hostname, "/" );
+ if( db_name == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_name");
+ free(buffer);
+ return(0);
+ }
+ *db_name = '\0';
+ db_name++;
+
+ db_table = (char *)strstr( db_name, "/" );
+ if( db_table == NULL ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Error parse db_table");
+ free(buffer);
+ return(0);
+ }
+ *db_table = '\0';
+ db_table++;
+
+/* Find some attributes for accounting */
+ a_username=rec->identity->username;
+ if (a_username==NULL ) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG,"db_acct: Can't find username!");
+ free(buffer);
+ return(0);
+ }
+ s_name=rec->identity->NAS_name;
+ if (s_name==NULL) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG,"db_acct: Can't find NAS name!");
+ free(buffer);
+ return(0);
+ }
+ c_name=find_attr_value("addr", rec->args, rec->num_args);
+ if (c_name==NULL) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG,"db_acct: Can't find client adress!");
+ /* Can't find client adress so give NAC_address attribute value */
+ c_name=rec->identity->NAC_address;
+ }
+ elapsed_time=find_attr_value("elapsed_time", rec->args, rec->num_args);
+ if (elapsed_time==NULL) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG,"db_acct: Can't get elapsed time!");
+ free(buffer);
+ return(0);
+ }
+ bytes_in=find_attr_value("bytes_in", rec->args, rec->num_args);
+ if (bytes_in==NULL) bytes_in="0";
+ bytes_out=find_attr_value("bytes_out", rec->args, rec->num_args);
+ if (bytes_out==NULL) bytes_out="0";
+
+
+ /* Parse database connection string */
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "db_verify: db_pref=%s, db_user=%s,db_password=%s , db_hostname=%s, db_name=%s ,db_table=%s ",
+ db_pref, db_user, db_password,
+ db_hostname, db_name,db_table );
+
+ /* Check for empty passwords */
+ if (db_user == NULL || db_password == '\0' ) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "One from passwords is empty");
+ free(buffer);
+ return (0);
+ }
+
+ ret = 0;
+ /* Run database depend function */
+#if defined(DB_ORACLE)
+ if (!strcmp(db_pref, "oracle")) {
+ ret = oracle_db_acct(
+ db_user, db_password, db_hostname, db_name, db_table);
+ }
+#endif
+
+#if defined(DB_MYSQL)
+ if (!strcmp(db_pref, "mysql")) {
+ ret = mysql_db_acct(
+ db_user, db_password, db_hostname, db_name, db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
+ }
+#endif
+
+#if defined(DB_PGSQL)
+ if (!strcmp(db_pref, "pgsql")) {
+ ret = pgsql_db_acct(
+ db_user, db_password, db_hostname, db_name, db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
+ }
+#endif
+
+#if defined(DB_NULL)
+ if (!strcmp(db_pref, "null")) {
+ ret = null_db_acct(
+ db_user, db_password, db_hostname, db_name, db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
+ }
+#endif
+#if defined(DB_GDBM)
+ if (!strcmp(db_pref, "gdbm")) {
+ gdb_db_acct();
+ }
+#endif
+
+ free(buffer); /* Free unused memory */
+ return (ret); /* error */
+
+}
+
+/* For checking DB type */
+int
+check_db_type(db_type)
+char *db_type;
+{
+char *dbp[]=DEFINED_DB;
+int ret=1,i;
+
+for (i=0; dbp[i] ; i++ ) {
+ if(!strcmp(db_type,dbp[i])) {
+ ret=0;
+ break;
+ }
+}
+return ret;
+}
+#endif /* DB */
--- /dev/null
+#if defined(DB_MYSQL) && defined(DB)
+
+/*
+Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+*/
+
+#include "tac_plus.h"
+#include <stdio.h>
+#include "mysql.h"
+#define SQLCMDL 1024
+#define AUTHSQL "SELECT %s FROM %s WHERE %s=\"%s\""
+#define ACCTSQL "INSERT INTO %s (usern,s_name,c_name,elapsed_time,bytes_in,bytes_out,fin_t) VALUES (\"%s\",\"%s\",\"%s\",%s,%s,%s,NOW())"
+
+MYSQL mysqldb;
+MYSQL_RES *res;
+MYSQL_ROW row;
+MYSQL_FIELD *table_field;
+
+int mysql_db_verify(user, users_passwd, db_user, db_password,
+ db_hostname,db_name, db_table, dbfield_name, dbfield_passwd)
+
+
+char *user, *users_passwd; /* Username and gived password */
+char *db_user; /* db's parameters */
+char *db_password;
+char *db_hostname;
+char *db_name;
+char *db_table;
+char *dbfield_name;
+char *dbfield_passwd;
+
+{
+
+char *real_passwd;
+char *mysqlcmd;
+int sql_len;
+
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: verify %s", user);
+
+/* Connect database server */
+
+ if ( !( mysql_connect(&mysqldb,db_hostname,db_user,db_password) ) )
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot connect as %s", db_user);
+ return(0);
+ }
+
+/*Select tacacs db */
+
+ if ( mysql_select_db(&mysqldb,db_name) )
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot find database named %s",db_name);
+ return(0);
+ }
+
+/* Check select string length */
+
+sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+ if ( sql_len> SQLCMDL )
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Sql cmd exceed alowed limits");
+ return(0);
+ }
+
+/* Prepare select string */
+
+mysqlcmd=(char *) malloc(sql_len);
+
+if(mysqlcmd==NULL) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_ERR, "mysql_db_verify: mysqlcmd malloc error");
+ return(0);
+}
+
+sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+/* Query database */
+
+ if (mysql_query(&mysqldb,mysqlcmd))
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database ");
+ free(mysqlcmd);
+ return(0);
+ }
+
+ free(mysqlcmd);
+
+ if (!(res = mysql_store_result(&mysqldb)))
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot store result");
+ return(0);
+ }
+
+ if(!(row = mysql_fetch_row(res)))
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot fetch row");
+ return(0);
+ }
+
+ if (strlen(row[0]) <=0 )
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: DB passwd entry is NULL");
+ return(0);
+ }
+ /* Allocate memory for real_passwd */
+ real_passwd=(char *) malloc(strlen(row[0])+1);
+ strcpy(real_passwd,row[0]);
+
+ if (!mysql_eof(res))
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Result not end!!");
+ return(0);
+ }
+
+ mysql_free_result(res);
+ mysql_close(&mysqldb);
+
+if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: verify password '%s' to DES encrypted string '%s'", users_passwd, real_passwd);
+
+ /* Try to verify the password */
+ if (!des_verify(users_passwd, real_passwd)) {
+ free(real_passwd);
+ return (0);
+ }
+ free(real_passwd);
+ return (1); /* Return 1 if verified, 0 otherwise. */
+}
+
+int
+mysql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+
+char *db_user; /* db's parameters */
+char *db_password;
+char *db_hostname;
+char *db_name;
+char *db_table;
+char *s_name, *c_name,*a_username,*elapsed_time,*bytes_in,*bytes_out;
+
+{
+
+char *mysqlcmd;
+int sql_len;
+
+/* Connect database server */
+
+ if (!(mysql_connect(&mysqldb,db_hostname,db_user,db_password)))
+ {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot connect as %s", db_user);
+ return(0);
+ }
+
+/*Select tacacs db */
+
+ if (mysql_select_db(&mysqldb,db_name))
+ {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot find database named %s",db_name);
+ return(0);
+ }
+
+/* Check buffer overflow for select string */
+sql_len=strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
+
+if ( sql_len >SQLCMDL)
+ {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: Sql cmd exceed alowed limits");
+ return(0);
+ }
+
+
+/* Prepare select string */
+mysqlcmd=(char *) malloc(sql_len);
+
+if(mysqlcmd==NULL) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_ERR, "mysql_db_acct: mysqlcmd malloc error");
+ return(0);
+}
+
+sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+/* Query database */
+
+ if (mysql_query(&mysqldb,mysqlcmd))
+ {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database");
+ free(mysqlcmd);
+ return(0);
+ }
+
+ free(mysqlcmd);
+
+/* Check if accounting is sucess */
+ if ( mysql_affected_rows( &mysqldb ) < 0 )
+ {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: Insert isn't sucess");
+ return(0);
+ }
+ return (1); /* Return 1 if verified, 0 otherwise. */
+}
+#endif
--- /dev/null
+/*
+** Simple NULL driver for database interface. I created this for testing
+** db_* on my notebook by home. There i dont have Oracle or any database
+** server. Fil/27-nov-1998
+**
+** DO_NOT_USE_THIS_FOR_WORK!
+*/
+
+#if defined(DB_NULL) && defined(DB)
+#include "tac_plus.h"
+
+int null_db_verify(user, users_passwd, db_user, db_password, db_hostname,
+ db_table, dbfield_name, dbfield_passwd)
+
+char *user, *users_passwd; /* Username and gived password */
+char *db_user; /* db's parametr's */
+char *db_password;
+char *db_hostname;
+char *db_table;
+char *dbfield_name;
+char *dbfield_passwd;
+
+{
+//report(LOG_DEBUG, "DB_NULL(%u) - ok", __LINE__);
+
+ /* Try to verify the password
+ Successful if username and password equal */
+ if (strcmp(user, users_passwd)) {
+ return (0);
+ }
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "DB Null: verify password '%s'", users_passwd);
+
+ return (1); /* Return 1 if verified, 0 otherwise. */
+}
+
+/* Null Database Accounting */
+
+int
+null_db_acct(db_user, db_password, db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+char *db_user; /* db's parametr's */
+char *db_password;
+char *db_hostname;
+char *db_name;
+char *db_table;
+char *s_name;
+char *c_name;
+char *a_username;
+char *elapsed_time;char *bytes_in;char *bytes_out;
+{
+report(LOG_INFO,"Db accounting user=%s pass=%s host=%s
+db_name=%s table=%s servern=%s clientn=%s username=%s et=%s bi=%s bo=%s",db_user,db_password,db_hostname,
+db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out);
+return (1);
+}
+#endif
+
--- /dev/null
+#if defined(DB_PGSQL) && defined(DB)
+
+/*
+Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+For PostgreSQL Authentication And Accounting
+ 28-01-2001
+This program protected with GPL License.
+*/
+
+#include "tac_plus.h"
+#include <stdio.h>
+#include "libpq-fe.h"
+#define SQLCMDL 1024
+#define PWLEN 13
+#define AUTHSQL "SELECT %s FROM %s WHERE %s='%s'"
+#define ACCTSQL "INSERT INTO %s (usern,s_name,c_name,elapsed_time,bytes_in,bytes_out,fin_t) VALUES ('%s','%s','%s',%s,%s,%s,NOW())"
+
+PGconn *conn;
+PGresult *res;
+
+int pgsql_db_verify(user, users_passwd, db_user, db_password,
+ db_hostname,db_name, db_table, dbfield_name, dbfield_passwd)
+
+
+char *user, *users_passwd; /* Username and gived password */
+char *db_user; /* db's parameters */
+char *db_password;
+char *db_hostname;
+char *db_name;
+char *db_table;
+char *dbfield_name;
+char *dbfield_passwd;
+
+{
+
+char *real_passwd;
+char *pgsqlcmd;
+int sql_len;
+int nrow;
+
+if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: verify %s", user);
+
+/* Connect database server */
+
+conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+if ( PQstatus(conn) == CONNECTION_BAD )
+{
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Connection to database %s failed", db_name);
+ return(0);
+}
+
+/* Check select string length */
+
+sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+if ( sql_len> SQLCMDL )
+{
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Sql cmd exceed alowed limits");
+ return(0);
+}
+
+/* Prepare select string */
+
+pgsqlcmd=(char *) malloc(sql_len);
+
+if(pgsqlcmd==NULL)
+{
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+ return(0);
+}
+
+sprintf(pgsqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+/* Query database */
+res=PQexec(conn,pgsqlcmd);
+
+if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+{
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "PGSQL: cannot query database ");
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+ }
+ free(pgsqlcmd);
+ exit_nicely(conn,res);
+ return(0);
+}
+
+free(pgsqlcmd);
+
+if( nrow=PQntuples(res)!=1)
+{
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: Have we got more than one password!!");
+ exit_nicely(conn,res);
+ return(0);
+}
+
+if ( PQgetisnull(res,0,PQfnumber(res,dbfield_passwd)) )
+{
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: DB passwd entry is NULL");
+ exit_nicely(conn,res);
+ return(0);
+}
+
+ /* Allocate memory for real_passwd */
+ real_passwd=(char *) malloc(PWLEN+1);
+ strncpy(real_passwd,PQgetvalue(res,0,PQfnumber(res,dbfield_passwd)),PWLEN);
+ real_passwd[PWLEN]='\0';
+
+exit_nicely(conn,res);
+
+if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: verify password '%s' to DES encrypted string '%s'", users_passwd, real_passwd);
+
+ /* Try to verify the password */
+ if (!des_verify(users_passwd, real_passwd))
+ {
+ return (0);
+ }
+
+ return (1); /* Return 1 if verified, 0 otherwise. */
+}
+
+/* PGSQL ACCOUNTING function */
+
+int pgsql_db_acct(db_user,db_password,db_hostname,db_name,db_table,s_name,c_name,a_username,elapsed_time,bytes_in,bytes_out)
+
+char *db_user; /* db's parameters */
+char *db_password;
+char *db_hostname;
+char *db_name;
+char *db_table;
+char *s_name, *c_name,*a_username,*elapsed_time,*bytes_in,*bytes_out;
+
+{
+
+char *pgsqlcmd;
+int sql_len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Accounting for %s begin", a_username);
+
+/* Connect database server */
+
+conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+if ( PQstatus(conn) == CONNECTION_BAD )
+{
+ if (debug & DEBUG_ACCT_FLAG) {
+ report(LOG_DEBUG, "PGSQL: Connection to database %s failed", db_name);
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+ }
+ return(0);
+}
+
+/* Check select string length */
+
+sql_len=strlen(db_table)+strlen(a_username)+strlen(s_name)+strlen(c_name)+strlen(elapsed_time)+strlen(bytes_in)+strlen(bytes_out)+strlen(ACCTSQL);
+
+if ( sql_len> SQLCMDL )
+{
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Sql cmd exceed alowed limits");
+ return(0);
+}
+
+/* Prepare select string */
+
+pgsqlcmd=(char *) malloc(sql_len);
+
+if(pgsqlcmd==NULL)
+{
+if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+ return(0);
+}
+
+sprintf(pgsqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+/* Query database */
+res=PQexec(conn,pgsqlcmd);
+
+if (!res || PQresultStatus(res) != PGRES_COMMAND_OK )
+{
+ if (debug & DEBUG_ACCT_FLAG) {
+ report(LOG_DEBUG, "PGSQL: cannot establish database query");
+ report(LOG_DEBUG, "PGSQL: Error message->%s", PQerrorMessage(conn) );
+}
+ free(pgsqlcmd);
+ exit_nicely(conn,res);
+ return(0);
+}
+
+free(pgsqlcmd);
+
+/* Flush all result and close connection */
+exit_nicely(conn,res);
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Accounting for %s finished", a_username);
+
+ return (1); /* Return 1 if verified, 0 otherwise. */
+}
+
+int
+exit_nicely(PGconn *cn,PGresult *r)
+{
+ PQclear(r);
+ PQfinish(cn);
+}
+
+#endif
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+#include "md5.h"
+
+#ifdef MSCHAP
+#include "md4.h"
+#include "mschap.h"
+
+#ifdef MSCHAP_DES
+#include "arap_des.h"
+#endif
+#endif /* MSCHAP */
+
+#ifdef ARAP_DES
+#include "arap_des.h"
+#endif
+
+/* internal state variables */
+#define STATE_AUTHEN_START 0 /* no requests issued */
+#define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+#define STATE_AUTHEN_GETPASS 2 /* password has been requested */
+
+struct private_data {
+ char password[MAX_PASSWD_LEN + 1];
+ int state;
+};
+
+static void chap_verify();
+#ifdef MSCHAP
+static void mschap_verify();
+#endif /* MSCHAP */
+static void arap_verify();
+static void pap_verify();
+static void tac_login();
+
+/*
+ * Default tacacs login authentication function. Wants a username
+ * and a password, and tries to verify them.
+ *
+ * Choose_authen will ensure that we already have a username before this
+ * gets called.
+ *
+ * We will query for a password and keep it in the method_data.
+ *
+ * Any strings returned via pointers in authen_data must come from the
+ * heap. They will get freed by the caller.
+ *
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
+int
+default_fn(data)
+struct authen_data *data;
+{
+ struct private_data *p;
+ char *name = data->NAS_id->username;
+
+ p = (struct private_data *) data->method_data;
+
+ /* An abort has been received. Clean up and return */
+ if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+ return (1);
+ }
+ /* Initialise method_data if first time through */
+ if (!p) {
+ p = (struct private_data *) tac_malloc(sizeof(struct private_data));
+ bzero(p, sizeof(struct private_data));
+ data->method_data = p;
+ p->state = STATE_AUTHEN_START;
+ }
+ if (STREQ(name, DEFAULT_USERNAME)) {
+ /* Never authenticate this user. It's for authorization only */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ if (debug) {
+ report(LOG_DEBUG,
+ "authentication query for '%s' %s from %s rejected",
+ name && name[0] ? name : "unknown",
+ session.port, session.peer);
+ }
+ return (0);
+ }
+ if (data->action != TAC_PLUS_AUTHEN_LOGIN) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ } else {
+ switch (data->type) {
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ /* set status inside chap_verify */
+ chap_verify(data);
+
+ if (debug) {
+ report(LOG_DEBUG, "chap-login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ session.port, session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+ /* set status inside mschap_verify */
+ mschap_verify(data);
+
+ if (debug) {
+ report(LOG_DEBUG, "mschap-login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ session.port, session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_ARAP:
+ /* set status inside arap_verify */
+ arap_verify(data);
+
+ if (debug) {
+ report(LOG_DEBUG, "arap query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ session.port, session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ pap_verify(data);
+
+ if (debug) {
+ report(LOG_INFO, "pap-login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ session.port,
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+
+ case TAC_PLUS_AUTHEN_TYPE_ASCII:
+ tac_login(data, p);
+ switch (data->status) {
+ case TAC_PLUS_AUTHEN_STATUS_GETPASS:
+ case TAC_PLUS_AUTHEN_STATUS_GETUSER:
+ case TAC_PLUS_AUTHEN_STATUS_GETDATA:
+ /* Authentication still in progress. More data required */
+ return (0);
+
+ default:
+ /* Authentication finished */
+ if (debug)
+ report(LOG_INFO, "login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ session.port,
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+
+ switch (data->status) {
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ return (0);
+
+ default:
+ report(LOG_ERR, "%s %s: default_fn set bogus status value %d",
+ session.peer, session.port, data->status);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return (0);
+ }
+}
+
+/* Do a login requiring a username & password. We already know the
+ * username. We may return GETPASS to get a password if we need it.
+ * The password will be stored in the private data
+ *
+ */
+
+static void
+tac_login(data, p)
+struct authen_data *data;
+struct private_data *p;
+{
+ char *name, *passwd;
+ int pwlen;
+
+ name = data->NAS_id->username;
+
+ if (!name[0]) {
+ /* something awful has happened. Give up and die */
+ report(LOG_ERR, "%s %s: no username for login",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ /* Do we have a password? */
+ passwd = p->password;
+
+ if (!passwd[0]) {
+
+ /* no password yet. Either we need to ask for one and expect to get
+ * called again when it's supplied, or we already asked for one and
+ * we should have a reply. */
+
+ switch (p->state) {
+ case STATE_AUTHEN_GETPASS:
+ /* We already asked for a password. This should be the reply */
+ if (data->client_msg) {
+ pwlen = MIN((int) strlen(data->client_msg), MAX_PASSWD_LEN);
+ } else {
+ pwlen = 0;
+ }
+ strncpy(passwd, data->client_msg, pwlen);
+ passwd[pwlen] = '\0';
+ break;
+
+ case STATE_AUTHEN_START:
+ /* if we're at the username stage, and the user has
+ * nopasswd defined, then return a PASS
+ */
+ if (cfg_get_user_nopasswd(name, TAC_PLUS_RECURSE)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ return;
+ }
+ /* FALL-THRU */
+ default:
+ data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
+ data->server_msg = tac_strdup("Password: ");
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
+ p->state = STATE_AUTHEN_GETPASS;
+ return;
+ }
+ }
+ /* Now we have a username and password. Try validating */
+
+ /* Assume the worst */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ verify(name, passwd, data, TAC_PLUS_RECURSE);
+ return;
+}
+
+/*
+ * Process an inbound PAP login. The username & password should be in
+ * the START packet.
+ */
+
+static void
+pap_verify(data)
+struct authen_data *data;
+{
+ char *name, *passwd;
+
+ name = data->NAS_id->username;
+
+ if (!name[0]) {
+ /* something awful has happened. Give up and die */
+ report(LOG_ERR, "%s %s: no username for inbound PAP login",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ /* get the password */
+ passwd = tac_malloc(data->client_dlen + 1);
+ bcopy(data->client_data, passwd, data->client_dlen);
+ passwd[data->client_dlen] = '\0';
+
+ /* Assume the worst */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ verify(name, passwd, data, TAC_PLUS_RECURSE);
+ free(passwd);
+}
+
+
+/* Verify the challenge and id against the response by looking up the
+ * chap secret in the config file. Set data->status appropriately.
+ */
+static void
+chap_verify(data)
+struct authen_data *data;
+{
+ char *name, *secret, *chal, digest[MD5_LEN];
+ char *exp_date, *p;
+ u_char *mdp;
+ char id;
+ int chal_len, inlen;
+ MD5_CTX mdcontext;
+
+ if (!(char) data->NAS_id->username[0]) {
+ report(LOG_ERR, "%s %s: no username for chap_verify",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ name = data->NAS_id->username;
+
+ id = data->client_data[0];
+
+ chal_len = data->client_dlen - 1 - MD5_LEN;
+ if (chal_len < 0) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: chap user=%s, id=%d chal_len=%d",
+ session.peer, session.port, name, (int) id, chal_len);
+
+ /* report_hex(LOG_DEBUG, (u_char *)data->client_data + 1, chal_len); */
+ }
+ /* Assume failure */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+
+ /* Get the secret */
+ secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
+
+ /* If there is no chap password for this user, see if there is a global
+ * password for her that we can use */
+ if (!secret) {
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ }
+ if (!secret) {
+ /* No secret. Fail */
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: No chap or global secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ report(LOG_ERR, "%s %s: %s chap secret %s is not cleartext",
+ session.peer, session.port, name, secret);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ secret = p;
+
+ /* We now have the secret, the id, and the challenge value. Put them all
+ * together, and run them through the MD5 digest algorithm. */
+
+ inlen = sizeof(u_char) + strlen(secret) + chal_len;
+ mdp = (u_char *) tac_malloc(inlen);
+ mdp[0] = id;
+ bcopy(secret, &mdp[1], strlen(secret));
+ chal = data->client_data + 1;
+ bcopy(chal, mdp + strlen(secret) + 1, chal_len);
+ MD5Init(&mdcontext);
+ MD5Update(&mdcontext, mdp, inlen);
+ MD5Final((u_char *) digest, &mdcontext);
+ free(mdp);
+
+ /* Now compare the received response value with the just calculated
+ * digest value. If they are equal, it's a pass, otherwise it's a
+ * failure */
+
+ if (bcmp(digest, data->client_data + 1 + chal_len, MD5_LEN)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
+ set_expiration_status(exp_date, data);
+}
+
+
+/*
+ * Force the "parity" bit to zero on a password before passing it to
+ * des. This is not documented anywhere. (I believe forcing the parity
+ * to zero reduces the integrity of the encrypted keys but this is
+ * what Apple chose to do).
+ */
+void
+pw_bitshift(pw)
+char *pw;
+{
+ int i;
+ unsigned char pws[8];
+
+ /* key is 0 padded */
+ for (i = 0; i < 8; i++)
+ pws[i] = 0;
+
+ /* parity bit is always zero (this seem bogus) */
+ for (i = 0; i < 8 && pw[i]; i++)
+ pws[i] = pw[i] << 1;
+
+ bcopy(pws, pw, 8);
+}
+
+
+static void
+arap_verify(data)
+struct authen_data *data;
+{
+ char nas_chal[8], r_chal[8], r_resp[8], secret[8];
+ char *name, *cfg_secret, *exp_date, *p;
+
+ if (!(char) data->NAS_id->username[0]) {
+ report(LOG_ERR, "%s %s: no username for arap_verify",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ name = data->NAS_id->username;
+
+ bcopy(data->client_data, nas_chal, 8);
+ bcopy(data->client_data + 8, r_chal, 8);
+ bcopy(data->client_data + 8 + 8, r_resp, 8);
+
+ /* Assume failure */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+
+ /* Get the secret */
+ cfg_secret = cfg_get_arap_secret(name, TAC_PLUS_RECURSE);
+
+ /* If there is no arap password for this user, see if there is a global
+ * password for her that we can use */
+ if (!cfg_secret) {
+ cfg_secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ }
+ if (!cfg_secret) {
+ /* No secret. Fail */
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: No arap or global secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+ p = tac_find_substring("cleartext ", cfg_secret);
+ if (!p) {
+ report(LOG_ERR, "%s %s: %s arap secret %s is not cleartext",
+ session.peer, session.port, name, cfg_secret);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ /* need to allocate 8 bytes for secret, even if it's actually shorter */
+ bzero(secret, sizeof(secret));
+ strcpy(secret, p);
+
+ pw_bitshift(secret);
+
+#ifdef ARAP_DES
+ des_init(0);
+ des_setkey(secret);
+ des_endes(nas_chal);
+ des_done();
+#endif /* ARAP_DES */
+
+ /* Now compare the remote's response value with the just calculated one
+ * value. If they are equal, it's a pass, otherwise it's a failure */
+
+ if (bcmp(nas_chal, r_resp, 8)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+#ifdef ARAP_DES
+ /* Now calculate the response to the remote's challenge */
+ des_init(0);
+ des_setkey(secret);
+ des_endes(r_chal);
+ des_done();
+#endif /* ARAP_DES */
+
+ data->server_data = tac_malloc(8);
+ data->server_dlen = 8;
+ bcopy(r_chal, data->server_data, 8);
+
+ exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
+ set_expiration_status(exp_date, data);
+}
+
+
+#ifdef MSCHAP
+
+/* Following code is added for ms-chap */
+static void
+mschap_desencrypt(clear, str, cypher)
+char *clear;
+unsigned char *str;
+unsigned char *cypher;
+{
+ unsigned char key[8];
+
+ /* des_state_type *des_state = NULL; */
+
+ memset(key, 0, 8);
+
+ /* Copy the key inserting parity bits */
+
+#ifdef old
+ /* This method makes it obvious what we are doing */
+
+#define getbit(bit,array) ((array[bit/8] & (1 << (7-(bit%8)))) !=0)
+#define setbit(bit,array) (array[bit/8] |= (1 << (7-(bit%8))))
+
+ {
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < 56; i++) {
+ if (i && (i % 7 == 0)) {
+ j++;
+ }
+ if (getbit(i, str))
+ setbit(j, key);
+ j++;
+ }
+ }
+#else
+ /* this is a little more cryptic, but faster basicly we are insering a
+ * bit into the stream after every 7 bits */
+
+ key[0] = ((str[0] & 0xfe));
+ key[1] = ((str[0] & 0x01) << 7) | ((str[1] & 0x0fc) >> 1);
+ key[2] = ((str[1] & 0x03) << 6) | ((str[2] & 0x0f8) >> 2);
+ key[3] = ((str[2] & 0x07) << 5) | ((str[3] & 0x0f0) >> 3);
+ key[4] = ((str[3] & 0x0f) << 4) | ((str[4] & 0x0e0) >> 4);
+ key[5] = ((str[4] & 0x1f) << 3) | ((str[5] & 0x0c0) >> 5);
+ key[6] = ((str[5] & 0x3f) << 2) | ((str[6] & 0x080) >> 6);
+ key[7] = ((str[6] & 0x7f) << 1);
+
+#endif
+
+ /* copy clear to cypher, cause our des encrypts in place */
+ memcpy(cypher, clear, 8);
+/*
+ des_init(0,&des_state);
+ des_setkey(des_state,key);
+ des_endes(des_state,cypher);
+ des_done(des_state);
+*/
+#ifdef MSCHAP_DES
+ des_init(0);
+ des_setkey(key);
+ des_endes(cypher);
+ des_done();
+#endif /* MSCHAP_DES */
+}
+
+
+static void
+mschap_deshash(clear, cypher)
+char *clear;
+char *cypher;
+{
+ mschap_desencrypt(MSCHAP_KEY, clear, cypher);
+}
+
+
+static void
+mschap_lmpasswordhash(password, passwordhash)
+char *password;
+char *passwordhash;
+{
+ unsigned char upassword[15];
+ int i = 0;
+
+ memset(upassword, 0, 15);
+ while (password[i]) {
+ upassword[i] = toupper(password[i]);
+ i++;
+ };
+
+ mschap_deshash(&upassword[0], &passwordhash[0]);
+ mschap_deshash(&upassword[7], &passwordhash[8]);
+}
+
+
+static void
+mschap_challengeresponse(challenge, passwordhash, response)
+char *challenge;
+char *passwordhash;
+char *response;
+{
+ char zpasswordhash[21];
+
+ memset(zpasswordhash, 0, 21);
+ memcpy(zpasswordhash, passwordhash, 16);
+
+ mschap_desencrypt(challenge, &zpasswordhash[0], &response[0]);
+ mschap_desencrypt(challenge, &zpasswordhash[7], &response[8]);
+ mschap_desencrypt(challenge, &zpasswordhash[14], &response[16]);
+}
+
+
+void
+mschap_lmchallengeresponse(challenge, password, response)
+char *challenge;
+char *password;
+char *response;
+{
+ char passwordhash[16];
+
+ mschap_lmpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+}
+
+
+static int
+mschap_unicode_len(password)
+char *password;
+{
+ int i;
+
+ i = 0;
+ while ((password[i] || password[i + 1]) && (i < 512)) {
+ i += 2;
+ }
+
+ return i;
+}
+
+
+static void
+mschap_ntpasswordhash(password, passwordhash)
+char *password;
+char *passwordhash;
+{
+ MD4_CTX context;
+ int i;
+ char *cp;
+ unsigned char unicode_password[512];
+
+ memset(unicode_password, 0, 512);
+
+ i = 0;
+ memset(unicode_password, 0, 512);
+ cp = password;
+ while (*cp) {
+ unicode_password[i++] = *cp++;
+ unicode_password[i++] = '\0';
+ }
+
+ MD4Init(&context);
+ MD4Update(&context, unicode_password,
+ mschap_unicode_len(unicode_password));
+ MD4Final(passwordhash, &context);
+}
+
+
+void
+mschap_ntchallengeresponse(challenge,
+ password,
+ response)
+char *challenge;
+char *password;
+char *response;
+{
+ char passwordhash[16];
+
+ mschap_ntpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+}
+
+
+/* Verify the challenge and id against the response by looking up the
+ * ms-chap secret in the config file. Set data->status appropriately.
+ */
+static void
+mschap_verify(data)
+struct authen_data *data;
+{
+ char *name, *secret, *chal, *resp;
+ char *exp_date, *p;
+ char id;
+ int chal_len;
+ char lmresponse[24];
+ char ntresponse[24];
+ int bcmp_status;
+
+ if (!(char) data->NAS_id->username[0]) {
+ report(LOG_ERR, "%s %s: no username for mschap_verify",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ name = data->NAS_id->username;
+
+ id = data->client_data[0];
+
+ chal_len = data->client_dlen - 1 - MSCHAP_DIGEST_LEN;
+ if (data->client_dlen <= (MSCHAP_DIGEST_LEN + 2)) {
+ /* Invalid packet or NULL challenge */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: ms-chap user=%s, id=%d chal_len=%d",
+ session.peer, session.port, name, (int) id, chal_len);
+
+ /* report_hex(LOG_DEBUG, (u_char *)data->client_data + 1, chal_len); */
+ }
+ /* Assume failure */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+
+ /* Get the secret */
+ secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
+
+ /* If there is no ms-chap password for this user, see if there is a
+ * global password for her that we can use */
+ if (!secret) {
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ }
+ if (!secret) {
+ /* No secret. Fail */
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: No ms-chap or global secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ report(LOG_ERR, "%s %s: %s ms-chap secret %s is not cleartext",
+ session.peer, session.port, name, secret);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+ secret = p;
+
+ /* We now have the secret, the id, and the challenge value. Put them all
+ * together, and run them through the MD4 digest algorithm. */
+ chal = data->client_data + 1;
+ resp = data->client_data + 1 + chal_len;
+
+ mschap_lmchallengeresponse(chal, secret, lmresponse);
+ mschap_ntchallengeresponse(chal, secret, ntresponse);
+
+ /* Now compare the received response value with the just calculated
+ * digest value. If they are equal, it's a pass, otherwise it's a
+ * failure */
+ if (resp[48])
+ bcmp_status = bcmp(ntresponse, &resp[24], 24);
+ else
+ bcmp_status = bcmp(lmresponse, &resp[0], 24);
+
+ if (bcmp_status) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
+ set_expiration_status(exp_date, data);
+}
+
+#endif /* MSCHAP */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+
+/* internal state variables */
+#define STATE_AUTHEN_START 0 /* no requests issued */
+#define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+#define STATE_AUTHEN_GETPASS 2 /* password has been requested */
+
+struct private_data {
+ char password[MAX_PASSWD_LEN + 1];
+ int state;
+};
+
+/*
+ * Default tacacs login authentication function. Wants a username
+ * and a password, and tries to verify them.
+ *
+ * Choose_authen will ensure that we already have a username before this
+ * gets called.
+ *
+ * We will query for a password and keep it in the method_data.
+ *
+ * Any strings returned via pointers in authen_data must come from the
+ * heap. They will get freed by the caller.
+ *
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
+int
+default_v0_fn(data)
+struct authen_data *data;
+{
+ char *name, *passwd;
+ struct private_data *p;
+ char *prompt;
+
+ p = (struct private_data *) data->method_data;
+
+ /* An abort has been received. Clean up and return */
+ if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+ return (1);
+ }
+ /* Initialise method_data if first time through */
+ if (!p) {
+ p = (struct private_data *) tac_malloc(sizeof(struct private_data));
+ bzero(p, sizeof(struct private_data));
+ data->method_data = p;
+ p->state = STATE_AUTHEN_START;
+ }
+
+ /* Unless we're enabling, we need a username */
+ if (data->service != TAC_PLUS_AUTHEN_SVC_ENABLE &&
+ !(char) data->NAS_id->username[0]) {
+ switch (p->state) {
+
+ case STATE_AUTHEN_GETUSER:
+ /* we have previously asked for a username but none came back.
+ * This is a gross error */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s: No username supplied after GETUSER",
+ session.peer);
+ return (0);
+
+ case STATE_AUTHEN_START:
+ /* No username. Try requesting one */
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETUSER;
+ if (data->service == TAC_PLUS_AUTHEN_SVC_LOGIN) {
+ prompt = "\nUser Access Verification\n\nUsername: ";
+ } else {
+ prompt = "Username: ";
+ }
+ data->server_msg = tac_strdup(prompt);
+ p->state = STATE_AUTHEN_GETUSER;
+ return (0);
+
+ default:
+ /* something awful has happened. Give up and die */
+ report(LOG_ERR, "%s: default_fn bad state %d",
+ session.peer, p->state);
+ return (1);
+ }
+ }
+
+ /* we now have a username if we needed one */
+ name = data->NAS_id->username;
+
+ /* Do we have a password? */
+ passwd = p->password;
+
+ if (!passwd[0]) {
+
+ /* no password yet. Either we need to ask for one and expect to get
+ * called again, or we asked but nothing came back, which is fatal */
+
+ switch (p->state) {
+ case STATE_AUTHEN_GETPASS:
+ /* We already asked for a password. This should be the reply */
+ strncpy(passwd, data->client_msg, MAX_PASSWD_LEN);
+ passwd[MAX_PASSWD_LEN + 1] = '\0';
+ break;
+
+ default:
+ data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
+ data->server_msg = tac_strdup("Password: ");
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
+ p->state = STATE_AUTHEN_GETPASS;
+ return (0);
+ }
+ }
+
+ /* We have a username and password. Try validating */
+
+ if (STREQ(name, DEFAULT_USERNAME)) {
+ /* Never authenticate this user. It's for authorization only */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ if (debug) {
+ report(LOG_DEBUG,
+ "authentication query for '%s' %s from %s rejected",
+ name && name[0] ? name : "unknown",
+ session.port, session.peer);
+ }
+ return(0);
+ }
+
+ /* Assume the worst */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ switch (data->service) {
+ case TAC_PLUS_AUTHEN_SVC_NASI:
+ case TAC_PLUS_AUTHEN_SVC_LOGIN:
+ case TAC_PLUS_AUTHEN_SVC_PPP:
+ verify(name, passwd, data, TAC_PLUS_RECURSE);
+ if (debug)
+ report(LOG_INFO, "login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ data->NAS_id->NAS_port && data->NAS_id->NAS_port[0] ?
+ data->NAS_id->NAS_port : "unknown",
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ break;
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s: Bogus service value %d from packet",
+ session.peer, data->service);
+ break;
+ }
+
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+
+ switch (data->status) {
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ return (0);
+ default:
+ report(LOG_ERR, "%s: default_v0_fn can't set status %d",
+ session.peer, data->status);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return (1);
+ }
+}
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+static int acctfd = 0;
+
+/* Make a acct entry into the accounting file for accounting.
+ Return 1 on error */
+
+static int
+acct_write(string)
+ char *string;
+{
+ if (write(acctfd, string, strlen(string)) != strlen(string)) {
+ report(LOG_ERR, "%s: couldn't write acct file %s %s",
+ session.peer,
+ session.acctfile, sys_errlist[errno]);
+ return(1);
+ }
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "'%s'", string);
+
+ return(0);
+}
+
+/* Write a string or "unknown" into the accounting file.
+ Return 1 on error */
+static int
+acct_write_field(string)
+ char *string;
+{
+ if (string && string[0]) {
+ if (acct_write(string))
+ return(1);
+ } else {
+ if (acct_write("unknown"))
+ return(1);
+ }
+ return(0);
+}
+
+int
+do_acct(rec)
+struct acct_rec *rec;
+{
+ int i, errors;
+ time_t t = time(NULL);
+ char *ct = ctime(&t);
+
+ ct[24] = '\0';
+
+ if (!acctfd) {
+ acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND,0640);
+ if (acctfd < 0) {
+ report(LOG_ERR, "Can't open acct file %s -- %s",
+ session.acctfile, sys_errlist[errno]);
+ return(1);
+ }
+ }
+
+ if (!tac_lockfd(session.acctfile, acctfd)) {
+ rec->admin_msg = tac_strdup("Cannot lock log file");
+ report(LOG_ERR, "%s: Cannot lock %s",
+ session.peer, session.acctfile);
+ return(1);
+ }
+
+ errors = 0;
+
+ errors += acct_write(ct);
+ errors += acct_write("\t");
+
+ errors += acct_write_field(rec->identity->NAS_name);
+ errors += acct_write("\t");
+
+ errors += acct_write_field(rec->identity->username);
+ errors += acct_write("\t");
+
+ errors += acct_write_field(rec->identity->NAS_port);
+ errors += acct_write("\t");
+
+ errors += acct_write_field(rec->identity->NAC_address);
+ errors += acct_write("\t");
+
+ switch(rec->acct_type) {
+ case ACCT_TYPE_UPDATE:
+ errors += acct_write("update\t");
+ break;
+ case ACCT_TYPE_START:
+ errors += acct_write("start\t");
+ break;
+ case ACCT_TYPE_STOP:
+ errors += acct_write("stop\t");
+ break;
+ default:
+ errors += acct_write("unknown\t");
+ break;
+ }
+
+ for (i=0; i < rec->num_args; i++) {
+ errors += acct_write(rec->args[i]);
+ if (i < (rec->num_args-1))
+ errors += acct_write("\t");
+ }
+ errors += acct_write("\n");
+
+ close(acctfd);
+ acctfd = 0;
+
+ if (errors) {
+ return(1);
+ }
+ return (0);
+}
+
+int
+wtmp_entry (line, name, host, utime)
+ char *line, *name, *host;
+ time_t utime;
+{
+ struct utmp entry;
+
+ if (!wtmpfile) {
+ return(1);
+ }
+
+ bzero(&entry, sizeof entry);
+
+ if (strlen(line) < sizeof entry.ut_line)
+ strcpy(entry.ut_line, line);
+ else bcopy(line, entry.ut_line, sizeof entry.ut_line);
+
+ if (strlen(name) < sizeof entry.ut_name)
+ strcpy(entry.ut_name, name);
+ else bcopy(name, entry.ut_name, sizeof entry.ut_name);
+
+#ifndef SOLARIS
+ if (strlen(host) < sizeof entry.ut_host)
+ strcpy(entry.ut_host, host);
+ else bcopy(host, entry.ut_host, sizeof entry.ut_host);
+#endif
+ entry.ut_time = utime;
+
+ wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0644);
+ if (wtmpfd < 0) {
+ report(LOG_ERR, "Can't open wtmp file %s -- %s",
+ wtmpfile, sys_errlist[errno]);
+ return(1);
+ }
+
+ if (!tac_lockfd(wtmpfile, wtmpfd)) {
+ report(LOG_ERR, "%s: Cannot lock %s", session.peer, wtmpfile);
+ return(1);
+ }
+
+ if (write(wtmpfd, &entry, sizeof entry) != (sizeof entry)) {
+ report(LOG_ERR, "%s: couldn't write wtmp file %s %s",
+ session.peer, wtmpfile, sys_errlist[errno]);
+ return(1);
+ }
+
+ close(wtmpfd);
+
+ if (debug & DEBUG_ACCT_FLAG) {
+ report(LOG_DEBUG, "wtmp: %s, %s %s %d", line, name, host, utime);
+ }
+
+ return(0);
+}
+
+char *
+find_attr_value (attr, args, cnt)
+ char *attr, **args;
+ int cnt;
+{
+ int i;
+
+ for (i=0; i < cnt; i++) {
+ if (!strncmp(attr, args[i], strlen(attr))) {
+ char *ptr;
+
+ for (ptr = args[i]; ptr && *ptr; ptr++) {
+ if ((*ptr == '*') || (*ptr == '=')) {
+ return(ptr+1);
+ }
+ }
+ return(NULL);
+ }
+ }
+ return(NULL);
+}
+
+int
+do_wtmp(rec)
+ struct acct_rec *rec;
+{
+ time_t now = time(NULL);
+ char *service;
+ char *elapsed_time, *start_time;
+ time_t start_utime = 0, stop_utime = 0, elapsed_utime = 0;
+
+
+ switch(rec->acct_type) {
+ case ACCT_TYPE_START:
+ case ACCT_TYPE_STOP:
+ break;
+
+ case ACCT_TYPE_UPDATE:
+ default:
+ return(0);
+ }
+
+ service = find_attr_value("service", rec->args, rec->num_args);
+
+ if (!service) {
+ /* An error */
+ return(1);
+ }
+
+ if (STREQ(service, "system")) {
+ if (rec->acct_type == ACCT_TYPE_START) {
+ /* A reload */
+ wtmp_entry("~", "", session.peer, now);
+ }
+ return(0);
+ }
+
+ if (rec->acct_type != ACCT_TYPE_STOP) {
+ return(0);
+ }
+
+ /*
+ * Since xtacacs logged start records containing the peer address
+ * for a connection, we have to generate them from T+ stop records.
+ * Might as well do this for exec records too.
+ */
+
+ elapsed_time = find_attr_value("elapsed_time", rec->args, rec->num_args);
+
+ if (elapsed_time) {
+ elapsed_utime = strtol(elapsed_time, NULL, 10);
+ }
+
+ start_time = find_attr_value("start_time", rec->args, rec->num_args);
+
+ /*
+ * Use the start_time if there is one. If not (e.g. the NAS may
+ * not know the time), assume the stop time is now, and calculate
+ * the rest
+ */
+
+ if (start_time) {
+ start_utime = strtol(start_time, NULL, 10);
+ stop_utime = start_utime + elapsed_utime;
+ } else {
+ start_utime = now - elapsed_utime;
+ stop_utime = now;
+ }
+
+ if (STREQ(service, "slip") || STREQ(service, "ppp")) {
+ char *dest_addr = find_attr_value("addr", rec->args, rec->num_args);
+
+ /* The start record */
+ wtmp_entry(rec->identity->NAS_port,
+ rec->identity->username,
+ dest_addr,
+ start_utime);
+
+ /* The stop record */
+ wtmp_entry(rec->identity->NAS_port,
+ "",
+ dest_addr,
+ stop_utime);
+ return(0);
+ }
+
+ if (STREQ(service, "shell")) {
+ /* Start */
+ wtmp_entry(rec->identity->NAS_port,
+ rec->identity->username,
+ session.peer,
+ start_utime);
+
+ /* Stop */
+ wtmp_entry(rec->identity->NAS_port,
+ "",
+ session.peer,
+ stop_utime);
+ return(0);
+ }
+ return(0);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "regexp.h"
+
+static int get_nas_svc();
+static int authorize_cmd();
+static int authorize_exec();
+static int authorize_svc();
+static void post_authorization();
+static int pre_authorization();
+
+/* Return 0 is data->status is valid */
+int
+do_author(data)
+struct author_data *data;
+{
+ char *username = data->id->username;
+ int status;
+ int svc;
+ char *cmd, *protocol, *svcname;
+#ifdef USE_PAM
+ char *pam_service= NULL;
+#endif
+ protocol = NULL;
+
+ data->status = AUTHOR_STATUS_FAIL; /* for safety */
+
+ data->output_args = NULL;
+ data->num_out_args = 0;
+
+ /* If this user doesn't exist in our configs, do the default */
+
+ if (!cfg_user_exists(username) && !cfg_user_exists(DEFAULT_USERNAME)) {
+
+ if (cfg_no_user_permitted()) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+ "user '%s' or '%s' not found, permitted by default",
+ username, DEFAULT_USERNAME);
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->output_args = NULL;
+ data->num_out_args = 0;
+ return (0);
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+ "user '%s' or '%s' not found, denied by default",
+ username, DEFAULT_USERNAME);
+ data->status = AUTHOR_STATUS_FAIL;
+ return (0);
+ }
+
+ if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'",
+ DEFAULT_USERNAME, username);
+ }
+ username = DEFAULT_USERNAME;
+ }
+
+ /* See if there's a program defined which will do authorization for us */
+ if (pre_authorization(username, data))
+ return(0);
+
+ /*
+ * Decide what kind of authorization request this is. Currently
+ * one of: exec, cmd, slip, arap, ppp or <string>
+ *
+ * If it's a command typed to the exec, return its text in cmd.
+ *
+ * If it's a ppp request, return the protocol name in protocol.
+ */
+
+ svc = get_nas_svc(data, &cmd, &protocol, &svcname);
+
+ if (!svc) {
+ /* if we can't identify the service in the request it's an error */
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg =
+ tac_strdup("No identifiable service/protocol in authorization request");
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "user %s %s", username, data->admin_msg);
+ }
+ return (0);
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "user '%s' found", username);
+
+#ifdef MAXSESS
+ /* Never permit if they're going over their session limit */
+ switch (svc) {
+ case N_svc_arap:
+ case N_svc_ppp:
+ case N_svc_slip:
+ case N_svc_exec:
+/* case N_svc: */
+ if (maxsess_check_count(username, data)) {
+ return(0);
+ }
+
+ default:
+ break;
+ }
+#endif /* MAXSESS */
+
+#ifdef USE_PAM
+ /* Check PAM Authorization */
+ switch (svc) {
+ case N_svc_ppp:
+ case N_svc_exec:
+if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM Authorization begin for user %s",data->id->username);
+ if(tac_pam_authorization(data->id->username,data,pam_service))
+ {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM Authorization Fail");
+ return(0);
+ }
+} /* Pam_service */
+ default:
+ break;
+ }
+#endif
+
+ switch(svc) {
+ default:
+ report(LOG_ERR, "%s: Bad service type %d", session.peer, svc);
+ data->status = AUTHOR_STATUS_FAIL;
+ return(0);
+
+ case N_svc_cmd:
+ /* A command authorisation request */
+ status = authorize_cmd(username, cmd, data);
+ break;
+
+ case N_svc_exec:
+ if (authorize_exec(username, data))
+ return(0);
+ /* FALLTHRU */
+
+ case N_svc_arap:
+ case N_svc_ppp:
+ case N_svc_slip:
+ status = authorize_svc(username, svc, protocol, NULL, data);
+ break;
+
+ case N_svc:
+ status = authorize_svc(username, svc, protocol, svcname, data);
+ break;
+ }
+
+ post_authorization(username, data);
+ return(status);
+}
+
+/* If an before-authorization program has been specified, call it.
+
+ A return value of 1 means no further authorization is required
+*/
+
+static int
+pre_authorization(username, data)
+char *username;
+struct author_data *data;
+{
+ int status;
+ char **out_args;
+ int out_cnt, i;
+ char *cmd;
+ char error_str[255];
+ int error_len = 255;
+
+ out_cnt = 0;
+ out_args = NULL;
+
+ /* If a before-authorization program exists, call it to see how to
+ proceed */
+
+ cmd = cfg_get_pvalue(username, TAC_IS_USER,
+ S_before, TAC_PLUS_RECURSE);
+ if (!cmd)
+ return(0);
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Before authorization call: %s", cmd);
+
+ status = call_pre_process(cmd, data, &out_args, &out_cnt, error_str,
+ error_len);
+
+ switch (status) {
+ default:
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)",
+ cmd, status);
+
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg =
+ tac_strdup("Illegal return status from pre-authorization command");
+ data->msg = tac_strdup(error_str);
+ data->num_out_args = 0;
+ data->output_args = NULL;
+ /* throw away out_args */
+ for(i=0; i < out_cnt; i++) {
+ free(out_args[i]);
+ }
+ if (out_args) {
+ free(out_args);
+ }
+ return(1);
+
+ case 0: /* Permit */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns 0 (unconditional permit)", cmd);
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+
+ /* throw away out_args */
+ for(i=0; i < out_cnt; i++) {
+ free(out_args[i]);
+ }
+ if (out_args) {
+ free(out_args);
+ }
+ return(1);
+
+ case 1: /* Deny */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
+ cmd, status);
+
+ data->status = AUTHOR_STATUS_FAIL;
+ data->msg = tac_strdup(error_str);
+ data->num_out_args = 0;
+ data->output_args = NULL;
+
+ /* throw away out_args */
+ for(i=0; i < out_cnt; i++) {
+ free(out_args[i]);
+ }
+ if (out_args) {
+ free(out_args);
+ }
+ return(1);
+
+ case 2: /* Use replacement AV pairs from program as final result */
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)",
+ cmd, status);
+ for(i=0; i < out_cnt; i++)
+ report(LOG_DEBUG, "%s", out_args[i]);
+ }
+
+ /* and install the new set of AV pairs as output */
+ data->output_args = out_args;
+ data->num_out_args = out_cnt;
+ data->status = AUTHOR_STATUS_PASS_REPL;
+ return(1); /* no more processing required */
+
+ case 3: /* deny, but return attributes and server-msg to NAS */
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)",
+ cmd, status);
+ for(i=0; i < out_cnt; i++)
+ report(LOG_DEBUG, "%s", out_args[i]);
+ }
+
+ /* and install the new set of AV pairs as output */
+ data->output_args = out_args;
+ data->num_out_args = out_cnt;
+ data->msg = tac_strdup(error_str);
+ data->status = AUTHOR_STATUS_FAIL;
+ return(1); /* no more processing required */
+ }
+}
+
+/* If an after-authorization program has been specified, call it. It
+ can rewrite the output arguments in the authorization data, or
+ change the authorization status by calling an external program.
+*/
+
+static void
+post_authorization(username, data)
+ char *username;
+ struct author_data *data;
+{
+ char **out_args;
+ int out_cnt, i;
+ int status;
+ char *after = cfg_get_pvalue(username, TAC_IS_USER,
+ S_after, TAC_PLUS_RECURSE);
+ if (!after)
+ return;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "After authorization call: %s", after);
+
+ status = call_post_process(after, data, &out_args, &out_cnt);
+
+ if (status != 2) {
+ /* throw away out_args */
+ for(i=0; i < out_cnt; i++) {
+ free(out_args[i]);
+ }
+ free(out_args);
+ }
+
+ switch (status) {
+ default:
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+ "cmd %s returns %d (Error)", after, status);
+
+ data->status = AUTHOR_STATUS_ERROR;
+ return;
+
+ case 0: /* Permit */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns 0 (no change)", after);
+ return;
+
+ case 1: /* Deny */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)",
+ after, status);
+
+ data->status = AUTHOR_STATUS_FAIL;
+ return;
+
+ case 2:
+ /* Use replacement AV pairs from program */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s returns 2 (replace & continue)",
+ after);
+
+ /* Free any existing AV output pairs */
+ if (data->num_out_args) {
+ for(i=0; i < data->num_out_args; i++) {
+ free(data->output_args[i]);
+ }
+ free(data->output_args);
+ data->output_args = NULL;
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "status is now AUTHOR_STATUS_PASS_REPL");
+ }
+
+ data->status = AUTHOR_STATUS_PASS_REPL;
+ data->output_args = out_args;
+ data->num_out_args = out_cnt;
+ return;
+ }
+}
+
+
+/* Return a pointer to the value part of an attr=value string */
+static char *
+value(s)
+char *s;
+{
+ while (*s && *s != '=' && *s != '*')
+ s++;
+ if (*s)
+ return (++s);
+ return (NULL);
+}
+
+/* Reassemble the command arguments as typed by the user, out of the
+ array of args we received. Return "" if there are no arguments */
+
+static char *
+assemble_args(data)
+struct author_data *data;
+{
+ char *buf;
+ int i;
+ char *nas_arg, *v;
+ int len;
+
+ len = 0;
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+ if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg"))==0)
+ len += strlen(value(nas_arg)) + 1;
+ }
+
+ if (len <= 0) {
+ return(tac_strdup(""));
+ }
+
+ buf = tac_malloc(len);
+ buf[0] = '\0';
+
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+ if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg")))
+ continue;
+
+ v = value(nas_arg);
+ if (!v) {
+ free(buf);
+ return (NULL);
+ }
+ strcat(buf, v);
+ if (i < (data->num_in_args - 1))
+ strcat(buf, " ");
+ }
+ return (buf);
+}
+
+
+/* See if an exec is authorized. Either the user has explicitly
+ authorized the exec, or she has authorized some commands (which
+ implicitly authorizes an exec), or the default is permit.
+
+ If she has explicitly authorized an exec, we need to process its
+ attribute=value pairs. We indicate this by returning zero to the
+ caller.
+
+ Otherwise, we return 1, indicating no further processing is
+ required for this request. */
+
+static int
+authorize_exec(user, data)
+char *user;
+struct author_data *data;
+{
+ NODE *svc;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec authorization request for %s", user);
+
+ /* Is an exec explicitly configured? If so, return 0 so we know to
+ process its attributes */
+
+ svc = cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE);
+ if (svc) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec is explicitly permitted by line %d",
+ svc->line);
+ return (0);
+ }
+
+ /* No exec is configured. Are any commands configured? */
+ svc = cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE);
+ if (svc) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec permitted because commands are configured");
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->output_args = NULL;
+ data->num_out_args = 0;
+ return (1);
+ }
+
+ /* No exec or commands configured. What's the default? */
+ if (cfg_user_svc_default_is_permit(user)) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec permitted by default");
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->output_args = NULL;
+ data->num_out_args = 0;
+ return (1);
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec denied by default");
+
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ return(1);
+}
+
+/* Is an exec command authorized per our database(s)?
+ Return 0 if status is valid */
+
+static int
+authorize_cmd(user, cmd, data)
+char *user, *cmd;
+struct author_data *data;
+{
+ NODE *node;
+ char *args;
+ int match;
+
+ args = assemble_args(data);
+
+ if (!cmd) {
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg = tac_strdup("No command found");
+ report(LOG_ERR, "%s: %s %s", session.peer, cmd, data->admin_msg);
+ data->num_out_args = 0;
+ return (0);
+ }
+
+ node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE);
+
+ /* The command does not exist. Do the default */
+ if (!node) {
+
+ if (cfg_user_svc_default_is_permit(user)) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s does not exist, permitted by default",
+ cmd);
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ if (args)
+ free(args);
+ return(0);
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "cmd %s does not exist, denied by default",
+ cmd);
+
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ if (args)
+ free(args);
+ return(0);
+ }
+
+ /* The command exists. The default if nothing matches is DENY */
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+
+ for (node=node->value1; node && args; node = node->next) {
+ match = regexec((regexp *) node->value1, args);
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
+ node->line, cmd,
+ node->type == N_permit ? "permit" : "deny",
+ node->value, args, (match ? "" : "no "));
+ }
+
+ if (!match)
+ continue;
+
+ switch (node->type) {
+ case N_permit:
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "%s %s permitted by line %d",
+ cmd, args, node->line);
+ }
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ break;
+ case N_deny:
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "%s %s denied by line %d",
+ cmd, args, node->line);
+ }
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ break;
+ default:
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg = tac_strdup("Server error illegal configuration node");
+ report(LOG_ERR, "%s: %s %s %s",
+ session.peer, cmd, args, data->admin_msg);
+ break;
+ }
+ if (args)
+ free(args);
+ args = NULL;
+ return (0);
+ }
+ if (args)
+ free(args);
+ return (0);
+}
+
+static int
+is_separator(ch)
+char ch;
+{
+ return (ch == '=' || ch == '*');
+}
+
+/* check an attr=value pair for well-formedness */
+static int
+arg_ok(arg)
+char *arg;
+{
+ char *p = arg;
+
+ /* It must contain an attribute */
+ if (!*p)
+ return(0);
+
+ for(p=arg; *p; p++) {
+ if (is_separator(*p)) {
+ if (p == arg) /* no attribute */
+ return(0);
+ return(1);
+ }
+ }
+ /* no separator */
+ return(0);
+}
+
+
+/* return 1 if attrs match, 0 otherwise */
+static int
+match_attrs(nas_arg, server_arg)
+char *nas_arg, *server_arg;
+{
+ while (*nas_arg && *server_arg) {
+ if (is_separator(*nas_arg) && is_separator(*server_arg)) {
+ return (1);
+ }
+ if (*nas_arg != *server_arg)
+ return (0);
+ nas_arg++;
+ server_arg++;
+ }
+ return (0);
+}
+
+/* return 1 if values match, 0 otherwise */
+static int
+match_values(nas_arg, server_arg)
+char *nas_arg, *server_arg;
+{
+ while (*nas_arg &&
+ *server_arg &&
+ !is_separator(*nas_arg)) {
+ nas_arg++;
+ server_arg++;
+ }
+
+ if (!*nas_arg)
+ return(0);
+
+ /* skip separator */
+ nas_arg++;
+ if (*server_arg)
+ server_arg++;
+
+ /* compare values */
+ return(STREQ(nas_arg, server_arg));
+}
+
+/* Return 1 if arg is mandatory, 0 otherwise */
+static int
+mandatory(arg)
+char *arg;
+{
+ char *p = arg;
+
+ while (*p && !is_separator(*p))
+ p++;
+
+ /* if we're not at the end, this must be the separator */
+ if (*p && !is_separator(*p)) {
+ report(LOG_ERR, "%s: Error on arg %s cannot find separator",
+ session.peer, arg);
+ return (0);
+ }
+ return (*p == '=');
+}
+
+static int
+optional(arg)
+char *arg;
+{
+ return (!mandatory(arg));
+}
+
+/* PPP-LCP requests are a special case. If they are not explicitly
+ configured, but there are other ppp services explicitly configured,
+ we admit (return 0) any PPP-LCP request */
+
+static int
+ppp_lcp_allowed(svc, protocol, user)
+ int svc;
+ char *user, *protocol;
+{
+ /* This is not a ppp/lcp request. Just Say No */
+ if (!(svc == N_svc_ppp &&
+ protocol &&
+ STREQ(protocol, "lcp")))
+ return(0);
+
+ /* It is an LCP request. Are there PPP services configured */
+ if (cfg_ppp_is_configured(user, TAC_PLUS_RECURSE)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG,
+ "ppp/lcp request permitted (ppp is configured for %s)",
+ user);
+ }
+ return(1);
+ }
+
+ /* It is an LCP request but no PPP services are configured */
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "ppp/lcp request denied (ppp not configured for %s)",
+ user);
+ }
+ return(0);
+}
+
+/* Return 0 means data->status is valid */
+static int
+authorize_svc(user, svc, protocol, svcname, data)
+ char *user;
+ int svc;
+ char *svcname;
+ struct author_data *data;
+ char *protocol; /* valid only if svc == ppp */
+{
+ int max_args;
+ char **out_args, **outp;
+ char *nas_arg, *cfg_arg;
+ int i, j;
+ char **cfg_args;
+ char **cfg_argp;
+ int deny_by_default;
+ NODE *node;
+
+ int replaced = 0;
+ int added = 0;
+ int cfg_cnt;
+
+ /* Does this service exist? */
+ node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE);
+
+ if (!node) {
+ /* Service not found. If the default is permit, or this is an
+ PPP/LCP request and other ppp services are configured,
+ we'll allow it. */
+
+ if (cfg_user_svc_default_is_permit(user)) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+ "svc=%s protocol=%s svcname=%s not found, permitted by default",
+ cfg_nodestring(svc),
+ protocol ? protocol : "",
+ svcname ? svcname : "");
+
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+ return(0);
+ }
+
+ if (ppp_lcp_allowed(svc, protocol, user)) {
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+ return(0);
+ }
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "svc=%s protocol=%s not found, denied by default",
+ cfg_nodestring(svc), protocol ? protocol : "");
+
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+ return(0);
+ }
+
+ /* Get server args configured in the config file. */
+ cfg_args = cfg_get_svc_attrs(node, &deny_by_default);
+
+ /* Check the nas args for well-formedness */
+ for (i = 0; i < data->num_in_args; i++) {
+ if (!arg_ok(data->input_args[i])) {
+ char buf[MAX_INPUT_LINE_LEN+50];
+ sprintf(buf, "Illegal arg %s from NAS", data->input_args[i]);
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg = tac_strdup(buf);
+ report(LOG_ERR, "%s: Error %s", session.peer, buf);
+
+ /* free any server arguments */
+ for(cfg_argp = cfg_args; cfg_args && *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+ return (0);
+ }
+ }
+
+ /* How many configured AV pairs are there ? */
+ for (cfg_cnt = 0; cfg_args && cfg_args[cfg_cnt];)
+ cfg_cnt++;
+
+ /* Allocate space for in + out args */
+ max_args = cfg_cnt + data->num_in_args;
+ out_args = (char **) tac_malloc((max_args + 1) * sizeof(char *));
+ outp = out_args;
+ data->num_out_args = 0;
+
+ bzero(out_args, (max_args + 1) * sizeof(char *));
+
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+
+ /* always pass these pairs through unchanged */
+ if (match_attrs(nas_arg, "service=") ||
+ match_attrs(nas_arg, "protocol=") ||
+ match_attrs(nas_arg, "cmd=")) {
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s (passed thru)", nas_arg);
+ }
+ *outp++ = tac_strdup(nas_arg);
+ data->num_out_args++;
+ continue;
+ }
+
+ /* NAS AV pair is mandatory */
+ if (mandatory(nas_arg)) {
+
+ /* a). look for an exact attribute,value match in the daemon's
+ mandatory list. If found, add the AV pair to the output */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (optional(cfg_arg))
+ continue;
+
+ if (STREQ(nas_arg, cfg_arg)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (a)",
+ nas_arg, cfg_arg, nas_arg);
+ }
+ *outp++ = tac_strdup(nas_arg);
+ data->num_out_args++;
+ goto next_nas_arg;
+ }
+ }
+
+ /* b). If an exact match doesn't exist, look in the
+ daemon's optional list for the first attribute
+ match. If found, add the NAS AV pair to the output */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (mandatory(cfg_arg))
+ continue;
+
+ if (match_attrs(nas_arg, cfg_arg)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (b)",
+ nas_arg, cfg_arg, nas_arg);
+ }
+ *outp++ = tac_strdup(nas_arg);
+ data->num_out_args++;
+ goto next_nas_arg;
+ }
+ }
+
+ /* c). If no attribute match exists, deny the command if the
+ default is to deny */
+
+ if (deny_by_default) {
+ data->status = AUTHOR_STATUS_FAIL;
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:absent, default=deny -> denied (c)",
+ nas_arg);
+ }
+ if (out_args) {
+ for (i = 0; i < data->num_out_args; i++)
+ free(out_args[i]);
+ free(out_args);
+ }
+
+ data->num_out_args = 0;
+ data->output_args = NULL;
+
+ /* free the server arguments */
+ for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+ return (0);
+ }
+
+ /* d). If the default is permit, add the NAS AV pair to
+ the output */
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG,
+ "nas:%s, svr:absent, default=permit -> add %s (d)",
+ nas_arg, nas_arg);
+ }
+ *outp++ = tac_strdup(nas_arg);
+ data->num_out_args++;
+ goto next_nas_arg;
+
+ } else {
+
+ /* NAS AV pair is Optional */
+
+ /* e). look for an exact attribute,value match in the mandatory
+ list. If found, add DAEMON's AV pair to output */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (optional(cfg_arg))
+ continue;
+
+ if (match_attrs(nas_arg, cfg_arg) &&
+ match_values(nas_arg, cfg_arg)) {
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (e)",
+ nas_arg, cfg_arg, cfg_arg);
+ }
+ *outp++ = tac_strdup(cfg_arg);
+ data->num_out_args++;
+ replaced++;
+ goto next_nas_arg;
+ }
+ }
+
+ /* f). If not found, look for the first attribute match in
+ the mandatory list. If found, add DAEMONS's AV pair to
+ output */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (optional(cfg_arg))
+ continue;
+
+ if (match_attrs(nas_arg, cfg_arg)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (f)",
+ nas_arg, cfg_arg, cfg_arg);
+ }
+ *outp++ = tac_strdup(cfg_arg);
+ data->num_out_args++;
+ replaced++;
+ goto next_nas_arg;
+ }
+ }
+
+ /* g). If no mandatory match exists, look for an exact
+ attribute,value pair match among the daemon's optional AV
+ pairs. If found add the DAEMON's matching AV pair to the
+ output.
+ */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (!optional(cfg_arg))
+ continue;
+
+ if (match_attrs(nas_arg, cfg_arg) &&
+ match_values(nas_arg, cfg_arg)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (g)",
+ nas_arg, cfg_arg, cfg_arg);
+ }
+ *outp++ = tac_strdup(cfg_arg);
+ data->num_out_args++;
+ replaced++;
+ goto next_nas_arg;
+ }
+ }
+
+ /* h). If no exact match exists, locate the first
+ attribute match among the daemon's optional AV
+ pairs. If found add the DAEMON's matching AV pair to
+ the output */
+
+ for (j = 0; j < cfg_cnt; j++) {
+ cfg_arg = cfg_args[j];
+ if (!optional(cfg_arg))
+ continue;
+
+ if (match_attrs(nas_arg, cfg_arg)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s (h)",
+ nas_arg, cfg_arg, cfg_arg);
+ }
+ *outp++ = tac_strdup(cfg_arg);
+ data->num_out_args++;
+ replaced++;
+ goto next_nas_arg;
+ }
+ }
+
+
+ /* i). If no match is found, delete the AV pair if default is
+ deny */
+
+ if (deny_by_default) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:absent/deny -> delete %s (i)",
+ nas_arg, nas_arg);
+ }
+ replaced++;
+ goto next_nas_arg;
+ }
+
+ /* j). If the default is permit add the NAS AV pair to the output */
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:%s svr:absent/permit -> add %s (j)",
+ nas_arg, nas_arg);
+ }
+ *outp++ = tac_strdup(nas_arg);
+ data->num_out_args++;
+ goto next_nas_arg;
+ }
+ next_nas_arg:;
+
+ }
+
+
+ /* k). After all AV pairs have been processed, for each mandatory
+ DAEMON AV pair, if there is no attribute match already in the
+ output list, add the AV pair (add only one AV pair for each
+ mandatory attribute) */
+
+ for (i = 0; i < cfg_cnt; i++) {
+ cfg_arg = cfg_args[i];
+
+ if (!mandatory(cfg_arg))
+ continue;
+
+ for (j = 0; j < data->num_out_args; j++) {
+ char *output_arg = out_args[j];
+
+ if (match_attrs(cfg_arg, output_arg)) {
+ goto next_cfg_arg;
+ }
+ }
+
+ /* Attr is required by daemon but not present in
+ output. Add it */
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "nas:absent, server:%s -> add %s (k)",
+ cfg_arg, cfg_arg);
+ }
+ added++;
+ *outp++ = tac_strdup(cfg_arg);
+ data->num_out_args++;
+
+ next_cfg_arg:;
+
+ }
+
+ /* If we replaced or deleted some pairs we must return the entire
+ list we've constructed */
+
+ if (replaced) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "replaced %d args", replaced);
+ }
+ data->status = AUTHOR_STATUS_PASS_REPL;
+ data->output_args = out_args;
+
+ /* free the server arguments */
+ for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+
+ return (0);
+ }
+
+ /* We added something not on the original nas list, but didn't
+ replace or delete anything. We should return only the
+ additions */
+
+ if (added) {
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "added %d args", added);
+
+ /* throw away output args which are just copies of the input args */
+ for (i = 0; i < data->num_in_args; i++) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "out_args[%d] = %s input copy discarded",
+ i, out_args[i]);
+ }
+ free(out_args[i]);
+ out_args[i] = NULL;
+ }
+
+ /* Now compact the new args added to the end of the array down
+ to the beginning */
+
+ j = 0;
+ for (i = data->num_in_args; i < data->num_out_args; i++) {
+ if (out_args[j]) /* we goofed */
+ report(LOG_ERR, "%s: out_args[%d] should be NULL",
+ session.peer, j);
+ if (!out_args[i]) /* we goofed */
+ report(LOG_ERR, "%s: out_args[%d] should not be NULL",
+ session.peer, i);
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]",
+ i, out_args[i], j);
+ }
+ out_args[j++] = out_args[i];
+ out_args[i] = NULL;
+ }
+ data->num_out_args = j;
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "%d output args", data->num_out_args);
+ }
+
+ /* should/could do a realloc here but it won't matter */
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->output_args = out_args;
+
+ /* free the server arguments */
+ for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+
+ return (0);
+ }
+
+ /* no additions or replacements. Input and output are
+ identical. Don't need to return anything */
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "added %d", added);
+ }
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ if (out_args) {
+ for (i = 0; i < data->num_out_args; i++) {
+ free(out_args[i]);
+ }
+ free(out_args);
+ }
+
+ /* Final sanity check */
+ if (data->num_out_args != data->num_in_args) {
+ data->status = AUTHOR_STATUS_ERROR;
+ data->admin_msg = tac_strdup("Bad output arg cnt from do_author");
+ report(LOG_ERR, "%s: Error %s", session.peer, data->admin_msg);
+
+ /* free the server arguments */
+ for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+
+ return (0);
+ }
+
+ data->num_out_args = 0;
+ data->output_args = NULL;
+
+ /* free the server arguments */
+ for(cfg_argp = cfg_args; *cfg_argp; cfg_argp++)
+ free(*cfg_argp);
+ free(cfg_args);
+
+ return (0);
+}
+
+/* Return an integer indicating which kind of service is being
+ requested.
+
+ Conveniently this integer is one of our node types. If the service
+ is a command authorisation request, also return the command name in
+ cmdname.
+
+ If this service is a ppp request, return the protocol name in
+ protocol.
+
+ If this service is not one of the standard, known ones, return its
+ name in svcname.
+*/
+
+static int
+get_nas_svc(data, cmdname, protocol, svcname)
+struct author_data *data;
+char **cmdname, **protocol, **svcname;
+{
+ int i;
+ char *nas_arg;
+
+ *cmdname = NULL;
+ *protocol = NULL;
+ *svcname = NULL;
+
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+
+ if (STREQ(nas_arg, "service=shell")) {
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+ if (strncmp(nas_arg, "cmd", strlen("cmd")) == 0) {
+ /* A cmd=<nothing> means we are authorising exec startup */
+ if ((int)strlen(nas_arg) <= 4)
+ return (N_svc_exec);
+
+ /* A non-null command means we are authorising a command */
+ *cmdname = nas_arg + strlen("cmd") + 1;
+ return (N_svc_cmd);
+ }
+ }
+ return(0);
+ }
+
+ if (STREQ(nas_arg, "service=slip")) {
+ return (N_svc_slip);
+ }
+ if (STREQ(nas_arg, "service=arap")) {
+ return (N_svc_arap);
+ }
+ if (STREQ(nas_arg, "service=ppp")) {
+ for (i = 0; i < data->num_in_args; i++) {
+ nas_arg = data->input_args[i];
+ if (strncmp(nas_arg, "protocol", strlen("protocol")) == 0) {
+ *protocol = nas_arg + strlen("protocol") + 1;
+ return(N_svc_ppp);
+ }
+ }
+ }
+
+ if (strncmp(nas_arg, "service=", strlen("service=")) ==0 ) {
+ *svcname = nas_arg + strlen("service=");
+ return(N_svc);
+ }
+ }
+ return (0);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+/* Routines for dumping packets to stderr */
+char *
+summarise_outgoing_packet_type(pak)
+u_char *pak;
+{
+ HDR *hdr;
+ struct authen_reply *authen;
+ struct author_reply *author;
+ char *p;
+
+ hdr = (HDR *) pak;
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ authen = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
+
+ switch (authen->status) {
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ p = "AUTHEN/SUCCEED";
+ break;
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+ p = "AUTHEN/FAIL";
+ break;
+ case TAC_PLUS_AUTHEN_STATUS_GETDATA:
+ p = "AUTHEN/GETDATA";
+ break;
+ case TAC_PLUS_AUTHEN_STATUS_GETUSER:
+ p = "AUTHEN/GETUSER";
+ break;
+ case TAC_PLUS_AUTHEN_STATUS_GETPASS:
+ p = "AUTHEN/GETPASS";
+ break;
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ p = "AUTHEN/ERROR";
+ break;
+ default:
+ p = "AUTHEN/UNKNOWN";
+ break;
+ }
+ break;
+
+ case TAC_PLUS_AUTHOR:
+ author = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
+ switch (author->status) {
+ case AUTHOR_STATUS_PASS_ADD:
+ p = "AUTHOR/PASS_ADD";
+ break;
+ case AUTHOR_STATUS_FAIL:
+ p = "AUTHOR/FAIL";
+ break;
+ case AUTHOR_STATUS_PASS_REPL:
+ p = "AUTHOR/PASS_REPL";
+ break;
+ case AUTHOR_STATUS_ERROR:
+ p = "AUTHOR/ERROR";
+ break;
+ default:
+ p = "AUTHOR/UNKNOWN";
+ break;
+ }
+ break;
+ case TAC_PLUS_ACCT:
+ p = "ACCT";
+ break;
+ default:
+ p = "UNKNOWN";
+ break;
+ }
+ return (p);
+}
+
+void
+dump_header(pak)
+u_char *pak;
+{
+ HDR *hdr;
+ u_char *data;
+
+ hdr = (HDR *) pak;
+
+ report(LOG_DEBUG, "PACKET: key=%s", session.key ? session.key : "<NULL>");
+ report(LOG_DEBUG, "version %d (0x%x), type %d, seq no %d, encryption %d",
+ hdr->version, hdr->version,
+ hdr->type, hdr->seq_no, hdr->encryption);
+ report(LOG_DEBUG, "session_id %u (0x%x), Data length %d (0x%x)",
+ ntohl(hdr->session_id), ntohl(hdr->session_id),
+ ntohl(hdr->datalength), ntohl(hdr->datalength));
+
+ report(LOG_DEBUG, "End header");
+
+ if (debug & DEBUG_HEX_FLAG) {
+ report(LOG_DEBUG, "Packet body hex dump:");
+ data = (u_char *) (pak + TAC_PLUS_HDR_SIZE);
+ report_hex(LOG_DEBUG, data, ntohl(hdr->datalength));
+ }
+}
+
+
+/* Dump packets originated by a NAS */
+dump_nas_pak(pak)
+u_char *pak;
+{
+ struct authen_start *start;
+ struct authen_cont *cont;
+ struct author *author;
+ struct acct *acct;
+ int i;
+ HDR *hdr;
+ u_char *p, *argsizep;
+ int seq;
+
+ dump_header(pak);
+
+ hdr = (HDR *) pak;
+
+ seq = hdr->seq_no;
+ if (seq % 2 != 1) {
+ report(LOG_DEBUG, "nas packets should be odd numbered seq=%d",
+ seq);
+ exit(1);
+ }
+ switch (hdr->type) {
+
+ case TAC_PLUS_AUTHEN:
+ start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
+
+ switch (hdr->seq_no) {
+
+ case 1:
+ report(LOG_DEBUG, "type=AUTHEN/START, priv_lvl = %d",
+ start->priv_lvl);
+
+ switch (start->action) {
+ case TAC_PLUS_AUTHEN_LOGIN:
+ report(LOG_DEBUG, "action=login");
+ break;
+ case TAC_PLUS_AUTHEN_CHPASS:
+ report(LOG_DEBUG, "action=chpass");
+ break;
+ case TAC_PLUS_AUTHEN_SENDPASS:
+ report(LOG_DEBUG, "action=sendpass");
+ break;
+ case TAC_PLUS_AUTHEN_SENDAUTH:
+ report(LOG_DEBUG, "action=sendauth");
+ break;
+ default:
+ report(LOG_DEBUG, "action=UNKNOWN %d", start->action);
+ break;
+ }
+
+ switch(start->authen_type) {
+ case TAC_PLUS_AUTHEN_TYPE_ASCII:
+ report(LOG_DEBUG, "authen_type=ascii");
+ break;
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ report(LOG_DEBUG, "authen_type=pap");
+ break;
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ report(LOG_DEBUG, "authen_type=chap");
+ break;
+ case TAC_PLUS_AUTHEN_TYPE_ARAP:
+ report(LOG_DEBUG, "authen_type=arap");
+ break;
+ default:
+ report(LOG_DEBUG, "authen_type=unknown %d", start->authen_type);
+ break;
+ }
+
+ switch(start->service) {
+
+ case TAC_PLUS_AUTHEN_SVC_LOGIN:
+ report(LOG_DEBUG, "service=login");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_ENABLE:
+ report(LOG_DEBUG, "service=enable");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_PPP:
+ report(LOG_DEBUG, "service=ppp");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_ARAP:
+ report(LOG_DEBUG, "service=arap");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_PT:
+ report(LOG_DEBUG, "service=pt");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_RCMD:
+ report(LOG_DEBUG, "service=rcmd");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_X25:
+ report(LOG_DEBUG, "service=x25");
+ break;
+ case TAC_PLUS_AUTHEN_SVC_NASI:
+ report(LOG_DEBUG, "service=nasi");
+ break;
+ default:
+ report(LOG_DEBUG, "service=unknown %d", start->service);
+ break;
+ }
+
+ report(LOG_DEBUG,
+ "user_len=%d port_len=%d (0x%x), rem_addr_len=%d (0x%x)",
+ start->user_len, start->port_len, start->port_len,
+ start->rem_addr_len, start->rem_addr_len);
+
+ report(LOG_DEBUG, "data_len=%d", start->data_len);
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE;
+
+ report(LOG_DEBUG, "User: ");
+ report_string(LOG_DEBUG, p, start->user_len);
+ p += start->user_len;
+
+ report(LOG_DEBUG, "port: ");
+ report_string(LOG_DEBUG, p, start->port_len);
+ p += start->port_len;
+
+ report(LOG_DEBUG, "rem_addr: ");
+ report_string(LOG_DEBUG, p, start->rem_addr_len);
+ p += start->rem_addr_len;
+
+ report(LOG_DEBUG, "data: ");
+ report_string(LOG_DEBUG, p, start->data_len);
+
+ report(LOG_DEBUG, "End packet");
+ return;
+
+ default:
+ cont = (struct authen_cont *) (pak + TAC_PLUS_HDR_SIZE);
+ report(LOG_DEBUG, "type=AUTHEN/CONT");
+ report(LOG_DEBUG, "user_msg_len %d (0x%x), user_data_len %d (0x%x)",
+ cont->user_msg_len, cont->user_msg_len,
+ cont->user_data_len, cont->user_data_len);
+ report(LOG_DEBUG, "flags=0x%x", cont->flags);
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE +
+ TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
+
+ report(LOG_DEBUG, "User msg: ");
+ report_string(LOG_DEBUG, p, cont->user_msg_len);
+ p += cont->user_msg_len;
+
+ report(LOG_DEBUG, "User data: ");
+ report_string(LOG_DEBUG, p, cont->user_data_len);
+
+ report(LOG_DEBUG, "End packet");
+ return;
+ }
+
+ case TAC_PLUS_AUTHOR:
+ author = (struct author *) (pak + TAC_PLUS_HDR_SIZE);
+
+ report(LOG_DEBUG, "type=AUTHOR, priv_lvl=%d, authen=%d",
+ author->priv_lvl,
+ author->authen_type);
+
+ switch(author->authen_method) {
+ case AUTHEN_METH_NONE:
+ report(LOG_DEBUG, "method=none");
+ break;
+ case AUTHEN_METH_KRB5:
+ report(LOG_DEBUG, "method=krb5");
+ break;
+ case AUTHEN_METH_LINE:
+ report(LOG_DEBUG, "method=line");
+ break;
+ case AUTHEN_METH_ENABLE:
+ report(LOG_DEBUG, "method=enable");
+ break;
+ case AUTHEN_METH_LOCAL:
+ report(LOG_DEBUG, "method=local");
+ break;
+ case AUTHEN_METH_TACACSPLUS:
+ report(LOG_DEBUG, "method=tacacs+");
+ break;
+ case AUTHEN_METH_RCMD:
+ report(LOG_DEBUG, "method=rcmd");
+ break;
+ default:
+ report(LOG_DEBUG, "method=unknown %d", author->authen_method);
+ break;
+ }
+
+ report(LOG_DEBUG, "svc=%d user_len=%d port_len=%d rem_addr_len=%d",
+ author->service, author->user_len,
+ author->port_len, author->rem_addr_len);
+
+ report(LOG_DEBUG, "arg_cnt=%d", author->arg_cnt);
+
+ /* variable length data start here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
+ argsizep = p;
+ p += author->arg_cnt;
+
+ report(LOG_DEBUG, "User: ");
+ report_string(LOG_DEBUG, p, author->user_len);
+ p += author->user_len;
+
+ report(LOG_DEBUG, "port: ");
+ report_string(LOG_DEBUG, p, author->port_len);
+ p += author->port_len;
+
+ report(LOG_DEBUG, "rem_addr: ");
+ report_string(LOG_DEBUG, p, author->rem_addr_len);
+ p += author->rem_addr_len;
+
+ for (i = 0; i < (int) author->arg_cnt; i++) {
+ report(LOG_DEBUG, "arg[%d]: size=%d ", i, *argsizep);
+ report_string(LOG_DEBUG, p, *argsizep);
+ p += *argsizep;
+ argsizep++;
+ }
+ break;
+
+ case TAC_PLUS_ACCT:
+ acct = (struct acct *) (pak + TAC_PLUS_HDR_SIZE);
+ report(LOG_DEBUG, "ACCT, flags=0x%x method=%d priv_lvl=%d",
+ acct->flags, acct->authen_method, acct->priv_lvl);
+ report(LOG_DEBUG, "type=%d svc=%d",
+ acct->authen_type, acct->authen_service);
+ report(LOG_DEBUG, "user_len=%d port_len=%d rem_addr_len=%d",
+ acct->user_len, acct->port_len, acct->rem_addr_len);
+ report(LOG_DEBUG, "arg_cnt=%d", acct->arg_cnt);
+
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
+ argsizep = p;
+ p += acct->arg_cnt;
+
+ report(LOG_DEBUG, "User: ");
+ report_string(LOG_DEBUG, p, acct->user_len);
+ p += acct->user_len;
+
+ report(LOG_DEBUG, "port: ");
+ report_string(LOG_DEBUG, p, acct->port_len);
+ p += acct->port_len;
+
+ report(LOG_DEBUG, "rem_addr: ");
+ report_string(LOG_DEBUG, p, acct->rem_addr_len);
+ p += acct->rem_addr_len;
+
+ for (i = 0; i < (int) acct->arg_cnt; i++) {
+ report(LOG_DEBUG, "arg[%d]: size=%d ", i, *argsizep);
+ report_string(LOG_DEBUG, p, *argsizep);
+ p += *argsizep;
+ argsizep++;
+ }
+ break;
+
+ default:
+ report(LOG_DEBUG, "dump_nas_pak: unrecognized header type %d", hdr->type);
+ }
+ report(LOG_DEBUG, "End packet");
+}
+
+/* Dump packets originated by Tacacsd */
+
+dump_tacacs_pak(pak)
+u_char *pak;
+{
+ struct authen_reply *authen;
+ struct author_reply *author;
+ struct acct_reply *acct;
+ HDR *hdr;
+ u_char *p, *argsizep;
+ int i;
+ int seq;
+
+ dump_header(pak);
+
+ hdr = (HDR *) pak;
+ seq = hdr->seq_no;
+
+ if (seq % 2 != 0) {
+ report(LOG_ERR, "%s: Bad sequence number %d should be even",
+ session.peer, seq);
+ tac_exit(1);
+ }
+ switch (hdr->type) {
+
+ case TAC_PLUS_AUTHEN:
+ authen = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
+
+ report(LOG_DEBUG, "type=AUTHEN status=%d (%s) flags=0x%x",
+ authen->status, summarise_outgoing_packet_type(pak),
+ authen->flags);
+
+ report(LOG_DEBUG, "msg_len=%d, data_len=%d",
+ authen->msg_len, authen->data_len);
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE;
+
+ report(LOG_DEBUG, "msg: ");
+ report_string(LOG_DEBUG, p, authen->msg_len);
+ p += authen->msg_len;
+
+ report(LOG_DEBUG, "data: ");
+ report_string(LOG_DEBUG, p, authen->data_len);
+
+ report(LOG_DEBUG, "End packet");
+ return;
+
+ case TAC_PLUS_AUTHOR:
+ author = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
+
+ report(LOG_DEBUG, "type=AUTHOR/REPLY status=%d (%s) ",
+ author->status, summarise_outgoing_packet_type(pak));
+ report(LOG_DEBUG, "msg_len=%d, data_len=%d arg_cnt=%d",
+ author->msg_len, author->data_len, author->arg_cnt);
+
+ /* start of variable length data is here */
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
+
+ /* arg sizes come next */
+ argsizep = p;
+
+ p += author->arg_cnt;
+
+ report(LOG_DEBUG, "msg: ");
+ report_string(LOG_DEBUG, p, author->msg_len);
+ p += author->msg_len;
+
+ report(LOG_DEBUG, "data: ");
+ report_string(LOG_DEBUG, p, author->data_len);
+ p += author->data_len;
+
+ /* args follow */
+ for (i = 0; i < (int) author->arg_cnt; i++) {
+ int size = argsizep[i];
+
+ report(LOG_DEBUG, "arg[%d] size=%d ", i, size);
+ report_string(LOG_DEBUG, p, size);
+ p += size;
+ }
+ break;
+
+ case TAC_PLUS_ACCT:
+ acct = (struct acct_reply *) (pak + TAC_PLUS_HDR_SIZE);
+ report(LOG_DEBUG, "ACCT/REPLY status=%d", acct->status);
+
+ report(LOG_DEBUG, "msg_len=%d data_len=%d",
+ acct->msg_len, acct->data_len);
+
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE;
+
+ report(LOG_DEBUG, "msg: ");
+
+ report_string(LOG_DEBUG, p, acct->msg_len);
+ p += acct->msg_len;
+
+ report(LOG_DEBUG, "data: ");
+ report_string(LOG_DEBUG, p, acct->data_len);
+
+ break;
+
+ default:
+ report(LOG_DEBUG, "dump_tacacs_pak: unrecognized header type %d",
+ hdr->type);
+ }
+ report(LOG_DEBUG, "End packet");
+}
+
+/* summarise packet types for logging routines. */
+char *
+summarise_incoming_packet_type(pak)
+u_char *pak;
+{
+ HDR *hdr;
+ char *p;
+
+ hdr = (HDR *) pak;
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ switch (hdr->seq_no) {
+ case 1:
+ p = "AUTHEN/START";
+ break;
+ default:
+ p = "AUTHEN/CONT";
+ break;
+ }
+ return (p);
+
+ case TAC_PLUS_AUTHOR:
+ p = "AUTHOR";
+ break;
+ case TAC_PLUS_ACCT:
+ p = "ACCT";
+ break;
+ default:
+ p = "UNKNOWN";
+ break;
+ }
+ return (p);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+
+/* internal state variables */
+#define STATE_AUTHEN_START 0 /* no requests issued */
+#define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+#define STATE_AUTHEN_GETPASS 2 /* password has been requested */
+
+struct private_data {
+ char password[MAX_PASSWD_LEN + 1];
+ int state;
+};
+
+static void
+enable(passwd, data)
+char *passwd;
+struct authen_data *data;
+{
+ int level = data->NAS_id->priv_lvl;
+
+ /* sanity check */
+ if (level < TAC_PLUS_PRIV_LVL_MIN || level > TAC_PLUS_PRIV_LVL_MAX) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("Invalid privilege level in packet");
+ report(LOG_ERR, "%s level=%d %s", session.peer, level, data->server_msg);
+ return;
+ }
+ /* 0 <= level <= 14: look for $enab<n>$ and verify */
+ if (level < TAC_PLUS_PRIV_LVL_MAX) {
+ char buf[11];
+
+ sprintf(buf, "$enab%d$", level);
+ if (!verify(buf, passwd, data, TAC_PLUS_NORECURSE))
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+
+ /* 2). level=15. Try $enab15$ or $enable$ (for backwards
+ compatibility) and verify */
+
+ if (verify("$enable$", passwd, data, TAC_PLUS_NORECURSE) ||
+ verify("$enab15$", passwd, data, TAC_PLUS_NORECURSE)) {
+ return;
+ }
+
+ /* return fail */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ return;
+}
+
+
+/*
+ * Tacacs enable authentication function. Wants an enable
+ * password, and tries to verify it.
+ *
+ * Choose_authen will ensure that we already have a username before this
+ * gets called.
+ *
+ * We will query for a password and keep it in the method_data.
+ *
+ * Any strings returned via pointers in authen_data must come from the
+ * heap. They will get freed by the caller.
+ *
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
+int
+enable_fn(data)
+struct authen_data *data;
+{
+ char *passwd;
+ struct private_data *p;
+ int pwlen;
+
+ p = (struct private_data *) data->method_data;
+
+ /* An abort has been received. Clean up and return */
+ if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+ return (1);
+ }
+ /* Initialise method_data if first time through */
+ if (!p) {
+ p = (struct private_data *) tac_malloc(sizeof(struct private_data));
+ bzero(p, sizeof(struct private_data));
+ data->method_data = p;
+ p->state = STATE_AUTHEN_START;
+ }
+
+ /* As we're enabling, we don't need a username, but do we have a
+ password? */
+
+ passwd = p->password;
+
+ if (!passwd[0]) {
+
+ /* No password. Either we need to ask for one and expect to get
+ * called again, or we asked but nothing came back, which is fatal */
+
+ switch (p->state) {
+ case STATE_AUTHEN_GETPASS:
+ /* We already asked for a password. This should be the
+ reply */
+ if (data->client_msg) {
+ pwlen = MIN((int)strlen(data->client_msg), MAX_PASSWD_LEN);
+ } else {
+ pwlen = 0;
+ }
+ strncpy(passwd, data->client_msg, pwlen);
+ passwd[pwlen] = '\0';
+ break;
+
+ default:
+ /* Request a password */
+ data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
+ data->server_msg = tac_strdup("Password: ");
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
+ p->state = STATE_AUTHEN_GETPASS;
+ return (0);
+ }
+ }
+
+ /* We have a password. Try validating */
+
+ /* Assume the worst */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ switch (data->service) {
+ case TAC_PLUS_AUTHEN_SVC_ENABLE:
+ enable(passwd, data);
+ if (debug) {
+ char *name = data->NAS_id->username;
+
+ report(LOG_INFO, "enable query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ data->NAS_id->NAS_port && data->NAS_id->NAS_port[0] ?
+ data->NAS_id->NAS_port : "unknown",
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ break;
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s: Bogus service value %d from packet",
+ session.peer, data->service);
+ break;
+ }
+
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+
+ switch (data->status) {
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ return (0);
+ default:
+ report(LOG_ERR, "%s: authenticate_fn can't set status %d",
+ session.peer, data->status);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return (1);
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "md5.h"
+
+/*
+ * create_md5_hash(): create an md5 hash of the "session_id", "the user's
+ * key", "the version number", the "sequence number", and an optional
+ * 16 bytes of data (a previously calculated hash). If not present, this
+ * should be NULL pointer.
+ *
+ * Write resulting hash into the array pointed to by "hash".
+ *
+ * The caller must allocate sufficient space for the resulting hash
+ * (which is 16 bytes long). The resulting hash can safely be used as
+ * input to another call to create_md5_hash, as its contents are copied
+ * before the new hash is generated.
+ *
+ *
+ */
+
+void
+create_md5_hash(session_id, key, version, seq_no, prev_hash, hash)
+int session_id;
+char *key;
+u_char version;
+u_char seq_no;
+u_char *prev_hash;
+u_char *hash;
+{
+ u_char *md_stream, *mdp;
+ int md_len;
+ MD5_CTX mdcontext;
+
+ md_len = sizeof(session_id) + strlen(key) + sizeof(version) +
+ sizeof(seq_no);
+
+ if (prev_hash) {
+ md_len += MD5_LEN;
+ }
+ mdp = md_stream = (u_char *) tac_malloc(md_len);
+ bcopy(&session_id, mdp, sizeof(session_id));
+ mdp += sizeof(session_id);
+
+ bcopy(key, mdp, strlen(key));
+ mdp += strlen(key);
+
+ bcopy(&version, mdp, sizeof(version));
+ mdp += sizeof(version);
+
+ bcopy(&seq_no, mdp, sizeof(seq_no));
+ mdp += sizeof(seq_no);
+
+ if (prev_hash) {
+ bcopy(prev_hash, mdp, MD5_LEN);
+ mdp += MD5_LEN;
+ }
+ MD5Init(&mdcontext);
+ MD5Update(&mdcontext, md_stream, md_len);
+ MD5Final(hash, &mdcontext);
+ free(md_stream);
+ return;
+}
+
+/*
+ * Overwrite input data with en/decrypted version by generating an MD5 hash and
+ * xor'ing data with it.
+ *
+ * When more than 16 bytes of hash is needed, the MD5 hash is performed
+ * again with the same values as before, but with the previous hash value
+ * appended to the MD5 input stream.
+ *
+ * Return 0 on success, -1 on failure.
+ */
+
+md5_xor(hdr, data, key)
+HDR *hdr;
+u_char *data;
+char *key;
+{
+ int i, j;
+ u_char hash[MD5_LEN]; /* the md5 hash */
+ u_char last_hash[MD5_LEN]; /* the last hash we generated */
+ u_char *prev_hashp = (u_char *) NULL; /* pointer to last created
+ * hash */
+ int data_len;
+ int session_id;
+ u_char version;
+ u_char seq_no;
+
+ data_len = ntohl(hdr->datalength);
+ session_id = hdr->session_id; /* always in network order for hashing */
+ version = hdr->version;
+ seq_no = hdr->seq_no;
+
+ if (!key)
+ return (0);
+
+ for (i = 0; i < data_len; i += 16) {
+
+ create_md5_hash(session_id, key, version, seq_no, prev_hashp, hash);
+
+ if (debug & DEBUG_MD5_HASH_FLAG) {
+ int k;
+
+ report(LOG_DEBUG,
+ "hash: session_id=%u, key=%s, version=%d, seq_no=%d",
+ session_id, key, version, seq_no);
+ if (prev_hashp) {
+ report(LOG_DEBUG, "prev_hash:");
+ for (k = 0; k < MD5_LEN; k++)
+ report(LOG_DEBUG, "0x%x", prev_hashp[k]);
+ } else {
+ report(LOG_DEBUG, "no prev. hash");
+ }
+
+ report(LOG_DEBUG, "hash: ");
+ for (k = 0; k < MD5_LEN; k++)
+ report(LOG_DEBUG, "0x%x", hash[k]);
+ } /* debug */
+ bcopy(hash, last_hash, MD5_LEN);
+ prev_hashp = last_hash;
+
+ for (j = 0; j < 16; j++) {
+
+ if ((i + j) >= data_len) {
+ hdr->encryption = (hdr->encryption == TAC_PLUS_CLEAR)
+ ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR;
+ return (0);
+ }
+ if (debug & DEBUG_XOR_FLAG) {
+ report(LOG_DEBUG,
+ "data[%d] = 0x%x, xor'ed with hash[%d] = 0x%x -> 0x%x\n",
+ i + j,
+ data[i + j],
+ j,
+ hash[j],
+ data[i + j] ^ hash[j]);
+ } /* debug */
+ data[i + j] ^= hash[j];
+ }
+ }
+ hdr->encryption = (hdr->encryption == TAC_PLUS_CLEAR)
+ ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR;
+ return (0);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+
+/*
+ * check a date for expiry. If the field specifies
+ * a shell return PW_OK
+ *
+ * Return PW_OK if not expired
+ * Return PW_EXPIRING if expiry is coming soon
+ * Return PW_EXPIRED if already expired
+ */
+
+#define SEC_IN_DAY (24*60*60)
+#define WARNING_PERIOD 14
+
+static char *monthname[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
+static long days_ere_month[] = {0, 31, 59, 90, 120, 151,
+181, 212, 243, 273, 304, 334};
+
+int
+check_expiration(date)
+char *date;
+{
+ long day, month, year, leaps, now, expiration, warning;
+ char monthstr[10];
+ int i;
+
+ monthstr[0] = '\0';
+
+ /* If no date or a shell, let it pass. (Backward compatibility.) */
+ if (!date || (strlen(date) == 0) || (*date == '/'))
+ return (PW_OK);
+
+ /* Parse date string. Fail it upon error. */
+ if (sscanf(date, "%s %d %d", monthstr, &day, &year) != 3)
+ return (PW_EXPIRED);
+
+ for(i=0; i < 3; i++) {
+ monthstr[i] = toupper(monthstr[i]);
+ }
+
+ /* Compute the expiration date in days. */
+ for (month = 0; month < 12; month++)
+ if (strncmp(monthstr, monthname[month], 3) == 0)
+ break;
+
+ if (month > 11)
+ return (PW_EXPIRED);
+
+ leaps = (year - 1969) / 4 + (((year % 4) == 0) && (month > 2));
+ expiration = (((year - 1970) * 365) + days_ere_month[month] + (day - 1) + leaps);
+ warning = expiration - WARNING_PERIOD;
+
+ /* Get the current time (to the day) */
+ now = time(NULL) / SEC_IN_DAY;
+
+ if (now > expiration)
+ return (PW_EXPIRED);
+
+ if (now > warning)
+ return (PW_EXPIRING);
+
+ return (PW_OK);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+#define PW_OK 0 /* pw not expired and not due to expire soon */
+#define PW_EXPIRED 1 /* pw has expired */
+#define PW_EXPIRING 2 /* pw will expire soon */
+
+#define MAX_PASSWD_LEN 256
+
+extern int check_expiration();
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* Program to des encrypt a password like Unix
+ It prompts for the password to encrypt.
+ You can optionally supply a salt to verify a password.
+ Usage: a.out [salt]
+*/
+
+#define NULL 0
+
+main(argc, argv)
+char **argv;
+{
+ char *crypt();
+ char pass[25], *salt, buf[24];
+ char *result;
+ int n;
+ char *prompt = "Password to be encrypted: ";
+
+ salt = NULL;
+
+ if (argc == 2) {
+ salt = argv[1];
+ }
+
+ write(1, prompt, strlen(prompt));
+ n = read(0, pass, sizeof(pass));
+ pass[n-1] = NULL;
+
+ if (!salt) {
+ int i, r, r1, r2;
+
+ srand(time(0));
+
+ for(i=0; i <= 1; i++) {
+
+ r = rand();
+
+ r = r & 127;
+
+ if (r < 46)
+ r += 46;
+
+ if (r > 57 && r < 65)
+ r += 7;
+
+ if (r > 90 && r < 97)
+ r += 6;
+
+ if (r > 122)
+ r -= 5;
+
+ if (i == 0)
+ r1 = r;
+
+ if (i == 1)
+ r2 = r;
+ }
+
+ sprintf(buf, "%c%c", r1, r2);
+ salt = buf;
+ }
+
+ result = crypt(pass, salt);
+
+ write(1, result, strlen(result));
+ write(1, "\n", 1);
+}
+
+
+
+
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+struct entry {
+ char *name;
+ void *hash;
+};
+
+typedef struct entry ENTRY;
+
+/* Calculate hash value from a string */
+static int
+calculate_hash(name)
+char *name;
+{
+ int i;
+ int len = strlen(name);
+ int hashval = 0;
+
+ for (i = 0; i < len; i++) {
+ hashval += name[i] * (i + 1);
+ }
+ hashval += name[0];
+ hashval = hashval > 0 ? hashval : -hashval;
+ return (hashval);
+}
+
+/* Lookup a name in a hash table. Return its node if it exists, NULL
+ otherwise */
+void *
+hash_lookup(hashtab, name)
+void **hashtab;
+char *name;
+{
+ ENTRY *entry;
+ int hashval = calculate_hash(name);
+
+ entry = hashtab[hashval % HASH_TAB_SIZE];
+
+ while (entry) {
+ if (STREQ(name, entry->name))
+ /* Node exists in table. return it */
+ return (entry);
+ entry = entry->hash;
+ }
+ return (NULL);
+}
+
+/* Add a node to a hash table. Return node if it exists, NULL
+ otherwise */
+void *
+hash_add_entry(hashtab, newentry)
+void **hashtab;
+ENTRY *newentry;
+{
+ ENTRY *entry;
+ int hashval;
+
+ entry = hash_lookup(hashtab, newentry->name);
+ if (entry)
+ return (entry);
+
+ /* Node does not exist in table. Add it */
+ hashval = calculate_hash(newentry->name);
+ newentry->hash = hashtab[hashval % HASH_TAB_SIZE];
+ hashtab[hashval % HASH_TAB_SIZE] = newentry;
+ return (NULL);
+}
+
+
+/* Return an array of pointers to all the entries in a hash table */
+void **
+hash_get_entries(hashtab)
+void **hashtab;
+{
+ int i;
+ int cnt;
+ ENTRY *entry;
+ void **entries, **p;
+ int n, longest;
+
+ longest = 0;
+ cnt = 0;
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = hashtab[i];
+ n = 0;
+ while (entry) {
+ cnt++;
+ n++;
+ entry = entry->hash;
+ }
+ if (n > longest)
+ longest = n;
+ }
+ cnt++; /* Add space for NULL entry at end */
+
+ p = entries = (void **) tac_malloc(cnt * sizeof(void *));
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = hashtab[i];
+ while (entry) {
+ *p++ = entry;
+ entry = entry->hash;
+ }
+ }
+ *p++ = NULL;
+ return (entries);
+}
--- /dev/null
+/*
+ Verify that this user/password is valid per a database LDAP server
+ Return 1 if verified, 0 otherwise.
+
+ Format of connection string (look like internet URL):
+
+ ldap://LDAP-hostname
+
+ -------------------------------------------------------
+ patrick.harpes@tudor.lu http://www.santel.lu
+ http://www.tudor.lu
+
+
+
+ Dependencies: You need to get the OpenLDAP libraries
+ from http://www.openldap.org
+
+ License: tac_ldap is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+--------------------------------------------------------------------------
+ Changes:
+ Ok i am back again..:)
+ I changed lot of thing.. First off all i add port feature to ldap string.
+ And also add more check for buffer overflows.
+
+Connect format would be:
+ ldap://LDAP-hostname:100
+
+Port name isn't required.. I would like to change format with :
+ ldap://LDAP-hostname:100/dn_for_user&dn_for_passwd
+
+ devrim seral <devrim@gazi.edu.tr>
+
+*/
+
+
+#if defined(USE_LDAP)
+#include <stdio.h>
+#include <string.h>
+#include <lber.h>
+#include <ldap.h>
+#include <ldap_cdefs.h>
+
+#include "tac_plus.h"
+#include "ldap.h"
+
+
+int
+ldap_verify(user, users_passwd, str_conn)
+char *user, *users_passwd; /* Username and gived password */
+char *str_conn; /* String connection to database */
+{
+ char *buf;
+ char *ldapServer;
+ char *ldap_port;
+ LDAP *ld;
+ int port;
+ int err;
+
+/* Don't allow null username and passwd */
+ if ( *user == '0' || *users_passwd == '0' ) return (1);
+
+ buf=(char *)malloc(strlen(str_conn)+1);
+ if (buf == NULL ){
+ report(LOG_DEBUG, "Error can't allocate memory");
+ return(1);
+ }
+
+ strcpy(buf,str_conn);
+ ldapServer=strstr(buf, "://");
+
+ if(ldapServer == NULL && strlen(ldapServer) <4 ) {
+ if (debug) {
+ report(LOG_DEBUG, "Error parse ldap server");
+ return(1);
+ }
+ }
+
+ ldapServer=ldapServer+3;
+
+ ldap_port=(char *)strstr(ldapServer, ":");
+
+ if (ldap_port != NULL ) {
+ *ldap_port='\0';
+ port=atoi(++ldap_port);
+ } else {
+ port = LDAP_PORT;
+ }
+
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG, "In verify_ldap : Before ldap_init : ldapserver = %s port= %d", ldapServer, port);
+
+
+ if( (ld = ldap_init(ldapServer, port)) == NULL)
+ {
+ report(LOG_DEBUG, "Unable to connect to LDAP server:%s port:%d",ldapServer, port);
+ return 1;
+ }
+
+ err=ldap_simple_bind_s(ld, user, users_passwd);
+
+ if(err != LDAP_SUCCESS)
+ {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG,"Error while bind : %d %s",err, ldap_err2string(err) );
+ return 1;
+ }
+ else
+ {
+ /* Success */
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG, "LDAP authentication Sucess ");
+ ldap_unbind_s(ld);
+ return 0;
+ }
+}
+#endif /* LDAP */
--- /dev/null
+int ldap_verify();
--- /dev/null
+/*
+ Verify that this user/password is valid per a database LDAP server
+ Return 1 if verified, 0 otherwise.
+
+ Format of connection string (look like internet URL):
+
+ ldap://LDAP-hostname
+
+ -------------------------------------------------------
+ patrick.harpes@tudor.lu http://www.santel.lu
+ http://www.tudor.lu
+
+
+
+ Dependencies: You need to get the OpenLDAP libraries
+ from http://www.openldap.org
+
+ License: tac_ldap is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+--------------------------------------------------------------------------
+ Changes:
+ Ok i am back again..:)
+ I changed lot of thing.. First off all i add port feature to ldap string.
+ And also add more check for buffer overflows.
+
+Connect format would be:
+ ldap://LDAP-hostname:100
+
+Port name isn't required.. I would like to change format with :
+ ldap://LDAP-hostname:100/dn_for_user&dn_for_passwd
+
+ devrim seral <devrim@gazi.edu.tr>
+
+*/
+
+
+#if defined(USE_LDAP)
+#include <stdio.h>
+#include <string.h>
+#include <lber.h>
+#include <ldap.h>
+#include <ldap_cdefs.h>
+
+#include "tac_plus.h"
+#include "ldap.h"
+
+
+int
+ldap_verify(user, users_passwd, str_conn)
+char *user, *users_passwd; /* Username and gived password */
+char *str_conn; /* String connection to database */
+{
+ char *buf;
+ char *ldapServer;
+ char *ldap_port;
+ LDAP *ld;
+ int port;
+ int err;
+
+/* Don't allow null username and passwd */
+ if ( *user == '0' || *users_passwd == '0' ) return (1);
+
+ buf=(char *)malloc(strlen(str_conn)+1);
+ if (buf == NULL ){
+ report(LOG_DEBUG, "Error can't allocate memory");
+ return(1);
+ }
+
+ strcpy(buf,str_conn);
+ ldapServer=strstr(buf, "://");
+
+ if(ldapServer == NULL && strlen(ldapServer) <4 ) {
+ if (debug) {
+ report(LOG_DEBUG, "Error parse ldap server");
+ return(1);
+ }
+ }
+
+ ldapServer=ldapServer+3;
+
+ ldap_port=(char *)strstr(ldapServer, ":");
+
+ if (ldap_port != NULL ) {
+ *ldap_port='\0';
+ port=atoi(++ldap_port);
+ } else {
+ port = LDAP_PORT;
+ }
+
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG, "In verify_ldap : Before ldap_init : ldapserver = %s port= %d", ldapServer, port);
+
+
+ if( (ld = ldap_init(ldapServer, port)) == NULL)
+ {
+ report(LOG_DEBUG, "Unable to connect to LDAP server:%s port:%d",ldapServer, port);
+ return 1;
+ }
+
+ err=ldap_simple_bind_s(ld, user, users_passwd);
+
+ if(err != LDAP_SUCCESS)
+ {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG,"Error while bind : %d %s",err, ldap_err2string(err) );
+ return 1;
+ }
+ else
+ {
+ /* Success */
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG, "LDAP authentication Sucess ");
+ ldap_unbind_s(ld);
+ return 0;
+ }
+}
+#endif /* LDAP */
--- /dev/null
+int ldap_verify();
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+#ifdef MAXSESS
+char *wholog = WHOLOG_DEFAULT;
+/*
+ * initialize wholog file for tracking of user logins/logouts from
+ * accounting records.
+ */
+void
+maxsess_loginit()
+{
+ int fd;
+
+ fd = open(wholog, O_CREAT | O_RDWR, 0600);
+ if (fd < 0) {
+ report(LOG_ERR, "Can't create: %s", wholog);
+ } else {
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "Initialize %s", wholog);
+ }
+ close(fd);
+ }
+}
+
+/*
+ * Given a port description, return it in a canonical format.
+ *
+ * This piece of goo is to cover the fact that an async line in EXEC
+ * mode is known as "ttyXX", but the same line doing PPP or SLIP is
+ * known as "AsyncXX".
+ */
+static char *
+portname(oldport)
+char *oldport;
+{
+ char *p = oldport;
+
+ if (!strncmp(p, "Async", 5) || !strncmp(p, "tty", 3)) {
+ while (!isdigit(*p) && *p) {
+ ++p;
+ }
+ }
+ if (!*p) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "Maxsess -- Malformed portname: %s", oldport);
+ return (oldport);
+ }
+ return (p);
+}
+
+/*
+ * Seek to offset and write a buffer into the file pointed to by fp
+ */
+static void
+write_record(name, fp, buf, size, offset)
+FILE *fp;
+long offset;
+int size;
+void *buf;
+char *name;
+{
+ if (fseek(fp, offset, SEEK_SET) < 0) {
+ report(LOG_ERR, "%s fd=%d Cannot seek to %d %s",
+ name, fileno(fp), offset, sys_errlist[errno]);
+ }
+ if (fwrite(buf, size, 1, fp) != 1) {
+ report(LOG_ERR, "%s fd=%d Cannot write %d bytes",
+ name, fileno(fp), size);
+ }
+}
+
+static void
+process_stop_record(idp)
+struct identity *idp;
+{
+ int recnum;
+ struct peruser pu;
+ FILE *fp;
+ char *nasport = portname(idp->NAS_port);
+
+ /* If we can't access the file, skip all checks. */
+ fp = fopen(wholog, "r+");
+ if (fp == NULL) {
+ report(LOG_ERR, "Can't open %s for updating", wholog);
+ return;
+ }
+ tac_lockfd(wholog, fileno(fp));
+
+ for (recnum = 0; 1; recnum++) {
+
+ fseek(fp, recnum * sizeof(struct peruser), SEEK_SET);
+
+ if (fread(&pu, sizeof(pu), 1, fp) <= 0) {
+ break;
+ }
+
+ /* A match for this record? */
+ if (!(STREQ(pu.NAS_name, idp->NAS_name) &&
+ STREQ(pu.NAS_port, nasport))) {
+ continue;
+ }
+
+ /* A match. Zero out this record */
+ bzero(&pu, sizeof(pu));
+
+ write_record(wholog, fp, &pu, sizeof(pu),
+ recnum * sizeof(struct peruser));
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "STOP record -- clear %s entry %d for %s/%s",
+ wholog, recnum, idp->username, nasport);
+ }
+ }
+ fclose(fp);
+}
+
+static void
+process_start_record(idp)
+struct identity *idp;
+{
+ int recnum;
+ int foundrec = -1;
+ int freerec = -1;
+ char *nasport = portname(idp->NAS_port);
+ struct peruser pu;
+ FILE *fp;
+
+ /* If we can't access the file, skip all checks. */
+ fp = fopen(wholog, "r+");
+ if (fp == NULL) {
+ report(LOG_ERR, "Can't open %s for updating", wholog);
+ return;
+ }
+ tac_lockfd(wholog, fileno(fp));
+
+ for (recnum = 0; (fread(&pu, sizeof(pu), 1, fp) > 0); recnum++) {
+ /* Match for this NAS/Port record? */
+ if (STREQ(pu.NAS_name, idp->NAS_name) && STREQ(pu.NAS_port, nasport)) {
+ foundrec = recnum;
+ break;
+ }
+ /* Found a free slot on the way */
+ if (pu.username[0] == '\0') {
+ freerec = recnum;
+ }
+ }
+
+ /* This is a START record, so write a new record or update the existing
+ * one. Note that we bzero(), so the strncpy()'s will truncate long
+ * names and always leave a null-terminated string.
+ */
+
+ bzero(&pu, sizeof(pu));
+ strncpy(pu.username, idp->username, sizeof(pu.username) - 1);
+ strncpy(pu.NAS_name, idp->NAS_name, sizeof(pu.NAS_name) - 1);
+ strncpy(pu.NAS_port, nasport, sizeof(pu.NAS_port) - 1);
+ strncpy(pu.NAC_address, idp->NAC_address, sizeof(pu.NAC_address) - 1);
+
+ /* Already in DB? */
+ if (foundrec >= 0) {
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG,
+ "START record -- overwrite existing %s entry %d for %s %s/%s",
+ wholog, foundrec, pu.NAS_name, pu.username, pu.NAS_port);
+ }
+ write_record(wholog, fp, &pu, sizeof(pu),
+ foundrec * sizeof(struct peruser));
+ fclose(fp);
+ return;
+ }
+
+ /* Not found in DB, but we have a free slot */
+ if (freerec >= 0) {
+
+ write_record(wholog, fp, &pu, sizeof(pu),
+ freerec * sizeof(struct peruser));
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "START record -- %s entry %d for %s %s/%s added",
+ wholog, freerec, pu.NAS_name, pu.username, pu.NAS_port);
+ }
+ fclose(fp);
+ return;
+ }
+
+ /* No free slot. Add record at the end */
+ write_record(wholog, fp, &pu, sizeof(pu),
+ recnum * sizeof(struct peruser));
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "START record -- %s entry %d for %s %s/%s added",
+ wholog, recnum, pu.NAS_name, pu.username, pu.NAS_port);
+ }
+ fclose(fp);
+}
+
+
+/*
+ * Given a start or a stop accounting record, update the file of
+ * records which tracks who's logged on and where.
+ */
+loguser(rec)
+struct acct_rec *rec;
+{
+ struct identity *idp;
+ int i;
+
+ /* We're only interested in start/stop records */
+ if ((rec->acct_type != ACCT_TYPE_START) &&
+ (rec->acct_type != ACCT_TYPE_STOP)) {
+ return;
+ }
+ /* ignore command accounting records */
+ for (i = 0; i < rec->num_args; i++) {
+ char *avpair = rec->args[i];
+ if ((strncmp(avpair, "cmd=", 4) == 0) && strlen(avpair) > 4) {
+ return;
+ }
+ }
+
+ /* Extract and store just the port number, since the port names are
+ * different depending on whether this is an async interface or an exec
+ * line. */
+ idp = rec->identity;
+
+ switch (rec->acct_type) {
+ case ACCT_TYPE_START:
+ process_start_record(idp);
+ return;
+
+ case ACCT_TYPE_STOP:
+ process_stop_record(idp);
+ return;
+ }
+}
+
+/* Read up to n bytes from descriptor fd into array ptr with timeout t
+ * seconds.
+ *
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes read. */
+
+int
+timed_read(fd, ptr, nbytes, timeout)
+int fd;
+u_char *ptr;
+int nbytes;
+int timeout;
+{
+ int nread;
+ fd_set readfds, exceptfds;
+ struct timeval tout;
+
+ tout.tv_sec = timeout;
+ tout.tv_usec = 0;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &exceptfds);
+
+ while (1) {
+ int status = select(fd + 1, &readfds, (fd_set *) NULL,
+ &exceptfds, &tout);
+
+ if (status == 0) {
+ report(LOG_DEBUG, "%s: timeout reading fd %d", session.peer, fd);
+ return (-1);
+ }
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ report(LOG_DEBUG, "%s: error in select %s fd %d",
+ session.peer, sys_errlist[errno], fd);
+ return (-1);
+ }
+ if (FD_ISSET(fd, &exceptfds)) {
+ report(LOG_DEBUG, "%s: exception on fd %d",
+ session.peer, fd);
+ return (-1);
+ }
+ if (!FD_ISSET(fd, &readfds)) {
+ report(LOG_DEBUG, "%s: spurious return from select",
+ session.peer);
+ continue;
+ }
+ nread = read(fd, ptr, nbytes);
+
+ if (nread < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ report(LOG_DEBUG, "%s %s: error reading fd %d nread=%d %s",
+ session.peer, session.port, fd, nread, sys_errlist[errno]);
+ return (-1); /* error */
+ }
+ if (nread == 0) {
+ return (-1); /* eof */
+ }
+ return (nread);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Contact a NAS (using finger) to check how many sessions this USER
+ * is currently running on it.
+ *
+ * Note that typically you run this code when you are in the middle of
+ * trying to login to a Cisco NAS on a given port. Because you are
+ * part way through a login when you do this, you can get inconsistent
+ * reports for that particular port about whether the user is
+ * currently logged in on it or not, so we ignore output which claims
+ * that the user is using that line currently.
+ *
+ * This is extremely Cisco specific -- finger formats appear to vary wildly.
+ * The format we're expecting is:
+
+ Line User Host(s) Idle Location
+ 0 con 0 idle never
+ 18 vty 0 usr0 idle 30 barley.cisco.com
+ 19 vty 1 usr0 Virtual Exec 2
+ 20 vty 2 idle 0 barley.cisco.com
+
+ * Column zero contains a space or an asterisk character. The line number
+ * starts at column 1 and is 3 digits wide. User names start at column 13,
+ * with a maximum possible width of 10.
+ */
+
+static int
+ckfinger(user, nas, idp)
+char *user, *nas;
+struct identity *idp;
+{
+ struct sockaddr_in sin;
+ struct servent *serv;
+ int count, s, bufsize;
+ char *buf, *p, *pn;
+ int incr = 4096, slop = 32;
+ u_long inaddr;
+ char *curport = portname(idp->NAS_port);
+ char *name;
+
+ /* The finger service, aka port 79 */
+ serv = getservbyname("finger", "tcp");
+ if (serv) {
+ sin.sin_port = serv->s_port;
+ } else {
+ sin.sin_port = 79;
+ }
+
+ /* Get IP addr for the NAS */
+ inaddr = inet_addr(nas);
+ if (inaddr != -1) {
+ /* A dotted decimal address */
+ bcopy(&inaddr, &sin.sin_addr, sizeof(inaddr));
+ sin.sin_family = AF_INET;
+ } else {
+ struct hostent *host = gethostbyname(nas);
+
+ if (host == NULL) {
+ report(LOG_ERR, "ckfinger: gethostbyname %s failure: %s",
+ nas, sys_errlist[errno]);
+ return (0);
+ }
+ bcopy(host->h_addr, &sin.sin_addr, host->h_length);
+ sin.sin_family = host->h_addrtype;
+ }
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ report(LOG_ERR, "ckfinger: socket: %s", sys_errlist[errno]);
+ return (0);
+ }
+ if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
+ report(LOG_ERR, "ckfinger: connect failure %s", sys_errlist[errno]);
+ close(s);
+ return (0);
+ }
+ /* Read in the finger output into a single flat buffer */
+ buf = NULL;
+ bufsize = 0;
+ for (;;) {
+ int x;
+
+ buf = tac_realloc(buf, bufsize + incr + slop);
+ x = timed_read(s, buf + bufsize, incr, 10);
+ if (x <= 0) {
+ break;
+ }
+ bufsize += x;
+ }
+
+ /* Done talking here */
+ close(s);
+ buf[bufsize] = '\0';
+
+ if (bufsize <= 0) {
+ report(LOG_ERR, "ckfinger: finger failure");
+ free(buf);
+ return (0);
+ }
+ /* skip first line in buffer */
+ p = strchr(buf, '\n');
+ if (p) {
+ p++;
+ }
+ p = strchr(p, '\n');
+ if (p) {
+ p++;
+ }
+ /* Tally each time this user appears */
+ for (count = 0; p && *p; p = pn) {
+ int i, len, nmlen;
+ char nmbuf[11];
+
+ /* Find next line */
+ pn = strchr(p, '\n');
+ if (pn) {
+ ++pn;
+ }
+ /* Calculate line length */
+ if (pn) {
+ len = pn - p;
+ } else {
+ len = strlen(p);
+ }
+
+ /* Line too short -> ignore */
+ if (len < 14) {
+ continue;
+ }
+ /* Always ignore the NAS/port we're currently trying to login on. */
+ if (isdigit(*curport)) {
+ int thisport;
+
+ if (sscanf(p + 1, " %d", &thisport) == 1) {
+ if ((atoi(curport) == thisport) &&
+ !strcmp(idp->NAS_name, nas)) {
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "%s session on %s/%s discounted",
+ user, idp->NAS_name, idp->NAS_port);
+ }
+ continue;
+ }
+ }
+ }
+ /* Extract username, up to 10 chars wide, starting at char 13 */
+ nmlen = 0;
+ name = p + 13;
+ for (i = 0; *name && !isspace(*name) && (i < 10); i++) {
+ nmbuf[nmlen++] = *name++;
+ }
+ nmbuf[nmlen++] = '\0';
+
+ /* If name matches, up the count */
+ if (STREQ(user, nmbuf)) {
+ count++;
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ char c = *pn;
+
+ *pn = '\0';
+ report(LOG_DEBUG, "%s matches: %s", user, p);
+ *pn = c;
+ }
+ }
+ }
+ free(buf);
+ return (count);
+}
+
+/*
+ * Verify how many sessions a user has according to the wholog file.
+ * Use finger to contact each NAS that wholog says has this user
+ * logged on.
+ */
+static int
+countusers_by_finger(idp)
+struct identity *idp;
+{
+ FILE *fp;
+ struct peruser pu;
+ int x, naddr, nsess, n;
+ char **addrs, *uname;
+
+ fp = fopen(wholog, "r+");
+ if (fp == NULL) {
+ return (0);
+ }
+ uname = idp->username;
+
+ /* Count sessions */
+ tac_lockfd(wholog, fileno(fp));
+ nsess = 0;
+ naddr = 0;
+ addrs = NULL;
+
+ while (fread(&pu, sizeof(pu), 1, fp) > 0) {
+ int dup;
+
+ /* Ignore records for everyone except this user */
+ if (strcmp(pu.username, uname)) {
+ continue;
+ }
+ /* Only check a given NAS once */
+ for (dup = 0, x = 0; x < naddr; ++x) {
+ if (STREQ(addrs[x], pu.NAS_name)) {
+ dup = 1;
+ break;
+ }
+ }
+ if (dup) {
+ continue;
+ }
+ /* Add this address to our list */
+ addrs = (char **) tac_realloc((char *) addrs,
+ (naddr + 1) * sizeof(char *));
+ addrs[naddr] = tac_strdup(pu.NAS_name);
+ naddr += 1;
+
+ /* Validate via finger */
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "Running finger on %s for user %s/%s",
+ pu.NAS_name, uname, idp->NAS_port);
+ }
+ n = ckfinger(uname, pu.NAS_name, idp);
+
+ if (debug & DEBUG_MAXSESS_FLAG) {
+ report(LOG_DEBUG, "finger reports %d active session%s for %s on %s",
+ n, (n == 1 ? "" : "s"), uname, pu.NAS_name);
+ }
+ nsess += n;
+ }
+
+ /* Clean up and return */
+ fclose(fp);
+ for (x = 0; x < naddr; ++x) {
+ free(addrs[x]);
+ }
+ free(addrs);
+
+ return (nsess);
+}
+
+/*
+ * Estimate how many sessions a named user currently owns by looking in
+ * the wholog file.
+ */
+static int
+countuser(idp)
+struct identity *idp;
+{
+ FILE *fp;
+ struct peruser pu;
+ int nsess;
+
+ /* Access log */
+ fp = fopen(wholog, "r+");
+ if (fp == NULL) {
+ return (0);
+ }
+ /* Count sessions. Skip any session associated with the current port. */
+ tac_lockfd(wholog, fileno(fp));
+ nsess = 0;
+ while (fread(&pu, sizeof(pu), 1, fp) > 0) {
+
+ /* Current user */
+ if (strcmp(pu.username, idp->username)) {
+ continue;
+ }
+ /* skip current port on current NAS */
+ if (STREQ(portname(pu.NAS_port), portname(idp->NAS_port)) &&
+ STREQ(pu.NAS_name, idp->NAS_name)) {
+ continue;
+ }
+ nsess += 1;
+ }
+
+ /* Clean up and return */
+ fclose(fp);
+ return (nsess);
+}
+
+/*
+ * is_async()
+ * Tell if the named NAS port is an async-like device.
+ *
+ * Finger reports async users, but not ISDN ones (yay). So we can do
+ * a "slow" double check for async, but not ISDN.
+ */
+static int
+is_async(portname)
+char *portname;
+{
+ if (isdigit(*portname) || !strncmp(portname, "Async", 5) ||
+ !strncmp(portname, "tty", 3)) {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * See if this user can have more sessions.
+ */
+int
+maxsess_check_count(user, data)
+char *user;
+struct author_data *data;
+{
+ int sess, maxsess;
+ struct identity *id;
+
+ /* No max session configured--don't check */
+ id = data->id;
+
+ maxsess = cfg_get_intvalue(user, TAC_IS_USER, S_maxsess, TAC_PLUS_RECURSE);
+ if (!maxsess) {
+ if (debug & (DEBUG_MAXSESS_FLAG | DEBUG_AUTHOR_FLAG)) {
+ report(LOG_DEBUG, "%s may run an unlimited number of sessions",
+ user);
+ }
+ return (0);
+ }
+ /* Count sessions for this user by looking in our wholog file */
+ sess = countuser(id);
+
+ if (debug & (DEBUG_MAXSESS_FLAG | DEBUG_AUTHOR_FLAG)) {
+ report(LOG_DEBUG,
+ "user %s is running %d out of a maximum of %d sessions",
+ user, sess, maxsess);
+ }
+ if ((sess >= maxsess) && is_async(id->NAS_port)) {
+ /* If we have finger available, double check this count by contacting
+ * the NAS */
+ sess = countusers_by_finger(id);
+ }
+ /* If it's really too high, don't authorize more services */
+ if (sess >= maxsess) {
+ char buf[80];
+
+ sprintf(buf,
+ "Login failed; too many active sessions (%d maximum)",
+ maxsess);
+
+ data->msg = tac_strdup(buf);
+
+ if (debug & (DEBUG_AUTHOR_FLAG | DEBUG_MAXSESS_FLAG)) {
+ report(LOG_DEBUG, data->msg);
+ }
+ data->status = AUTHOR_STATUS_FAIL;
+ data->output_args = NULL;
+ data->num_out_args = 0;
+ return (1);
+ }
+ return (0);
+}
+
+#else /* MAXSESS */
+
+/*
+ * The following code is not needed or used. It exists solely to
+ * prevent compilers from "helpfully" complaining that this source
+ * file is empty when MAXSESS is not defined. This upsets novices
+ * building the software, and I get complaints
+ */
+
+static int dummy = 0;
+
+#endif /* MAXSESS */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ * $Id$
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+
+#include <string.h>
+#include "md4.h"
+/*
+#include "master.h"
+#include <ciscolib.h>
+*/
+
+typedef unsigned char *POINTER;
+typedef unsigned short int UINT2;
+typedef unsigned long int UINT4;
+
+#define PROTO_LIST(list) ()
+#define const
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform PROTO_LIST ((UINT4 [4], const unsigned char [64]));
+static void Encode PROTO_LIST
+ ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+ ((UINT4 *, const unsigned char *, unsigned int));
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+void MD4Init (context)
+MD4_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void MD4Update (context, input, inputLen)
+MD4_CTX *context; /* context */
+const unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void MD4Final (digest, context)
+unsigned char digest[16]; /* message digest */
+MD4_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+static void MD4Transform (state, block)
+UINT4 state[4];
+const unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11); /* 1 */
+ FF (d, a, b, c, x[ 1], S12); /* 2 */
+ FF (c, d, a, b, x[ 2], S13); /* 3 */
+ FF (b, c, d, a, x[ 3], S14); /* 4 */
+ FF (a, b, c, d, x[ 4], S11); /* 5 */
+ FF (d, a, b, c, x[ 5], S12); /* 6 */
+ FF (c, d, a, b, x[ 6], S13); /* 7 */
+ FF (b, c, d, a, x[ 7], S14); /* 8 */
+ FF (a, b, c, d, x[ 8], S11); /* 9 */
+ FF (d, a, b, c, x[ 9], S12); /* 10 */
+ FF (c, d, a, b, x[10], S13); /* 11 */
+ FF (b, c, d, a, x[11], S14); /* 12 */
+ FF (a, b, c, d, x[12], S11); /* 13 */
+ FF (d, a, b, c, x[13], S12); /* 14 */
+ FF (c, d, a, b, x[14], S13); /* 15 */
+ FF (b, c, d, a, x[15], S14); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 0], S21); /* 17 */
+ GG (d, a, b, c, x[ 4], S22); /* 18 */
+ GG (c, d, a, b, x[ 8], S23); /* 19 */
+ GG (b, c, d, a, x[12], S24); /* 20 */
+ GG (a, b, c, d, x[ 1], S21); /* 21 */
+ GG (d, a, b, c, x[ 5], S22); /* 22 */
+ GG (c, d, a, b, x[ 9], S23); /* 23 */
+ GG (b, c, d, a, x[13], S24); /* 24 */
+ GG (a, b, c, d, x[ 2], S21); /* 25 */
+ GG (d, a, b, c, x[ 6], S22); /* 26 */
+ GG (c, d, a, b, x[10], S23); /* 27 */
+ GG (b, c, d, a, x[14], S24); /* 28 */
+ GG (a, b, c, d, x[ 3], S21); /* 29 */
+ GG (d, a, b, c, x[ 7], S22); /* 30 */
+ GG (c, d, a, b, x[11], S23); /* 31 */
+ GG (b, c, d, a, x[15], S24); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 0], S31); /* 33 */
+ HH (d, a, b, c, x[ 8], S32); /* 34 */
+ HH (c, d, a, b, x[ 4], S33); /* 35 */
+ HH (b, c, d, a, x[12], S34); /* 36 */
+ HH (a, b, c, d, x[ 2], S31); /* 37 */
+ HH (d, a, b, c, x[10], S32); /* 38 */
+ HH (c, d, a, b, x[ 6], S33); /* 39 */
+ HH (b, c, d, a, x[14], S34); /* 40 */
+ HH (a, b, c, d, x[ 1], S31); /* 41 */
+ HH (d, a, b, c, x[ 9], S32); /* 42 */
+ HH (c, d, a, b, x[ 5], S33); /* 43 */
+ HH (b, c, d, a, x[13], S34); /* 44 */
+ HH (a, b, c, d, x[ 3], S31); /* 45 */
+ HH (d, a, b, c, x[11], S32); /* 46 */
+ HH (c, d, a, b, x[ 7], S33); /* 47 */
+ HH (b, c, d, a, x[15], S34); /* 48 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* 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;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (output, input, len)
+
+UINT4 *output;
+const unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* MD4.H - header file for MD4C.C
+ * $Id$
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+#ifndef _MD4_H_
+#define _MD4_H_
+/* MD4 context. */
+typedef struct MD4Context {
+ unsigned long int state[4]; /* state (ABCD) */
+ unsigned long int count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+void MD4Init();
+void MD4Update();
+void MD4Final();
+char * MD4End();
+char * MD4File();
+char * MD4Data();
+
+#endif /* _MD4_H_ */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/*
+ * RFC 1321 version #includes global.h, but md5.h has been locally modified
+ * to contain all the information that RFC 1321's global.h contains.
+ */
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64]));
+static void Encode PROTO_LIST
+ ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+ ((UINT4 *, unsigned char *, unsigned int));
+
+#if !defined(MD5_NEED_MEM_FUNCS)
+
+#define MD5_memcpy(out,in,len) memcpy(out, in, len)
+#define MD5_memset(ptr,val,len) memset(ptr, val, len)
+
+#else /* !defined(MD5_NEED_MEM_FUNCS) */
+
+static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int));
+static void MD5_memset PROTO_LIST((POINTER, int, unsigned int));
+
+#endif /* !defined(MD5_NEED_MEM_FUNCS) */
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* 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. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ 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;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4) inputLen << 3))
+ < ((UINT4) inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4) inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((POINTER) & context->buffer[index], (POINTER) input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((POINTER) & context->buffer[index], (POINTER) & input[i],
+ inputLen - i);
+}
+
+/* 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;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64. */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+ MD5_memset((POINTER) context, 0, sizeof(*context));
+}
+
+/* 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];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ MD5_memset((POINTER) x, 0, sizeof(x));
+}
+
+/* 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;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char) (input[i] & 0xff);
+ output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
+ output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
+ output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
+ }
+}
+
+/* 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;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) |
+ (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24);
+}
+
+#if defined(MD5_NEED_MEM_FUNC)
+
+/* 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;
+}
+
+#endif /* defined(MD5_NEED_MEM_FUNC) */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+
+/*
+ * MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+/* delineate the cisco changes to the RSA supplied module */
+#define CISCO_MD5_MODS
+
+#if defined(CISCO_MD5_MODS)
+
+/* typedef a 32-bit type */
+typedef unsigned long int UINT4;
+
+/* typedef a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* enable prototyping */
+/* #define PROTO_LIST(x) x */
+/* disable prototyping */
+#define PROTO_LIST(x) ()
+
+#endif /* defined(CISCO_MD5_MODS) */
+
+/* MD5 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST
+ ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
+
+
+#endif /* _MD5_H */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+#define MSCHAP_KEY "Contact Microsoft for the MSCHAP key"
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+/* Everything to do with reading and writing packets */
+
+/* send an accounting response packet */
+send_acct_reply(status, msg, data)
+ u_char status;
+ char *msg, *data;
+{
+ u_char *pak, *p;
+ HDR *hdr;
+ int len;
+ struct acct_reply *reply;
+ int msg_len, data_len;
+
+ msg_len = msg ? strlen(msg) : 0;
+ data_len = data ? strlen(data) : 0;
+
+ len = TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len;
+
+ pak = (u_char *) tac_malloc(len);
+ reply = (struct acct_reply *) (pak + TAC_PLUS_HDR_SIZE);
+ hdr = (HDR *) pak;
+
+ bzero(pak, len);
+
+ hdr->version = TAC_PLUS_VER_0;
+ hdr->type = TAC_PLUS_ACCT;
+ hdr->seq_no = ++session.seq_no;
+ hdr->encryption = TAC_PLUS_CLEAR;
+ hdr->session_id = htonl(session.session_id);
+ hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE);
+
+ reply->status = status;
+ reply->msg_len = msg_len;
+ reply->data_len = data_len;
+
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE;
+ bcopy(msg, p, msg_len);
+ p += msg_len;
+
+ bcopy(data, p, data_len);
+
+ if (debug & DEBUG_PACKET_FLAG) {
+ report(LOG_DEBUG, "Writing %s size=%d",
+ summarise_outgoing_packet_type(pak), len);
+ dump_tacacs_pak(pak);
+ }
+
+ reply->msg_len = ntohs(reply->msg_len);
+ reply->data_len = ntohs(reply->data_len);
+
+ write_packet(pak);
+ free(pak);
+}
+
+/* send an authorization reply packet */
+send_author_reply(status, msg, data, arg_cnt, args)
+u_char status;
+char *msg;
+char *data;
+int arg_cnt;
+char **args;
+{
+ u_char *pak, *p;
+ HDR *hdr;
+ struct author_reply *reply;
+ int msg_len;
+ int len;
+ int data_len;
+ int i;
+
+ data_len = (data ? strlen(data) : 0);
+ msg_len = (msg ? strlen(msg) : 0);
+
+ /* start calculating final packet size */
+ len = TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + msg_len +
+ data_len;
+
+ for (i=0; i < arg_cnt; i++) {
+ /* space for the arg and its length */
+ len += strlen(args[i]) + 1;
+ }
+
+ pak = (u_char *) tac_malloc(len);
+
+ bzero(pak, len);
+
+ hdr = (HDR *) pak;
+
+ reply = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
+
+ hdr->version = TAC_PLUS_VER_0;
+ hdr->type = TAC_PLUS_AUTHOR;
+ hdr->seq_no = ++session.seq_no;
+ hdr->encryption = TAC_PLUS_CLEAR;
+ hdr->session_id = htonl(session.session_id);
+ hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE);
+
+ reply->status = status;
+ reply->msg_len = msg_len;
+ reply->data_len = data_len;
+ reply->arg_cnt = arg_cnt;
+
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
+
+ /* place arg sizes into packet */
+ for (i=0; i < arg_cnt; i++) {
+ *p++ = strlen(args[i]);
+ }
+
+ bcopy(msg, p, msg_len);
+ p += msg_len;
+
+ bcopy(data, p, data_len);
+ p += data_len;
+
+ /* copy arg bodies into packet */
+ for (i=0; i < arg_cnt; i++) {
+ int arglen = strlen(args[i]);
+
+ bcopy(args[i], p, arglen);
+ p += arglen;
+ }
+
+ if (debug & DEBUG_PACKET_FLAG) {
+ report(LOG_DEBUG, "Writing %s size=%d",
+ summarise_outgoing_packet_type(pak), len);
+ dump_tacacs_pak(pak);
+ }
+
+ reply->msg_len = htons(reply->msg_len);
+ reply->data_len = htons(reply->data_len);
+
+ write_packet(pak);
+ free(pak);
+}
+
+
+/* Send an authentication reply packet indicating an error has
+ occurred. msg is a null terminated character string */
+
+send_authen_error(msg)
+char *msg;
+{
+ char buf[255];
+
+ sprintf(buf, "%s %s: %s", session.peer, session.port, msg);
+ report(LOG_ERR, buf);
+ send_authen_reply(TAC_PLUS_AUTHEN_STATUS_ERROR,
+ buf,
+ strlen(buf),
+ NULL,
+ 0,
+ 0);
+}
+
+/* create and send an authentication reply packet from tacacs+ to a NAS */
+
+send_authen_reply(status, msg, msg_len, data, data_len, flags)
+int status;
+char *msg;
+u_short msg_len;
+char *data;
+u_short data_len;
+u_char flags;
+{
+ u_char *pak, *p;
+ HDR *hdr;
+ struct authen_reply *reply;
+
+ int len = TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len;
+
+ pak = (u_char *) tac_malloc(len);
+ bzero(pak, len);
+
+ hdr = (HDR *) pak;
+ reply = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
+
+ hdr->version = session.version;
+ hdr->type = TAC_PLUS_AUTHEN;
+ hdr->seq_no = ++session.seq_no;
+ hdr->encryption = TAC_PLUS_CLEAR;
+ hdr->session_id = htonl(session.session_id);
+ hdr->datalength = htonl(TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len);
+
+ reply->status = status;
+ reply->msg_len = msg_len;
+ reply->data_len = data_len;
+ reply->flags = flags;
+
+ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE;
+
+ bcopy(msg, p, msg_len);
+ p += msg_len;
+ bcopy(data, p, data_len);
+
+ if (debug & DEBUG_PACKET_FLAG) {
+ report(LOG_DEBUG, "Writing %s size=%d",
+ summarise_outgoing_packet_type(pak), len);
+ dump_tacacs_pak(pak);
+ }
+
+ reply->msg_len = htons(reply->msg_len);
+ reply->data_len = htons(reply->data_len);
+
+ write_packet(pak);
+ free(pak);
+}
+
+
+/* read an authentication GETDATA packet from a NAS. Return 0 on failure */
+u_char *
+get_authen_continue()
+{
+ HDR *hdr;
+ u_char *pak, *read_packet();
+ struct authen_cont *cont;
+ char msg[255];
+
+ pak = read_packet();
+ if (!pak)
+ return(NULL);
+ hdr = (HDR *) pak;
+ cont = (struct authen_cont *) (pak + TAC_PLUS_HDR_SIZE);
+
+ if ((hdr->type != TAC_PLUS_AUTHEN) || (hdr->seq_no <= 1)) {
+ sprintf(msg,
+ "%s: Bad packet type=%d/seq no=%d when expecting authentication cont",
+ session.peer, hdr->type, hdr->seq_no);
+ report(LOG_ERR, msg);
+ send_authen_error(msg);
+ return(NULL);
+ }
+
+ cont->user_msg_len = ntohs(cont->user_msg_len);
+ cont->user_data_len = ntohs(cont->user_data_len);
+
+ if (TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE +
+ cont->user_msg_len +
+ cont->user_data_len !=
+ ntohl(hdr->datalength)) {
+ char *m = "Illegally sized authentication cont packet";
+ report(LOG_ERR, "%s: %s", session.peer, m);
+ send_authen_error(m);
+ return(NULL);
+ }
+
+ if (debug & DEBUG_PACKET_FLAG)
+ dump_nas_pak(pak);
+
+ return (pak);
+}
+
+/* Read n bytes from descriptor fd into array ptr with timeout t
+ * seconds. Note the timeout is applied to each read, not for the
+ * overall operation.
+ *
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes read. */
+
+int
+sockread(fd, ptr, nbytes, timeout)
+int fd;
+u_char *ptr;
+int nbytes;
+int timeout;
+{
+ int nleft, nread;
+ fd_set readfds, exceptfds;
+ struct timeval tout;
+
+ tout.tv_sec = timeout;
+ tout.tv_usec = 0;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &exceptfds);
+
+ nleft = nbytes;
+
+ while (nleft > 0) {
+ int status = select(fd + 1, &readfds, (fd_set *) NULL,
+ &exceptfds, &tout);
+
+ if (status == 0) {
+ report(LOG_DEBUG, "%s: timeout reading fd %d", session.peer, fd);
+ return(-1);
+ }
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ report(LOG_DEBUG, "%s: error in select %s fd %d",
+ session.peer, sys_errlist[errno], fd);
+ return (-1);
+ }
+ if (FD_ISSET(fd, &exceptfds)) {
+ report(LOG_DEBUG, "%s: exception on fd %d",
+ session.peer, fd);
+ return (-1);
+ }
+ if (!FD_ISSET(fd, &readfds)) {
+ report(LOG_DEBUG, "%s: spurious return from select",
+ session.peer);
+ continue;
+ }
+ again:
+ nread = read(fd, ptr, nleft);
+
+ if (nread < 0) {
+ if (errno == EINTR)
+ goto again;
+ report(LOG_DEBUG, "%s %s: error reading fd %d nread=%d %s",
+ session.peer, session.port, fd, nread, sys_errlist[errno]);
+ return (-1); /* error */
+
+ } else if (nread == 0) {
+ report(LOG_DEBUG, "%s %s: fd %d eof (connection closed)",
+ session.peer, session.port, fd);
+ return (-1); /* eof */
+ }
+ nleft -= nread;
+ if (nleft)
+ ptr += nread;
+ }
+ return (nbytes - nleft);
+}
+
+/* Write n bytes to descriptor fd from array ptr with timeout t
+ * seconds. Note the timeout is applied to each write, not for the
+ * overall operation.
+ *
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes written. */
+
+int
+sockwrite(fd, ptr, bytes, timeout)
+int fd;
+u_char *ptr;
+int bytes;
+int timeout;
+{
+ int remaining, sent;
+ fd_set writefds, exceptfds;
+ struct timeval tout;
+
+ sent = 0;
+
+ tout.tv_sec = timeout;
+ tout.tv_usec = 0;
+
+ FD_ZERO(&writefds);
+ FD_SET(fd, &writefds);
+
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &exceptfds);
+
+ remaining = bytes;
+
+ while (remaining > 0) {
+ int status = select(fd + 1, (fd_set *) NULL,
+ &writefds, &exceptfds, &tout);
+
+ if (status == 0) {
+ report(LOG_DEBUG, "%s: timeout writing to fd %d",
+ session.peer, fd);
+ return (-1);
+ }
+ if (status < 0) {
+ report(LOG_DEBUG, "%s: error in select fd %d",
+ session.peer, fd);
+ return (-1);
+ }
+ if (FD_ISSET(fd, &exceptfds)) {
+ report(LOG_DEBUG, "%s: exception on fd %d",
+ session.peer, fd);
+ return (sent); /* error */
+ }
+
+ if (!FD_ISSET(fd, &writefds)) {
+ report(LOG_DEBUG, "%s: spurious return from select",
+ session.peer);
+ continue;
+ }
+ sent = write(fd, ptr, remaining);
+
+ if (sent <= 0) {
+ report(LOG_DEBUG, "%s: error writing fd %d sent=%d",
+ session.peer, fd, sent);
+ return (sent); /* error */
+ }
+ remaining -= sent;
+ ptr += sent;
+ }
+ return (bytes - remaining);
+}
+
+/* read a packet from the wire, and decrypt it. Increment the global
+ seq_no return NULL on failure */
+
+u_char *
+read_packet()
+{
+ HDR hdr;
+ u_char *pkt, *data;
+ int len;
+ char *tkey;
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Waiting for packet");
+
+ /* read a packet header */
+ len = sockread(session.sock, (u_char *) & hdr, TAC_PLUS_HDR_SIZE, TAC_PLUS_READ_TIMEOUT);
+ if (len != TAC_PLUS_HDR_SIZE) {
+ report(LOG_DEBUG, "Read %d bytes from %s %s, expecting %d",
+ len, session.peer, session.port, TAC_PLUS_HDR_SIZE);
+ return(NULL);
+ }
+
+ if ((hdr.version & TAC_PLUS_MAJOR_VER_MASK) != TAC_PLUS_MAJOR_VER) {
+ report(LOG_ERR,
+ "%s: Illegal major version specified: found %d wanted %d\n",
+ session.peer, hdr.version, TAC_PLUS_MAJOR_VER);
+ return(NULL);
+ }
+
+ /* get memory for the packet */
+ len = TAC_PLUS_HDR_SIZE + ntohl(hdr.datalength);
+ if ((ntohl(hdr.datalength) & ~0xffffUL) ||
+ len < TAC_PLUS_HDR_SIZE || len > 0x10000) {
+ report(LOG_ERR,
+ "%s: Illegal data size: %lu\n",
+ session.peer, ntohl(hdr.datalength));
+ return(NULL);
+ }
+ pkt = (u_char *) tac_malloc(len);
+
+ /* initialise the packet */
+ bcopy(&hdr, pkt, TAC_PLUS_HDR_SIZE);
+
+ /* the data start here */
+ data = pkt + TAC_PLUS_HDR_SIZE;
+
+ /* read the rest of the packet data */
+ if (sockread(session.sock, data, ntohl(hdr.datalength),
+ TAC_PLUS_READ_TIMEOUT) !=
+ ntohl(hdr.datalength)) {
+ report(LOG_ERR, "%s: start_session: bad socket read", session.peer);
+ return (NULL);
+ }
+ session.seq_no++; /* should now equal that of incoming packet */
+ session.last_exch = time(NULL);
+
+ if (session.seq_no != hdr.seq_no) {
+ report(LOG_ERR, "%s: Illegal session seq # %d != packet seq # %d",
+ session.peer,
+ session.seq_no, hdr.seq_no);
+ return (NULL);
+ }
+
+ /* decrypt the data portion */
+ if ( !(tkey=(char *)cfg_get_host_key(session.peer)) )
+ tkey = session.key;
+
+ if (md5_xor((HDR *)pkt, data, tkey)) {
+ report(LOG_ERR, "%s: start_session error decrypting data",
+ session.peer);
+ return (NULL);
+ }
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Read %s size=%d",
+ summarise_incoming_packet_type(pkt), len);
+
+ session.version = hdr.version;
+
+ return (pkt);
+}
+
+/* write a packet to the wire, encrypting it */
+write_packet(pak)
+u_char *pak;
+{
+ HDR *hdr = (HDR *) pak;
+ u_char *data;
+ int len;
+ char *tkey;
+
+ len = TAC_PLUS_HDR_SIZE + ntohl(hdr->datalength);
+
+ /* the data start here */
+ data = pak + TAC_PLUS_HDR_SIZE;
+
+ /* encrypt the data portion */
+ if ( !(tkey=(char *)cfg_get_host_key(session.peer)) )
+ tkey = session.key;
+
+ if (md5_xor((HDR *)pak, data, tkey)) {
+ report(LOG_ERR, "%s: write_packet: error encrypting data", session.peer);
+ return (-1);
+ }
+
+ if (sockwrite(session.sock, pak, len, TAC_PLUS_WRITE_TIMEOUT) != len) {
+ return (-1);
+ }
+ session.last_exch = time(NULL);
+ return (0);
+}
+
+send_error_reply(type, msg)
+int type;
+char *msg;
+{
+ switch (type) {
+ case TAC_PLUS_AUTHEN:
+ send_authen_error(msg);
+ return;
+
+ case TAC_PLUS_AUTHOR:
+ send_author_reply(AUTHOR_STATUS_ERROR, msg, NULL, 0, NULL);
+ return;
+
+ case TAC_PLUS_ACCT:
+ send_acct_reply(TAC_PLUS_ACCT_STATUS_ERROR, msg, NULL);
+ return;
+
+ default:
+ report(LOG_ERR, "Illegal type %d for send_error_reply", type);
+ return;
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* Keywords of the configuration language */
+
+#include "tac_plus.h"
+
+static void *wordtable[HASH_TAB_SIZE]; /* Table of keyword declarations */
+
+struct keyword {
+ char *word;
+ void *hash;
+ u_char value;
+};
+
+typedef struct keyword KEYWORD;
+
+static void
+declare(name, value)
+ char *name;
+ int value;
+{
+ KEYWORD *n;
+ KEYWORD *k = (KEYWORD *)tac_malloc(sizeof(KEYWORD));
+
+ k->word = tac_strdup(name);
+ k->value = value;
+
+ n = hash_add_entry(wordtable, (void *) k);
+
+ if (n) {
+ report(LOG_ERR, "Attempt to multiply define keyword %s",
+ name);
+ tac_exit(1);
+ }
+}
+
+/* Declare keywords of the "configuration language". */
+
+void
+parser_init()
+{
+ bzero(wordtable, sizeof(wordtable));
+
+ declare("access", S_access);
+ declare("accounting", S_accounting);
+ declare("after", S_after);
+ declare("arap", S_arap);
+ declare("attribute", S_attr);
+ declare("authentication", S_authentication);
+ declare("authorization", S_authorization);
+ declare("before", S_before);
+ declare("chap", S_chap);
+#ifdef MSCHAP
+ declare("ms-chap", S_mschap);
+#endif /* MSCHAP */
+ declare("cleartext", S_cleartext);
+#ifdef USE_PAM
+ declare("pam", S_pam);
+#endif /*USE_PAM */
+ declare("nopassword", S_nopasswd);
+ declare("cmd", S_cmd);
+ declare("default", S_default);
+ declare("deny", S_deny);
+ declare("des", S_des);
+ declare("exec", S_exec);
+ declare("expires", S_expires);
+ declare("file", S_file);
+ declare("group", S_group);
+ declare("global", S_global);
+ declare("host", S_host);
+ declare("type", S_type);
+ declare("ip", S_ip);
+ declare("ipx", S_ipx);
+ declare("key", S_key);
+ declare("lcp", S_lcp);
+#ifdef MAXSESS
+ declare("maxsess", S_maxsess);
+#endif
+#ifdef DB
+ declare("db", S_db);
+ declare("db_accounting",S_db_accounting);
+#endif
+#ifdef USE_LDAP
+ declare ("ldap", S_ldap);
+#endif
+ declare("member", S_member);
+ declare("message", S_message);
+ declare("name", S_name);
+ declare("optional", S_optional);
+ declare("login", S_login);
+ declare("permit", S_permit);
+ declare("pap", S_pap);
+ declare("opap", S_opap);
+ declare("ppp", S_ppp);
+ declare("protocol", S_protocol);
+ declare("skey", S_skey);
+ declare("slip", S_slip);
+ declare("service", S_svc);
+ declare("user", S_user);
+ declare("time", S_time);
+}
+
+/* Return a keyword code if a keyword is recognized. 0 otherwise */
+int
+keycode(keyword)
+char *keyword;
+{
+ KEYWORD *k = hash_lookup(wordtable, keyword);
+
+ if (k)
+ return (k->value);
+ return (S_unknown);
+}
+
+char *
+codestring(type)
+int type;
+{
+ switch (type) {
+ default:
+ return ("<unknown symbol>");
+ case S_eof:
+ return ("end-of-file");
+ case S_unknown:
+ return ("unknown");
+ case S_separator:
+ return ("=");
+ case S_string:
+ return ("<string>");
+ case S_openbra:
+ return ("{");
+ case S_closebra:
+ return ("}");
+ case S_key:
+ return ("key");
+ case S_user:
+ return ("user");
+ case S_group:
+ return ("group");
+ case S_host:
+ return ("host");
+ case S_type:
+ return ("type");
+ case S_file:
+ return ("file");
+ case S_skey:
+ return ("skey");
+ case S_name:
+ return ("name");
+ case S_login:
+ return ("login");
+ case S_member:
+ return ("member");
+#ifdef MAXSESS
+ case S_maxsess:
+ return ("maxsess");
+#endif
+#ifdef DB
+ case S_db:
+ return ("db");
+ case S_db_accounting:
+ return ("db_accounting");
+#endif
+ case S_expires:
+ return ("expires");
+ case S_after:
+ return ("after");
+ case S_before:
+ return ("before");
+ case S_message:
+ return ("message");
+ case S_arap:
+ return ("arap");
+ case S_global:
+ return ("global");
+ case S_chap:
+ return ("chap");
+#ifdef MSCHAP
+ case S_mschap:
+ return ("ms-chap");
+#endif /* MSCHAP */
+ case S_pap:
+ return ("pap");
+ case S_opap:
+ return ("opap");
+ case S_cleartext:
+ return ("cleartext");
+#ifdef USE_PAM
+ case S_pam:
+ return ("pam");
+#endif /*USE_PAM */
+ case S_nopasswd:
+ return("nopassword");
+ case S_des:
+ return("des");
+ case S_svc:
+ return ("service");
+ case S_default:
+ return ("default");
+ case S_access:
+ return ("access");
+ case S_deny:
+ return ("deny");
+ case S_permit:
+ return ("permit");
+ case S_exec:
+ return ("exec");
+ case S_protocol:
+ return ("protocol");
+ case S_optional:
+ return ("optional");
+ case S_ip:
+ return ("ip");
+ case S_ipx:
+ return ("ipx");
+ case S_slip:
+ return ("slip");
+ case S_ppp:
+ return ("ppp");
+ case S_authentication:
+ return ("authentication");
+ case S_authorization:
+ return ("authorization");
+ case S_cmd:
+ return ("cmd");
+ case S_attr:
+ return ("attribute");
+ case S_svc_dflt:
+ return ("svc_dflt");
+ case S_accounting:
+ return ("accounting");
+ case S_lcp:
+ return("lcp");
+ case S_time:
+ return("time");
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* Dummy password, if nopasswd is specified */
+extern char *nopasswd_str;
+
+/* Keywords & values */
+
+#define S_eof 99
+#define S_unknown 101
+#define S_separator 104
+#define S_string 106
+#define S_openbra 107
+#define S_closebra 108
+#define S_svc_dflt 109
+
+#define S_key 1
+#define S_user 2
+#define S_group 3
+#define S_host 4
+#define S_accounting 5
+#define S_name 7
+#define S_login 8
+#define S_member 9
+#define S_expires 10
+#define S_cleartext 11
+#define S_message 12
+#define S_arap 13
+#define S_chap 14
+#define S_after 15
+#define S_pap 16
+#define S_svc 17
+#define S_before 18
+#define S_default 19
+#define S_access 20
+#define S_deny 21
+#define S_permit 22
+#define S_exec 23
+#define S_protocol 24
+#define S_optional 25
+#define S_ip 26
+#define S_ipx 27
+#define S_slip 28
+#define S_ppp 29
+#define S_file 30
+#define S_skey 31
+#define S_authorization 32
+#define S_authentication 33
+#define S_cmd 34
+#define S_attr 35
+#define S_lcp 36
+#define S_global 37
+#define S_des 38
+#define S_opap 39
+#ifdef MAXSESS
+#define S_maxsess 40
+#endif
+#define S_nopasswd 41
+#ifdef MSCHAP
+#define S_mschap 42
+#endif /* MSCHAP */
+#ifdef USE_PAM
+#define S_pam 43
+#endif /*USE_PAM */
+#ifdef DB
+#define S_db 44
+#define S_db_accounting 45
+#endif /*DB*/
+#define S_type 46
+#ifdef USE_LDAP
+#define S_ldap 47
+#endif /* LDAP */
+#define S_time 48
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* Routines to fork children and communicate with them via pipes */
+
+#include "tac_plus.h"
+#include "sys/wait.h"
+#include <unistd.h>
+#include "signal.h"
+
+/* Support for dollar variables. Look in the authorization data and
+return strings representing values found there. If not found, return
+"unknown". Recognized strings and their interpolated value types are:
+
+user -- user name
+name -- NAS name
+port -- NAS port
+address -- NAC address (remote user location)
+priv -- privilege level (0 to 15)
+method -- (1 to 4)
+type -- (1 to 4)
+service -- (1 to 7)
+status -- (pass, fail, error, unknown) */
+
+static char *
+lookup(sym, data)
+char *sym;
+struct author_data *data;
+{
+ static char buf[5];
+
+ if (STREQ(sym, "user")) {
+ return (tac_strdup(data->id->username));
+ }
+ if (STREQ(sym, "name")) {
+ return (tac_strdup(data->id->NAS_name));
+ }
+ if (STREQ(sym, "port")) {
+ return (tac_strdup(data->id->NAS_port));
+ }
+ if (STREQ(sym, "port")) {
+ return (tac_strdup(data->id->NAS_port));
+ }
+ if (STREQ(sym, "address")) {
+ return (tac_strdup(data->id->NAC_address));
+ }
+ if (STREQ(sym, "priv")) {
+ sprintf(buf, "%d", data->id->priv_lvl);
+ return (tac_strdup(buf));
+ }
+ if (STREQ(sym, "method")) {
+ sprintf(buf, "%d", data->authen_method);
+ return (tac_strdup(buf));
+ }
+ if (STREQ(sym, "type")) {
+ sprintf(buf, "%d", data->authen_type);
+ return (tac_strdup(buf));
+ }
+ if (STREQ(sym, "service")) {
+ sprintf(buf, "%d", data->service);
+ return (tac_strdup(buf));
+ }
+ if (STREQ(sym, "status")) {
+ switch (data->status) {
+ default:
+ return (tac_strdup("unknown"));
+ case AUTHOR_STATUS_PASS_ADD:
+ case AUTHOR_STATUS_PASS_REPL:
+ return (tac_strdup("pass"));
+ case AUTHOR_STATUS_FAIL:
+ return (tac_strdup("fail"));
+ case AUTHOR_STATUS_ERROR:
+ return (tac_strdup("error"));
+ }
+ }
+ return (tac_strdup("unknown"));
+}
+
+/* Interpolate values of dollar variables into a string. Determine
+ values for the various $ variables by looking in the authorization
+ data */
+
+static char *
+substitute(string, data)
+char *string;
+struct author_data *data;
+{
+ char *cp;
+ char out[MAX_INPUT_LINE_LEN], *outp;
+ char sym[MAX_INPUT_LINE_LEN], *symp;
+ char *value, *valuep;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "substitute: %s", string);
+
+ cp = string;
+ outp = out;
+
+ while (*cp) {
+ if (*cp != DOLLARSIGN) {
+ *outp++ = *cp++;
+ continue;
+ }
+ cp++; /* skip dollar sign */
+ symp = sym;
+
+ /* does it have curly braces e.g. ${foo} ? */
+ if (*cp == '{') {
+ cp++; /* skip { */
+ while (*cp && *cp != '}')
+ *symp++ = *cp++;
+ cp++; /* skip } */
+
+ } else {
+ /* copy symbol into sym */
+ while (*cp && isalpha(*cp))
+ *symp++ = *cp++;
+ }
+
+ *symp = '\0';
+ /* lookup value */
+
+ if (debug & DEBUG_SUBST_FLAG)
+ report(LOG_DEBUG, "Lookup %s", sym);
+
+ valuep = value = lookup(sym, data);
+
+ if (debug & DEBUG_SUBST_FLAG)
+ report(LOG_DEBUG, "Expands to: %s", value);
+
+ /* copy value into output */
+ while (valuep && *valuep)
+ *outp++ = *valuep++;
+ free(value);
+ }
+ *outp++ = '\0';
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Dollar substitution: %s", out);
+
+ return (tac_strdup(out));
+}
+
+/* Wait for a (child) pid to terminate. Return its status. Probably
+ horribly implementation dependent. */
+
+static int
+waitfor(pid)
+int pid;
+{
+ int ret;
+
+#ifdef UNIONWAIT
+ union wait status;
+#else
+ int status;
+#endif /* UNIONWAIT */
+
+ ret = waitpid(pid, &status, 0);
+
+ if (ret < 0) {
+ report(LOG_ERR, "%s: pid %d no child exists", session.peer, pid);
+ return (-1);
+ }
+ if (!WIFEXITED(status)) {
+ report(LOG_ERR, "%s: pid %d child in illegal state", session.peer, pid);
+ return (-1);
+ }
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "pid %d child exited status %d",
+ pid, WEXITSTATUS(status));
+
+ return (WEXITSTATUS(status));
+}
+
+/* Write an argv array of strings to fd, adding a newline to each one */
+static int
+write_args(fd, args, arg_cnt)
+int fd, arg_cnt;
+char **args;
+{
+ int i, m;
+
+ for (i = 0; i < arg_cnt; i++) {
+ int n = strlen(args[i]);
+
+ m = write(fd, args[i], n);
+ m += write(fd, "\n", 1);
+
+ if (m != (n + 1)) {
+ report(LOG_ERR, "%s: Process write failure", session.peer);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/* Close the three given file-descruptors */
+static void
+close_fds(fd1, fd2, fd3)
+ int fd1, fd2, fd3;
+{
+ if (fd1 >= 0) {
+ close(fd1);
+ }
+ if (fd2 >= 0) {
+ close(fd2);
+ }
+ if (fd3 >= 0) {
+ close(fd3);
+ }
+}
+
+/* Fork a command. Return read and write file descriptors in readfdp
+ and writefdp. Return the pid or -1 if unsuccessful */
+
+static int
+my_popen(cmd, readfdp, writefdp, errorfdp)
+char *cmd;
+int *readfdp, *writefdp, *errorfdp;
+{
+ int fd1[2], fd2[2], fd3[2];
+ int pid;
+
+ fd1[0] = fd1[1] = fd2[0] = fd2[1] = fd3[0] = fd3[1] = -1;
+ *readfdp = *writefdp = *errorfdp = -1;
+
+ if (pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(fd3) < 0) {
+ report(LOG_ERR, "%s: Cannot create pipes", session.peer);
+ close_fds(fd1[0], fd2[0], fd3[0]);
+ close_fds(fd1[1], fd2[1], fd3[1]);
+ return (-1);
+ }
+
+ /* The parent who forked us is set to reap all children
+ automatically. We disable this so we can explicitly reap our
+ children to read their status */
+
+ signal(SIGCHLD, SIG_DFL);
+
+ pid = fork();
+
+ if (pid < 0) {
+ report(LOG_ERR, "%s: fork failure", session.peer);
+ close_fds(fd1[0], fd2[0], fd3[0]);
+ close_fds(fd1[1], fd2[1], fd3[1]);
+ return (-1);
+ }
+ if (pid > 0) {
+ /* parent */
+ close_fds(fd1[0], fd2[1], fd3[1]);
+
+ *writefdp = fd1[1];
+ *readfdp = fd2[0];
+ *errorfdp = fd3[0];
+
+ return (pid);
+ }
+ /* child */
+ closelog();
+ close(session.sock);
+ close_fds(fd1[1], fd2[0], fd3[0]);
+
+ if (fd1[0] != STDIN_FILENO) {
+ if (dup2(fd1[0], STDIN_FILENO) < 0)
+ exit(-1);
+ close(fd1[0]);
+ }
+ if (fd2[1] != STDOUT_FILENO) {
+ if (dup2(fd2[1], STDOUT_FILENO) < 0)
+ exit(-1);
+ close(fd2[1]);
+ }
+ if (fd3[1] != STDERR_FILENO) {
+ if (dup2(fd3[1], STDERR_FILENO) < 0)
+ exit(-1);
+ close(fd3[1]);
+ }
+ (void) execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
+ _exit(-1);
+ return(0); /* keep Codecenter quiet */
+}
+
+/* read the file descriptor and stuff the data into the given array for
+ * the number of bytes given. Throw the rest away.
+ */
+static int
+read_string (fd, string, len)
+int fd, len;
+char *string;
+{
+ uint i, ret;
+ char c;
+
+ i=0;
+ do {
+ ret = read(fd, &c, 1);
+ if ( (ret > 0) && ((i+1)<len) ) {
+ string[i++] = c;
+ string[i] = '\0';
+ }
+ } while ((i<len) && (ret>0));
+ return(ret);
+}
+
+/* Read lines from fd and place them into an argv style array. Highly
+ recursive so we don't have to count lines in advance. Uses "n" as
+ the count of lines seen so far. When eof is read, the array is
+ allocated, and the recursion unravels */
+
+static char **
+read_args(n, fd)
+int n, fd;
+{
+ char buf[255], *bufp, c, **out;
+
+ bufp = buf;
+
+ while (read(fd, &c, 1) > 0) {
+ if (c != '\n') {
+ *bufp++ = c;
+ continue;
+ }
+ *bufp = '\0';
+ out = read_args(n + 1, fd);
+ out[n] = (char *) tac_malloc(strlen(buf) + 1);
+ strcpy(out[n], buf);
+ return (out);
+ }
+ /* eof */
+ out = (char **) tac_malloc(sizeof(char *) * (n + 1));
+ out[n] = NULL;
+
+ return (out);
+}
+
+
+/* Do variable interpolation on a string, then invoke it as a shell
+ command. Write an appropriate set of AV pairs to the command's
+ standard input and read its standard output into outarray. Return
+ the commands final status when it terminates */
+
+int
+call_pre_process(string, data, outargsp, outargs_cntp, error, err_len)
+char *string, *error;
+struct author_data *data;
+char ***outargsp;
+int *outargs_cntp, err_len;
+{
+ char **new_args;
+ int readfd, writefd, errorfd;
+ int status, i;
+ char *cmd = substitute(string, data);
+ int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
+
+ memset(error, '\0', err_len);
+
+ free(cmd);
+
+ if (pid < 0) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* deny */
+ }
+
+ for (i = 0; i < data->num_in_args; i++) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "input %s", data->input_args[i]);
+ }
+
+ if (write_args(writefd, data->input_args, data->num_in_args)) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* deny */
+ }
+
+ close(writefd);
+ writefd = -1;
+
+ new_args = read_args(0, readfd);
+ *outargsp = new_args;
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ for (i = 0; new_args[i]; i++) {
+ report(LOG_DEBUG, "output %s", new_args[i]);
+ }
+ }
+
+ read_string(errorfd, error, err_len);
+ if (error[0] != '\0') {
+ report(LOG_ERR, "Error from program (%d): \"%s\" ",
+ strlen(error), error);
+ }
+
+ /* count the args */
+ for (i = 0; new_args[i]; i++)
+ /* NULL stmt */ ;
+
+ *outargs_cntp = i;
+
+ status = waitfor(pid);
+ close_fds(readfd, writefd, errorfd);
+ return (status);
+}
+
+/* Do variable interpolation on a string, then invoke it as a shell
+ command. Write an appropriate set of AV pairs to the command's
+ standard input and read its standard output into outarray. Return
+ the commands final status when it terminates */
+
+int
+call_post_process(string, data, outargsp, outargs_cntp)
+char *string;
+struct author_data *data;
+char ***outargsp;
+int *outargs_cntp;
+{
+ char **new_args;
+ int status;
+ int readfd, writefd, errorfd;
+ int i;
+ char *cmd = substitute(string, data);
+ int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
+
+ free(cmd);
+
+ if (pid < 0) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* deny */
+ }
+
+ /* If the status is AUTHOR_STATUS_PASS_ADD then the current output args
+ * represent *additions* to the input args, not the full set */
+
+ if (data->status == AUTHOR_STATUS_PASS_ADD) {
+
+ for (i = 0; i < data->num_in_args; i++) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "input %s", data->input_args[i]);
+ }
+
+ if (write_args(writefd, data->input_args, data->num_in_args)) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* deny */
+ }
+ }
+ for (i = 0; i < data->num_out_args; i++) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "input %s", data->output_args[i]);
+ }
+
+ if (write_args(writefd, data->output_args, data->num_out_args)) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* deny */
+ }
+
+ close(writefd);
+ writefd = -1;
+
+ new_args = read_args(0, readfd);
+ *outargsp = new_args;
+
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ for (i = 0; new_args[i]; i++) {
+ report(LOG_DEBUG, "output %s", new_args[i]);
+ }
+ }
+ /* count the output args */
+ for (i = 0; new_args[i]; i++)
+ /* NULL stmt */ ;
+
+ *outargs_cntp = i;
+
+ status = waitfor(pid);
+ close_fds(readfd, writefd, errorfd);
+ return (status);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/* Tacacs+ password lookup routine for those systems which don't have
+ setpwfile. Not for use on /etc/passwd files */
+
+#include "tac_plus.h"
+#include <pwd.h>
+#include <string.h>
+
+static struct passwd pw_passwd;
+
+struct passwd *
+tac_passwd_lookup(name, file)
+ char *name, *file;
+{
+ FILE *passwd_fp = NULL;
+
+ static char uname[512];
+ static char password[1024];
+ static char gecos[1024];
+ static char homedir[1024];
+ static char shell[1024];
+ char buf[1024];
+ char *s, *e;
+
+ passwd_fp = fopen(file, "r");
+
+ if (passwd_fp) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "tac_passwd_lookup: open %s %d",
+ file, fileno(passwd_fp));
+ } else {
+ report(LOG_ERR, "tac_passwd_lookup: cannot open file %s for reading",
+ file);
+ return(NULL);
+ }
+
+ while (fgets(buf, sizeof(buf), passwd_fp)) {
+
+ /* uname, password, uid, gid, gecos, homedir, shell */
+
+ s = buf;
+ e = index(buf, ':');
+ if (!e)
+ break;
+
+ strncpy(uname, s, e - s);
+ uname[e - s] = '\0';
+
+ /* try next entry */
+ if (strcmp(uname, name))
+ continue;
+
+ s = e + 1;
+ e = index(s, ':');
+ if (!e) {
+ break;
+ }
+ strncpy(password, s, e - s);
+ password[e - s] = '\0';
+
+ s = e + 1;
+ e = index(s, ':');
+ if (!e) {
+ break;
+ }
+ pw_passwd.pw_uid = atoi(s);
+
+ s = e + 1;
+ e = index(s, ':');
+ pw_passwd.pw_gid = atoi(s);
+
+ s = e + 1;
+ e = index(s, ':');
+ if (!e) {
+ break;
+ }
+ strncpy(gecos, s, e - s);
+ gecos[e - s] = '\0';
+
+ s = e + 1;
+ e = index(s, ':');
+ if (!e) {
+ break;
+ }
+ strncpy(homedir, s, e - s);
+ homedir[e - s] = '\0';
+
+ s = e + 1;
+ e = index(s, '\n');
+ if (!e) {
+ break;
+ }
+ strncpy(shell, s, e - s);
+ shell[e - s] = '\0';
+
+ pw_passwd.pw_name = uname;
+ pw_passwd.pw_passwd = password;
+#ifndef NO_PWAGE
+ pw_passwd.pw_age = NULL;
+ pw_passwd.pw_comment = NULL;
+#endif /* NO_PWAGE */
+ pw_passwd.pw_gecos = gecos;
+ pw_passwd.pw_dir = homedir;
+ pw_passwd.pw_shell = shell;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "tac_passwd_lookup: close %s %d",
+ file, fileno(passwd_fp));
+ fclose(passwd_fp);
+ return(&pw_passwd);
+ }
+
+ /* no match found */
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "tac_passwd_lookup: close %s %d",
+ file, fileno(passwd_fp));
+ fclose(passwd_fp);
+ return(NULL);
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+#include "time_limit.h"
+
+#ifdef SHADOW_PASSWORDS
+#include <shadow.h>
+#endif
+
+#ifdef USE_PAM
+int
+tac_pam_auth(char *UserName,char *Password,struct authen_data *data,char *Service);
+#endif /* USE_PAM */
+
+/* For database verification */
+#ifdef DB
+int db_verify();
+#endif /* DB */
+
+/* For LDAP verification */
+#ifdef USE_LDAP
+#include "ldap.h"
+#endif /* LDAP */
+
+/* Generic password verification routines for des, file and cleartext
+ passwords */
+
+static int passwd_file_verify();
+
+/* Adjust data->status depending on whether a user has expired or not */
+
+void
+set_expiration_status(exp_date, data)
+char *exp_date;
+struct authen_data *data;
+{
+ int expired;
+
+ /* if the status is anything except pass, there's no point proceeding */
+ if (data->status != TAC_PLUS_AUTHEN_STATUS_PASS) {
+ return;
+ }
+
+ /* Check the expiration date, if any. If NULL, this check will return
+ * PW_OK */
+ expired = check_expiration(exp_date);
+
+ switch (expired) {
+ case PW_OK:
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password has not expired %s",
+ exp_date ? exp_date : "<no expiry date set>");
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ return;
+
+ case PW_EXPIRING:
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password will expire soon %s",
+ exp_date ? exp_date : "<no expiry date set>");
+ if (data->server_msg)
+ free(data->server_msg);
+ data->server_msg = tac_strdup("Password will expire soon");
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ return;
+
+ case PW_EXPIRED:
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password has expired %s",
+ exp_date ? exp_date : "<no expiry date set>");
+ if (data->server_msg)
+ free(data->server_msg);
+ data->server_msg = tac_strdup("Password has expired");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+
+ default:
+ report(LOG_ERR, "%s: Bogus return value %d from check_expiration",
+ session.peer, expired);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+}
+
+/* Verify that this user/password is valid. Works only for cleartext,
+ file and des passwords.
+
+ Return 1 if password is valid */
+
+int
+verify(name, passwd, data, recurse)
+char *name, *passwd;
+struct authen_data *data;
+int recurse;
+{
+ char *exp_date;
+ char *timestamp;
+ char *cfg_passwd;
+ char *p;
+
+ timestamp = (char *)cfg_get_timestamp(name, recurse);
+ if ( timestamp != NULL ) {
+ if( time_limit_process(timestamp) == 0 ) {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG,"Timestamp check failed");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ }
+ }
+
+ if (data->type == TAC_PLUS_AUTHEN_TYPE_PAP) {
+ cfg_passwd = cfg_get_pap_secret(name, recurse);
+ } else {
+ cfg_passwd = cfg_get_login_secret(name, recurse);
+ }
+
+ /* If there is no login or pap password for this user, see if there is
+ a global password for her that we can use */
+
+ if (!cfg_passwd) {
+ cfg_passwd = cfg_get_global_secret(name, recurse);
+ }
+
+ /* If we still have no password for this user (or no user for that
+ matter) but the default authentication = file <file> statement
+ has been issued, attempt to use this password file */
+
+ if (!cfg_passwd) {
+ char *file = cfg_get_authen_default();
+ switch (cfg_get_authen_default_method()) {
+ case (S_file):
+
+ if (file) {
+ return (passwd_file_verify(name, passwd, data, file));
+ }
+ break;
+#ifdef DB
+ case (S_db):
+ /* ugly check for database connect string */
+ if( strstr(file, "://") ){
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG,"%s %s: DB access to %s for user %s",session.peer, session.port, file, name);
+ if (!db_verify(name, passwd, file)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+ exp_date = NULL;
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+ break;
+#endif
+
+#ifdef USE_LDAP
+ case (S_ldap):
+ if (ldap_verify(name, passwd, file)==1) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+ exp_date = NULL;
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ break;
+#endif /* USE_LDAP */
+
+#ifdef USE_PAM
+ case (S_pam):
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "PAM verify daemon %s == NAS %s", p,passwd);
+ if (tac_pam_auth(name, passwd, data,file)) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "PAM default authentication fail");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, " PAM default authentication pass");
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ break;
+#endif
+ default:
+ /* otherwise, we fail */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+
+ }
+}
+
+ /* We have a configured password. Deal with it depending on its
+ type */
+
+
+ p = tac_find_substring("cleartext ", cfg_passwd);
+ if (p) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify daemon %s == NAS %s", p, passwd);
+
+ if (strcmp(passwd, p)) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is incorrect");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is correct");
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+
+#ifdef USE_PAM
+ p = tac_find_substring("pam ", cfg_passwd);
+ if (p) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "PAM verify daemon %s == NAS %s", p,passwd);
+
+ if (tac_pam_auth(name, passwd, data,p)) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "PAM Password is incorrect");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "PAM Password is correct");
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+
+#endif /* USE_PAM */
+
+ p = tac_find_substring("des ", cfg_passwd);
+ if (p) {
+ /* try to verify this des password */
+ if (!des_verify(passwd, p)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+
+#ifdef DB
+ p = tac_find_substring("db ", cfg_passwd);
+ if (p) {
+ /* try to verify this password from database */
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "DB verify daemon %s == NAS %s", p, passwd);
+
+ if (!db_verify(name, passwd, p)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "DB Password is incorrect");
+
+ return (0);
+ } else {
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "DB Password is correct");
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+#endif /* DB */
+
+ p = tac_find_substring("file ", cfg_passwd);
+ if (p) {
+ return (passwd_file_verify(name, passwd, data, p));
+ }
+
+ /* Oops. No idea what kind of password this is. This should never
+ happen as the parser should never create such passwords. */
+
+ report(LOG_ERR, "%s: Error cannot identify password type %s for %s",
+ session.peer,
+ cfg_passwd && cfg_passwd[0] ? cfg_passwd : "<NULL>",
+ name ? name : "<unknown>");
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+}
+
+/* verify that this user/password is valid per /etc/passwd.
+ Return 0 if invalid. */
+static int
+etc_passwd_file_verify(user, supplied_passwd, data)
+char *user, *supplied_passwd;
+struct authen_data *data;
+{
+ struct passwd *pw;
+ char *exp_date;
+ char *cfg_passwd;
+#ifdef SHADOW_PASSWORDS
+ char buf[12];
+#endif /* SHADOW_PASSWORDS */
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ setpwent();
+ pw = getpwnam(user);
+ endpwent();
+
+ if (pw == NULL) {
+ /* no entry exists */
+ return (0);
+ }
+
+ if (*pw->pw_passwd == '\0' ||
+ supplied_passwd == NULL ||
+ *supplied_passwd == '\0') {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ }
+ cfg_passwd = pw->pw_passwd;
+ exp_date = pw->pw_shell;
+
+#ifdef SHADOW_PASSWORDS
+ if (STREQ(pw->pw_passwd, "x")) {
+ struct spwd *spwd = getspnam(user);
+
+ if (!spwd) {
+ if (debug & DEBUG_PASSWD_FLAG) {
+ report(LOG_DEBUG, "No entry for %s in shadow file", user);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ }
+ if (debug & DEBUG_PASSWD_FLAG) {
+ report(LOG_DEBUG, "Found entry for %s in shadow file", user);
+ }
+ cfg_passwd = spwd->sp_pwdp;
+
+ /*
+ * Sigh. The Solaris shadow password file contains its own
+ * expiry date as the number of days after the epoch
+ * (January 1, 1970) when the password expires.
+ * Convert this to ascii so that the traditional tacacs
+ * password expiration routines work correctly.
+ */
+
+ if (spwd->sp_expire > 0) {
+ long secs = spwd->sp_expire * 24 * 60 * 60;
+ char *p = ctime(&secs);
+ bcopy(p+4, buf, 7);
+ bcopy(p+20, buf+7, 4);
+ buf[11] = '\0';
+ exp_date = buf;
+ }
+ }
+#endif /* SHADOW_PASSWORDS */
+
+ /* try to verify the password */
+ if (!des_verify(supplied_passwd, cfg_passwd)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ /* password ok. Check expiry field */
+ set_expiration_status(exp_date, data);
+
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+}
+
+/* verify that this user/password is valid per a passwd(5) style
+ database. Return 0 if invalid. */
+
+static int
+passwd_file_verify(user, supplied_passwd, data, filename)
+char *user, *supplied_passwd;
+struct authen_data *data;
+char *filename;
+{
+ struct passwd *pw;
+ char *exp_date;
+ char *cfg_passwd;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ if (filename && (STREQ(filename, "/etc/passwd")|| STREQ(filename,"/etc/shadow") )) {
+ return(etc_passwd_file_verify(user, supplied_passwd, data));
+ }
+
+
+
+ /* an alternate filename */
+ if (!(access(filename, R_OK) == 0)) {
+ report(LOG_ERR, "%s %s: Cannot access %s for user %s -- %s",
+ session.peer, session.port, filename, user, sys_errlist[errno]);
+ return (0);
+ }
+
+ pw = tac_passwd_lookup(user, filename);
+
+ if (pw == NULL)
+ /* no entry exists */
+ return (0);
+
+ if (*pw->pw_passwd == '\0' ||
+ supplied_passwd == NULL ||
+ *supplied_passwd == '\0') {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ }
+ cfg_passwd = pw->pw_passwd;
+ exp_date = pw->pw_shell;
+
+ /* try to verify the password */
+ if (!des_verify(supplied_passwd, cfg_passwd)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ /* password ok. Check expiry field */
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+}
+
+/*
+ * verify a provided password against a des encrypted one
+ * return 1 if verified, 0 otherwise.
+ */
+
+int
+des_verify(users_passwd, encrypted_passwd)
+char *users_passwd, *encrypted_passwd;
+{
+ char *ep;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify %s %s", users_passwd, encrypted_passwd);
+
+ if (users_passwd == NULL ||
+ *users_passwd == '\0' ||
+ encrypted_passwd == NULL ||
+ *encrypted_passwd == '\0') {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify returns 0");
+ return (0);
+ }
+
+ ep = (char *) crypt(users_passwd, encrypted_passwd);
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "%s encrypts to %s", users_passwd, ep);
+
+ if (strcmp(ep, encrypted_passwd) == 0) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is correct");
+ return (1);
+ }
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is incorrect");
+
+ return (0);
+}
--- /dev/null
+.TH REGEXP 3 local
+.DA 2 April 1986
+.SH NAME
+regcomp, regexec, regsub, regerror \- regular expression handler
+.SH SYNOPSIS
+.ft B
+.nf
+#include <regexp.h>
+
+regexp *regcomp(exp)
+char *exp;
+
+int regexec(prog, string)
+regexp *prog;
+char *string;
+
+regsub(prog, source, dest)
+regexp *prog;
+char *source;
+char *dest;
+
+regerror(msg)
+char *msg;
+.SH DESCRIPTION
+These functions implement
+.IR egrep (1)-style
+regular expressions and supporting facilities.
+.PP
+.I Regcomp
+compiles a regular expression into a structure of type
+.IR regexp ,
+and returns a pointer to it.
+The space has been allocated using
+.IR malloc (3)
+and may be released by
+.IR free .
+.PP
+.I Regexec
+matches a NUL-terminated \fIstring\fR against the compiled regular expression
+in \fIprog\fR.
+It returns 1 for success and 0 for failure, and adjusts the contents of
+\fIprog\fR's \fIstartp\fR and \fIendp\fR (see below) accordingly.
+.PP
+The members of a
+.I regexp
+structure include at least the following (not necessarily in order):
+.PP
+.RS
+char *startp[NSUBEXP];
+.br
+char *endp[NSUBEXP];
+.RE
+.PP
+where
+.I NSUBEXP
+is defined (as 10) in the header file.
+Once a successful \fIregexec\fR has been done using the \fIregexp\fR,
+each \fIstartp\fR-\fIendp\fR pair describes one substring
+within the \fIstring\fR,
+with the \fIstartp\fR pointing to the first character of the substring and
+the \fIendp\fR pointing to the first character following the substring.
+The 0th substring is the substring of \fIstring\fR that matched the whole
+regular expression.
+The others are those substrings that matched parenthesized expressions
+within the regular expression, with parenthesized expressions numbered
+in left-to-right order of their opening parentheses.
+.PP
+.I Regsub
+copies \fIsource\fR to \fIdest\fR, making substitutions according to the
+most recent \fIregexec\fR performed using \fIprog\fR.
+Each instance of `&' in \fIsource\fR is replaced by the substring
+indicated by \fIstartp\fR[\fI0\fR] and
+\fIendp\fR[\fI0\fR].
+Each instance of `\e\fIn\fR', where \fIn\fR is a digit, is replaced by
+the substring indicated by
+\fIstartp\fR[\fIn\fR] and
+\fIendp\fR[\fIn\fR].
+To get a literal `&' or `\e\fIn\fR' into \fIdest\fR, prefix it with `\e';
+to get a literal `\e' preceding `&' or `\e\fIn\fR', prefix it with
+another `\e'.
+.PP
+.I Regerror
+is called whenever an error is detected in \fIregcomp\fR, \fIregexec\fR,
+or \fIregsub\fR.
+The default \fIregerror\fR writes the string \fImsg\fR,
+with a suitable indicator of origin,
+on the standard
+error output
+and invokes \fIexit\fR(2).
+.I Regerror
+can be replaced by the user if other actions are desirable.
+.SH "REGULAR EXPRESSION SYNTAX"
+A regular expression is zero or more \fIbranches\fR, separated by `|'.
+It matches anything that matches one of the branches.
+.PP
+A branch is zero or more \fIpieces\fR, concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.PP
+A piece is an \fIatom\fR possibly followed by `*', `+', or `?'.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a match of the atom, or the null string.
+.PP
+An atom is a regular expression in parentheses (matching a match for the
+regular expression), a \fIrange\fR (see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of the input string), `$' (matching the null string at the
+end of the input string), a `\e' followed by a single character (matching
+that character), or a single character with no other significance
+(matching that character).
+.PP
+A \fIrange\fR is a sequence of characters enclosed in `[]'.
+It normally matches any single character from the sequence.
+If the sequence begins with `^',
+it matches any single character \fInot\fR from the rest of the sequence.
+If two characters in the sequence are separated by `\-', this is shorthand
+for the full list of ASCII characters between them
+(e.g. `[0-9]' matches any decimal digit).
+To include a literal `]' in the sequence, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character.
+.SH AMBIGUITY
+If a regular expression could match two different parts of the input string,
+it will match the one which begins earliest.
+If both begin in the same place but match different lengths, or match
+the same length in different ways, life gets messier, as follows.
+.PP
+In general, the possibilities in a list of branches are considered in
+left-to-right order, the possibilities for `*', `+', and `?' are
+considered longest-first, nested constructs are considered from the
+outermost in, and concatenated constructs are considered leftmost-first.
+The match that will be chosen is the one that uses the earliest
+possibility in the first choice that has to be made.
+If there is more than one choice, the next will be made in the same manner
+(earliest possibility) subject to the decision on the first choice.
+And so forth.
+.PP
+For example, `(ab|a)b*c' could match `abc' in one of two ways.
+The first choice is between `ab' and `a'; since `ab' is earlier, and does
+lead to a successful overall match, it is chosen.
+Since the `b' is already spoken for,
+the `b*' must match its last possibility\(emthe empty string\(emsince
+it must respect the earlier choice.
+.PP
+In the particular case where no `|'s are present and there is only one
+`*', `+', or `?', the net effect is that the longest possible
+match will be chosen.
+So `ab*', presented with `xabbbby', will match `abbbb'.
+Note that if `ab*' is tried against `xabyabbbz', it
+will match `ab' just after `x', due to the begins-earliest rule.
+(In effect, the decision on where to start the match is the first choice
+to be made, hence subsequent choices must respect it even if this leads them
+to less-preferred alternatives.)
+.SH SEE ALSO
+egrep(1), expr(1)
+.SH DIAGNOSTICS
+\fIRegcomp\fR returns NULL for a failure
+(\fIregerror\fR permitting),
+where failures are syntax errors, exceeding implementation limits,
+or applying `+' or `*' to a possibly-null operand.
+.SH HISTORY
+Both code and manual page were
+written at U of T.
+They are intended to be compatible with the Bell V8 \fIregexp\fR(3),
+but are not derived from Bell code.
+.SH BUGS
+Empty branches and empty regular expressions are not portable to V8.
+.PP
+The restriction against
+applying `*' or `+' to a possibly-null operand is an artifact of the
+simplistic implementation.
+.PP
+Does not support \fIegrep\fR's newline-separated branches;
+neither does the V8 \fIregexp\fR(3), though.
+.PP
+Due to emphasis on
+compactness and simplicity,
+it's not strikingly fast.
+It does give special attention to handling simple cases quickly.
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ * @(#)regexp.c 1.3 of 18 April 87
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+#include <stdio.h>
+#include "regexp.h"
+#include "regmagic.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this string. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define OPEN 20 /* no Mark this point in input as start of #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+#define META "^$.[()|?+*\\"
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; ®dummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regnode();
+STATIC char *regnext();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp(exp)
+char *exp;
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+ extern char *malloc();
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+ regparse = exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = ®dummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ */
+ if (flags&SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN+parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE+parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("*+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *?+");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[': {
+ register int class;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ class = UCHARAT(regparse-2)+1;
+ classend = UCHARAT(regparse);
+ if (class > classend+1)
+ FAIL("invalid [] range");
+ for (; class <= classend; class++)
+ regc(class);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing");
+ break;
+ case '\\':
+ if (*regparse == '\0')
+ FAIL("trailing \\");
+ ret = regnode(EXACTLY);
+ regc(*regparse++);
+ regc('\0');
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ default: {
+ register int len;
+ register char ender;
+
+ regparse--;
+ len = strcspn(regparse, META);
+ if (len <= 0)
+ FAIL("internal disaster");
+ ender = *(regparse+len);
+ if (len > 1 && ISMULT(ender))
+ len--; /* Back off clear of ?+* operand. */
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ ret = regnode(EXACTLY);
+ while (len > 0) {
+ regc(*regparse++);
+ len--;
+ }
+ regc('\0');
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode(op)
+char op;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == ®dummy) {
+ regsize += 3;
+ return(ret);
+ }
+
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+ if (regcode != ®dummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+ if (regcode == ®dummy) {
+ regsize += 3;
+ return;
+ }
+
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == ®dummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == ®dummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(prog, string)
+register regexp *prog;
+register char *string;
+{
+ register char *s;
+ extern char *strchr();
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return(0);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != NULL) {
+ s = string;
+ while ((s = strchr(s, prog->regmust[0])) != NULL) {
+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == NULL) /* Not present. */
+ return(0);
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return(regtry(prog, string));
+
+ /* Messy cases: unanchored match. */
+ s = string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return(1);
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return(1);
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+regexp *prog;
+char *string;
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if (regmatch(prog->program + 1)) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return(1);
+ } else
+ return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+ register char *scan; /* Current node. */
+ char *next; /* Next node. */
+ extern char *strchr();
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ next = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return(0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return(0);
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return(0);
+ reginput++;
+ break;
+ case EXACTLY: {
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return(0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return(0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+ return(0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+ return(0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[no] == NULL)
+ regstartp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[no] == NULL)
+ regendp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case BRANCH: {
+ register char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return(1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case STAR:
+ case PLUS: {
+ register char nextch;
+ register int no;
+ register char *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ nextch = *OPERAND(next);
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return(0);
+ }
+ break;
+ case END:
+ return(1); /* Success! */
+ break;
+ default:
+ regerror("memory corruption");
+ return(0);
+ break;
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ regerror("corrupted pointers");
+ return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+char *p;
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+ extern char *strchr();
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext(p)
+register char *p;
+{
+ register int offset;
+
+ if (p == ®dummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+ extern char *strchr();
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop(op)
+char *op;
+{
+ register char *p;
+ static char buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9:
+ sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ p = NULL;
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9:
+ sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(s1, s2)
+char *s1;
+char *s2;
+{
+ register char *scan1;
+ register char *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return(count);
+ count++;
+ }
+ return(count);
+}
+#endif
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+extern regexp *regcomp();
+extern int regexec();
+extern void regsub();
+extern void regerror();
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include <stdio.h>
+
+#ifdef AIX
+#include <sys/types.h>
+#else
+#include <time.h>
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h> /* ANSI C, variable length args */
+#else
+#include <varargs.h> /* has 'vararg' definitions */
+#endif
+
+FILE *ostream = NULL;
+
+char *logfile = LOGFILE_DEFAULT;
+
+/* report:
+ *
+ * This routine reports errors and such via stderr and syslog() if
+ * appopriate. It just helps avoid a lot of if-else in the code.
+ *
+ * LOG_DEBUG messages are ignored unless debugging is on.
+ * All other priorities are always logged to syslog.
+ */
+
+#ifdef __STDC__
+void
+report(int priority, char *fmt,...)
+#else
+/* VARARGS2 */
+void
+report(priority, fmt, va_alist)
+int priority;
+char *fmt;
+va_dcl /* no terminating semi-colon */
+#endif
+{
+ char msg[255]; /* temporary string */
+ char *fp, *bufp, *charp;
+ int len, m, i, n;
+ char digits[16];
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /* ensure that msg is never overwritten */
+ n = 255;
+ fp = fmt;
+ len = 0;
+ msg[n-1] = '\0';
+ bufp = msg;
+
+ while (*fp) {
+
+ if (*fp != '%') {
+ if ((len+1) >= n) {
+ break;
+ }
+ *bufp++ = *fp++;
+ len++;
+ continue;
+ }
+
+ /* seen a '%' */
+ fp++;
+
+ switch (*fp) {
+
+ case 's':
+ fp++;
+ charp = va_arg(ap, char *);
+ m = strlen(charp);
+ break;
+
+ case 'u':
+ fp++;
+ i = va_arg(ap, uint);
+ sprintf(digits, "%u", i);
+ m = strlen(digits);
+ charp = digits;
+ break;
+ case 'x':
+ fp++;
+ i = va_arg(ap, uint);
+ sprintf(digits, "%x", i);
+ m = strlen(digits);
+ charp = digits;
+ break;
+ case 'd':
+ fp++;
+ i = va_arg(ap, int);
+ sprintf(digits, "%d", i);
+ m = strlen(digits);
+ charp = digits;
+ break;
+ }
+
+ if ((len + m + 1) >= n) {
+ break;
+ }
+
+ memcpy(bufp, charp, m);
+ bufp += m;
+ len += m;
+ continue;
+ }
+
+ msg[len] = '\0';
+
+ /* check we never overwrote the end of the buffer */
+ if (msg[n-1]) {
+ abort();
+ }
+
+ va_end(ap);
+
+
+ if (console) {
+ extern int errno;
+
+ if (!ostream)
+ ostream = fopen("/dev/console", "w");
+
+ if (ostream) {
+ if (priority == LOG_ERR)
+ fprintf(ostream, "Error ");
+ fprintf(ostream, "%s\n", msg);
+ }
+ else
+ syslog(LOG_ERR, "Cannot open /dev/console errno=%d", errno);
+ }
+
+ if (debug) {
+ int logfd;
+
+ logfd = open(logfile, O_CREAT | O_WRONLY | O_APPEND, 0640);
+ if (logfd >= 0) {
+ char buf[512];
+ time_t t = time(NULL);
+ char *ct = ctime(&t);
+
+ ct[24] = '\0';
+ tac_lockfd(logfile, logfd);
+ sprintf(buf, "%s [%d]: ", ct, getpid());
+ write(logfd, buf, strlen(buf));
+ if (priority == LOG_ERR)
+ write(logfd, "Error ", 6);
+ write(logfd, msg, strlen(msg));
+ write(logfd, "\n", 1);
+ close(logfd);
+ }
+ }
+
+ if (single) {
+ fprintf(stderr, "%s\n", msg);
+ }
+
+ if (priority == LOG_DEBUG)
+ return;
+
+ if (priority == LOG_ERR)
+ syslog(priority, "Error %s", msg);
+ else
+ syslog(priority, "%s", msg);
+}
+
+/* format a hex dump for syslog */
+void
+report_hex(priority, p, len)
+u_char *p;
+int len;
+{
+ char buf[256];
+ char digit[10];
+ int buflen;
+ int i;
+
+ if (len <= 0)
+ return;
+
+ buf[0] = '\0';
+ buflen = 0;
+ for (i = 0; i < len && i < 255; i++, p++) {
+
+ sprintf(digit, "0x%x ", *p);
+ strcat(buf, digit);
+ buflen += strlen(digit);
+
+ if (buflen > 75) {
+ report(priority, "%s", buf);
+ buf[0] = '\0';
+ buflen = 0;
+ }
+ }
+
+ if (buf[0]) {
+ report(priority, "%s", buf);
+ }
+}
+
+
+/* format a non-null terminated string for syslog */
+void
+report_string(priority, p, len)
+u_char *p;
+int len;
+{
+ char buf[256];
+ char *bufp = buf;
+ int i;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len && i < 255; i++) {
+ if (32 <= *p && *p <= 126) {
+ *bufp++ = *p++;
+ } else {
+ sprintf(bufp, " 0x%x ", *p);
+ bufp += strlen(bufp);
+ p++;
+ }
+ }
+ *bufp = '\0';
+ report(priority, "%s", buf);
+}
+
+void
+regerror(s)
+char *s;
+{
+ report(LOG_ERR, "in regular expression %s", s);
+}
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+#include "md5.h"
+
+static int do_sendauth_fn();
+static void outbound_chap();
+#ifdef MSCHAP
+static void outbound_mschap();
+#endif /* MSCHAP */
+void outbound_pap();
+
+int sendauth_fn(data)
+struct authen_data *data;
+{
+ int status;
+ char *name, *p;
+
+ name = data->NAS_id->username;
+
+ if (STREQ(name, DEFAULT_USERNAME)) {
+ /* This username is only valid for authorization */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ } else {
+ status = do_sendauth_fn(data);
+ }
+
+ if (debug) {
+ switch (data->type) {
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ p = "chap";
+ break;
+
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+ p = "ms-chap";
+ break;
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ p = "pap";
+ break;
+
+ default:
+ p = "unknown";
+ break;
+ }
+
+ report(LOG_INFO, "%s-sendauth query for '%s' %s from %s %s",
+ p,
+ name && name[0] ? name : "unknown",
+ session.peer, session.port,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+ return(status);
+}
+
+/*
+ * For PAP we need to supply the outgoing PAP cleartext password.
+ * from the config file.
+ *
+ * For CHAP, we expect an id and a challenge. We will return an MD5 hash
+ * if we're successful,
+ *
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
+static int
+do_sendauth_fn(data)
+struct authen_data *data;
+{
+ char *name, *exp_date;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ /* We must have a username */
+ if (!data->NAS_id->username[0]) {
+ /* Missing username is a gross error */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("No username supplied");
+ report(LOG_ERR, "%s: No username for sendauth_fn", session.peer);
+ return (0);
+ }
+ name = data->NAS_id->username;
+
+ switch (data->type) {
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ outbound_chap(data);
+ break;
+
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+ outbound_mschap(data);
+ break;
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ outbound_pap(data);
+ break;
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s %s: %s Illegal data type for sendauth_fn",
+ session.peer, session.port, name);
+ return (0);
+ }
+
+ exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
+ set_expiration_status(exp_date, data);
+ return (0);
+}
+
+void
+outbound_pap(data)
+struct authen_data *data;
+{
+ char *secret, *p, *name;
+
+ name = data->NAS_id->username;
+
+ /* We must have a username */
+ if (!name) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+
+ /* Return her secret outbound PAP info */
+ secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE);
+ if (!secret) {
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_ERR, "%s %s: No opap secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ /* Should never happen */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s %s: Illegal opap secret format %s",
+ session.peer, session.port, secret);
+ return;
+ }
+
+ data->server_data = tac_strdup(p);
+ data->server_dlen = strlen(data->server_data);
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+}
+
+static void
+outbound_chap(data)
+struct authen_data *data;
+{
+ char *name, *secret, *chal, digest[MD5_LEN];
+ char *p;
+ u_char *mdp;
+ char id;
+ int chal_len, inlen;
+ MD5_CTX mdcontext;
+
+ name = data->NAS_id->username;
+
+ if (!name) {
+ report(LOG_ERR, "%s %s: no username for outbound_chap",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+
+ id = data->client_data[0];
+
+ chal_len = data->client_dlen - 1;
+ if (chal_len < 0) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: user %s, id=%d chal_len=%d",
+ session.peer, session.port, name, (int)id, chal_len);
+ }
+
+ /* Assume failure */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ /* Get the secret */
+ secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
+
+ /* If there is no chap password for this user, see if there is
+ a global password for her that we can use */
+ if (!secret) {
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ }
+
+ if (!secret) {
+ /* No secret. Fail */
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: No chap or global secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+
+
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ /* Should never happen */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s %s: Illegal opap secret format %s",
+ session.peer, session.port, secret);
+ return;
+ }
+ secret = p;
+
+ /*
+ * We now have the secret, the id, and the challenge value.
+ * Put them all together, and run them through the MD5 digest
+ * algorithm. */
+
+ inlen = sizeof(u_char) + strlen(secret) + chal_len;
+ mdp = (u_char *)tac_malloc(inlen);
+ mdp[0] = id;
+ bcopy(secret, &mdp[1], strlen(secret));
+ chal = data->client_data + 1;
+ bcopy(chal, mdp + strlen(secret) + 1, chal_len);
+ MD5Init(&mdcontext);
+ MD5Update(&mdcontext, mdp, inlen);
+ MD5Final((u_char *)digest, &mdcontext);
+ free(mdp);
+
+ /*
+ * Now return the calculated response value */
+
+ data->server_data = tac_malloc(MD5_LEN);
+ bcopy(digest, data->server_data, MD5_LEN);
+ data->server_dlen = MD5_LEN;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+}
+
+#ifdef MSCHAP
+
+static void
+outbound_mschap(data)
+struct authen_data *data;
+{
+ char *name, *secret, *chal;
+ char *p;
+ char id;
+ int chal_len;
+
+ name = data->NAS_id->username;
+
+ if (!name) {
+ report(LOG_ERR, "%s %s: no username for outbound_mschap",
+ session.peer, session.port);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+
+ id = data->client_data[0];
+
+ chal_len = data->client_dlen - 1;
+ if (data->client_dlen <= 2) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return;
+ }
+
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: user %s, id=%d chal_len=%d",
+ session.peer, session.port, name, (int)id, chal_len);
+ }
+
+ /* Assume failure */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ /* Get the secret */
+ secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
+
+ /* If there is no chap password for this user, see if there is
+ a global password for her that we can use */
+ if (!secret) {
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ }
+
+ if (!secret) {
+ /* No secret. Fail */
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ report(LOG_DEBUG, "%s %s: No ms-chap or global secret for %s",
+ session.peer, session.port, name);
+ }
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return;
+ }
+
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ /* Should never happen */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s %s: Illegal ms-chap secret format %s",
+ session.peer, session.port, secret);
+ return;
+ }
+ secret = p;
+
+ /*
+ * We now have the secret, the id, and the challenge value.
+ * Put them all together, and run them through the MD4 digest
+ * algorithm. */
+
+ chal = data->client_data + 1;
+
+ /*
+ * Now return the calculated response value */
+
+ data->server_data = tac_malloc(MSCHAP_DIGEST_LEN);
+
+ mschap_lmchallengeresponse(chal,secret,&data->server_data[0]);
+ mschap_ntchallengeresponse(chal,secret,&data->server_data[24]);
+
+ data->server_data[48] = 1; /* Mark it to use the NT response*/
+ data->server_dlen = MSCHAP_DIGEST_LEN;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+}
+
+#endif /* MSCHAP */
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+#include "expire.h"
+
+static int
+do_sendpass_fn();
+
+int sendpass_fn(data)
+struct authen_data *data;
+{
+ int status;
+ char *name = data->NAS_id->username;
+ char *port = data->NAS_id->NAS_port;
+
+ if (sendauth_only) {
+ /* sendpass is disallowed */
+ report(LOG_ERR, "%s: %s %s sendpass request rejected",
+ session.peer, session.port, name ? name : "<unknown>");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ }
+
+ if (STREQ(name, DEFAULT_USERNAME)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ status = 0;
+ } else {
+ status = do_sendpass_fn(data);
+ }
+
+ if (debug)
+ report(LOG_INFO, "sendpass query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ port && port[0] ? port : "unknown",
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+
+ return(status);
+}
+
+/*
+ * Cleartext password information has been requested. Look this up in
+ * the config file. Set authen_data->status.
+ *
+ * Any strings pointed to by authen_data must come from the heap. They
+ * will get freed by the caller.
+ *
+ * Return 0 if data->status is valid, otherwise 1 */
+
+static int
+do_sendpass_fn(data)
+struct authen_data *data;
+{
+ char *name;
+ char *p;
+ int expired;
+ char *exp_date;
+ char *secret;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ /* We must have a username */
+ if (!data->NAS_id->username[0]) {
+ /* choose_authen should have already asked for a username, so this is
+ * a gross error */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("No username supplied");
+ report(LOG_ERR, "%s: No username for sendpass_fn", session.peer);
+ return (0);
+ }
+ name = data->NAS_id->username;
+
+ exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
+
+ /* The user exists. Check her expiration date, if any */
+ expired = check_expiration(exp_date);
+
+ switch (expired) {
+ case PW_EXPIRED:
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ data->server_msg = tac_strdup("Password has expired");
+ return (0);
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("Bad return value for password expiration check");
+ report(LOG_ERR, "%s: Bogus return value %d from check_expiration",
+ session.peer, expired);
+ return (0);
+
+ case PW_OK:
+ case PW_EXPIRING:
+
+ /* The user exists, and has not expired. Return her secret info */
+ switch (data->type) {
+ case TAC_PLUS_AUTHEN_TYPE_CHAP:
+ secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
+ if (!secret)
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ break;
+
+#ifdef MSCHAP
+ case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
+ secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
+ if (!secret)
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ break;
+#endif /* MSCHAP */
+
+ case TAC_PLUS_AUTHEN_TYPE_ARAP:
+ secret = cfg_get_arap_secret(name, TAC_PLUS_RECURSE);
+ if (!secret)
+ secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
+ break;
+
+ case TAC_PLUS_AUTHEN_TYPE_PAP:
+ secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE);
+ break;
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("Illegal authentication type");
+ report(LOG_ERR, "%s: Illegal authentication type %d",
+ session.peer, data->type);
+ return (0);
+ }
+
+ if (!secret) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ data->server_msg = tac_strdup("No secret");
+ return (0);
+ }
+
+ p = tac_find_substring("cleartext ", secret);
+ if (!p) {
+ /* Should never happen */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ data->server_msg = tac_strdup("Illegal secret format");
+ report(LOG_ERR, "%s: Illegal secret format %s",
+ session.peer, secret);
+ return(0);
+ }
+
+ data->server_data = tac_strdup(p);
+ data->server_dlen = strlen(data->server_data);
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ if (expired == PW_EXPIRING) {
+ data->server_msg = tac_strdup("Secret will expire soon");
+ }
+ return (0);
+ }
+ /* never reached */
+}
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+#ifdef SKEY
+#include "tac_plus.h"
+#include "expire.h"
+
+/* internal state variables */
+#define STATE_AUTHEN_START 0 /* no requests issued */
+#define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+#define STATE_AUTHEN_GETPASS 2 /* password has been requested */
+
+#include <skey.h>
+
+struct private_data {
+ struct skey skey;
+ char password[MAX_PASSWD_LEN + 1];
+ int state;
+};
+
+/* Use s/key to verify a supplied password using state set up earlier
+when the username was supplied */
+
+static int
+skey_verify(passwd, data)
+char *passwd;
+struct authen_data *data;
+{
+ struct private_data *p = data->method_data;
+ struct skey *skeyp = &p->skey;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ if (skeyverify(skeyp, passwd) == 0) {
+ /* S/Key authentication succeeded */
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ if (skeyp->n < 5) {
+ data->server_msg = tac_strdup("Password will expire soon");
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Skey tacacs login authentication function. Wants a username
+ * and a password, and tries to verify them via skey.
+ *
+ * Choose_authen will ensure that we already have a username before this
+ * gets called.
+ *
+ * We will query for a password and keep it in the method_data.
+ *
+ * Any strings returned via pointers in authen_data must come from the
+ * heap. They will get freed by the caller.
+ *
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
+int
+skey_fn(data)
+struct authen_data *data;
+{
+ char *name, *passwd;
+ struct private_data *p;
+ char *prompt;
+ int pwlen;
+
+ p = (struct private_data *) data->method_data;
+
+ /* An abort has been received. Clean up and return */
+ if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+ return (1);
+ }
+ /* Initialise method_data if first time through */
+ if (!p) {
+ p = (struct private_data *) tac_malloc(sizeof(struct private_data));
+ bzero(p, sizeof(struct private_data));
+ data->method_data = p;
+ p->state = STATE_AUTHEN_START;
+ }
+
+ /* Unless we're enabling, we need a username */
+ if (data->service != TAC_PLUS_AUTHEN_SVC_ENABLE &&
+ !(char) data->NAS_id->username[0]) {
+ switch (p->state) {
+
+ case STATE_AUTHEN_GETUSER:
+ /* we have previously asked for a username but none came back.
+ * This is a gross error */
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s: No username supplied after GETUSER",
+ session.peer);
+ return (0);
+
+ case STATE_AUTHEN_START:
+ /* No username. Try requesting one */
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETUSER;
+ if (data->service == TAC_PLUS_AUTHEN_SVC_LOGIN) {
+ prompt = "\nUser Access Verification\n\nUsername: ";
+ } else {
+ prompt = "Username: ";
+ }
+ data->server_msg = tac_strdup(prompt);
+ p->state = STATE_AUTHEN_GETUSER;
+ return (0);
+
+ default:
+ /* something awful has happened. Give up and die */
+ report(LOG_ERR, "%s: skey_fn bad state %d",
+ session.peer, p->state);
+ return (1);
+ }
+ }
+
+ /* we now have a username if we needed one */
+ name = data->NAS_id->username;
+
+ /* Do we have a password? */
+ passwd = p->password;
+
+ if (!passwd[0]) {
+ char skeyprompt[80];
+
+ /* no password yet. Either we need to ask for one and expect to get
+ * called again, or we asked but nothing came back, which is fatal */
+
+ switch (p->state) {
+ case STATE_AUTHEN_GETPASS:
+ /* We already asked for a password. This should be the reply */
+ if (data->client_msg) {
+ pwlen = MIN(strlen(data->client_msg), MAX_PASSWD_LEN);
+ } else {
+ pwlen = 0;
+ }
+ strncpy(passwd, data->client_msg, pwlen);
+ passwd[pwlen] = '\0';
+ break;
+
+ default:
+ /* Request a password */
+ passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE);
+ if (!passwd && !STREQ(passwd, "skey")) {
+ report(LOG_ERR, "Cannot find skey password declaration for %s",
+ name);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return(1);
+ }
+
+ if (skeychallenge(&p->skey, name, skeyprompt) == 0) {
+ char buf[256];
+ sprintf(buf, "%s\nPassword: ", skeyprompt);
+ data->server_msg = tac_strdup(buf);
+ data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
+ p->state = STATE_AUTHEN_GETPASS;
+ return (0);
+ }
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "Cannot generate skey prompt for %s", name);
+ return(1);
+ }
+ }
+
+ /* We have a username and password. Try validating */
+
+ /* Assume the worst */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ switch (data->service) {
+ case TAC_PLUS_AUTHEN_SVC_LOGIN:
+ skey_verify(passwd, data);
+ if (debug)
+ report(LOG_INFO, "login query for '%s' %s from %s %s",
+ name && name[0] ? name : "unknown",
+ data->NAS_id->NAS_port && data->NAS_id->NAS_port[0] ?
+ data->NAS_id->NAS_port : "unknown",
+ session.peer,
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ break;
+
+ default:
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ report(LOG_ERR, "%s: Bogus service value %d from packet",
+ session.peer, data->service);
+ break;
+ }
+
+ if (data->method_data)
+ free(data->method_data);
+ data->method_data = NULL;
+
+ switch (data->status) {
+ case TAC_PLUS_AUTHEN_STATUS_ERROR:
+ case TAC_PLUS_AUTHEN_STATUS_FAIL:
+ case TAC_PLUS_AUTHEN_STATUS_PASS:
+ return (0);
+ default:
+ report(LOG_ERR, "%s: skey_fn couldn't set recognizable status %d",
+ session.peer, data->status);
+ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
+ return (1);
+ }
+}
+#else /* SKEY */
+
+/* The following code is not needed or used. It exists solely to
+ prevent compilers from "helpfully" complaining that this source
+ file is empty, which upsets novices building the software */
+
+static int dummy = 0;
+
+#endif /* SKEY */
--- /dev/null
+#ifdef USE_PAM
+
+/* tac_pam.auth.c
+ * A simple pam authentication routine written by
+ * Max Liccardo <ravel@tiscalinet.it>
+ * PAM_RUSER=username/rem_addr.
+ */
+
+ /*
+ This program was contributed by Shane Watts
+ [modifications by AGM]
+
+ You need to add the following (or equivalent) to the /etc/pam.conf file.
+ # check authorization
+ check_user auth required /usr/lib/security/pam_unix_auth.so
+ check_user account required /usr/lib/security/pam_unix_acct.so
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include "tac_plus.h"
+
+typedef struct
+{
+ char *UserName;
+ char *Passwd;
+} UserCred;
+
+
+static int fconv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ UserCred *lUserCred;
+
+
+ lUserCred = appdata_ptr;
+
+ if(lUserCred == NULL)
+ {
+ report(LOG_ERR,"argh....maybe a SunOs 5.6 ???");
+ return(PAM_CONV_ERR);
+ }
+
+
+ *resp = (struct pam_response *) calloc(num_msg,sizeof(struct pam_response));
+
+ for(i=0;i<num_msg;i++)
+ {
+ switch(msg[i]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ resp[i]->resp = strdup(lUserCred->Passwd);
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ resp[i]->resp = strdup(lUserCred->UserName);
+ break;
+
+ default:
+ report(LOG_DEBUG,"conv default");
+ break;
+ }
+ resp[i]->resp_retcode = 0;
+ }
+
+ return(PAM_SUCCESS);
+}
+
+
+
+
+int
+tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char *aszService)
+{
+ pam_handle_t *pamh=NULL;
+ int retval;
+ char *lpszRemoteUser; /* Username/NAC address */
+ struct pam_conv s_conv;
+ UserCred s_UserCred;
+
+
+ s_UserCred.UserName = aszUserName;
+ s_UserCred.Passwd = aszPassword;
+
+ s_conv.conv = fconv;
+ s_conv.appdata_ptr = (void *) &s_UserCred;
+
+
+ if((lpszRemoteUser = calloc(strlen(aszUserName)+strlen(data->NAS_id->NAC_address)+2,sizeof(char))) == NULL)
+ {
+ report(LOG_ERR,"cannot malloc");
+ return(1);
+ }
+
+ retval = pam_start(aszService,aszUserName , &s_conv, &pamh);
+
+ if (retval != PAM_SUCCESS)
+ {
+ report(LOG_ERR, "cannot start pam-authentication");
+ pamh = NULL;
+ return(1);
+ }
+
+ sprintf(lpszRemoteUser,"%s:%s",aszUserName,data->NAS_id->NAC_address);
+
+ pam_set_item(pamh,PAM_RUSER,lpszRemoteUser);
+ pam_set_item(pamh,PAM_RHOST,data->NAS_id->NAS_name);
+ pam_set_item(pamh,PAM_TTY,data->NAS_id->NAS_port);
+
+ free(lpszRemoteUser);
+
+ retval = pam_authenticate(pamh,0); /* is user really user? */
+
+ if(retval != PAM_SUCCESS)
+ report(LOG_ERR, "%s",pam_strerror(pamh,retval));
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
+ pamh = NULL;
+ return(1);
+ }
+
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+}
+
+
+/* PAM authorization rotine written by
+ * Devrim SERAL <devrim@tef.gazi.edu.tr>
+*/
+
+int
+tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszService)
+{
+ pam_handle_t *pamh=NULL;
+ int retval;
+ char *lpszRemoteUser; /* Username/NAC address */
+ struct pam_conv s_conv;
+ UserCred s_UserCred;
+
+
+ s_UserCred.UserName = aszUserName;
+
+ s_conv.conv = fconv;
+ s_conv.appdata_ptr = (void *) &s_UserCred;
+
+ if (aszService== NULL)
+ {
+ report(LOG_ERR,"Service Name doesn't available So authorize him");
+ return(0);
+ }
+
+
+ if((lpszRemoteUser = calloc(strlen(aszUserName)+strlen(data->id->NAC_address)+2,sizeof(char))) == NULL)
+ {
+ report(LOG_ERR,"cannot malloc");
+ return(1);
+ }
+
+ retval = pam_start(aszService,aszUserName , &s_conv, &pamh);
+
+ if (retval != PAM_SUCCESS)
+ {
+ report(LOG_ERR, "cannot start pam-authentication");
+ pamh = NULL;
+ return(1);
+ }
+
+ sprintf(lpszRemoteUser,"%s:%s",aszUserName,data->id->NAC_address);
+
+ pam_set_item(pamh,PAM_RUSER,lpszRemoteUser);
+ pam_set_item(pamh,PAM_RHOST,data->id->NAS_name);
+ pam_set_item(pamh,PAM_TTY,data->id->NAS_port);
+
+ free(lpszRemoteUser);
+
+ retval = pam_acct_mgmt(pamh, 0); /* Is user permit to gain access system */
+
+ if(retval != PAM_SUCCESS)
+ report(LOG_ERR, "Pam Account Managment:%s",pam_strerror(pamh,retval));
+ else
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM authorization allow user");
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
+ pamh = NULL;
+ return(1);
+ }
+
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+}
+
+
+#endif /* USE_PAM */
+
+
+
+
--- /dev/null
+.TH tac_plus 8 "10 February 1995"
+.SH NAME
+tac_plus \- tacacs plus daemon
+.SH SYNOPSIS
+.B tac_plus
+.B \-C\ <configfile>
+[
+.B \-t
+] [
+.B \-P
+] [
+.B \-g
+] [
+.B \-i
+] [
+.B \-v
+] [
+.B \-L
+] [
+.B \-p port
+] [
+.B \-d level
+]
+.SH DESCRIPTION
+tac_plus listens on tcp port
+.B
+49
+and provides Cisco systems routers and access servers with
+authentication, authorisation and accounting services.
+.LP
+A configuration file controls the details of authentication,
+authorisation and accounting.
+.LP
+On startup, tac_plus creates the file
+.B /var/run/tac_plus.pid ,
+if possible, containing its process id.
+.LP
+.SH ARGUMENTS and OPTIONS
+.TP
+.B \-C <configfile>
+.IP
+Specify the configuration file name. A configuration file is
+.B
+always required.
+.TP
+.B \-P
+Just parse the configuration file, echoing it to standard output while
+parsing, and then exit. Used for debugging configuration file syntax.
+.TP
+.B \-t
+Log all informational, debugging or error messages to
+.B
+/dev/console
+in addition to logging to syslogd. Useful for debugging.
+.IP
+.B
+NOTE:
+messages at priority LOG_DEBUG are never logged to syslog, Use the
+.B
+\-t, \-d or \-g
+flags to see all messages produced by tac_plus. These flags
+should not be used in normal service.
+.TP
+.B \-g
+Go into single threaded mode, only accepting and servicing a single
+connection at a time without forking and without closing file
+descriptors. Print all messages to standard output. For debugging
+only. Don't ever try to deliver normal service this way.
+.TP
+.B \-v
+Print the current version of tac_plus to stdout and then exit.
+.TP
+.B \-L
+Lookup the hostname of the client sending requests and use if for
+logging, instead of just using its ip address.
+.TP
+.B \-p <port>
+Use the specified port number instead of the default port
+.B
+49
+for incoming tcp connections. Note that this changes the name of the
+pid file created by the daemon, which will append the port number to
+the file name if the port is not the default one.
+.TP
+.B \-d <level>
+Switch on debugging and write debug output into
+.B
+/var/log/tac_plus.log.
+
+See the definitions of debugging flags at the bottom of tac_plus.h for
+available flags and their meanings. Most flags cause extra messages
+to be sent to
+.B
+/var/log/tac_plus.log
+and also to
+.B
+syslog.
+.IP
+.B
+NOTE:
+The
+.B
+\-g
+flag will cause these messages to also appear on stdout. The
+.B
+\-t
+flag will cause these messages to also be written to /dev/console.
+.IP
+The values represent bits, so they can be added together. Currently
+the following values are recognised:
+.nf
+
+Value Meaning
+8 authorisation debugging
+16 authentication debugging
+32 password file processing debugging
+64 accounting debugging
+128 config file parsing & lookup
+256 packet transmission/reception
+512 encryption/decryption
+1024 MD5 hash algorithm debugging
+2048 very low level encryption/decryption
+
+.fi
+.TP
+.B \-i
+Run under inetd instead of running standalone. Under inetd, the config
+file is parsed every time tac_plus starts up, so this is very
+inefficient if the config file is large or there are many incoming
+connections. The standalone version only reads the config file once at
+startup.
+.IP
+If the config file is small, and you don't have very frequent incoming
+connections, and authentication is being done via passwd(5) files or
+SKEY (which are not cached), running under inetd should be tolerable,
+but still isn't recommended.
+.TP
+\-s
+.IP
+The \-s flag will cause the daemon to always reject authentication
+requests which contain a minor version number of zero (SENDPASS). You
+can do this only if all your NASes are running an IOS version of 11.2
+or later.
+.IP
+This enhances security in the event that someone discovers your
+encryption key. SENDPASS requests permits requestors to obtain chap,
+pap and arap passwords from your daemon, if (and only if) they know
+your encryption key.
+.LP
+.SH INVOKING TAC_PLUS
+.LP
+Tac_plus is normally invoked by root, as follows:
+.LP
+
+ # tac_plus -C <configfile>
+
+.LP
+where <configfile> is a full path to the configuration file. Tac_plus
+will background itself and start listening on port 49 for incoming tcp
+connections.
+.LP
+Tac_plus must be invoked as root to obtain privileged network socket
+49 and to read the protected configuration file which may contain
+confidential information such as encryption keys and cleartext
+passwords.
+.LP
+After the port is acquired and the config file is read, root
+privileges are no longer required. You can arrange that tac_plus will
+change its user and groupid to more innocuous user and group (see the
+Makefile for instructions on how to do compile this) when
+appropriate.
+.LP
+.B
+NOTE:
+The new user and group still needs permission to read any
+passwd(5) files and S/KEY database if these are being used.
+.SH CONFIGURATION FILE PERMISSIONS
+.LP
+It goes without saying (though I say it here) that the configuration
+file should be unreadable and unwriteable by anyone except root, as it
+contains passwords and keys.
+.SH UPDATING THE CONFIGURATION FILE
+.LP
+If the daemon is sent a SIGUSR1, it will reinitialize itself,
+re-reading its config file from scratch. Note that if there is an
+error in the CONFIG file, the daemon will die.
+.LP
+.SH SYSLOG MESSAGES
+.LP
+tac_plus logs error messages to syslog, and informational messages to
+facility LOG_LOCAL6. Debug messages are never sent to syslog.
+.LP
+You may wish to add a line similar to the following to your
+syslog.conf file to see the informational messages logged using this
+facility.
+.nf
+
+local6.info /var/adm/messages
+
+.fi
+.LP
+Note that in some versions of syslogd e.g. SunOS, this line must
+contain only tabs, not spaces, and that syslogd gives very little in
+the way of diagnostics when it encounters errors in the syslog.conf
+file.
+.fi
+.SH SEE ALSO
+.LP
+The tac_plus User's Guide.
+.SH FILES
+.TP 30
+.B /var/log/tac_plus.log
+Contains debugging output when -d is in effect.
+.TP
+.B /var/run/tac_plus.pid or /var/run/tac_plus.pid.port
+contains the process id of the currently running daemon. The port
+number is appended to the filename only if the port being used is not
+the default one of 49.
+.SH BUGS
+The configuration file syntax is too complex.
--- /dev/null
+/*
+ * tac_plus.c
+ *
+ * TACACS_PLUS daemon suitable for using on Un*x systems.
+ *
+ * October 1994, Lol Grant
+ *
+ * Copyright (c) 1994-1998 by Cisco systems, 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 of the
+ * software and supporting documentation, the name of Cisco Systems,
+ * 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 modification,
+ * copying and distribution is by permission of Cisco Systems, Inc.
+
+ * Cisco Systems, Inc. makes no representations about the suitability
+ * of this software for any purpose. 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 "tac_plus.h"
+#include "sys/wait.h"
+#include "signal.h"
+
+static int standalone = 1; /* running standalone (1) or under inetd (0) */
+static int initialised = 0; /* data structures have been allocated */
+int sendauth_only = 0; /* don't respond to sendpass requests */
+int debug = 0; /* debugging flags */
+int port = 0; /* port we're listening on */
+int console = 0; /* write all syslog messages to console */
+int parse_only = 0; /* exit after verbose parsing */
+int single = 0; /* single thread (for debugging) */
+int wtmpfd = 0; /* for wtmp file logging */
+char *wtmpfile = NULL;
+
+struct timeval started_at;
+
+struct session session; /* session data */
+
+static char pidfilebuf[75]; /* holds current name of the pidfile */
+
+void start_session();
+
+#ifndef REAPCHILD
+static
+#ifdef VOIDSIG
+void
+#else
+int
+#endif /* VOIDSIG */
+reapchild()
+{
+#ifdef UNIONWAIT
+ union wait status;
+#else
+ int status;
+#endif
+ int pid;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, 0);
+ if (pid <= 0)
+ return;
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "%d reaped", pid);
+ }
+}
+#endif /* REAPCHILD */
+
+static void
+die(signum)
+int signum;
+{
+ report(LOG_INFO, "Received signal %d, shutting down", signum);
+ unlink(pidfilebuf);
+ tac_exit(0);
+}
+
+static void
+init()
+{
+ if (initialised)
+ cfg_clean_config();
+
+ report(LOG_INFO, "Reading config");
+
+ session.acctfile = tac_strdup("/var/log/acctfile");
+
+ if (!session.cfgfile) {
+ report(LOG_ERR, "no config file specified");
+ tac_exit(1);
+ }
+
+ /* read the config file */
+ if (cfg_read_config(session.cfgfile)) {
+ report(LOG_ERR, "Parsing %s", session.cfgfile);
+ fprintf(stderr,"Config file not found!!\n");
+ tac_exit(1);
+ }
+
+ initialised++;
+
+ report(LOG_INFO, "Version %s Initialized %d", VERSION, initialised);
+
+}
+
+static void
+handler(signum)
+int signum;
+{
+ report(LOG_INFO, "Received signal %d", signum);
+ init();
+#ifdef REARMSIGNAL
+ signal(SIGUSR1, handler);
+ signal(SIGHUP, handler);
+#endif REARMSIGNAL
+}
+
+/*
+ * Return a socket bound to an appropriate port number/address. Exits
+ * the program on failure */
+
+get_socket()
+{
+ int s;
+ struct sockaddr_in sin;
+ struct servent *sp;
+ int on = 1;
+
+ bzero((char *) &sin, sizeof(sin));
+
+ if (port) {
+ sin.sin_port = htons(port);
+ } else {
+ sp = getservbyname("tacacs", "tcp");
+ if (sp)
+ sin.sin_port = sp->s_port;
+ else {
+ report(LOG_ERR, "Cannot find socket port");
+ tac_exit(1);
+ }
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s < 0) {
+ console++;
+ report(LOG_ERR, "get_socket: socket: %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+#ifdef SO_REUSEADDR
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
+ sizeof(on)) < 0)
+ perror("setsockopt - SO_REUSEADDR");
+#endif /* SO_REUSEADDR */
+
+ if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ console++;
+ report(LOG_ERR, "get_socket: bind %d %s",
+ ntohs(sin.sin_port),
+ sys_errlist[errno]);
+ tac_exit(1);
+ }
+ return (s);
+}
+
+static void
+open_logfile()
+{
+#ifdef LOG_LOCAL6
+ openlog("tac_plus", LOG_PID, LOG_LOCAL6);
+#else
+ openlog("tac_plus", LOG_PID);
+#endif
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+}
+
+/*
+ * main
+ *
+ * We will eventually be called from inetd or via the rc scripts directly
+ * Parse arguments and act appropiately.
+ */
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ extern char *optarg;
+ int childpid;
+ int c;
+ int s;
+ FILE *fp;
+ int lookup_peer = 0;
+
+ debug = 0; /* no debugging */
+ standalone = 1; /* standalone */
+ single = 0; /* single threaded */
+
+ /* initialise global session data */
+ bzero(&session, sizeof(session));
+ session.peer = tac_strdup("unknown");
+
+ open_logfile();
+
+#ifdef TAC_PLUS_PORT
+ port = TAC_PLUS_PORT;
+#endif
+
+ if (argc <= 1) {
+ fprintf(stderr, "Usage: tac_plus -C <configuration file>\n");
+ fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p <port> ]\n");
+ fprintf(stderr, "\t[ -d <debug level> ] [ -i ] [ -v ] [ -s ]\n");
+ fprintf(stderr, "\t[ -l logfile ]");
+#ifdef MAXSESS
+ fprintf(stderr, " [ -w whologfile ]");
+#endif
+ fprintf(stderr, "\n");
+ tac_exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "td:C:ip:PgvsLl:w:u:")) != EOF)
+ switch (c) {
+ case 'L': /* lookup peer names via DNS */
+ lookup_peer++;
+ break;
+ case 's': /* don't respond to sendpass */
+ sendauth_only++;
+ break;
+ case 'v': /* print version and exit */
+ version();
+ tac_exit(1);
+ case 't':
+ console++; /* log to console too */
+ break;
+ case 'P': /* Parse config file only */
+ parse_only++;
+ break;
+ case 'g': /* single threaded */
+ single++;
+ break;
+ case 'p': /* port */
+ port = atoi(optarg);
+ break;
+ case 'd': /* debug */
+ debug = atoi(optarg);
+ break;
+ case 'C': /* config file name */
+ session.cfgfile = tac_strdup(optarg);
+ break;
+ case 'i': /* stand-alone */
+ standalone = 0;
+ break;
+ case 'l': /* logfile */
+ logfile = tac_strdup(optarg);
+ break;
+#ifdef MAXSESS
+ case 'w': /* wholog file */
+ wholog = tac_strdup(optarg);
+ break;
+#endif
+ case 'u':
+ wtmpfile = tac_strdup(optarg);
+ break;
+
+ default:
+ fprintf(stderr, "%s: bad switch %c\n", argv[0], c);
+ tac_exit(1);
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "Warning, not running as uid 0\n");
+ fprintf(stderr, "Tac_plus is usually run as root\n");
+ }
+
+ parser_init();
+
+ init();
+
+ signal(SIGUSR1, handler);
+ signal(SIGHUP, handler);
+ signal(SIGTERM, die);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (parse_only)
+ tac_exit(0);
+
+ if (debug)
+ report(LOG_DEBUG, "tac_plus server %s starting", VERSION);
+
+ if (!standalone) {
+ /* running under inetd */
+ struct sockaddr_in name;
+ int name_len;
+ int on = 1;
+
+ name_len = sizeof(name);
+
+ session.sock = 0;
+ if (getpeername(session.sock, (struct sockaddr *) &name, &name_len)) {
+ report(LOG_ERR, "getpeername failure %s", sys_errlist[errno]);
+ } else {
+ struct hostent *hp;
+ hp = gethostbyaddr((char *) &name.sin_addr.s_addr,
+ sizeof(name.sin_addr.s_addr), AF_INET);
+ if (session.peer) {
+ free(session.peer);
+ }
+ session.peer = tac_strdup(hp ? hp->h_name :
+ (char *) inet_ntoa(name.sin_addr));
+ }
+#ifdef FIONBIO
+ if (ioctl(session.sock, FIONBIO, &on) < 0) {
+ report(LOG_ERR, "ioctl(FIONBIO) %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+#endif
+ start_session();
+ tac_exit(0);
+ }
+
+ if (!single) {
+ /* Running standalone. Background ourselves, let go of controlling tty */
+
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+ signal(SIGHUP, SIG_IGN);
+
+ if ((childpid = fork()) < 0)
+ report(LOG_ERR, "Can't fork first child");
+ else if (childpid > 0)
+ exit(0); /* parent */
+
+ if (debug)
+ report(LOG_DEBUG, "Backgrounded");
+
+#ifndef REAPCHILD
+
+#ifdef LINUX
+ if (setpgrp() == -1)
+#else /* LINUX */
+ if (setpgrp(0, getpid()) == -1)
+#endif /* LINUX */
+ report(LOG_ERR, "Can't change process group");
+
+ c = open("/dev/tty", O_RDWR);
+ if (c >= 0) {
+ ioctl(c, TIOCNOTTY, (char *) 0);
+ (void) close(c);
+ }
+ signal(SIGCHLD, reapchild);
+
+#else /* REAPCHILD */
+
+ if (setpgrp() == 1)
+ report(LOG_ERR, "Can't change process group");
+
+ signal(SIGHUP, SIG_IGN);
+
+ if ((childpid = fork()) < 0)
+ report(LOG_ERR, "Can't fork second child");
+ else if (childpid > 0)
+ exit(0);
+
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "Forked grandchild");
+
+ signal(SIGCHLD, SIG_IGN);
+
+#endif /* REAPCHILD */
+
+ closelog(); /* some systems require this */
+
+ for (c = 0; c < getdtablesize(); c++)
+ (void) close(c);
+
+ /* make sure we can still log to syslog now we've closed everything */
+ open_logfile();
+
+ } /* ! single threaded */
+
+ ostream = NULL;
+ /* chdir("/"); */
+ umask(0);
+ errno = 0;
+
+ s = get_socket();
+
+#ifndef SOMAXCONN
+#ifdef LINUX
+#define SOMAXCONN 128
+#else
+#define SOMAXCONN 5
+#endif /* LINUX */
+#endif /* SOMAXCONN */
+
+ if (listen(s, SOMAXCONN) < 0) {
+ console++;
+ report(LOG_ERR, "listen: %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+
+ if (port == TAC_PLUS_PORT) {
+ strcpy(pidfilebuf, TACPLUS_PIDFILE);
+ } else {
+ sprintf(pidfilebuf, "%s.%d", TACPLUS_PIDFILE, port);
+ }
+
+ /* write process id to pidfile */
+ if ((fp = fopen(pidfilebuf, "w")) != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ } else
+ report(LOG_ERR, "Cannot write pid to %s %s",
+ pidfilebuf, sys_errlist[errno]);
+
+#ifdef TACPLUS_GROUPID
+ if (setgid(TACPLUS_GROUPID))
+ report(LOG_ERR, "Cannot set group id to %d %s",
+ TACPLUS_GROUPID, sys_errlist[errno]);
+#endif
+
+#ifdef TACPLUS_USERID
+ if (setuid(TACPLUS_USERID))
+ report(LOG_ERR, "Cannot set user id to %d %s",
+ TACPLUS_USERID, sys_errlist[errno]);
+#endif
+
+#ifdef MAXSESS
+ maxsess_loginit();
+#endif /* MAXSESS */
+
+ report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
+ getuid(), geteuid(), getgid(), getegid(), s);
+
+ for (;;) {
+ int pid;
+ struct sockaddr_in from;
+ int from_len;
+ int newsockfd;
+ struct hostent *hp = NULL;
+
+ bzero((char *) &from, sizeof(from));
+ from_len = sizeof(from);
+
+ newsockfd = accept(s, (struct sockaddr *) &from, &from_len);
+
+ if (newsockfd < 0) {
+ if (errno == EINTR)
+ continue;
+
+ report(LOG_ERR, "accept: %s", sys_errlist[errno]);
+ continue;
+ }
+
+ if (lookup_peer) {
+ hp = gethostbyaddr((char *) &from.sin_addr.s_addr,
+ sizeof(from.sin_addr.s_addr), AF_INET);
+ }
+
+ if (session.peer) {
+ free(session.peer);
+ }
+ session.peer = tac_strdup(hp ? hp->h_name :
+ (char *) inet_ntoa(from.sin_addr));
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "session request from %s sock=%d",
+ session.peer, newsockfd);
+
+ if (!single) {
+ pid = fork();
+
+ if (pid < 0) {
+ report(LOG_ERR, "fork error");
+ tac_exit(1);
+ }
+ } else {
+ pid = 0;
+ }
+
+ if (pid == 0) {
+ /* child */
+ if (!single)
+ close(s);
+ session.sock = newsockfd;
+ start_session();
+ shutdown(session.sock, 2);
+ close(session.sock);
+ if (!single)
+ tac_exit(0);
+ } else {
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "forked %d", pid);
+ /* parent */
+ close(newsockfd);
+ }
+ }
+}
+
+#ifdef GETDTABLESIZE
+int
+getdtablesize()
+{
+ return(_NFILE);
+}
+#endif /* GETDTABLESIZE */
+
+/* Make sure version number is kosher. Return 0 if it is */
+int
+bad_version_check(pak)
+u_char *pak;
+{
+ HDR *hdr = (HDR *) pak;
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ /*
+ * Let authen routines take care of more sophisticated version
+ * checking as its now a bit involved.
+ */
+ return(0);
+
+ case TAC_PLUS_AUTHOR:
+ case TAC_PLUS_ACCT:
+ if (hdr->version != TAC_PLUS_VER_0) {
+ send_error_reply(hdr->type, "Illegal packet version");
+ return(1);
+ }
+ return(0);
+
+ default:
+ return(1);
+ }
+}
+
+/*
+ * Determine the packet type, read the rest of the packet data,
+ * decrypt it and call the appropriate service routine.
+ *
+ */
+
+void
+start_session()
+{
+ u_char *pak, *read_packet();
+ HDR *hdr;
+ void authen();
+
+ session.seq_no = 0;
+ session.aborted = 0;
+ session.version = 0;
+
+ pak = read_packet();
+ if (!pak) {
+ return;
+ }
+
+ if (debug & DEBUG_PACKET_FLAG) {
+ report(LOG_DEBUG, "validation request from %s", session.peer);
+ dump_nas_pak(pak);
+ }
+ hdr = (HDR *) pak;
+
+ session.session_id = ntohl(hdr->session_id);
+
+ /* Do some version checking */
+ if (bad_version_check(pak)) {
+ free(pak);
+ return;
+ }
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ authen(pak);
+ free(pak);
+ return;
+
+ case TAC_PLUS_AUTHOR:
+ author(pak);
+ free(pak);
+ return;
+
+ case TAC_PLUS_ACCT:
+ accounting(pak);
+ return;
+
+ default:
+ /* Note: can't send error reply if type is unknown */
+ report(LOG_ERR, "Illegal type %d in received packet", hdr->type);
+ free(pak);
+ return;
+ }
+}
+
+version()
+{
+ fprintf(stdout, "tac_plus version %s\n", VERSION);
+#ifdef AIX
+ fprintf(stdout,"AIX\n");
+#endif
+#ifdef ARAP_DES
+ fprintf(stdout,"ARAP_DES\n");
+#endif
+#ifdef BSDI
+ fprintf(stdout,"BSDI\n");
+#endif
+#ifdef CONST_SYSERRLIST
+ fprintf(stdout,"CONST_SYSERRLIST\n");
+#endif
+#ifdef DEBUG
+ fprintf(stdout,"DEBUG\n");
+#endif
+#ifdef DES_DEBUG
+ fprintf(stdout,"DES_DEBUG\n");
+#endif
+#ifdef FIONBIO
+ fprintf(stdout,"FIONBIO\n");
+#endif
+#ifdef FREEBSD
+ fprintf(stdout,"FREEBSD\n");
+#endif
+#ifdef GETDTABLESIZE
+ fprintf(stdout,"GETDTABLESIZE\n");
+#endif
+#ifdef HPUX
+ fprintf(stdout,"HPUX\n");
+#endif
+#ifdef LINUX
+ fprintf(stdout,"LINUX\n");
+#endif
+#ifdef LITTLE_ENDIAN
+ fprintf(stdout,"LITTLE_ENDIAN\n");
+#endif
+#ifdef LOG_LOCAL6
+ fprintf(stdout,"LOG_LOCAL6\n");
+#endif
+#ifdef MAXSESS
+ fprintf(stdout,"MAXSESS\n");
+#endif
+#ifdef MIPS
+ fprintf(stdout,"MIPS\n");
+#endif
+#ifdef NEED_BZERO
+ fprintf(stdout,"NEED_BZERO\n");
+#endif
+#ifdef NETBSD
+ fprintf(stdout,"NETBSD\n");
+#endif
+#ifdef NO_PWAGE
+ fprintf(stdout,"NO_PWAGE\n");
+#endif
+#ifdef REAPCHILD
+ fprintf(stdout,"REAPCHILD\n");
+#endif
+#ifdef REARMSIGNAL
+ fprintf(stdout,"REARMSIGNAL\n");
+#endif
+#ifdef SHADOW_PASSWORDS
+ fprintf(stdout,"SHADOW_PASSWORDS\n");
+#endif
+#ifdef SIGTSTP
+ fprintf(stdout,"SIGTSTP\n");
+#endif
+#ifdef SIGTTIN
+ fprintf(stdout,"SIGTTIN\n");
+#endif
+#ifdef SIGTTOU
+ fprintf(stdout,"SIGTTOU\n");
+#endif
+#ifdef SKEY
+ fprintf(stdout,"SKEY\n");
+#endif
+#ifdef SOLARIS
+ fprintf(stdout,"SOLARIS\n");
+#endif
+#ifdef SO_REUSEADDR
+ fprintf(stdout,"SO_REUSEADDR\n");
+#endif
+#ifdef STDLIB_MALLOC
+ fprintf(stdout,"STDLIB_MALLOC\n");
+#endif
+#ifdef STRCSPN
+ fprintf(stdout,"STRCSPN\n");
+#endif
+#ifdef SYSLOG_IN_SYS
+ fprintf(stdout,"SYSLOG_IN_SYS\n");
+#endif
+#ifdef SYSV
+ fprintf(stdout,"SYSV\n");
+#endif
+#ifdef TACPLUS_GROUPID
+ fprintf(stdout,"TACPLUS_GROUPID\n");
+#endif
+#ifdef TAC_PLUS_PORT
+ fprintf(stdout,"TAC_PLUS_PORT\n");
+#endif
+#ifdef TACPLUS_USERID
+ fprintf(stdout,"TACPLUS_USERID\n");
+#endif
+#ifdef TRACE
+ fprintf(stdout,"TRACE\n");
+#endif
+#ifdef UNIONWAIT
+ fprintf(stdout,"UNIONWAIT\n");
+#endif
+#ifdef VOIDSIG
+ fprintf(stdout,"VOIDSIG\n");
+#endif
+#ifdef _BSD1
+ fprintf(stdout,"_BSD1\n");
+#endif
+#ifdef _BSD_INCLUDES
+ fprintf(stdout,"_BSD_INCLUDES\n");
+#endif
+#ifdef __STDC__
+ fprintf(stdout,"__STDC__\n");
+#endif
+}
--- /dev/null
+# Created by Devrim SERAL(devrim@tef.gazi.edu.tr)
+# It's very simple configuration file
+# Please read user_guide and tacacs+ FAQ to more information to do more
+# complex tacacs+ configuration files.
+#
+
+key = put_you_key_here
+
+# Use /etc/passwd file to do authentication
+
+default authentication = file /etc/passwd
+
+# Now tacacs+ also use default PAM authentication
+#default authentication = pam pap
+
+#If you like to use DB authentication
+#default authentication = db "db_type://db_user:db_pass@db_hostname/db_name/db_table?name_field&pass_field
+# db_type: mysql or null
+# db_user: Database connect username
+# db_pass: Database connection password
+# db_hostname : Database hostname
+# db_name : Database name
+# db_table : authentication table name
+# name_field and pass_field: Username and password field name at the db_table
+
+# Accounting records log file
+
+accounting file = /var/log/tac_acc.log
+
+# Would you like to store accounting records in database..
+# db_accounting = "db_type://db_user:db_pass@db_hostname/db_name/db_table"
+# Same as above..
+
+#All services are alowed..
+
+user = DEFAULT {
+ service = ppp protocol = ip {}
+}
+
+# Yes we have more features like per host key
+#host = 127.0.0.1 {
+# key = test
+# type = cisco
+#}
+#user = test {
+# name = Test User
+# pap = cleartext test
+# member = staff
+#}
+#
+#group = staff {
+# time = "Wd1800-1817|!Wd1819-2000"
+#}
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+/* For autoconfig */
+#include "config.h"
+
+/*
+ * If you are defining a system from scratch, the following may be useful.
+ * Otherwise, just use the system definitions below this section.
+ */
+
+/* Define this for minor include file differences on SYSV-based systems */
+/* #define SYSV */
+
+/* Define this if your sys_errlist is defined using const */
+/* #define CONST_SYSERRLIST */
+
+/* Do you need tacacs+ versions of bzero etc. */
+/* #define NEED_BZERO */
+
+/* Define this if you have shadow passwords in /etc/passwd and
+ * /etc/shadow. Note that you usually need to be root to read
+ * /etc/shadow */
+/*#define SHADOW_PASSWORDS*/
+
+/* Define this if your malloc is defined in malloc.h instead of stdlib.h */
+/* #define STDLIB_MALLOC */
+
+/* Define this if your wait call status is a union as opposed to an int */
+/* #define UNIONWAIT */
+
+/* Define this if your signal() uses a function returning void instead
+ * of int
+ */
+/* #define VOIDSIG */
+
+/* Define this if your password file does not contain age and comment fields. */
+/* #define NO_PWAGE */
+
+/* Define this if you need a getdtablesize routine defined */
+/* #define GETDTABLESIZE */
+
+/* Define this if your system does not reap children automatically
+ * when you ignore SIGCLD */
+/* #define REAPCHILD */
+
+/* Define this if you have DES routines you can link to for ARAP (See
+ * the user's guide for more details).
+ */
+/* #define ARAP_DES */
+
+/* Define this if you find that your daemon quits after being sent more than
+ * one SIGUSR1. Some systems need to explicitly rearm signals after they've been
+ * used once
+ */
+/* #define REARMSIGNAL */
+
+#define VERSION "F4.0.3.alpha.v8 (Extended Tac_plus)"
+
+/*
+ * System definitions.
+ */
+
+#ifdef NETBSD
+#define STDLIB_MALLOC
+#define NO_PWAGE
+#define CONST_SYSERRLIST
+#define VOIDSIG
+#endif
+
+#ifdef AIX
+
+/*
+ * The only way to properly compile BSD stuff on AIX is to define a
+ * "bsdcc" compiler on your system. See /usr/lpp/bos/bsdport on your
+ * system for details. People who do NOT do this tell me that the code
+ * still compiles but that it then doesn't behave correctly e.g. child
+ * processes are not reaped correctly. Don't expect much sympathy if
+ * you do this.
+ */
+
+#define _BSD 1
+#define _BSD_INCLUDES
+#define UNIONWAIT
+#define NO_PWAGE
+#endif /* AIX */
+
+#ifdef LINUX
+#define VOIDSIG
+#define NO_PWAGE
+#define REAPCHILD
+#include <unistd.h>
+#define REARMSIGNAL
+#ifdef GLIBC
+#define CONST_SYSERRLIST
+#endif
+#endif /* LINUX */
+
+#ifdef MIPS
+#define SYSV
+#define GETDTABLESIZE
+#define REAPCHILD
+#define NEED_BZERO
+#endif /* MIPS */
+
+#ifdef SOLARIS
+#define SYSV
+#define GETDTABLESIZE
+#define REAPCHILD
+#define SHADOW_PASSWORDS
+#define NEED_BZERO
+#define REARMSIGNAL
+#endif /* SOLARIS */
+
+#ifdef HPUX
+#define SYSV
+#define GETDTABLESIZE
+#define REAPCHILD
+#define SYSLOG_IN_SYS
+#define REARMSIGNAL
+#endif /* HPUX */
+
+#ifdef FREEBSD
+#define CONST_SYSERRLIST
+#define STDLIB_MALLOC
+#define VOIDSIG
+#define NO_PWAGE
+#endif
+
+#ifdef BSDI
+#define VOIDSIG
+#define STDLIB_MALLOC
+#define NO_PWAGE
+#endif
+
+#define MD5_LEN 16
+#ifdef MSCHAP
+#define MSCHAP_DIGEST_LEN 49
+#endif /* MSCHAP */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <pwd.h>
+#include <netdb.h>
+
+#ifdef SYSLOG_IN_SYS
+#include <syslog.h>
+#else
+#include <sys/syslog.h>
+#endif
+
+#include <utmp.h>
+
+#include <unistd.h>
+
+#ifdef SYSV
+#include <fcntl.h>
+#define index strchr
+#else /* ! SYSV */
+#include <strings.h>
+#endif /* SYSV */
+
+#ifndef TACPLUS_PIDFILE
+#define TACPLUS_PIDFILE "/var/run/tac_plus.pid"
+#endif
+
+
+/*
+ * You probably shouldn't be changing much below this line unless you really
+ * know what you are doing.
+ */
+
+#define DOLLARSIGN '$'
+
+/*
+ * XTACACSP protocol defintions
+ */
+
+/*
+ * This structure describes an authentication method.
+ * authen_name contains the name of the authentication method.
+ * authen_func is a pointer to the authentication function.
+ * authen_method numeric value of authentication method
+ */
+
+#define AUTHEN_NAME_SIZE 128
+
+struct authen_type {
+ char authen_name[AUTHEN_NAME_SIZE];
+ int (*authen_func)();
+ int authen_type;
+};
+
+/*
+ * This structure describes a principal that is to be authenticated.
+ * username is the principals name (ASCII, null terminated)
+ * NAS_name is the name of the NAS where the user is
+ * NAS_port is the port on the NAS where the user is
+ * NAC_address is the remote user location. This may be
+ * a remote IP address or a caller-ID or ...
+ * priv_lvl user's requested privilege level.
+ */
+
+struct identity {
+ char *username;
+ char *NAS_name;
+ char *NAS_port;
+ char *NAC_address;
+ int priv_lvl;
+};
+
+/*
+ * The authen_data structure is the data structure for passing
+ * information to and from the authentication function
+ * (authen_type.authen_func).
+ */
+
+struct authen_data {
+ struct identity *NAS_id; /* user identity */
+ char *server_msg; /* null-terminated output msg */
+
+ int server_dlen; /* output data length */
+ char *server_data; /* output data */
+
+ char *client_msg; /* null-terminated input msg a user typed */
+
+ int client_dlen; /* input data length */
+ char *client_data; /* input data */
+
+ void *method_data; /* opaque private method data */
+ int action; /* what's to be done */
+ int service; /* calling service */
+ int status; /* Authen status */
+ int type; /* Authen type */
+ u_char flags; /* input & output flags fields */
+};
+
+
+/* return values for choose_authen(); */
+
+#define CHOOSE_FAILED -1 /* failed to choose an authentication function */
+#define CHOOSE_OK 0 /* successfully chose an authentication function */
+#define CHOOSE_GETUSER 1 /* need a username before choosing */
+#define CHOOSE_BADTYPE 2 /* Invalid preferred authen function specified */
+
+
+/*
+ * This structure is the data structure for passing information to
+ * and from the authorization function (do_author()).
+ */
+struct author_data {
+ struct identity *id; /* user id */
+ int authen_method; /* authentication method */
+
+#define AUTHEN_METH_NONE 0x01
+#define AUTHEN_METH_KRB5 0x02
+#define AUTHEN_METH_LINE 0x03
+#define AUTHEN_METH_ENABLE 0x04
+#define AUTHEN_METH_LOCAL 0x05
+#define AUTHEN_METH_TACACSPLUS 0x06
+#define AUTHEN_METH_RCMD 0x20
+
+ int authen_type; /* authentication type see authen_type */
+ int service; /* calling service */
+ char *msg; /* optional NULL-terminated return message */
+ char *admin_msg; /* optional NULL-terminated admin message */
+ int status; /* return status */
+
+#define AUTHOR_STATUS_PASS_ADD 0x01
+#define AUTHOR_STATUS_PASS_REPL 0x02
+#define AUTHOR_STATUS_FAIL 0x10
+#define AUTHOR_STATUS_ERROR 0x11
+
+ int num_in_args; /* input arg count */
+ char **input_args; /* input arguments */
+ int num_out_args; /* output arg cnt */
+ char **output_args; /* output arguments */
+
+};
+
+/* An API accounting record structure */
+struct acct_rec {
+ int acct_type; /* start, stop, update */
+
+#define ACCT_TYPE_START 1
+#define ACCT_TYPE_STOP 2
+#define ACCT_TYPE_UPDATE 3
+
+ struct identity *identity;
+ int authen_method;
+ int authen_type;
+ int authen_service;
+ char *msg; /* output field */
+ char *admin_msg; /* output field */
+ int num_args;
+ char **args;
+};
+
+#ifndef TAC_PLUS_PORT
+#define TAC_PLUS_PORT 49
+#endif
+
+/* Define tac_plus name for hosts.* files */
+#ifdef TCPWRAPPER
+#define TACNAME "tac_plus"
+#endif
+
+#define TAC_PLUS_READ_TIMEOUT 180 /* seconds */
+#define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */
+
+#define NAS_PORT_MAX_LEN 255
+
+struct session {
+ int session_id; /* host specific unique session id */
+ int aborted; /* have we received an abort flag? */
+ int seq_no; /* seq. no. of last packet exchanged */
+ time_t last_exch; /* time of last packet exchange */
+ int sock; /* socket for this connection */
+ char *key; /* the key */
+ int keyline; /* line number key was found on */
+ char *peer; /* name of connected peer */
+ char *cfgfile; /* config file name */
+ char *acctfile; /* name of accounting file */
+ char *db_acct; /* name of db accounting string */
+ char port[NAS_PORT_MAX_LEN+1]; /* For error reporting */
+ u_char version; /* version of last packet read */
+};
+
+extern struct session session; /* the session */
+
+/* Global variables */
+
+extern int debug; /* debugging flag */
+extern int logging; /* syslog logging flag */
+extern int single; /* do not fork (for debugging) */
+extern int console; /* log to console */
+extern FILE *ostream; /* for logging to console */
+extern int parse_only; /* exit after parsing verbosely */
+extern int sendauth_only; /* don't do sendauth */
+
+/* All tacacs+ packets have the same header format */
+
+struct tac_plus_pak_hdr {
+ u_char version;
+
+#define TAC_PLUS_MAJOR_VER_MASK 0xf0
+#define TAC_PLUS_MAJOR_VER 0xc0
+
+#define TAC_PLUS_MINOR_VER_0 0x0
+#define TAC_PLUS_VER_0 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_0)
+
+#define TAC_PLUS_MINOR_VER_1 0x01
+#define TAC_PLUS_VER_1 (TAC_PLUS_MAJOR_VER | TAC_PLUS_MINOR_VER_1)
+
+ u_char type;
+
+#define TAC_PLUS_AUTHEN 1
+#define TAC_PLUS_AUTHOR 2
+#define TAC_PLUS_ACCT 3
+
+ u_char seq_no; /* packet sequence number */
+ u_char encryption; /* packet is encrypted or cleartext */
+
+#define TAC_PLUS_ENCRYPTED 0x0 /* packet is encrypted */
+#define TAC_PLUS_CLEAR 0x1 /* packet is not encrypted */
+
+ int session_id; /* session identifier FIXME: Is this needed? */
+ int datalength; /* length of encrypted data following this
+ * header */
+ /* datalength bytes of encrypted data */
+};
+
+#define HASH_TAB_SIZE 157 /* user and group hash table sizes */
+
+#define TAC_PLUS_HDR_SIZE 12
+
+typedef struct tac_plus_pak_hdr HDR;
+
+/* Authentication packet NAS sends to us */
+
+struct authen_start {
+ u_char action;
+
+#define TAC_PLUS_AUTHEN_LOGIN 0x1
+#define TAC_PLUS_AUTHEN_CHPASS 0x2
+#define TAC_PLUS_AUTHEN_SENDPASS 0x3 /* deprecated */
+#define TAC_PLUS_AUTHEN_SENDAUTH 0x4
+
+ u_char priv_lvl;
+
+#define TAC_PLUS_PRIV_LVL_MIN 0x0
+#define TAC_PLUS_PRIV_LVL_MAX 0xf
+
+ u_char authen_type;
+
+#define TAC_PLUS_AUTHEN_TYPE_ASCII 1
+#define TAC_PLUS_AUTHEN_TYPE_PAP 2
+#define TAC_PLUS_AUTHEN_TYPE_CHAP 3
+#define TAC_PLUS_AUTHEN_TYPE_ARAP 4
+#ifdef MSCHAP
+#define TAC_PLUS_AUTHEN_TYPE_MSCHAP 5
+#endif /* MSCHAP */
+
+ u_char service;
+
+#define TAC_PLUS_AUTHEN_SVC_LOGIN 1
+#define TAC_PLUS_AUTHEN_SVC_ENABLE 2
+#define TAC_PLUS_AUTHEN_SVC_PPP 3
+#define TAC_PLUS_AUTHEN_SVC_ARAP 4
+#define TAC_PLUS_AUTHEN_SVC_PT 5
+#define TAC_PLUS_AUTHEN_SVC_RCMD 6
+#define TAC_PLUS_AUTHEN_SVC_X25 7
+#define TAC_PLUS_AUTHEN_SVC_NASI 8
+
+ u_char user_len;
+ u_char port_len;
+ u_char rem_addr_len;
+ u_char data_len;
+ /* <user_len bytes of char data> */
+ /* <port_len bytes of char data> */
+ /* <rem_addr_len bytes of u_char data> */
+ /* <data_len bytes of u_char data> */
+};
+
+#define TAC_AUTHEN_START_FIXED_FIELDS_SIZE 8
+
+/* Authentication continue packet NAS sends to us */
+struct authen_cont {
+ u_short user_msg_len;
+ u_short user_data_len;
+ u_char flags;
+
+#define TAC_PLUS_CONTINUE_FLAG_ABORT 0x1
+
+ /* <user_msg_len bytes of u_char data> */
+ /* <user_data_len bytes of u_char data> */
+};
+
+#define TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE 5
+
+/* Authentication reply packet we send to NAS */
+struct authen_reply {
+ u_char status;
+
+#define TAC_PLUS_AUTHEN_STATUS_PASS 1
+#define TAC_PLUS_AUTHEN_STATUS_FAIL 2
+#define TAC_PLUS_AUTHEN_STATUS_GETDATA 3
+#define TAC_PLUS_AUTHEN_STATUS_GETUSER 4
+#define TAC_PLUS_AUTHEN_STATUS_GETPASS 5
+#define TAC_PLUS_AUTHEN_STATUS_RESTART 6
+#define TAC_PLUS_AUTHEN_STATUS_ERROR 7
+#define TAC_PLUS_AUTHEN_STATUS_FOLLOW 0x21
+
+ u_char flags;
+
+#define TAC_PLUS_AUTHEN_FLAG_NOECHO 0x1
+
+ u_short msg_len;
+ u_short data_len;
+
+ /* <msg_len bytes of char data> */
+ /* <data_len bytes of u_char data> */
+};
+
+#define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6
+
+/* An authorization request packet */
+struct author {
+ u_char authen_method;
+ u_char priv_lvl;
+ u_char authen_type;
+ u_char service;
+
+ u_char user_len;
+ u_char port_len;
+ u_char rem_addr_len;
+ u_char arg_cnt; /* the number of args */
+
+ /* <arg_cnt u_chars containing the lengths of args 1 to arg n> */
+ /* <user_len bytes of char data> */
+ /* <port_len bytes of char data> */
+ /* <rem_addr_len bytes of u_char data> */
+ /* <char data for each arg> */
+};
+
+#define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8
+
+/* An authorization reply packet */
+struct author_reply {
+ u_char status;
+ u_char arg_cnt;
+ u_short msg_len;
+ u_short data_len;
+
+ /* <arg_cnt u_chars containing the lengths of arg 1 to arg n> */
+ /* <msg_len bytes of char data> */
+ /* <data_len bytes of char data> */
+ /* <char data for each arg> */
+};
+
+#define TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE 6
+
+struct acct {
+ u_char flags;
+
+#define TAC_PLUS_ACCT_FLAG_MORE 0x1
+#define TAC_PLUS_ACCT_FLAG_START 0x2
+#define TAC_PLUS_ACCT_FLAG_STOP 0x4
+#define TAC_PLUS_ACCT_FLAG_WATCHDOG 0x8
+
+ u_char authen_method;
+ u_char priv_lvl;
+ u_char authen_type;
+ u_char authen_service;
+ u_char user_len;
+ u_char port_len;
+ u_char rem_addr_len;
+ u_char arg_cnt; /* the number of cmd args */
+ /* one u_char containing size for each arg */
+ /* <user_len bytes of char data> */
+ /* <port_len bytes of char data> */
+ /* <rem_addr_len bytes of u_char data> */
+ /* char data for args 1 ... n */
+};
+
+#define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9
+
+struct acct_reply {
+ u_short msg_len;
+ u_short data_len;
+ u_char status;
+
+#define TAC_PLUS_ACCT_STATUS_SUCCESS 0x1
+#define TAC_PLUS_ACCT_STATUS_ERROR 0x2
+#define TAC_PLUS_ACCT_STATUS_FOLLOW 0x21
+
+};
+
+#define TAC_ACCT_REPLY_FIXED_FIELDS_SIZE 5
+
+/* Odds and ends */
+#define TAC_PLUS_MAX_ITERATIONS 50
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define STREQ(a,b) (strcmp(a,b)==0)
+#define MAX_INPUT_LINE_LEN 255
+
+/* Debugging flags */
+
+#define DEBUG_PARSE_FLAG 2
+#define DEBUG_FORK_FLAG 4
+#define DEBUG_AUTHOR_FLAG 8
+#define DEBUG_AUTHEN_FLAG 16
+#define DEBUG_PASSWD_FLAG 32
+#define DEBUG_ACCT_FLAG 64
+#define DEBUG_CONFIG_FLAG 128
+#define DEBUG_PACKET_FLAG 256
+#define DEBUG_HEX_FLAG 512
+#define DEBUG_MD5_HASH_FLAG 1024
+#define DEBUG_XOR_FLAG 2048
+#define DEBUG_CLEAN_FLAG 4096
+#define DEBUG_SUBST_FLAG 8192
+#define DEBUG_PROXY_FLAG 16384
+#define DEBUG_MAXSESS_FLAG 32768
+#define DEBUG_LOCK_FLAG 65536
+
+extern char *codestring();
+extern int keycode();
+
+#define TAC_IS_USER 1
+#define TAC_PLUS_RECURSE 1
+#define TAC_PLUS_NORECURSE 0
+
+#define DEFAULT_USERNAME "DEFAULT"
+
+#include "parse.h"
+
+/* Node types */
+
+#define N_arg 50
+#define N_optarg 51
+#define N_svc_exec 52
+#define N_svc_slip 53
+#define N_svc_ppp 54
+#define N_svc_arap 55
+#define N_svc_cmd 56
+#define N_permit 57
+#define N_deny 58
+#define N_svc 59
+
+/* A parse tree node */
+struct node {
+ int type; /* node type (arg, svc, proto) */
+ void *next; /* pointer to next node in chain */
+ void *value; /* node value */
+ void *value1; /* node value */
+ int dflt; /* default value for node */
+ int line; /* line number declared on */
+};
+
+typedef struct node NODE;
+
+union v {
+ int intval;
+ void *pval;
+};
+
+typedef union v VALUE;
+
+/* acct.c */
+extern void accounting();
+
+/* report.c */
+extern void report_string();
+extern void report_hex();
+#ifdef __STDC__
+extern void report(int priority, char *fmt,...);
+#else
+extern void report();
+#endif
+
+/* packet.c */
+extern u_char *get_authen_continue();
+extern int send_authen_reply();
+
+/* utils.c */
+extern char *tac_malloc();
+extern char *tac_strdup();
+extern char *tac_make_string();
+extern char *tac_find_substring();
+extern char *tac_realloc();
+
+/* dump.c */
+extern char *summarise_outgoing_packet_type();
+extern char *summarise_incoming_packet_type();
+
+/* author.c */
+extern void author();
+
+/* hash.c */
+extern void *hash_add_entry();
+extern void **hash_get_entries();
+extern void *hash_lookup();
+
+/* config.c */
+extern int cfg_get_intvalue();
+extern char * cfg_get_pvalue();
+extern char *cfg_get_authen_default();
+extern int cfg_get_authen_default_method();
+extern char **cfg_get_svc_attrs();
+extern NODE *cfg_get_cmd_node();
+extern NODE *cfg_get_svc_node();
+extern char *cfg_get_expires();
+extern char *cfg_get_login_secret();
+extern int cfg_get_user_nopasswd();
+extern char *cfg_get_arap_secret();
+extern char *cfg_get_chap_secret();
+#ifdef MSCHAP
+extern char *cfg_get_mschap_secret();
+#endif /* MSCHAP */
+extern char *cfg_get_pap_secret();
+extern char *cfg_get_opap_secret();
+extern char *cfg_get_global_secret();
+#ifdef USE_PAM
+extern char *cfg_get_pam_service();
+#endif / *PAM */
+extern void cfg_clean_config();
+extern char *cfg_nodestring();
+
+/* pw.c */
+extern struct passwd *tac_passwd_lookup();
+
+/* parse.c */
+extern void parser_init();
+
+/* pwlib.c */
+extern void set_expiration_status();
+
+/* miscellaneous */
+#ifdef CONST_SYSERRLIST
+extern const char *const sys_errlist[];
+#else
+extern char *sys_errlist[];
+#endif
+extern int errno;
+extern int sendauth_fn();
+extern int sendpass_fn();
+extern int enable_fn();
+extern int default_fn();
+extern int default_v0_fn();
+extern int skey_fn();
+#ifdef MSCHAP
+extern void mschap_lmchallengeresponse();
+extern void mschap_ntchallengeresponse();
+#endif /* MSCHAP */
+
+#ifdef MAXSESS
+
+extern void maxsess_loginit();
+extern int maxsess_check_count();
+
+/*
+ * This is a shared file used to maintain a record of who's on
+ */
+#define WHOLOG_DEFAULT "/var/log/tac_who.log"
+extern char *wholog;
+/*
+ * This is state kept per user/session
+ */
+struct peruser {
+ char username[64]; /* User name */
+ char NAS_name[32]; /* NAS user logged into */
+ char NAS_port[32]; /* ...port on that NAS */
+ char NAC_address[32]; /* ...IP address of NAS */
+};
+
+#endif /* MAXSESS */
+
+#ifdef USE_PAM
+extern int tac_pam_authorization();
+#endif
+
+#define LOGFILE_DEFAULT "/var/log/tac_plus.log"
+
+extern struct timeval started_at;
+extern char *logfile;
+extern char *wtmpfile;
+extern int wtmpfd;
--- /dev/null
+#!/bin/sh
+#
+# tac_plus This shell script takes care of starting and stopping
+# tac_plus (TACACS+ daemon).
+#
+# chkconfig: 235 80 20
+# description: tac_plus is TACACS+ daemon.
+# processname: tac_plus
+# config: /etc/tacacs/tac_plus.cfg
+# pidfile: /var/run/tac_plus.pid
+# debug : 0
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Some config parameters
+#For config file
+tacacs_config="/etc/tacacs/tac_plus.cfg"
+#For debug option
+debug=0
+
+[ -f /usr/sbin/tac_plus ] || exit 0
+
+[ -f $tacacs_config ] || exit 0
+
+
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemon.
+ if [ $debug -gt 0 ]
+ then
+ echo -n "Starting TACACS+ with debug level $debug : "
+ daemon tac_plus -C $tacacs_config -d $debug
+ else
+ echo -n "Starting TACACS+ :"
+ daemon tac_plus -C $tacacs_config
+ fi
+ echo
+ touch /var/lock/subsys/tac_plus
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down TACACS+: "
+ killproc tac_plus
+ rm -f /var/lock/subsys/tac_plus
+ echo
+ ;;
+ status)
+ status tac_plus
+ exit $?
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ reload)
+ echo "TACACS+ now reloading......"
+ kill -SIGUSR1 `cat /var/run/tac_plus.pid`
+ exit $?
+ ;;
+ test)
+ echo "TACACS+ config being testing..."
+ /usr/sbin/tac_plus -P -C $tacacs_config
+ ;;
+ *)
+ echo "Usage: tac_plus {start|stop|status|restart|reload|test}"
+ exit 1
+esac
+
+exit 0
--- /dev/null
+# This is tac_plus logrotate config file
+# For more info please refer logrotate man page
+/var/log/tac_plus.log {
+ size 3M
+ missingok
+ errors root@localhost
+ compress
+ postrotate
+ /usr/bin/killall -HUP tac_plus 2> /dev/null || true
+ endscript
+}
+
+/var/log/tac_acc.log {
+ size 5M
+ missingok
+ errors root@localhost
+ nocompress
+ postrotate
+ /usr/bin/killall -HUP tac_plus 2> /dev/null || true
+ endscript
+}
--- /dev/null
+# This file created by Devrim SERAL<devrim@gazi.edu.tr>
+# For creating tac_plus related database and tables
+
+CREATE DATABASE tacacs;
+USE tacacs;
+
+# id table fields are:
+# name | surname | usern | tel | address
+# name : Real name
+# surname : Surname
+# usern : Account name
+# tel: Tel number
+# address: Address
+
+CREATE TABLE id ( name char(40) NOT NULL, surname char(40) NOT NULL,usern char(15) NOT NULL,tel char(20), address char(50) ,PRIMARY KEY(usern));
+
+# auth tables fields are:
+# usern | passwd | exp_time | time_limit | lck
+# usern : Account name
+# passwd : usern password
+# exp_time : Expire date
+# time_limit : Time limiting
+# lck : Adminstrative lock
+
+CREATE TABLE auth( usern CHAR(15) NOT NULL, passwd CHAR(15) NOT NULL, exp_time TIMESTAMP(8) NOT NULL ,time_limit CHAR(30) DEFAULT "*",lck ENUM("T","F") DEFAULT "F",PRIMARY KEY(usern) );
+
+# acct tables fields are:
+# usern | s_name | c_name | elapsed_time | bytes_in | bytes_out | fin_t
+# usern : Account name
+# s_name : Server name(RAS)
+# c_name : Client Name
+# elapsed_time : How much the user spent on router
+# bytes_in : Incoming bytes to port
+# bytes_out : Outgoing bytes from port
+# fin_t : When the accounting is finished
+
+CREATE TABLE acct( usern CHAR(15) NOT NULL, s_name CHAR(30) NOT NULL, c_name CHAR(30) NOT NULL, elapsed_time INT NOT NULL, bytes_in INT DEFAULT 0, bytes_out INT DEFAULT 0,fin_t TIMESTAMP(14) NOT NULL,INDEX acct_index(usern(10)));
--- /dev/null
+.TH REGEXP 3 local
+.DA 2 April 1986
+.SH NAME
+regcomp, regexec, regsub, regerror \- regular expression handler
+.SH SYNOPSIS
+.ft B
+.nf
+#include <regexp.h>
+
+regexp *regcomp(exp)
+char *exp;
+
+int regexec(prog, string)
+regexp *prog;
+char *string;
+
+regsub(prog, source, dest)
+regexp *prog;
+char *source;
+char *dest;
+
+regerror(msg)
+char *msg;
+.SH DESCRIPTION
+These functions implement
+.IR egrep (1)-style
+regular expressions and supporting facilities.
+.PP
+.I Regcomp
+compiles a regular expression into a structure of type
+.IR regexp ,
+and returns a pointer to it.
+The space has been allocated using
+.IR malloc (3)
+and may be released by
+.IR free .
+.PP
+.I Regexec
+matches a NUL-terminated \fIstring\fR against the compiled regular expression
+in \fIprog\fR.
+It returns 1 for success and 0 for failure, and adjusts the contents of
+\fIprog\fR's \fIstartp\fR and \fIendp\fR (see below) accordingly.
+.PP
+The members of a
+.I regexp
+structure include at least the following (not necessarily in order):
+.PP
+.RS
+char *startp[NSUBEXP];
+.br
+char *endp[NSUBEXP];
+.RE
+.PP
+where
+.I NSUBEXP
+is defined (as 10) in the header file.
+Once a successful \fIregexec\fR has been done using the \fIregexp\fR,
+each \fIstartp\fR-\fIendp\fR pair describes one substring
+within the \fIstring\fR,
+with the \fIstartp\fR pointing to the first character of the substring and
+the \fIendp\fR pointing to the first character following the substring.
+The 0th substring is the substring of \fIstring\fR that matched the whole
+regular expression.
+The others are those substrings that matched parenthesized expressions
+within the regular expression, with parenthesized expressions numbered
+in left-to-right order of their opening parentheses.
+.PP
+.I Regsub
+copies \fIsource\fR to \fIdest\fR, making substitutions according to the
+most recent \fIregexec\fR performed using \fIprog\fR.
+Each instance of `&' in \fIsource\fR is replaced by the substring
+indicated by \fIstartp\fR[\fI0\fR] and
+\fIendp\fR[\fI0\fR].
+Each instance of `\e\fIn\fR', where \fIn\fR is a digit, is replaced by
+the substring indicated by
+\fIstartp\fR[\fIn\fR] and
+\fIendp\fR[\fIn\fR].
+To get a literal `&' or `\e\fIn\fR' into \fIdest\fR, prefix it with `\e';
+to get a literal `\e' preceding `&' or `\e\fIn\fR', prefix it with
+another `\e'.
+.PP
+.I Regerror
+is called whenever an error is detected in \fIregcomp\fR, \fIregexec\fR,
+or \fIregsub\fR.
+The default \fIregerror\fR writes the string \fImsg\fR,
+with a suitable indicator of origin,
+on the standard
+error output
+and invokes \fIexit\fR(2).
+.I Regerror
+can be replaced by the user if other actions are desirable.
+.SH "REGULAR EXPRESSION SYNTAX"
+A regular expression is zero or more \fIbranches\fR, separated by `|'.
+It matches anything that matches one of the branches.
+.PP
+A branch is zero or more \fIpieces\fR, concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.PP
+A piece is an \fIatom\fR possibly followed by `*', `+', or `?'.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a match of the atom, or the null string.
+.PP
+An atom is a regular expression in parentheses (matching a match for the
+regular expression), a \fIrange\fR (see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of the input string), `$' (matching the null string at the
+end of the input string), a `\e' followed by a single character (matching
+that character), or a single character with no other significance
+(matching that character).
+.PP
+A \fIrange\fR is a sequence of characters enclosed in `[]'.
+It normally matches any single character from the sequence.
+If the sequence begins with `^',
+it matches any single character \fInot\fR from the rest of the sequence.
+If two characters in the sequence are separated by `\-', this is shorthand
+for the full list of ASCII characters between them
+(e.g. `[0-9]' matches any decimal digit).
+To include a literal `]' in the sequence, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character.
+.SH AMBIGUITY
+If a regular expression could match two different parts of the input string,
+it will match the one which begins earliest.
+If both begin in the same place but match different lengths, or match
+the same length in different ways, life gets messier, as follows.
+.PP
+In general, the possibilities in a list of branches are considered in
+left-to-right order, the possibilities for `*', `+', and `?' are
+considered longest-first, nested constructs are considered from the
+outermost in, and concatenated constructs are considered leftmost-first.
+The match that will be chosen is the one that uses the earliest
+possibility in the first choice that has to be made.
+If there is more than one choice, the next will be made in the same manner
+(earliest possibility) subject to the decision on the first choice.
+And so forth.
+.PP
+For example, `(ab|a)b*c' could match `abc' in one of two ways.
+The first choice is between `ab' and `a'; since `ab' is earlier, and does
+lead to a successful overall match, it is chosen.
+Since the `b' is already spoken for,
+the `b*' must match its last possibility\(emthe empty string\(emsince
+it must respect the earlier choice.
+.PP
+In the particular case where no `|'s are present and there is only one
+`*', `+', or `?', the net effect is that the longest possible
+match will be chosen.
+So `ab*', presented with `xabbbby', will match `abbbb'.
+Note that if `ab*' is tried against `xabyabbbz', it
+will match `ab' just after `x', due to the begins-earliest rule.
+(In effect, the decision on where to start the match is the first choice
+to be made, hence subsequent choices must respect it even if this leads them
+to less-preferred alternatives.)
+.SH SEE ALSO
+egrep(1), expr(1)
+.SH DIAGNOSTICS
+\fIRegcomp\fR returns NULL for a failure
+(\fIregerror\fR permitting),
+where failures are syntax errors, exceeding implementation limits,
+or applying `+' or `*' to a possibly-null operand.
+.SH HISTORY
+Both code and manual page were
+written at U of T.
+They are intended to be compatible with the Bell V8 \fIregexp\fR(3),
+but are not derived from Bell code.
+.SH BUGS
+Empty branches and empty regular expressions are not portable to V8.
+.PP
+The restriction against
+applying `*' or `+' to a possibly-null operand is an artifact of the
+simplistic implementation.
+.PP
+Does not support \fIegrep\fR's newline-separated branches;
+neither does the V8 \fIregexp\fR(3), though.
+.PP
+Due to emphasis on
+compactness and simplicity,
+it's not strikingly fast.
+It does give special attention to handling simple cases quickly.
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ * @(#)regexp.c 1.3 of 18 April 87
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+#include <stdio.h>
+#include "regexp.h"
+#include "regmagic.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this string. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define OPEN 20 /* no Mark this point in input as start of #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+#define META "^$.[()|?+*\\"
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; ®dummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regnode();
+STATIC char *regnext();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+#ifdef STRCSPN
+STATIC int strcspn();
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp(exp)
+char *exp;
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+ extern char *malloc();
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+ regparse = exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = ®dummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ */
+ if (flags&SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN+parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE+parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("*+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *?+");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[': {
+ register int class;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ class = UCHARAT(regparse-2)+1;
+ classend = UCHARAT(regparse);
+ if (class > classend+1)
+ FAIL("invalid [] range");
+ for (; class <= classend; class++)
+ regc(class);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing");
+ break;
+ case '\\':
+ if (*regparse == '\0')
+ FAIL("trailing \\");
+ ret = regnode(EXACTLY);
+ regc(*regparse++);
+ regc('\0');
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ default: {
+ register int len;
+ register char ender;
+
+ regparse--;
+ len = strcspn(regparse, META);
+ if (len <= 0)
+ FAIL("internal disaster");
+ ender = *(regparse+len);
+ if (len > 1 && ISMULT(ender))
+ len--; /* Back off clear of ?+* operand. */
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ ret = regnode(EXACTLY);
+ while (len > 0) {
+ regc(*regparse++);
+ len--;
+ }
+ regc('\0');
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode(op)
+char op;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == ®dummy) {
+ regsize += 3;
+ return(ret);
+ }
+
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+ if (regcode != ®dummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+ if (regcode == ®dummy) {
+ regsize += 3;
+ return;
+ }
+
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == ®dummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == ®dummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+STATIC char *regprop();
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(prog, string)
+register regexp *prog;
+register char *string;
+{
+ register char *s;
+ extern char *strchr();
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return(0);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != NULL) {
+ s = string;
+ while ((s = strchr(s, prog->regmust[0])) != NULL) {
+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == NULL) /* Not present. */
+ return(0);
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return(regtry(prog, string));
+
+ /* Messy cases: unanchored match. */
+ s = string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return(1);
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return(1);
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+regexp *prog;
+char *string;
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if (regmatch(prog->program + 1)) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return(1);
+ } else
+ return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+ register char *scan; /* Current node. */
+ char *next; /* Next node. */
+ extern char *strchr();
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ next = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return(0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return(0);
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return(0);
+ reginput++;
+ break;
+ case EXACTLY: {
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return(0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return(0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+ return(0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+ return(0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[no] == NULL)
+ regstartp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[no] == NULL)
+ regendp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case BRANCH: {
+ register char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return(1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case STAR:
+ case PLUS: {
+ register char nextch;
+ register int no;
+ register char *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ nextch = *OPERAND(next);
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return(0);
+ }
+ break;
+ case END:
+ return(1); /* Success! */
+ break;
+ default:
+ regerror("memory corruption");
+ return(0);
+ break;
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ regerror("corrupted pointers");
+ return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+char *p;
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+ extern char *strchr();
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext(p)
+register char *p;
+{
+ register int offset;
+
+ if (p == ®dummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+}
+
+#ifdef DEBUG
+
+STATIC char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+ extern char *strchr();
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop(op)
+char *op;
+{
+ register char *p;
+ static char buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9:
+ sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ p = NULL;
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9:
+ sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(s1, s2)
+char *s1;
+char *s2;
+{
+ register char *scan1;
+ register char *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return(count);
+ count++;
+ }
+ return(count);
+}
+#endif
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+extern regexp *regcomp();
+extern int regexec();
+extern void regsub();
+extern void regerror();
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+*/
+
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
--- /dev/null
+/*
+ This function use for check access control rules from hosts.deny and
+ hosts.access file.
+ Writen by Devrim SERAL<devrim@gazi.edu.tr>. This file protected by
+ GNU Copyright agreement.
+*/
+#ifdef TCPWRAPPER
+#include <tcpd.h>
+#include "tac_plus.h"
+
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+
+
+int
+check_from_wrap(datap)
+struct identity *datap;
+{
+ struct request_info req;
+
+ request_init(&req, RQ_DAEMON,TACNAME,RQ_CLIENT_ADDR,datap->NAS_name , NULL);
+ fromhost(&req); /* validate client host info */
+ if (!hosts_access(&req))
+ {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "Access denied for NAS=%s",datap->NAS_name);
+ send_authen_error("You are not allowed to access here");
+ refuse(&req); /* If connection is not allowed, clean up and exit. */
+ return 0;
+ }
+
+ if (debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG, "Access permited for NAS=%s",datap->NAS_name);
+return 1;
+
+}
+#endif /* TCPWRAPPER */
--- /dev/null
+/*
+ These functions writen by Devrim SERAL <devrim@gazi.edu.tr>
+
+Applicable format is : <day str><time str> [,|] <day str><time str> [,|] and so on
+
+The accept parameter for day str is:
+SU = Sunday
+MO = Monday
+TU = Tuesday
+WE = Wendsday
+TH = Thursday
+FR = Friday
+SA = Saturday
+WK = For week days
+WD = For Week and
+AL = For All days
+
+And time str must be:
+Hourminute-Hourminute
+For example it's to be -> 0000-1200 or 1600-1700 or 1600-0800
+
+License: This code is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later version.
+
+*/
+
+#include"time_limit.h"
+#include "tac_plus.h"
+
+int problem=0;
+
+int
+time_limit_process(str)
+char *str;
+{
+int ret=0;
+char *tmp_str;
+
+tmp_str=(char *)strtok(str,",|");
+while ( tmp_str != NULL) {
+ ret|=str_token_proc(tmp_str);
+ tmp_str=(char *)strtok(NULL,",");
+ }
+return (ret);
+}
+
+int
+str_token_proc(str)
+char *str;
+{
+int inv=0,ret;
+
+/* Pass space characters */
+while (isspace(*str)) str++;
+
+if (*str=='!') {
+ inv=1;str++;
+}
+
+ret=process(str);
+
+if (problem) {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG,"Timestamp format incorrect");
+ problem=0;
+ return(0);
+}
+
+if (inv)
+ ret=!ret;
+return(ret);
+}
+
+
+int
+process(str)
+char *str;
+{
+int count=0,ret=0,i,j,localtm;
+char *head,*buf,*gec;
+long sec;
+struct tm *tms;
+
+/* Pass space characters */
+while (isspace(*str)) str++;
+
+head=str;
+
+/* Count alphanumeric char */
+while (isalpha(*str)) {
+ count++;
+ str++;
+}
+
+if ( count==0 || count%2 ) {
+ problem++;
+ return 0;
+}
+
+buf=(char *)malloc(count+1);
+strncpy(buf,head,count);
+gec=buf;
+str_up(buf);
+
+for(i=1;i<=(count/2);i++) {
+ for (j=0;j<NUM;j++) {
+ if(!strncmp(gec,week_days[j],2)) {
+ ret=ret^week_day_val[j];
+ }
+ }
+ gec+=2;
+}
+
+/* We finished to use buffer so free it */
+free(buf);
+
+sec=time(0);
+tms=localtime(&sec);
+localtm=(tms->tm_hour)*60+tms->tm_min;
+ret=( week_day_val[tms->tm_wday] & ret ) && time_calc(str,localtm);
+
+if (ret>0)
+ return (1);
+else
+ return(0);
+}
+
+str_up(str)
+char *str;
+{
+ while(*str) {
+ if(islower(*str)) *str=toupper(*str);
+ str++;
+ }
+}
+
+int
+time_calc(str,lct)
+char *str;
+int lct;
+{
+char *t1,*t2,*head;
+int say1,say2,count=0;
+
+head=str;
+
+ while (isdigit(*head) || *head=='-') {
+ count++;
+ head++;
+ }
+
+if (*str=='\0' || count!= TPL ) {
+ problem++;
+ return (0);
+}
+
+ t1=(char *) malloc(count);
+ strncpy(t1,str,count); /*Put str value to t1*/
+
+ t2=(char *) strstr(t1,"-"); /* Find next time part */
+
+if (t2==NULL) {
+ free(t1);
+ problem++;
+ return(0);
+}
+
+*t2='\0';t2++;
+
+if ( strlen(t1)<4 || strlen(t2)<4 ) {
+ free(t1);
+ problem++;
+ return(0);
+}
+ say1=antoi(t1,2)*60+antoi(t1+2,2);
+ say2=antoi(t2,2)*60+antoi(t2+2,2);
+
+free(t1);
+
+if (say1<=say2) {
+ if( (lct>=say1) && (lct<=say2) ) return(1);
+}
+else {
+ if( (lct>=say1) || (lct<=say2) ) return(1);
+}
+return(0);
+
+}
+
+int
+antoi(str,n)
+char *str;int n;
+{
+char *buf;
+int ret;
+
+ buf=(char *) malloc(n);
+ strncpy(buf,str,n);
+ ret=atoi(buf);
+ free(buf);
+
+return(ret);
+}
--- /dev/null
+#include<stdlib.h>
+#include<ctype.h>
+#include<stdio.h>
+#include<time.h>
+#include<string.h>
+#define NUM 10
+#define TPL 9 /* time part len */
+
+/*Global variables */
+static char* week_days[]={"SU","MO","TU","WE","TH","FR","SA","WK","WD","AL"};
+static long week_day_val[]={1,2,4,8,16,32,64,62,65,127};
+
+extern int time_limit_process();
--- /dev/null
+ TAC_PLUS Developer's Kit vF4.0.2.alpha
+ --------------------------------------
+
+Author: Lol Grant
+
+Note: this is a DEVELOPER'S KIT. You probably shouldn't be using this
+if you don't need source code. Instead, consider using CiscoSecure,
+Cisco's supported, commercial Tacacs+ daemon.
+
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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.
+
+Please NOTE: None of the TACACS code available here comes with any
+warranty or support, however, comments or questions may be addressed
+to Cisco systems via email at the address:
+
+ customer-service@cisco.com (or more simply)
+ cs@cisco.com
+
+and we will do our best to handle them, though we cannot guarantee a
+timely response, as this code is UNSUPPORTED. Be sure you've read this
+user's guide, including the frequently asked questions include in it,
+before posting.
+
+Cisco systems also maintains an extensive World Wide Web site at
+
+ http://www.cisco.com/
+
+In addition, there are two mailing lists which may be of interest to
+users of Tacacs+.
+
+The first is a mailing list run by spot.Colorado.EDU which discusses
+many things pertaining to Cisco products. It is not run by Cisco
+Systems, Inc. and is not part of Cisco's formal service request
+channels, however, many knowledgeable people, including staff members
+of Cisco Systems, Inc. voluntarily read and respond on the list.
+
+Requests to be added to or deleted from the list at spot.Colorado.EDU,
+along with other administrative issues concerning it can be sent to:
+
+ cisco-request@spot.Colorado.EDU
+
+There is also a relatively new list called TACPLUS-L, run by
+disaster.com, created for the purpose of information exchange between
+TACACS+ Users. It is intended as a supplement to the list at
+spot.Colorado.EDU, aiding TACACS+ users and prospective users in many
+issues including but not limited to technical support, bug reports and
+workarounds, configuration information, recommendations for future
+versions of TACACS+, and general talk about TACACS+ development,
+implementation, administration, etc.
+
+Please note that neither of these lists is in fact connected with
+Cisco Systems, Inc. or any of its subsidiaries. Standard etiquette
+rules apply.
+
+To subscribe to the TACPLUS-L list, send a message to
+
+ tacplus-l-request@disaster.com
+
+In the body of the letter, enter
+
+ SUBSCRIBE TACPLUS-L your Name
+
+to be automatically added, or visit their web page at
+http://www.disaster.com/tacplus/.
+
+Also, Robert Kiessling maintains a TACACS+ FAQ at
+http://www.easynet.de/tacacs-faq.
+
+Lastly, I am always interested in seeing contributed patches, so
+consider mailing any modifications you make, as context diffs (be sure
+to indicate with the version your patches are based on), to
+tacacs-patches@cisco.com. As always, no support is implied, nor any
+assurance that patches will be made available via ftp (though that is
+my intent) or incorporated into any code.
+
+Definitions and Terms
+---------------------
+
+NAS --- A Network Access Server e.g. a Cisco box, or any other
+ *client* which makes tacacs+ authentication and authorization
+ requests, or generates Tacacs+ accounting packets.
+
+Daemon -- A program which services network requests for authentication
+ and authorization, verifies identities, grants or denies
+ authorizations, and logs accounting records.
+
+passwd(5) files -- files conforming to Unix password style format, as
+ documented in section 5 of the Unix manuals.
+
+AV pairs -- strings of text in the form "attribute=value" sent between a
+ NAS and a tacacs+ daemon as part of the Tacacs+ protocol.
+
+Since a NAS is sometimes referred to as a server, and a daemon is also
+often referred to as a server, the term "server" has been avoided here
+in favor of the less ambiguous terms "NAS" and "Daemon".
+
+TACACS, XTACACS and TACACS+
+---------------------------
+
+Note that there are now at least 3 versions of authentication protocol
+that people commonly refer to as "TACACS".
+
+The first is ordinary tacacs, which was the first one offered on Cisco
+boxes and has been in use for many years. The second is an extension
+to the first, commonly called Extended Tacacs or XTACACS, introduced
+in 1990.
+
+The third one is TACACS+ (or T+ or tac_plus) which is what is documented
+here. TACACS+ is NOT COMPATIBLE with any previous versions of tacacs.
+
+In addition to the 3 versions of tacacs running on Cisco boxes, the
+fact that we distribute the source code to the daemon has meant that
+additional implementations of tacacs daemons have been produced by
+people who have made modifications to our source code.
+
+BUILDING TAC_PLUS
+-----------------
+Tac_plus is known to build and run on the following platforms:
+
+AIXv3.2 (using -DAIX and bsdcc. See tac_plus.h for more details).
+HP/UX A.09.01 using -DHPUX
+i86 Solaris 2.4 (SUNOS 5.4), using SUNpro SW2.0.1 & -DSOLARIS
+Sun4 Solaris 2.4 using SUNpro SC3.0.1 and -DSOLARIS
+SUNOS 4.1.2 sparc-2
+SUNOS 4.1.3
+MIPS R3K SGI IRIX 4.05F (using -DMIPS)
+BSDI BSD/386 1.1 (using -DBSDI)
+FREEBSD 2.0-RELEASE (using -DFREEBSD)
+LINUX 1.2.8 (using -DLINUX)
+
+To build tac_plus, untar the tarfile distribution into a clean
+directory e.g.
+
+ tar -xvf tac_plus.tar
+
+Edit the top of the Makefile to select the appropriate defaults
+for your system. Then type
+
+ make tac_plus
+
+The default version can authenticate using its internal database,
+s/key or passwd(5) style files. Authorization is done via the
+internal database or via calls to external programs, which the
+administrator configures.
+
+To use S/KEY, you must obtain and build the s/key library (libskey.a)
+yourself. You can then compile in S/KEY support per the instructions
+for S/KEY in the Makefile. I got my S/KEY code originally from
+crimelab.com but now it appears the only source is ftp.bellcore.com. I
+suggest you try a web search for s/key source code.
+
+Note: S/KEY is a trademark of Bell Communications Research (Bellcore).
+
+Should you need them, there are routines for accessing password files
+(getpwnam,setpwent,endpwent,setpwfile) in pw.c.
+
+Lastly, you may also need to add lines to your /etc/services file to
+get tacacs+ to work correctly e.g. something along the lines of:
+
+tacacs tcp/49
+
+You'll need to consult your system manuals for exact details of how to
+do this.
+
+A NOTE ABOUT ARAP, MSCHAP AND DES
+---------------------------------
+If you have access to a DES library which implements the calls:
+
+int des_init();
+void des_setkey();
+void des_endes();
+void des_done();
+
+then you can define DES in tac_plus.h and link tac_plus with your DES
+library. This is recommended, as it will allow you to process ARAP and MSCHAP
+requests on your daemon, which is more efficient, and also more secure
+than processing them on the NAS.
+
+If you don't have access to des (which is U.S. export controlled), you
+can simply leave DES undefined in tac_plus.h. ARAP and MSCHAP
+authentication will still work, but it will be slightly less
+efficient, since the NAS will attempt to get the daemon to do the DES
+calculation before falling back to the alternative of calculating DES
+on the NAS. It's also slightly less secure, because if someone
+discovers your encryption key, they can then download ARAP and MSCHAP
+secrets from your daemon.
+
+Note that this issue arises solely because U.S. government regulations
+currently make it difficult to export the source code for DES outside
+the US and Canada, which is why it is not included in this
+distribution.
+
+Lastly, this limitation of MSCHAP, ARAP and DES has no bearing on the
+use of des passwords for regular logins. Regular logins also use DES
+but they do it via the "crypt" system call, which is usually found in
+a library on the Unix host where you compile your daemon.
+
+There are additional restrictions on doing MSCHAP (see the FAQ later
+in this document).
+
+CONFIGURING TAC_PLUS
+---------------------
+
+Tac_plus is configured via a single configuration file. You can create
+a configuration file from scratch or, if you have passwd(5) and
+supplementary files from earlier versions of tacacs, you can convert
+these to configuration file format by running the supplied perl script
+convert.pl.
+
+CONVERTING EXISTING PASSWD(5) FILES
+-----------------------------------
+
+To convert an existing passwd(5) file e.g. one used with an older
+version of tacacs, use the convert.pl perl script as follows:
+
+convert.pl <passwd file> [-g] [<supplementary file>]
+
+1). If you have no supplementary file, simply omit it.
+
+2). If the groupid field of your passwd file does NOT represent a
+valid acl number (e.g if it's really a unix passwd file this field is
+a group id, not an acl number), just omit the -g flag.
+
+The rest of this document assumes that you are configuring tac_plus
+from scratch.
+
+CONFIGURING TAC_PLUS FROM SCRATCH
+---------------------------------
+
+A configuration file consists of some top-level directives for setting
+defaults and for setting up the encryption key, followed by a
+declaration for each user and group you want to configure. Within
+each user or group declaration, there are declarations for
+authenticating and authorizing that user.
+
+1). Configuring the encryption key
+
+If you want tac_plus to encrypt its packets (and you almost certainly
+*DO* want this, as there can be usernames and passwords contained in
+these packets), then you must specify an encryption key in the
+configuration file. The identical key must also be configured on any
+NAS which communicates with tac_plus.
+
+This is done using the statement
+
+key = "your key here"
+
+NOTE: You only need double quotes on the daemon if your key contains
+spaces.
+
+Confusingly, even if your key does contain spaces, you should NEVER
+use double quotes when you configure the matching key on the NAS.
+
+During debugging, it may be convenient to temporarily switch off
+encryption by not specifying any key. Be careful to remember to
+switch encryption back on again after you've finished debugging.
+
+The current code does not support host-specific keys (left as an
+exercise to the reader).
+
+On the NAS, you also need to configure the *same* key. Do this by
+issuing:
+
+ aaa new-model
+ tacacs-server key <your key here>
+
+COMMENTS IN CONFIGURATION FILES
+-------------------------------
+Comments can appear anywhere in the configuration file, starting with
+the # character and extending to the end of the current line. Should
+you need to disable this special meaning of the # character, e.g. if
+you have a password containing a # character, simply enclose the string
+containing it within double quotes.
+
+CONFIGURING USERS AND GROUPS
+----------------------------
+
+Each user may belong to a group (but only one group). Each group may
+in turn belong to one other group and so on ad infinitum.
+
+Users and groups are declared as follows. Here we declare two users
+"fred" and "lily", and two groups, "admin" and "staff".
+
+Fred is a member of group "admin", and group "admin" is in turn a
+member of group "staff". Lily is not a member of any group.
+\f
+user = lily {
+ # user lily is not a member of any group
+ # and has nothing else configured as yet
+}
+
+user = fred {
+ # fred is a member of group admin
+ member = admin
+}
+
+group = admin {
+ # group admin is a member of group staff
+ member = staff
+}
+
+group = staff {
+ # group staff is not a member of any group
+}
+
+RECURSION AND GROUPS
+--------------------
+
+In general, when the daemon looks up values e.g. passwords, it will
+look first to see if the user has her own password. If not, it looks
+to see if she belongs to a group and if so, whether the group has a
+password defined. If not, this process continues through the hierarchy
+of groups (a group can be a member of another group) until a value is
+found, or there are no more groups.
+
+This recursive process occurs for lookups of expiration dates, for
+pap, arap and chap "secrets", and also for authorization parameters (see
+later).
+
+A typical configuration technique is thus to place users into groups
+and specify as many groupwide characteristics in the group declaration
+as possible. Then, individual user declarations can be used to
+override the group settings for selected users as needed.
+
+CONFIGURING USER AUTHENTICATION
+-------------------------------
+
+User Authentication can be specified separately for PAP, ARAP, CHAP,
+and normal logins. In addition, a user global authentication method
+can be given that will be used if a per-protocol method is not
+specified.
+
+PAP, ARAP, CHAP, and global user authentication must be given in clear
+text.
+
+The following assigns the user mary five different passwords for ARAP,
+inbound and outbound CHAP, inbound PAP, outbound PAP, and normal login
+respectively:
+
+ user = mary {
+ arap = cleartext "arap password"
+ chap = cleartext "chap password"
+ pap = cleartext "inbound pap password"
+ opap = cleartext "outbound pap password"
+ login = des XQj4892fjk
+ }
+
+
+The following assigns the user agnes a single password for all the
+above types of login (except outbound PAP):
+
+ user = agnes {
+ global = cleartext "Agnes global password"
+ }
+
+NOTE: you cannot use a global user password for outbound PAP. This is
+because outbound PAP is implemented by sending the password from the
+daemon to the NAS. This is a security issue if the TACACS+ key is ever
+compromised.
+
+There are 3 ways to authenticate a user for login.
+
+1). You can include a DES (or cleartext) password for a user or for a
+group that s/he is a member of, viz:
+
+ user = joe {
+ member = admin
+ # this is lily's encrypted DES password. It overrides the admin
+ # group's password
+ login = des XQkR21zMB0TDU
+ }
+
+ user = fred {
+ # fred is a member of group admin. He inherits the group's password
+ # as he does not have his one of his own.
+ member = admin
+ }
+
+ group = admin {
+ # group admin has a cleartext password which all members share
+ # unless they have their own password defined
+ login = cleartext foobar
+ }
+
+If no password is needed for this user, this can be accomplished with
+the 'nopassword' option:
+
+ user = foo {
+ login = nopassword
+ }
+
+NOTE: The C program built from generate_passwd.c may be used to
+hand-generate encrypted passwords, or they may be taken from a Unix
+passwd (or shadow) file.
+
+2). Authentication using passwd(5) files.
+
+For selected users, you can perform DES authentication using existing
+passwd(5) files instead of entering the password into the
+configuration file directly (though using passwd(5) files is
+noticeably less efficient for large files).
+
+You can specify this behavior per-user, by naming a passwd(5) file in
+the password declaration (instead of giving a DES password), as
+follows:
+
+ user = fred {
+ # look in file /etc/tac_plus_passwords to authenticate this user
+ login = file /etc/tac_plus_passwords
+ }
+
+3). Authentication using s/key.
+
+If you have successfully built and linked in a suitable s/key library
+and compiled tac_plus to use s/key, you can then specify that a user
+be authenticated via s/key, as follows:
+
+ user = fred {
+ login = skey
+ }
+
+RECURSIVE PASSWORD LOOKUPS
+---------------------------
+
+As stated earlier, authentication passwords are looked up recursively:
+The daemon looks first to see if the user has her own password. If
+not, it looks to see if she belongs to a group which has a
+password. This process recurses through the hierarchy of groups (a
+group can be a member of another group) until a password is found, or
+there are no more groups.
+
+CONFIGURING DEFAULT AUTHENTICATION
+-----------------------------------
+By default, an unrecognized user will be denied authentication (NOTE:
+there is no way to authenticate someone with no username).
+
+At the top level of the configuration file, you can set the default
+authentication to use a passwd(5) file, viz:
+
+ default authentication = file /etc/passwd
+
+The effect of this statement is that if a user does not appear in the
+configuration file, the daemon will attempt to authenticate the user
+using passwords from this file i.e. /etc/passwd in this example.
+
+If you have passwd(5) files from previous versions of tacacs daemons,
+this facility allows you to authenticate using the passwd(5) from
+older versions of tacacs, while you migrate to using the new
+configuration file.
+
+CONFIGURING EXPIRY DATES
+------------------------
+An entry of the form:
+
+user = lol {
+ expires = "MMM DD YYYY"
+ password = cleartext "bite me"
+}
+
+will cause the user's passwords to become invalid, starting on the
+expiry date. The only valid date format is e.g. "Jan 1 1980". Case is
+NOT significant.
+
+A expiry warning message is sent to the user when she logs in,
+starting at 14 days before the expiration date.
+
+On expiry, the administrator must re-set the expiry date in the
+configuration file in order to grant continued access. Expiry applies
+to all password types except "file" passwords.
+
+If passwd(5) files are being used for authentication, the "expires"
+field in the configuration file is not consulted. Instead, the daemon
+looks at the the "shell" field of the password file entry for a valid
+expiry date.
+
+If Solaris shadow password files are used for authentication, the
+"expires" field in the configuration file is not consulted. The expiry
+field from the shadow password file (if it exists) is used as the
+expiration date.
+
+CONFIGURING AUTHENTICATION ON THE NAS
+-------------------------------------
+
+On the NAS, to configure login authentication on all lines (including
+vty and console lines)
+
+ aaa new-model
+ aaa authentication login default tacacs+
+
+NOTE: As soon as you issue this command, you will no longer be able to
+create new logins to your NAS without a functioning tacacs+ daemon
+appropriately configured with usernames and password, so make sure you
+have this ready.
+
+As a safety measure while setting up, we suggest you configure an
+enable secret and make it the last resort authentication method, so
+if your tacacs+ daemon fails to respond you will be able to use the
+NAS enable password to login. To do this, configure:
+
+ enable secret foo
+ aaa authentication login default tacacs+ enable
+
+If all else fails, and you find yourself locked out of the NAS due to
+a configuration problem, the section on "recovering from lost
+passwords" on Cisco's CCO web page will help you dig your way out.
+
+CONFIGURING ENABLE PASSWORDS
+----------------------------
+
+The default privilege level for an ordinary user on the NAS is usually
+1. When a user enables, she can reset this level to a value between 0
+and 15 by using the NAS "enable" command. If she doesn't specify a
+level, the default level she enables to is 15.
+
+You can enable via tacacs+ e.g. by configuring on the NAS:
+
+ aaa authentication enable default tacacs+
+
+then whenever you attempt to enable, an authentication request is sent
+with the special username $enab<n>$ where <n> is the privilege level
+you are attempting to enable to.
+
+(Note: in order to be compatible with earlier versions of tacacs, when
+the requested enable level is 15, the daemon will also try the
+username $enable$ before trying username $enab15$).
+
+For example, with the above declaration, in order to enable on the
+NAS, you need a user declaration like this one, on the daemon:
+
+user = $enab15$ {
+ login = cleartext "the enable password for level 15"
+}
+
+Note: Be aware that this does have the side effect that you now have a
+user named $enab15$ who can then login to your NAS if she knows the
+enable password.
+
+Here is a similar declaration allowing users to enable to level 4:
+
+user = $enab4$ {
+ login = des bsoF4OivQCY8Q
+}
+\f
+CONFIGURING AUTHORIZATION
+-------------------------
+
+Authorization must be configured on both the NAS and the daemon to
+operate correctly. By default, the NAS will allow everything until you
+configure it to make authorization requests to the daemon.
+
+On the daemon, the opposite is true: The daemon will, by default, deny
+authorization of anything that isn't explicitly permitted.
+
+Authorization allows the daemon to deny commands and services
+outright, or to modify commands and services on a per-user
+basis. Authorization on the daemon is divided into two separate parts:
+commands and services.
+
+AUTHORIZING COMMANDS
+--------------------
+
+Exec commands are those commands which are typed at a Cisco exec
+prompt. When authorization is requested by the NAS, the entire command
+is sent to the tac_plus daemon for authorization.
+
+Command authorization is configured by specifying a list of
+egrep-style regular expressions to match command arguments (see the
+supplied man page, regexp.3, for a full description of regular
+expressions) and an action which is "deny" or "permit".
+
+The following configuration example permits user Fred to run the
+following commands:
+
+ telnet 131.108.13.<any number> and
+ telnet 128.<any number>.12.3 and
+ show <anything>
+
+All other commands are denied (by default).
+
+user=fred {
+
+ cmd = telnet {
+ # permit specified telnets
+ permit 131\.108\.13\.[0-9]+
+ permit 128\.[0-9]+\.12\.3
+ }
+ cmd = show {
+ # permit show commands
+ permit .*
+ }
+}
+
+NOTE: If an argument list you specify contains spaces or tabs, you
+must enclose it in double quotes.
+
+The command and arguments which the user types gets matched to the
+regular expressions you specify in the configuration file (in order of
+appearance). The first successful match performs the associated
+action (permit or deny). If there is no match, the command is denied
+by default.
+
+Conversely, the following configuration example can be used to deny
+the command:
+
+ telnet 131.108.13.<any number>
+
+and permit all other arguments, since the last line will match any
+argument list. All other commands and services are permitted due to
+the "default service = permit" clause.
+
+Note: the default statement must be the first in the user clause
+
+user=fred {
+ default service = permit
+ cmd = telnet {
+ # allow all fred's telnet commands except to 131.108.13.*
+ deny 131\.108\.13\.[0-9]+
+ permit .*
+ }
+}
+
+Note: Matches are not anchored, so "deny 131.108.13.[0-9]+" matches
+anywhere in the command. To anchor the match, use ^ at the beginning
+of the regular expression.
+
+Note: When a command has multiple arguments, users may enter them in
+many different permutations. It can be cumbersome to create regular
+expressions which will reliably authorize commands under these
+conditions, so administrators may wish to consider other methods of
+performing authorization e.g. by configuring NAS-local privileged
+enable levels on the NAS itself.
+
+COMMAND EXPANSION
+-----------------
+
+For command authorization, the Cisco NAS expands all commands to their
+full names e.g. when you type "config t" on the NAS, this will be
+expanded to "configuration terminal" before being sent to the daemon
+so that you don't need to list all the possible contractions of a
+command.
+
+CONFIGURING DEFAULT AUTHORIZATION
+---------------------------------
+
+There are 3 places where defaults for authorization may be
+configured. Unless specified to the contrary, the default is always to
+deny authorization.
+
+1). To override the default denial of authorization for users who are
+not explicitly listed in the configuration file, the ersatz user
+DEFAULT, if defined, can be used for authorizing such users, viz:
+
+default authentication = file /etc/passwd
+
+user = DEFAULT {
+ service = ppp protocol = ip {
+ addr-pool=foobar
+ }
+}
+
+In this example, users who do not appear elsewhere will be
+authenticated via the /etc/passwd file, and authorized by the contents
+of the user = DEFAULT entry.
+
+Note: For backward compatibility, the directive,
+
+ default authorization = permit
+
+may still be specifed at the top level of the configuration file. This
+overrides the default denial of authorization for users who are not
+explicitly listed in the configuration file, permitting all
+authorization requests for such users.
+
+2). At the user level i.e. inside the braces of a user declaration,
+the default for a user who doesn't have a service or command
+explicitly authorized is to deny that service or command. The
+following directive will permit the service or command by default
+instead:
+
+user = lol {
+ default service = permit
+}
+
+NOTE: This directive must appear first inside the user declaration.
+
+3). At the service authorization level i.e. inside the braces of a
+service declaration, arguments in an authorization request are
+processed according to the algorithm described later. Some actions
+when authorizing services (e.g. when matching attributes are not
+found) depend on how the default is configured. The following
+declaration changes the default from deny to permit for this user and
+service.
+
+user = lol {
+ service = exec {
+ default attribute = permit
+ }
+}
+
+NOTE: This directive must appear before any others inside the service
+declaration.
+
+NOTE: for command authorization (as opposed to service authorization
+being discussed here), you specify deny .* or permit .* as the last
+line of the regular expression matches to create default behavior.
+
+AUTHORIZING EXEC STARTUP
+-------------------------
+
+If you authorize some exec commands, you implicitly agree to allow
+that user to start an exec (it doesn't make sense to permit exec
+commands if an exec can't be started to run those commands)
+
+In addition to agreeing to allow an exec to start, you can supply some
+parameters whenever an exec starts e.g. an autocommand, a dialback
+string or a connection access list (acl).
+
+In the example below, when an exec is started on the NAS, an acl of 4
+will be returned to the NAS:
+
+user=fred {
+
+ # this following line permits an exec to start and permits
+ # all commands and services by default
+
+ default service = permit
+
+ service = exec {
+ # When an exec is started, its connection access list will be 4.
+ # It also has an autocmd.
+ acl = 4
+ autocmd = "telnet foobar"
+ }
+
+ cmd = telnet {
+ # allow all fred's telnet commands except telnet to 131.108.13.*
+ deny 131\.108\.13\.[0-9]+
+ permit .*
+ }
+}
+
+NOTE: specifying an autocommand, or any other exec services, is part
+of EXEC AUTHORIZATION. For it to work, you must also configure exec
+authorization on your NAS e.g.
+
+ aaa authorization exec tacacs+
+
+
+AUTHORIZING EXEC, SLIP, PPP and ARAP SERVICES
+----------------------------------------------
+
+Authorizing exec, slip, PPP and arap services is done quite
+differently from command authorization.
+
+When authorizing these services, the NAS sends a request containing a
+number of attribute-value (AV) pairs, each having the form
+
+ attribute=value
+
+(Note: during debugging, you may see AV pairs whose separator
+character is a "*" instead of a "=" sign. This is to signify that the
+value in a pair is optional. An "=" sign indicates a mandatory
+value. A "*" denotes an optional value).
+
+e.g. a user starting ppp/ip using an address of 131.108.12.44 would
+generate a request with the following AV pairs:
+
+ service=ppp
+ protocol=ip
+ addr*131.108.12.44
+
+You can use the NAS debugging command
+
+ debug aaa authorization
+
+to see what authorization AV pairs are being used by the NAS. Note: If
+you are not on the router console, you will also need to issue a
+'terminal monitor' command to see debug output.
+
+THE AUTHORIZATION PROCESS
+-------------------------
+
+Authorizing a single session can result in multiple requests being
+sent to the daemon. For example, in order to authorize a dialin ppp
+user for IP, the following authorization requests will be made from
+the NAS:
+
+1). An initial authorization request to startup ppp from the exec,
+using the AV pairs service=ppp, protocol=ip, will be made (Note: this
+initial request will be omitted if you are autoselecting ppp, since
+you won't know the username yet).
+
+This request is really done to find the address for dumb PPP (or SLIP)
+clients who can't do address negotiation. Instead, they expect you to
+tell them what address to use before PPP starts up, via a text message
+e.g. "Entering PPP. Your address is 1.2.3.4". They rely on parsing
+this address from the message to know their address.
+
+2). Next, an authorization request is made from the PPP subsystem to
+see if ppp's LCP layer is authorized. LCP parameters can be set at
+this time (e.g. callback). This request contains the AV pairs
+service=ppp, protocol=lcp.
+
+3). Next an authorization request to startup ppp's IPCP layer is made
+using the AV pairs service=ppp, protocol=ipcp. Any parameters returned
+by the daemon are cached.
+
+4). Next, during PPP's address negotiation phase, each time the remote
+peer requests a specific address, if that address isn't in the cache
+obtained in step 3, a new authorization request is made to see if the
+peers requested address is allowable. This step can be repeated
+multiple times until both sides agree on the remote peer's address or
+until the NAS (or client) decide they're never going to agree and they
+shut down PPP instead.
+
+As you can see from the above, a program which plans to handle
+authorization must be able to handle a variety of requests and respond
+appropriately.
+
+AUTHORIZATION RELIES ON AUTHENTICATION
+--------------------------------------
+
+Since we pretty much rely on having a username in authorization
+requests to decide which addresses etc. to hand out, it is important
+to know where the username for a PPP user comes from. There are
+generally 2 possible sources
+
+1). You force the user to authenticate by making her login to the exec
+and you use that login name in authorization requests. This username
+isn't propagated to PPP by default. To have this happen, you generally
+need to configure the "if-needed" method, e.g.
+
+aaa authentication login default tacacs+
+aaa authentication ppp default if-needed
+
+
+2). Alternatively, you can run an authentication protocol, PAP or CHAP
+(CHAP is much preferred), to identify the user. You don't need an
+explicit login step if you do this (so it's the only possibility if
+you are using autoselect). This authentication gets done before you
+see the first LCP authorization request of course. Typically you
+configure this by doing:
+
+aaa authentication ppp default tacacs+
+int async 1
+ppp authentication chap
+
+If you omit either of these authentication schemes, you will start to
+see authorization requests in which the username is missing.
+
+CONFIGURING SERVICE AUTHORIZATION
+---------------------------------
+
+A list of AV pairs is placed in the daemon's configuration file in
+order to authorize services. The daemon compares each NAS AV pair to
+its configured AV pairs and either allows or denies the service. If
+the service is allowed, the daemon may add, change or delete AV pairs
+before returning them to the NAS, thereby restricting what the user is
+permitted to do.
+
+The complete algorithm by which the daemon processes its configured
+AV pairs against the list the NAS sends, is given below.
+
+The Authorization Algorithm
+---------------------------
+
+Find the user (or group) entry for this service (and protocol), then
+for each AV pair sent from the NAS:
+
+ If the AV pair from the NAS is mandatory:
+
+ a). look for an exact attribute,value match in the user's
+ mandatory list. If found, add the AV pair to the output.
+
+ b). If an exact match doesn't exist, look in the user's
+ optional list for the first attribute match. If found, add the
+ NAS AV pair to the output.
+
+ c). If no attribute match exists, deny the command if the
+ default is to deny, or,
+
+ d). If the default is permit, add the NAS AV pair to the
+ output.
+
+ If the AV pair from the NAS is optional:
+
+ e). look for an exact attribute,value match in the user's
+ mandatory list. If found, add DAEMON's AV pair to output.
+
+ f). If not found, look for the first attribute match in the
+ user's mandatory list. If found, add DAEMONS's AV pair to output.
+
+ g). If no mandatory match exists, look for an exact
+ attribute,value pair match among the daemon's optional AV
+ pairs. If found add the DAEMON's matching AV pair to the
+ output.
+
+ h). If no exact match exists, locate the first attribute match
+ among the daemon's optional AV pairs. If found add the
+ DAEMON's matching AV pair to the output.
+
+ i). If no match is found, delete the AV pair if the default is
+ deny, or
+
+ j). If the default is permit add the NAS AV pair to the output.
+
+ k). After all AV pairs have been processed, for each mandatory
+ DAEMON AV pair, if there is no attribute match already in the
+ output list, add the AV pair (but add only ONE AV pair for each
+ mandatory attribute).
+
+RECURSIVE AUTHORIZATION
+-----------------------
+
+Remember that authorization is also recursive over groups, in the same
+way that password lookups are recursive. Thus, if you place a user in
+a group, the daemon will look in the group for authorization
+parameters if it cannot find them in the user declaration.
+
+EXAMPLES
+--------
+
+key = "your key here"
+
+user=fred {
+ login = des mEX027bHtzTlQ
+ name = "Fred Flintstone"
+ member = administrators
+ expires = "May 23 2005"
+ arap = cleartext "Fred's arap secret"
+ chap = cleartext "Fred's chap secret"
+
+ service = exec {
+ # When Fred starts an exec, his connection access list is 5
+ acl = 5
+
+ # We require this autocmd to be done at startup
+ autocmd = "telnet foo"
+ }
+
+ # All commands except show system are denied for Fred
+ cmd = show {
+
+ # Fred can run the following show command
+
+ permit system
+ deny .*
+ }
+
+ service = ppp protocol = ip {
+ # Fred can run ip over ppp only if he uses one
+ # of the following mandatory addresses. If he supplies no
+ # address, the first one here will be mandated
+
+ addr=131.108.12.11
+ addr=131.108.12.12
+ addr=131.108.12.13
+ addr=131.108.12.14
+
+ # Fred's mandatory input access list number is 101
+ inacl=101
+
+ # We will suggest an output access list of 102, but the NAS may
+ # choose to ignore or override it
+
+ optional outacl=102
+ }
+
+ service = slip {
+
+ # Fred can run slip. When he does, he will have to use
+ # these mandatory access lists
+
+ inacl=101
+ outacl=102
+ }
+}
+
+user = wilma {
+
+ # Wilma has no password of her own, but she's a group member so
+ # she'll use the group password if there is one. Same for her
+ # password expiry date
+
+ member = admin
+}
+
+group = admin {
+
+ # group members who don't have their own login password will be looked
+ # up in /etc/passwd
+
+ login = file /etc/passwd
+
+ # group members who have no expiry date set will use this one
+
+ expires = "Jan 1 1997"
+}
+
+
+USING PROGRAMS TO DO AUTHORIZATION
+----------------------------------
+
+There are some limitations to the authorization that can be done using
+a configuration file. The main ones are that you're constrained by the
+algorithm the daemon uses, and that the configuration is basically
+static, so if you're trying to use it to allocate dynamic things (such
+as addresses from a pool) that vary over time, you need another
+mechanism.
+
+One solution is to arrange for the daemon to call your own
+user-supplied programs to control authorization. These "callouts"
+permit almost complete control over authorization, allowing you to
+read all the fields in the authorization packet sent by the NAS
+including all its AV pairs, and to set authorization status and send a
+new set of AV pairs to the NAS in response.
+
+USING AV PAIRS FOR AUTHORIZATION
+--------------------------------
+
+During authorization, the NAS sends an authorization request packet
+containing various fields of interest and a set of AV pairs (see the
+tacacs+ protocol specification for a list of fields and pairs).
+
+Fields from the authorization packet can be supplied to the programs
+you call on their command line, by using the appropriate dollar
+variables in the configuration file (see below).
+
+AV pairs from the authorization packet are fed to the program's
+standard input, one per line. The program is expected to process the
+AV pairs and write them to its standard output, one per line. What
+happens then is determined by the exit status of the program.
+
+NOTE: AV pairs are text strings with the format
+attribute=value. Unlike the configuration file which allows spaces
+when specifying AV pairs, there should be no spaces surrounding the
+"=" sign when using the programmatic interface.
+
+CALLING SCRIPTS BEFORE AUTHORIZATION
+------------------------------------
+
+You can specify a per-user program to be called before any other
+attempt to authorize is made by using a "before" clause e.g.
+
+user = auth1 {
+ before authorization "/usr/bin/pre_authorize $user $port $address"
+}
+
+The AV pairs sent from the NAS will be supplied to this program's
+standard input, one pair per line.
+
+Fields from the initiating authorization packet which the NAS sends to
+the daemon can also be passed to the program by using dollar variables
+in the command line. A complete list of available variables is as
+follows (consult the API specification for more details).
+
+ user -- user name
+ name -- Nas name
+ port -- Nas port
+ address -- Nac address (remote user location)
+ priv -- privilege level (a digit, 0 to 15)
+ method -- (a digit, 1 to 4)
+ type -- (a digit, 1 to 4)
+ service -- (a digit, 1 to 7)
+ status -- (pass, fail, error, unknown)
+
+Unrecognized variables will appear as the string "unknown".
+
+If the program returns a status of 0, authorization is unconditionally
+permitted. No further processing is done on this request and no AV
+pairs are returned to the NAS.
+
+If the program returns a status of 1, authorization is unconditionally
+denied. No further processing is done on this request and no AV pairs
+are returned to the NAS.
+
+If the program returns a status of 2, authorization is permitted. The
+program is expected to modify the AV pairs that it receives on its
+standard input (or to create entirely new ones) and to write them, one
+per line, to its standard output. The new AV pairs will be sent to the
+NAS with a status of AUTHOR_STATUS_PASS_REPL. No further processing
+takes place on this request.
+
+If the program returns a status of 3, authorization is denied, but all
+attributes returned by the program via stdout are returned to the
+NAS. Also, whatever the program returns on stderr is placed into the
+server-msg field and returned to the NAS as well.
+
+Any other status value returned from the program will cause an error
+to be returned to the NAS.
+
+Note that a status of 2 is not acceptable when doing command
+authorization.
+
+CALLING PROGRAMS AFTER AUTHORIZATION
+------------------------------------
+
+You can specify a per-user program to be called after authorization
+processing has been carried out by the daemon (but before the
+authorization status and AV pairs have been transmitted to the NAS).
+
+The program can optionally modify the AV pairs being sent back to the
+NAS and change the authorization status if required.
+
+group = auth1 {
+ # call /usr/bin/post_authorize passing it the username, port
+ # and current authorization status.
+ after authorization "/usr/bin/post_authorize $user $port $status"
+}
+
+The AV pairs resulting from the authorization algorithm that the
+daemon proposes to return to the NAS, are supplied to the program on
+standard input, one AV pair per line, so they can be modified if
+required.
+
+Fields from the incoming authorization packet which the NAS sent to
+the daemon can also be passed to the program on its command line by
+specifying dollar variables in the command line (see previous
+section).
+
+The program is expected to process the AV pairs and write them to its
+standard output, one per line. What happens then is determined by the
+exit status of the program:
+
+If the program returns a status of 0, authorization continues as if
+the program had never been called. Use this if e.g. you just want a
+program to send mail when an authorization occurs, without otherwise
+affecting normal authorization.
+
+If the program returns a status of 1, authorization is unconditionally
+denied. No AV pairs are returned to the NAS. No further authorization
+processing occurs on this request.
+
+If the program returns a status of 2, authorization is permitted and
+any AV pairs returned from the program on its standard output are sent
+to the NAS in place of any AV pairs that the daemon may have
+constructed.
+
+Any other value will cause an error to be returned the the NAS by the
+daemon.
+
+WARNINGS AND CAUTIONS
+---------------------
+
+Customers attempting to write authorization scripts will find the NAS
+debugging command "debug aaa authorization" invaluable.
+
+Pre and post authorization programs are invoked by handing the command
+line to the Bourne shell. On many Unix systems, if the shell doesn't
+find the specified program it returns a status of one, which denies
+authorization. However, at least one Unix system (BSDI) returns a
+status code of 2 under these circumstances, which will permit
+authorization, and probably isn't what you intended.
+
+Note also that if your program hangs, the authorization will time out
+and return an error on the NAS, and you'll tie up a process slot on
+the daemon host, eventually running out of resources. There is no
+special code to detect this in the daemon.
+
+Unless you make special arrangements, the daemon will run as root and
+hence the programs it invokes will also run as root, which is a
+security weakness. It is strongly recommended that you use absolute
+pathnames when specifying programs to execute, and that you use the
+Makefile options TAC_PLUS_USERID and TAC_PLUS_GROUPID so that the
+daemon is not running as root when calling these programs,
+
+The daemon communicates with pre and post authorization programs over
+a pair of pipes. Programs using the standard i/o library will use full
+buffering in these circumstances. This shouldn't be a problem for most
+programs, since they'll read AV pairs till they see end of file on
+input, and they'll flush all output when they exit.
+
+Note that when avpairs containing spaces are listed in the
+configuration file, you need to enclose them in double quotes so that
+they are parsed correctly. Avpairs which are returned via standard
+output do not need delimiters and so should not be enclosed in double
+quotes.
+
+CONFIGURING AUTHORIZATION ON THE NAS
+------------------------------------
+
+If authorization is not explicitly configured on the NAS, no
+authorization takes place i.e. effectively, everything is
+permitted. Note that this is the converse of what happens on the
+daemon, where anything not explicitly permitted is denied by default.
+
+To configure command authorization on the NAS, issue the following NAS
+configuration commands:
+
+ aaa authorization commands 1 tacacs+
+ aaa authorization commands 15 tacacs+
+
+
+This will make the NAS send tacacs+ requests for all level 1 (ordinary
+user) and level 15 (privileged level) commands on all lines/interfaces.
+
+NOTE: As soon as you configure the above on your NAS, you will only be
+permitted to execute NAS commands which are permitted by your tacacs+
+daemon. So make sure you have configured, on the daemon, an
+authenticated user who is authorized to run commands, or you will be
+unable to do much on the NAS after turning on authorization.
+
+Alternatively, or in addition, you may also want to configure the
+following:
+
+ aaa authorization commands 1 tacacs+ if-authenticated
+
+This will use tacacs+ authorization for level 1 (user-level commands)
+but if problems arise, you can just switch off the tacacs+ server and
+authorization will then be granted to anyone who is authenticated.
+
+The following daemon configuration should be sufficient to ensure that
+you can always login as username "admin" (with a suitable password)
+and run any command as that user:
+
+user = admin {
+ default service = permit
+ login = des kppPfHq/j6gXs
+}
+
+
+ACCOUNTING
+-----------
+
+There is only one configurable accounting parameter -- the accounting
+file name. All accounting records are written, as text, to this
+filename. The filename is configured as follows at the top-level of
+the configuration file:
+
+accounting file = <filename>
+
+Since accounting requests occur (and are serviced) asynchronously, it
+is necessary to lock the accounting file so that two writers don't
+simultaneously update it. The daemon uses the fcntl call to do this
+locking, so it is recommended that the accounting file reside on a
+local filesystem. Although fcntl locking over NFS is supported on some
+Unix implementations, it is notoriously unreliable. Even if your
+implementation is reliable, locking is likely to be extremely
+inefficient over NFS.
+
+NAS CONFIGURATION
+-----------------
+
+To get accounting records equivalent to previous versions of tacacs,
+the following is sufficient. "Stop" records contain elapsed time for
+connections and exec sessions.
+
+aaa accounting network stop-only tacacs+
+aaa accounting exec stop-only tacacs+
+
+
+CONFIGURING CALLBACK WITH TACACS+
+---------------------------------
+
+Note: Callback is available only in IOS 11.1 and later, and can only
+be controlled via Tacacs+ for ASYNC lines. ISDN callback can be
+configured on the NAS but cannot be controlled via AAA.
+
+Here is an example of AAA configuration (with exec and network
+accounting enabled):
+
+NAS configuration:
+
+aaa new-model
+tacacs-server host XX.XX.XX.XX
+tacacs-server key fookey
+aaa accounting exec wait-start tacacs+
+aaa accounting network wait-start tacacs+
+
+! Example of AAA configuration for Exec:
+aaa authentication login execcheck tacacs+
+aaa authorization network tacacs+
+service exec-callback
+:
+line 4
+login authentication execcheck
+
+! Example of AAA configuration for ARAP:
+aaa authentication arap arapcheck tacacs+
+aaa authorization network tacacs+
+arap callback
+:
+line 4
+arap authentication arapcheck
+
+! Example of AAA-specific configuration for PPP callback:
+aaa new-model
+aaa authentication ppp pppcheck tacacs+
+aaa authorization network tacacs+
+:
+int async 6
+ppp authentication chap pppcheck
+ppp callback accept
+
+Daemon configuration:
+
+Example of remote TACACS+ server CONFIG file entry for username `foobar':
+
+user = foobar {
+ arap = cleartext AAAA
+ login = cleartext LLLL
+ chap = cleartext CCCC
+ pap = cleartext PPPP
+ opap = cleartext OOOO
+ service = ppp protocol = lcp {
+ callback-dialstring=123456
+ }
+ service = arap {
+ callback-dialstring=2345678
+ }
+ service = exec {
+ callback-dialstring=3456789
+ callback-line=7
+ nocallback-verify=1
+ }
+}
+
+
+
+
+DEBUGGING CONFIGURATION FILES
+-----------------------------
+
+When creating configuration files, it is convenient to check their
+syntax using the -P flag to tac_plus e.g.
+
+ tac_plus -P -C <config file name>
+
+will syntax check the configuration file and print any error messages
+on the terminal.
+
+DEBUGGING A RUNNING SERVER
+--------------------------
+
+There is a myriad of debugging values that can be used in conjunction
+with the -d flag to produce debugging output in /var/log/tac_plus.log.
+
+For example, starting the daemon with
+
+ tac_plus -C CONFIG -d 16
+
+will put authentication debugging into /var/log/tac_plus.log. You can
+view this information by using the tail command.
+
+ tail -f /var/log/tac_plus.log
+
+See the man page for more information.
+
+CHANGING CONFIGURATIONS
+-----------------------
+
+To change a configuration file, you must edit the configuration file
+and then send the daemon a SIGUSR1. This will cause it to reinitialize
+itself and re-read the configuration file.
+
+On startup, tac_plus creates the file /var/run/tac_plus.pid , if possible,
+containing its process id. If you invoke the daemon so that it listens
+on a non-standard port, the file created is /var/run/tac_plus.pid.<port>
+instead, where <port> is the port number the daemon is listening on.
+
+Assuming you are listening on the default port 49, something like the
+following should work:
+
+# kill -USR1 `cat /var/run/tac_plus.pid`
+
+It's a good idea to check that the daemon is still running after
+sending it a SIGUSR1, since a syntactically incorrect configuration
+file will cause the daemon to die.
+
+NOTE: The perl script generate_passwd.pl may be used to hand-generate
+encrypted passwords, or they may be taken from a Unix passwd file.
+
+FREQUENTLY ASKED QUESTIONS
+--------------------------
+Q). Does T+ required a working DNS?
+
+A). As distributed, whenever a START packet arrive, the daemon makes a
+call to getpeername to find out the name of the requestor. Depending
+entirely on how your Unix host is set up, this may make DNS
+queries. If this is a problem, comment out the code in tac_plus.c
+which does this, and just use ip addresses instead of host names.
+
+Q). Is the "name" field used for anything
+
+A). No. It's purely for documentation. I had once thought it might be
+useful when outputting error messages, and that you might need the
+information if you converted a passwd(5) style file, but no use is
+currently made of the field.
+
+Q). Why do I get PPP authorization failures because of "no username in
+request" when I've already logged in and authenticated?
+
+A). With "aaa authentication PPP default tacacs+", the ppp
+authentication overrides the earlier login authentication. If the ppp
+authentication fails, the username ends up blank. Changing the config
+to "aaa authentication ppp default if-needed tacacs+" fixes this
+problem.
+
+Q). I'm sure I configured it correctly, but accounting still doesn't
+work.
+
+A). You will find that although you can configure accounting in 10.3,
+and it outputs debug messages, it doesn't send any accounting
+packets. This is because Accounting only works from 11.0 onwards.
+
+Q). Does TACACS+ use a database instead of a flat (/etc/passwd like)
+file to decrease search times, say if we are talking about a large
+database of 40,000 users?
+
+A). The TACACS+ authentication database is held internally as a hash
+table. This makes lookup times fast and fairly linear, at the expense
+of making the server use potentially large amounts of memory space.
+NOTE: If you specify that the server uses passwd(5) files for
+authentication, then you don't get this speed benefit, but you save
+space.
+
+If you're willing to write the code, it should be a relatively simple
+matter to interface the code to a database scheme e.g. unix dbm files,
+or some proprietary database package, if you wish.
+
+Q). Is there any way to avoid having clear text versions of the
+ARAP and CHAP secrets in the configuration file?
+
+CHAP and ARAP require that the server knows the cleartext password (or
+equivalently, something from which the server can generate the
+cleartext password). Note that this is part of the definition of CHAP
+and ARAP, not just the whim of some Cisco engineer who drank too much
+coffee late one night.
+
+If we encrypted the CHAP and ARAP passwords in the database, then we'd
+need to keep a key around so that the server can decrypt them when
+CHAP or ARAP needs them. So this only ends up being a slight
+obfuscation and not much more secure than the original scheme.
+
+In extended TACACS, the CHAP and ARAP secrets were separated from the
+password file because the password file may be a system password file
+and hence world readable. But with TACACS+'s native database, there
+is no such requirement, so we think the best solution is to
+read-protect the files. Note that this is the same problem that a
+kerberos server has. If your security is compromised on the kerberos
+server, then your database is wide open. Kerberos does encrypt the
+database, but if you want your server to automatically restart, then
+you end up having to "kstash" the key in a file anyway and you're back
+to the same security problem.
+
+So storing the cleartext password on the security server is really an
+absolute requirement of the CHAP and ARAP protocols, not something
+imposed by TACACS+.
+
+We could have chosen a scheme where the NAS sends the challenge
+information to the TACACS+ daemon and the daemon uses the cleartext
+password to generate the response and returns that, but that means
+that we must include specific protocol knowledge into the protocol for
+both ARAP and CHAP and we would have to update the protocol every time
+a new authentication protocol is added. Hence we decided to go with
+the SENDPASS mechanism.
+
+Note that the above doesn't apply to PAP. You can keep an inbound PAP
+password des-encrypted, since all you need to do with it is verify
+that the password the principal gave you is correct.
+
+Q). How is the typical login authentication sequence done?
+
+A). NAS sends START packet to daemon
+ Daemon send GETUSER containing login prompt to NAS
+ NAS prompts user for username
+ NAS sends pkt to daemon
+ Daemon sends GETPASS containing password prompt to the NAS
+ NAS prompts user for password
+ NAS sends pkt to daemon
+ Daemon sends accept, reject or error to NAS
+
+
+Q). Is there a GUI for the configuration file?
+A). No. Use your favourite text editor.
+
+
+Q). What does "default service = permit" really do?
+
+A). When a request comes in to authorize exec startup, or ppp (with
+protocol lcp, ip, ipx), or slip, or arap or a specific command, the
+daemon looks for a matching declarations for the user (or groups the
+user is a member of).
+
+For exec startup, it looks for a "service=exec" OR any command
+configured.
+
+For ppp, it looks for a "service=ppp" and "protocol=(one of lcp, ip,
+ipx)".
+
+For slip there must be a "service=slip" and for arap a "service=arap"
+clause.
+
+For specific commands, there must be a matching cmd=<cmdname>.
+
+If these aren't found, authorization will fail, *unless* you say
+"default service = permit".
+
+Q). How do I make PAP work?
+
+A). Avoid using PAP if possible since it's not very secure. If you
+*must* use it, PAP passwords may be specified along with arap and chap
+passwords for each user. Note that the details of this changed in
+version 3.0 and onwards.
+
+For outbound PAP, where you are forced to send a password to the
+remote host to identify yourself, there is now a separate "opap"
+directive e.g.
+
+ opap = cleartext OOOO
+
+You use this to set the outbound PAP password. It must be a cleartext
+password.
+
+NOTE: It is very bad practice to use an outbound PAP password that is
+the same as any of your inbound passwords. For this reason, a "global"
+password does not apply to outbound PAP, only to inbound PAP,
+bidirectional CHAP and ARAP.
+
+Before 3.0, PAP logins were treated like ordinary user logins, so you
+needed to declare a user in the Daemon configuration file whose name
+was typically the remote hostname (or user), with a login password, in
+order to process the PAP request.
+
+Q). How can I deny some one from telneting from a commserver by ip
+address only. i.e. when command is 10.0.1.6 rather than telnet
+10.0.1.6.
+
+A). The best way to restrict telnet access is by applying an outbound
+access list via the access class command (or equivalently, via the
+"acl" avpair). The NAS configuration command "access-class <n> out"
+for example applies a pre-defined standard IP access list (where n is
+a number from 1 through 99) that governs telnet access from a NAS.
+
+E.g. the following configuration commands permit outgoing Telnet
+access from line 1 on the NAS *only* to hosts on network 192.85.55.0:
+
+ access-list 12 permit 192.85.55.0 0.0.0.255
+ line 1
+ access-class 12 out
+
+Note: you must define "access-list 12 permit 192.85.55.0 0.0.0.255" on
+the NAS. Only then can you use the acl avpair to apply it to a line
+that a user dials in on.
+
+Alternatively, you can try configuring "transport preferred none" on
+the lines in question. This will force a user to always type "telnet
+10.0.1.6" in order to telnet out from the NAS. Then you can apply
+command authorization to this command to restrict it.
+
+Q). I have an autocommand configured in the NAS-local database and I'm
+using "aaa authentication local-override". The autocommand doesn't
+work, but the username/password does. Why?
+
+A). The "local-override" only applies to the authentication portion of
+the local database, so if you want an autocommand for this user, you
+need to also do:
+
+ aaa authorization exec local if-authenticated
+
+This will use the local DB entry if one exists, allow authenticated
+users otherwise, or fail.
+
+We don't have a "aaa authorization local-override" like we do for
+authentication. Unlike authentication, the local method for
+authorization is sort of equivalent to a local-override.
+
+Q). Can tacacs+ only be enabled on a global basis? I want to
+selectively turn it on for, e.g. only modem-connected lines. How do I
+do this?
+
+A). You turn tacacs+ ON on a global basis, but you can then change the
+behavior of individual lines to whatever you want, e.g.
+
+ aaa authentication login default tacacs+ none
+ aaa authentication login oldstyle line
+ aaa authentication login none none
+ line 1 16
+ login authentication default
+ line vty 0 4
+ login authentication oldstyle
+ line 0
+ login authentication none
+
+Note that unfortunately, you can't (yet) apply authorization
+differently to selected lines and interfaces.
+
+Q). I have leased lines running PPP, and AAA authorization is also
+configured, so the authorization on the leased lines fails. What should
+I do?
+
+A). Since you can't (yet) configure authorization on a per-line basis,
+you have to turn on authentication on the leased lines running PPP and
+configure your T+ server so that it will authorize these lines
+correctly.
+
+A more demanding alternative is to modify the TACACS+ server source code
+to allow any authorizations coming in from the port "SerialXXX" to
+succeed.
+
+A third possibility is to not use PPP on those lines, e.g. use HDLC
+instead. HDLC doesn't require authentication or authorization.
+
+Q). What are the memory recommendations for TACACS+?
+
+A). Unless you're using passwd style files, TACACS+ holds entries in
+hash tables in memory. The overhead is modest e.g. each user entry
+occupies 72 bytes, plus space for strings like username and password
+etc. Access time should thus be pretty constant regardless of number
+of users. On a sparc 2, a config file containing 2000 users requires
+about 0.5M of swap.
+
+Q). How many users will a TACACS+ server support? What happens when
+the maximum is exceeded?
+
+There are 2 issues. The first is that each entry in the config file
+occupies memory (swap space) so you could run out of swap space either
+starting up the daemon or when it forks to answer incoming requests
+(see above). If this happens, the daemon will drop the connection
+which should appear as an error to the NAS) and the logfile will
+contain appropriate error messages. The solution is usually to add
+more disk space for swapping.
+
+The second issue is speed: Using config files containing 75,000 user
+entries, I'm seeing about 3 authentications per second on a sparc 2
+without noticeable performance impact, though I haven't benchmarked
+this formally.
+
+So more than about 3 authentications per second on this platform will
+result in users seeing delays and having to wait for prompts. The
+usual solution to this is to add more daemons to spread the load out.
+
+Q). How many characters may a TACACS+ Username and Password contain?
+
+A). The short answer is 31 bytes of username, with up to 254 bytes of
+password if they are cleartext (8 bytes if passwords are des
+encrypted).
+
+The long answer is that the Cisco NAS allocates a buffer of 1024
+bytes, so this is the maximum you can type in, in response to a NAS
+prompt.
+
+But the protocol spec allows a username or password length field of
+just one byte in an authentication packet, so only the first 255 of
+these characters can be sent to the daemon.
+
+Then, the API spec states that the username in the identity structure
+on the daemon is 32 bytes long, so only the first 31 bytes of username
+will be copied from the authentication packet into this structure,
+which is then null-terminated.
+
+The password, on the other hand, is copied into malloc'ed memory, so
+it can still be up to 255 characters long.
+
+Now if it's a des encrypted password, then only the first 8 bytes are
+significant, per the common unix implementations of crypt.
+
+Lastly, there is also the question of how long a username/password can
+be configured in the daemon configuration file. The answer is given by
+the value of MAX_INPUT_LINE_LEN, currently set to 255, which
+determines the length of the longest string you can enter in the
+configuration file.
+
+Q). What is the format of accounting records?
+
+Accounting records are text lines containing tab-separated fields. The
+first 6 fields are always the same. These are:
+
+timestamp, NAS name, username, port, address, record type.
+
+Following these, a variable number of fields are written, depending on
+the accounting record type. All are of the form attribute=value. There
+will always be a task_id field.
+
+Current attributes are:
+
+"unknown"
+"service"
+"start_time"
+"port"
+"elapsed_time"
+"status"
+"priv_level"
+"cmd"
+"protocol"
+"cmd-arg"
+"bytes_in"
+"bytes_out"
+"paks_in"
+"paks_out"
+"address"
+"task_id"
+"callback-dialstring"
+"nocallback-verify"
+"callback-line"
+"callback-rotary"
+
+I expect more will be added over time.
+
+Example records are thus:
+
+Thu Jul 13 13:35:28 1995 cherub.cisco.com chein tty5 171.69.1.141 stop task_id=12028 service=exec port=5 elapsed_time=875
+Thu Jul 13 13:37:04 1995 cherub.cisco.com lol tty18 171.69.1.129 stop task_id=11613 service=exec port=18 service=exec port=18 elapsed_time=909
+Thu Jul 13 14:09:02 1995 cherub.cisco.com billw tty18 171.69.1.152 start task_id=17150 service=exec port=18
+Thu Jul 13 14:09:02 1995 cherub.cisco.com billw tty18 171.69.1.152 start task_id=17150 service=exec port=18 service=exec port=18
+
+
+Elapsed time is in seconds, and is the field most people are usually
+interested in.
+
+Q). How do I limit the number of sessions a user can have?
+
+A). The TACACS+ daemon can enforce how many simultaneous sessions a
+given user is allowed to have. You must compile the daemon with the
+MAXSESS symbol defined (see the Makefile).
+
+Maximum sessions are configured on the daemon for a user as follows:
+
+user = joeslip {
+ login = cleartext XXX
+
+ # only allow two sessions max for joeslip
+ maxsess = 2
+
+ name = "Joe SLIP User"
+ ...
+}
+
+It can also be configured under a group:
+
+group = slip_users {
+ maxsess = 2
+ ...
+}
+...
+user = fred {
+ ...
+ member = slip_users
+ ...
+}
+
+The daemon keeps a count of how many sessions a given user owns by
+monitoring START and STOP accounting records. Thus, exec and network
+accounting must be configured for this feature to operate for exec and
+ppp users.
+
+As the restriction is enforced during the authorization phase of
+login, exec and network authorization must be configured as well, viz:
+
+aaa authentication login default tacacs+
+aaa authentication ppp default tacacs+
+aaa authorization exec tacacs+
+aaa authorization network tacacs+
+aaa accounting exec start-stop tacacs+
+aaa accounting network start-stop tacacs+
+
+Due to network outages (or other disruptions), it is possible for the
+TACACS+ daemon's record of usage to become out of sync with reality,
+so before denying access because it thinks a user is running too many
+sessions, the TACACS+ daemon will use the finger service on the NAS to
+verify how many sessions a user is running there.
+
+If the result of finger indicates that the daemon should permit
+access, access will be granted. Note that for this check to work via
+finger, "service finger" must also be configured on the NAS.
+
+Lastly, note that because finger output truncates usernames at 10
+characters, you may encounter trouble if you have users whose names
+are not unique within those first 10 characters.
+v
+Also recall that authorization works differently on the console. So
+many people locked themselves out of their boxes after configuring
+authorization, that we stopped requiring authorization on the console
+for authenticated users. Since there's no authorization on the
+console, MAXSESS is not enforced there.
+
+Q). How can I configure time-outs on an interface via Tacacs+?
+
+A). Certain per-user/per-interface timeouts may be set by Tacacs+
+during authorization. As of 11.0, you can set an arap session timeout,
+and an exec timeout. As of 11.1 you can also set an exec idle timeout.
+
+There are currently no settable timeouts for PPP or SLIP sessions, but
+there is a workaround which applies to ASYNC PPP/SLIP idle timeouts
+started via exec sessions only: This workaround is to set an EXEC
+(idletime) timeout on an exec session which is later used to start up
+PPP or SLIP (either via a T+ autocommand or via the user explicitly
+invoking PPP or SLIP). In this case, the exec idle timeout will
+correctly terminate an idle PPP or SLIP session. Note that this
+workaround cannot be used for sessions which autoselect PPP or SLIP.
+
+An idle timeout terminates a connection when the interface is idle for
+a given period of time (this is equivalent to the "session-timeout"
+Cisco IOS configuration directive). The other timeouts are
+absolute. Of course, any timeouts set by Tacacs+ apply only to the
+current connection.
+
+
+user = lol {
+ login = cleartext foobar
+ service = exec {
+ # disconnect lol if there is no traffic for 5 minutes
+ idletime = 5
+ # disconnect lol unconditionally after one hour
+ timeout = 60
+}
+
+You also need to configure exec authorization on the NAS for the above
+timeouts, e.g.
+
+ aaa authorization exec tacacs+
+
+Note that these timeouts only work for async lines, not for ISDN
+currently.
+
+
+Note also that you cannot use the authorization "if-authenticated"
+option with these parameters, since that skips authorization if the
+user has successfully authenticated.
+
+Q). How do I send VPDN forwarding decisions to an authorization
+server?
+
+A). In 11.2 onwards, VPDN NASs can use T+ to allow an authorization
+server to make the decision to forward users.
+
+If VPDN forwarding is turned on, and the username is of the form
+user@domain, and "domain" matches a vpdn outgoing configured domain,
+then an authorization attempt is made for "domain" (see below).
+
+When making an authorization call for VPDN, a service type of "ppp"
+with a protocol type of "vpdn", with a username of "domain" will be
+made e.g. when a PPP user comes up on a line with the username
+foo@bar.com, if "vpdn enable" and "aaa authorization ...." is enabled
+on the box, then a one-time authorization of the name "bar.com" is
+attempted.
+
+If this authorization is successful, no local authentication is
+attempted on the NAS, and the connection is forwarded via VPDN
+instead.
+
+If no VPDN-specific information comes back from this authorization
+call, the login proceeds as follows:
+
+If tacacs-server directed-requests are configured (note: this is true
+by default), then IOS will strip off the domain part of a name of the
+form user@domain and use "domain" to try and select a T+ server. If
+successful, the username portion "user", without the domain, will be
+used for all subsequent authentication, authorization and accounting.
+
+If directed requests are turned off, then the entire username
+user@domain is treated as a username.
+
+vpdn specific information includes the attributes "tunnel-id",
+"source-ip" (deprecated) and "ip-addresses":
+
+tunnel-id:
+ This AV pair specifies the username that will be used to
+ authenticate the tunnel over which the individual user MID
+ will be projected. This is analogous to the "NAS name" in the
+ "vpdn outgoing" command.
+
+ip-addresses:
+ This is a list of possible IP addresses that can be used for
+ the end-point of the tunnel. Consult the text at the end of
+ this document for more details on how to configure this
+ attribute.
+
+source-ip: (This is now deprecated. It began in release 11.2(1.4),
+ and was removed in 11.2(4.0.2)).
+ This ip address will be used as the source of all VPDN packets
+ generated as part of the VPDN tunnel (see the source-ip
+ keyword in the vpdn outgoing command).
+
+Tacacs+ syntax
+--------------
+
+The following syntax is used on the public domain Tacacs+ server.
+
+username = domain {
+ service = ppp protocol = vpdn {
+ tunnel-id = <name for tunnel authentication>
+ ip-addresses = <addr> [<addr> ...]
+ source-ip = <ip-address>
+ }
+}
+
+In addition the T+ server can be used to store the usernames for both
+the NAS (the username specified by "tunnel-id" above) and the Home
+Gateway. These will be used to authenticate the tunnel.
+
+Example:
+
+user = foobar.cisco.com {
+ service = ppp protocol = vpdn {
+ tunnel-id = my_nas
+ ip-addresses = "173.20.12.19 173.20.12.20"
+ source-ip = 173.5.10.1
+ }
+}
+
+user = my_nas {
+ global = cleartext egad
+}
+
+user = my_home_gateway {
+ global = cleartext wowser
+}
+
+IOS Configuration
+-----------------
+
+To enable AAA assisted VPDN forwarding on a NAS, the following minimum
+configuration is required:
+
+ vpdn enable
+ aaa new-model
+ aaa authorization network tacacs+ ...
+
+In addition, if the passwords for the home gateway and NAS are stored
+on the T+ daemon, the command
+
+ aaa authentication login tacacs+ ....
+
+should also be present.
+
+Beginning with release 11.2(1.4), the additional configuration
+
+ vpdn outgoing cisco.com ip NAS [ source-ip X.X.X.X ]
+
+can be used. This changes in 11.2(4.0.2) and becomes:
+
+ vpdn source-ip X.X.X.X
+ vpdn outgoing cisco.com ip NAS
+
+
+Q). Can someone expand on the use of the "optional" keyword.
+
+A). Most attributes are mandatory i.e. if the daemon sends them to the
+NAS, the NAS must obey them or deny the authorization. This is the
+default. It is possible to mark attributes as optional, in which case
+a NAS which cannot support the attribute is free to simply ignore it
+without causing the authorization to fail.
+
+This was intended to be useful in cutover situations where you have
+multiple NASes running different versions of IOS, some of which
+support more attributes than others. If you make the new attributes
+optional, older NASes could ignore the optional attributes while new
+NASes could apply them. Note that this weakens your security a little,
+since you are no longer guaranteed that attributes are always applied
+on successful authorization, so it should be used judiciously.
+
+Q). Can I do do address pooling on the T+ daemon?
+
+A). Not really since nothing on the daemon tracks whether an address
+is already in use. The best way to do manage address pools is to
+define a non-overlapping pool of addresses on each NAS and return the
+name of this pool during authorization whenever a user logs in e.g.
+
+NAS:
+
+ip local pool foo 1.0.0.1 1.0.0.10
+
+Daemon:
+
+user = lol {
+ service = ppp protocol = ip {
+ addr-pool = foo
+ }
+}
+
+
+Q). What about MSCHAP?
+
+A). Version F4.X contains mschap support. Mschap is configured the
+same way as chap, only using the "mschap" keyword in place of the
+"chap" keyword.
+
+Like ARAP, MSCHAP works best when you have a des library (see the note
+at the beginning of this document), but this is optional, as the DES
+calculation can be done by the NAS instead. It also optionally
+requires a key from Microsoft which is not public domain, but this can
+also be done on the NAS too, so you can live without it.
+
+To compile the daemon with MSCHAP support, uncomment the MSCHAP line
+in the Makefile. This will add the MSCHAP code to your daemon. You can
+leave MSCHAP_DES undefined, in which case the MSCHAP DES calculation
+will be done by NAS, and no special MSCHAP key will be required.
+
+If you have a DES library and want to use it with MSCHAP (this is more
+efficient for authentication), then you can also uncomment the
+MSCHAP_DES and MSCHAP_MD4_SRC lines in the Makefile. This will ensure
+that the DES calculation done in the daemon. A key will need to be
+obtained from Microsoft and used to replace the definition of
+MSCHAP_KEY in the file mschap.h. If you're thinking by now that this
+is all too much trouble, I can't say I blame you....
+
+Q). Can I do wtmp-style logging like xtacacd used to do?
+
+A). Wtmp file logging is supported. The "-u <wtmpfilename>" command
+line flag can be used to specify a filename which will be used for
+logging wtmp style records instead of the regular T+ accounting
+records.
+
+wtmp records are generated into wtmpfilename. Since file locking is
+also used when writing to wtmpfilename, the usual provisos regarding
+NFS and locking (see accounting above) should be observed.
+
+To generate correct wtmp log records, the NAS needs to be configured
+as follows:
+
+aaa accounting exec stop-only tacacs+
+aaa accounting network stop-only tacacs+
+aaa accounting system start-stop tacacs+
+
+CANNED CONFIGURATIONS
+---------------------
+
+Here are some canned configurations for getting demos started:
+
+1). A canned configuration for login authentication only. This allows
+user fred to login with password "abcdef". If the tacacs+ server dies,
+the enable secret will be accepted as a login password instead.
+
+DAEMON:
+
+key = <some key>
+
+# repeat as necessary for each user
+user = fred {
+ login = cleartext abcdef
+}
+
+NAS:
+
+aaa new-model
+enable secret foobar
+! use tacacs+. If server dies, use the enable secret password
+aaa authentication login default tacacs+ enable
+tacacs-server host <some host ip address>
+tacacs-server key <some key>
+
+2). A canned configuration for command authorization. This will allow
+user fred to login with password abcdef and to run the privileged
+(level 15) commands 'write terminal' and 'configure'. All other
+privileged commands will be denied.
+
+The "none" keyword in the NAS configuration line means that if the
+tacacs+ server dies, any command will be allowed.
+
+DAEMON:
+
+key = <some key>
+
+# repeat as necessary for each user
+user = fred {
+ login = cleartext abcdef
+ cmd = write {
+ permit terminal
+ }
+ cmd = configure {
+ permit .*
+ }
+}
+
+
+NAS:
+
+aaa new-model
+! all level 15 (privileged commands). If server dies, allow everything
+aaa authorization commands 15 tacacs+ none
+tacacs-server host <some host ip address>
+tacacs-server key <some key>
+
+3). Canned configuration for network access authorization. This config
+allows "fred" to login to line 1 with password abcdef (or to and to
+run ppp using chap authentication. The chap password is "lab".
+
+DAEMON:
+
+key = <some key>
+
+# repeat as necessary for each user
+user = fred {
+ login = cleartext abcdef
+ chap = cleartext lab
+ service = ppp protocol = ip {
+ addr=1.0.0.2
+ }
+}
+
+NAS:
+
+aaa new-model
+! authenticate exec logins (if not autoselecting)
+aaa authentication login default tacacs+
+! authorize network services via tacacs+
+aaa authorization network tacacs+
+! use tacacs+ for authenticating ppp users
+aaa authentication ppp default tacacs+
+tacacs-server host <some host ip address>
+tacacs-server key <some key>
+interface Async1
+ip address 1.0.0.1 255.0.0.0
+async default ip address 172.21.14.55
+encapsulation ppp
+async dynamic address
+async mode interactive
+! use chap to authenticate ppp users
+ppp authentication chap
+line 1
+! need "modem inout" here and flow control if using a modem
+
+4). Canned configuration for ARAP.
+
+NAS:
+
+aaa new-model
+aaa authentication arap default guest tacacs+
+aaa authorization network tacacs+
+aaa accounting network start-stop tacacs+
+!
+appletalk routing
+arap network <number> <name>
+!
+interface Ethernet0
+ appletalk cable-range <range>
+ appletalk zone <zonename>
+!
+tacacs-server host <host>
+tacacs-server key <key>
+!
+line 1
+ location a modem
+ modem answer-timeout 0
+ modem InOut
+ autoselect arap
+ autoselect during-login
+ arap enable
+ speed <speed>
+ flowcontrol hardware
+
+Daemon:
+
+key = "some key"
+
+user = lol {
+ arap = cleartext <arap secret>
+ service = arap { }
+}
+
+Authorization AV pairs
+----------------------
+The following authorization AV pairs are supported by 10.3(3) onwards
+except where specifically noted.
+
+The following AV pairs specify which service is being authorized. They
+are typically accompanied by protocol AV pairs and other, additional
+pairs from the lists below.
+
+service=arap
+service=shell (for exec startup, and also for command authorizations)
+service=ppp
+service=slip
+
+service=system (not used).
+
+service=raccess
+ Used for managing reverse telnet connections e.g.
+
+ user = jim {
+ login = cleartext lab
+ service = raccess {
+ port#1 = nasname1/tty2
+ port#2 = nasname2/tty5
+ }
+ }
+
+ Requires IOS configuration
+
+ aaa authorization reverse-access tacacs+
+
+ See the IOS docs for more details.
+
+protocol=lcp
+ The lower layer of PPP, always brought up before IP, IPX, etc.
+ is brought up.
+
+protocol=ip
+ Used with service=ppp and service=slip to indicate which
+ protocol layer is being authorized.
+
+
+protocol=ipx
+ Used with service=ppp to indicate which protocol layer
+ is being authorized.
+
+protocol=atalk
+ with service=ppp or service=arap
+
+protocol=vines
+ For vines over ppp.
+
+protocol=ccp
+ Authorization of CCP. Compression Control Protocol). No other
+ av-pairs associated with this.
+
+protocol=cdp
+ Authorization of CDP (Cisco Discovery Protocol). No other
+ av-pairs associated with this.
+
+protocol=multilink
+ Authorization of multilink PPP. See 'max-links' and 'load-threshold'.
+
+protocol=unknown
+ For undefined/unsupported conditions. Should not occur under
+ normal circumstances.
+
+cmd (EXEC)
+ If the value of cmd is NULL e.g. the AV pair is cmd=, then
+ this is an authorization request for starting an exec.
+
+ If cmd is non-null, this is a command authorization request,
+ It contains the name of the command being authorized
+ e.g. cmd=telnet
+
+cmd-arg (EXEC)
+ During command authorization, the name of the command is given
+ by an accompanying "cmd=" AV pair, and each command argument
+ is represented by a cmd-arg AV pair e.g. cmd-arg=archie.sura.net
+
+ NOTE: 'cmd-arg' should never appear in a configuration file.
+ It is used internally by the daemon to construct a string
+ which is then matched against the regular expressions which appear
+ in a cmd clause in the configuration file.
+
+acl (ARAP, EXEC)
+ For ARAP this contains an access-list number. For EXEC
+ authorization it contains an access-class number, e.g. acl=2.
+ which is applied to the line as the output access class equivalent
+ to the configuration command
+
+ line <n>
+ access-class 2 out
+
+ An outbound access-class is the best way to restrict outgoing telnet
+ connections. Note that a suitable access list (in this case,
+ numbered 2) must be predefined on the NAS.
+
+inacl (PPP/IP/IPX)
+ This AV pair contains an IP or IPX input access list number
+ for slip or PPP e.g. inacl=2. The access list itself must be
+ pre-configured on the Cisco box. Per-user access lists do not
+ work with ISDN interfaces unless you also configure a virtual
+ interface. After 11.2(5.1)F, you can also use the name of a
+ predefined named access list, instead of a number, for the
+ value of this attribute.
+
+ Note: For IPX, inacl is only valid after 11.2(4)F.
+
+inacl#<n> (PPP/IP, PPP/IPX, 11.2(4)F)
+ This AV pair contains the definition of an input access list
+ to be installed and applied to an interface for the duration
+ of the current connection, e.g.
+
+ inacl#1="permit ip any any precedence immediate"
+ inacl#2="deny igrp 0.0.1.2 255.255.0.0 any"
+
+ Attributes are sorted numerically before they are applied.
+ For IP, standard OR extended access list syntax may be used,
+ but it is an error to mix the two within a given access-list.
+
+ For IPX, only extended access list syntax may be used.
+
+ See also:
+ sho ip access-lists
+ sho ip interface
+ sho ipx access-lists
+ sho ipx interface
+ debug aaa author
+ debug aaa per-user
+
+outacl (PPP/IP, PPP/IPX)
+ This AV pair contains an IP or IPX output access list number
+ for SLIP. PPP/IP or PPP/IPX connections e.g. outacl=4. The
+ access list itself must be pre-configured on the Cisco
+ box. Per-user access lists do not work with ISDN interfaces
+ unless you also configure a virtual interface. PPP/IPX is
+ supported in 11.1 onwards only. After 11.2(5.1)F, you can also
+ use the name of a predefined named access list, as well as a
+ number, for the value of this attribute.
+
+
+outacl#<n> (PPP/IP, PPP/IPX, 11.2(4)F)
+ This AV pair contains an output access list definition to be
+ installed and applied to an interface for the duration of the
+ current connection, e.g.
+
+ outacl#1="permit ip any any precedence immediate"
+ outacl#2="deny igrp 0.0.9.10 255.255.0.0 any"
+
+ Attributes are sorted numerically before they are applied.
+ For IP, standard OR extended access list syntax may be used,
+ but it is an error to mix the two within a given access-list.
+
+ For IPX, only extended access list syntax may be used.
+
+ See also:
+ sho ip access-lists
+ sho ip interface
+ sho ipx access-lists
+ sho ipx interface
+ debug aaa author
+ debug aaa per-user
+
+addr (SLIP, PPP/IP)
+ The IP address the remote host should be assigned when a slip
+ or PPP/IP connection is made e.g. addr=1.2.3.4
+
+routing (SLIP, PPP/IP)
+ Equivalent to the /routing flag in slip and ppp commands. Can
+ have as its value the string "true" or "false".
+
+timeout (11.0 onwards, ARAP, EXEC)
+ Sets the time until an arap or exec session disconnects
+ unconditionally (in minutes), e.g. timeout=60
+
+autocmd (EXEC)
+ During exec startup, this specifies an autocommand, like the
+ autocommand option to the username configuration command,
+ e.g. autocmd="telnet foo.com"
+
+noescape (EXEC)
+ During exec startup, this specifies "noescape", like the
+ noescape option to the username configuration command. Can
+ have as its value the string "true" or "false",
+ e.g. noescape=true
+
+nohangup (EXEC)
+ During exec startup, this specifies "nohangup", like the
+ nohangup option to the username configuration command. Can
+ have as its value the string "true" or "false",
+ e.g. nohangup=true
+
+priv-lvl (EXEC)
+ Specifies the current privilege level for command
+ authorizations, a number from zero to 15 e.g. priv_lvl=5.
+
+ Note: in 10.3 this attribute was priv_lvl i.e.
+ it contained an underscore instead of a hyphen.
+
+zonelist (ARAP)
+ An Appletalk zonelist for arap equivalent to the line
+ configuration command "arap zonelist" e.g. zonelist=5
+
+addr-pool (11.0 onwards, PPP/IP, SLIP)
+ This AV pair specifies the name of a local pool from which to
+ get the IP address of the remote host.
+
+ Note: addr-pool works in conjunction with local pooling. It
+ specifies the name of a local pool (which needs to be
+ pre-configured on the NAS). Use the ip-local pool command to
+ declare local pools, e.g on the NAS:
+
+ ip address-pool local
+ ip local pool foo 1.0.0.1 1.0.0.10
+ ip local pool baz 2.0.0.1 2.0.0.20
+
+ then you can use Tacacs+ to return addr-pool=foo or
+ addr-pool=baz to indicate which address pool you want to get
+ this remote node's address from, e.g. on the daemon:
+
+ user = lol {
+ service = ppp protocol = ip {
+ addr-pool=foo
+ }
+ }
+
+route (11.1 onwards, PPP/IP, SLIP).
+ This AV pair specifies a route to be applied to an interface.
+
+ During network authorization, the "route" attribute may be
+ used to specify a per-user static route, to be installed via
+ Tacacs+.
+
+ The daemon side declaration is:
+
+ service=ppp protocol=ip {
+ route="<dst_addr> <mask> [ <gateway> ]"
+ }
+
+ This indicates a temporary static route that is to be
+ applied. "<dst_address>, <mask> and [<gateway>]" are expected
+ to be in the usual dotted-decimal notation, with meanings the
+ same as for the familiar "ip route" configuration command on a
+ NAS.
+
+ If gateway is omitted, the peer's address is taken to be the gateway.
+
+ The route is expunged once the connection terminates.
+
+route#<n> (PPP/IP/IPX, 11.2(4)F)
+ Same as the "route" attribute, except that these are valid for
+ IPX as well as IP, and they are numbered, allowing multiple
+ routes to be applied e.g.
+
+ route#1="3.0.0.0 255.0.0.0 1.2.3.4"
+ route#2="4.0.0.0 255.0.0.0"
+
+
+ or, for IPX,
+
+ route#1="4C000000 ff000000 30.12.3.4"
+ route#2="5C000000 ff000000 30.12.3.5"
+
+ See also:
+ sho ip route
+ sho ipx route
+ debug aaa author
+ debug aaa per-user
+
+callback-rotary (11.1 onwards, valid for ARAP, EXEC, SLIP or PPP)
+ The number of a rotary group (between 0 and 100 inclusive)
+ to use for callback e.g. callback-rotary=34. Not valid for ISDN.
+
+callback-dialstring (11.1 onwards, valid for ARAP, EXEC, SLIP or PPP)
+ sets the telephone number for a callback e.g.
+ callback-dialstring=408-555-1212. Not valid for ISDN.
+
+callback-line (11.1 onwards, valid for ARAP, EXEC, SLIP or PPP)
+ The number of a tty line to use for callback e.g.
+ callback-line=4. Not valid for ISDN.
+
+nocallback-verify (11.1 onwards, valid for ARAP, EXEC)
+ Indicates that no callback verification is required. The only
+ valid value for this parameter is the digit one i.e.
+ nocallback-verify=1. Not valid for ISDN.
+
+idletime (11.1 onwards, EXEC)
+ Sets a value, in minutes, after which an IDLE session will be
+ terminated. N.B. Does NOT work for PPP.
+
+tunnel-id (11.2 onwards, PPP/VPDN)
+ This AV pair specifies the username that will be used to
+ authenticate the tunnel over which the individual user MID
+ will be projected. This is analogous to the "NAS name" in the
+ "vpdn outgoing" command.
+
+ip-addresses (11.2 onwards, PPP/VPDN)
+ This is a space separated list of possible IP addresses that
+ can be used for the end-point of the tunnel.
+
+ In 11.2(5.4)F, this attribute was extended as follows:
+
+ 1) comma (',') is also consider as a delimiter
+ For example the avpair can now be written as
+
+ ip-addresses = 172.21.9.26,172.21.9.15,172.21.9.4
+
+ 2) '/' is considered a priority delimiter. When you have a
+ number of Home Gateway routers, it is desirable to consider some
+ as the primary routers and some as backup routers.
+
+ The '/' allow you to config the routers into priority groups,
+ so that the NAS will try to forward the users to the high
+ priority routers, before forwarding to the low priority one.
+
+ For example in the following avpair:
+
+ ip-addresses = "172.21.9.26 / 172.21.9.15 / 172.21.9.4"
+
+ 172.21.9.26 is considered to be priority 1
+ 172.21.9.15 is considered to be priority 2
+ 172.21.9.4 is considered to be priority 3
+
+ The NAS will try to forward the users to 172.21.9.26, before
+ trying 172.21.9.15. If the NAS can't forward users to
+ 172.21.9.26, it will try 172.21.9.15 next. If it fails with
+ 172.21.9.15, it will then try forwarding to 172.21.9.4.
+
+source-ip (PPP/VPDN, now deprecated, only existed in releases 11.2(1.4)
+ thru 11.2(4.0.2)). This specifies a single ip address will be
+ used as the source of all VPDN packets generated as part of
+ the VPDN tunnel (see the equivalent source-ip keyword in the
+ IOS vpdn outgoing command).
+
+nas-password (PPP/VPDN, 11.2(3.4)F, 11.2(4.0.2)F)
+ During L2F tunnel authentication, nas-password specifies the password
+ for the NAS.
+
+gw-password (PPP/VPDN, 11.2(3.4)F, 11.2(4.0.2)F)
+ During L2F tunnel authentication, gw-password specifies the password
+ for the home gateway.
+
+rte-ftr-in#<n> (PPP -- IP/IPX, 11.2(4)F)
+ This AV pair specifies an input access list definition to be
+ installed and applied to routing updates on the current
+ interface, for the duration of the current connection.
+
+ For IP, both standard and extended Cisco access list syntax is
+ recognised, but it is an error to mix the two within a given
+ access-list.
+
+ For IPX, only Cisco extended access list syntax is legal.
+
+ Attributes are sorted numerically before being applied. For
+ IP, the first attribute must contain the name of a routing
+ process and its identifier (except for rip, where no
+ identifier is needed), e.g.
+
+ rte-fltr-in#0="router igrp 60"
+ rte-fltr-in#1="permit 0.0.3.4 255.255.0.0"
+ rte-fltr-in#2="deny any"
+
+ For IPX, no routing process is needed, e.g.
+
+ rte-fltr-in#1="deny 3C01.0000.0000.0001"
+ rte-fltr-in#2="deny 4C01.0000.0000.0002"
+
+ See also:
+
+ show ip access-lists
+ show ip protocols
+ sho ipx access-lists
+ sho ipx interface
+ debug aaa author
+ debug aaa per-user
+
+ Related IOS commands:
+ IP:
+ router <routing process identifier>
+ [no] distribute-list <list-name> in <interface>
+
+ IPX:
+ ipx input-network-filter <access-list-number>
+
+
+rte-ftr-out#<n> (PPP/IP, 11.2(4)F)
+ This AV pair specifies an input access list definition to be
+ installed and applied to routing updates on the current
+ interface, for the duration of the current connection.
+
+ For IP, both standard and extended Cisco access list syntax is
+ recognised, but it is an error to mix the two within a given
+ access-list.
+
+ Attributes are sorted numerically before being applied. The
+ first attribute must contain the name of a routing process and
+ its identifier (except for rip, where no identifier is
+ needed), e.g.
+
+ rte-fltr-out#0="router igrp 60"
+ rte-fltr-out#3="permit 0.0.5.6 255.255.0.0"
+ rte-fltr-out#4="permit any"
+
+ For IPX, no routing process is specified, e.g.
+
+ rte-fltr-out#1="deny 3C01.0000.0000.0001"
+ rte-fltr-out#2="deny 4C01.0000.0000.0002"
+
+ See also:
+
+ sho ipx access-lists
+ sho ipx interface
+ show ip access-lists
+ show ip protocols
+ debug aaa author
+ debug aaa per-user
+
+ Related IOS commands:
+ IP:
+ router <routing process identifier>
+ [no] distribute-list <list-name> in <interface>
+
+ IPX:
+ ipx output-network-filter <access-list-number>
+
+
+sap#<n> (11.2(4)F PPP/IPX)
+ This AV pair specifies static saps to be installed for the duration
+ of a connection e.g.
+
+ sap#1="4 CE1-LAB 1234.0000.0000.0001 451 4"
+ sap#2="5 CE3-LAB 2345.0000.0000.0001 452 5"
+
+ The syntax of static saps is the same as that used by the IOS
+ "ipx sap" command.
+
+ See also:
+ sho ipx servers
+ debug aaa author
+ debug aaa per-user
+
+ Related IOS commands:
+ [no] ipx sap ....
+
+
+route#<n> (PPP/IP/IPX, 11.2(4)F)
+
+ Same as the "route" attribute, except that these are valid for
+ IPX as well as IP, and they are numbered, allowing multiple
+ routes to be applied e.g.
+
+ route#1="3.0.0.0 255.0.0.0 1.2.3.4"
+ route#2="4.0.0.0 255.0.0.0"
+
+
+ or, for IPX,
+
+ route#1="4C000000 ff000000 30.12.3.4"
+ route#2="5C000000 ff000000 30.12.3.5"
+
+ See also:
+ sho ip route
+ sho ipx route
+ debug aaa author
+ debug aaa per-user
+
+
+sap-fltr-in#<n> (PPP/IPX, 11.2(4)F)
+ This AV pair specifies an input sap filter access list
+ definition to be installed and applied on the current
+ interface, for the duration of the current connection.
+
+ Only Cisco extended access list syntax is legal, e.g
+
+ sap-fltr-in#1="deny 6C01.0000.0000.0001"
+ sap-fltr-in#2="permit -1"
+
+ Attributes are sorted numerically before being applied.
+
+ sho ipx access-lists
+ sho ipx interface
+ debug aaa author
+ debug aaa per-user
+
+ [no] ipx input-sap-filter <number>
+
+
+sap-fltr-out#<n> (PPP/IPX 11.2(4)F)
+ This AV pair specifies an output sap filter access list
+ definition to be installed and applied on the current
+ interface, for the duration of the current connection.
+
+ Only Cisco extended access list syntax is legal, e.g
+
+ sap-fltr-out#1="deny 6C01.0000.0000.0001"
+ sap-fltr-out#2="permit -1"
+
+ Attributes are sorted numerically before being applied.
+
+ sho ipx access-lists
+ sho ipx interface
+ debug aaa author
+ debug aaa per-user
+
+ [no] ipx output-sap-filter <number>
+
+
+
+pool-def#<n> (PPP/IP, 11.2(4)F)
+ This attribute is used to define ip address pools on the NAS.
+
+ During IPCP address negotiation, if an ip pool name is
+ specified for a user (see the addr-pool attribute), a check is
+ made to see if the named pool is defined on the NAS. If it is,
+ the pool is consulted for an ip address.
+
+ If the required pool is not present on the NAS (either in the
+ local config, or as a result of a previous download
+ operation), then an authorization call to obtain it is
+ attempted, using the special username:
+
+ <nas-name>-pools
+
+ where <nas-name> is the configured name of the NAS.
+
+ Note: This username can be changed using the IOS configuration
+ directive e.g.
+
+ aaa configuration config-name nas1-pools-definition.cisco.us
+
+ The pool-def attribute is used to define ip address pools for
+ the above authorization call e.g.
+
+ user = foo {
+ login = cleartext lab
+ service = ppp protocol = ip {
+ addr-pool=bbb
+ }
+ }
+
+ user = nas1-pools {
+ service = ppp protocol = ip {
+ pool-def#1 = "aaa 1.0.0.1 1.0.0.3"
+ pool-def#2 = "bbb 2.0.0.1 2.0.0.10"
+ pool-def#3 = "ccc 3.0.0.1 3.0.0.20"
+ pool-timeout=60
+ }
+ }
+
+
+ In the example above is a configuration file fragment for defining 3
+ pools named "aaa", "bbb" and "ccc" on the NAS named "nas1".
+
+ When the user "foo" refers to the pool named "bbb", if the
+ pool "bbb" isn't defined, the NAS will attempt to download the
+ definition contained in the "nas1-pools" entry.
+
+ The other pools will also be defined at the same time (or they
+ will be ignored if they are already defined).
+
+ Since this procedure is only activated when an undefined pool
+ is referenced, one way to redefine a pool once it has been
+ downloaded is to manually delete the definition e.g. by
+ logging into the NAS, enabling, and configuring:
+
+ config t
+ no ip local pool bbb
+ ^Z
+
+ When a pool is deleted, there is no interruption in service
+ for any user who is currently using a pool address. If a pool
+ is deleted and then subsequently redefined to include a pool
+ address that was previously allocated, the new pool will pick
+ up the allocated address and track it as expected.
+
+ Since downloaded pools do not appear in the NAS configuration,
+ any downloaded pool definitions automatically disappear
+ whenever a NAS reboots. These pools are marked as "dynamic"
+ when they appear in the output of the "show ip local pools"
+ NAS command.
+
+ Since it is desirable not to have to manually delete pools to
+ redefine them, the AV pair pool-timeout=<n> can be used to
+ timeout any downloaded pool definitions. The timeout <n> is in
+ minutes.
+
+ The effect of the pool-timeout attribute is to start a timer
+ when the pool definitions are downloaded. When the timer
+ expires, the pools are deleted. The next reference to a
+ deleted pool via will cause a re-fetch of the pool
+ definitions. This allows pool changes to be made on the
+ daemon and propagated to the NAS in a timely manner.
+
+ See also:
+
+ sho ip local pool
+ sho ip local pool <pool-name>
+
+ IOS commands:
+
+ ip local pool <name> <start address> <end address>
+
+
+old-prompts (PPP/SLIP)
+ This attribute is integrated into the following IOS images:
+
+ 11.2(7.4)P 11.2(7.4) 11.1(13.1) 11.(16.2) 11.1(13.1)AA
+ 11.1(13.1)CA 11.1(13.1)IA 11.2(8.0.1)F 11.0(16.2)BT)
+
+ This attribute allows providers to make the prompts in T+
+ appear identical to those of earlier systems (tacacs and
+ xtacacs). This will allow administrators to upgrade from
+ tacacs/xtacacs to T+ transparently to users.
+
+ The difference between the prompts is as follows:
+
+ In xtacacs, when the user types "slip" or "ppp" the system
+ prompts for an address followed by a password, whereas T+
+ prompts only for an address.
+
+ In xtacacs, if the user types "slip host" or "ppp host", the
+ system prompts for a password. In T+, there is no prompt.
+
+ Using this attribute, T+ can be made to mimic the prompting
+ behaviour of xtacacs, by configuring network authorization on
+ IOS, and using the "old-prompts=true" attribute value pair for
+ slip and ppp/ip, viz:
+
+ user = joe {
+ global = cleartext foo
+
+ service = exec {
+ }
+ service = slip {
+ default attribute = permit
+ old-prompts=true
+ }
+ service = ppp protocol = ip {
+ default attribute = permit
+ old-prompts=true
+ }
+ }
+
+ i.e. the prompts are controlled by the addition of the
+ "old-prompts=true" attribute.
+
+
+max-links (PPP/multilink - Multilink parameter; 11.3)
+ This AV pair restricts the number of multilink bundle links
+ that a user can have.
+
+ The daemon side declaration is:
+
+ service=ppp protocol=multilink {
+ max-links=<n>
+ }
+
+ The range of <n> is [1-255].
+
+ Related NAS commands:
+ int <foo>
+ [no] ppp multilink
+ int virtual-template X
+ multilink max-links <n>
+
+ show ppp multilink
+ debug multilink
+
+load-threshold (PPP/multilink - Multilink parameter; 11.3)
+ This AV pair sets the load threshold at which an additional
+ multilink link is added to the bundle (if load goes above) or
+ deleted (if load goes below).
+
+ The daemon side declaration is:
+
+ service=ppp protocol=multilink {
+ load-threshold=<n>
+ }
+
+ The range of <n> is [1-255].
+
+ Related NAS commands:
+ int <foo>
+ [no] ppp multilink
+ int virtual-template X
+ multilink load-threshold <n>
+
+ show ppp multilink
+ debug multilink
+
+
+Reserved for future use:
+
+ppp-vj-slot-compression
+link-compression
+asyncmap
+x25-addresses (PPP/VPDN)
+frame-relay (PPP/VPDN)
+
+
--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, 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 of the
+ software and supporting documentation, the name of Cisco Systems,
+ 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 modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. 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 "tac_plus.h"
+
+#ifdef STDLIB_MALLOC
+
+#include <stdlib.h>
+
+#else /* !STDLIB_MALLOC */
+
+#include <malloc.h>
+
+#endif /* STDLIB_MALLOC */
+
+char *
+tac_malloc(size)
+int size;
+{
+ char *p;
+
+ /* some mallocs don't like requests for zero length */
+ if (size == 0) {
+ size++;
+ }
+
+ p = (char *) malloc(size);
+
+ if (p == NULL) {
+ report(LOG_ERR, "malloc %d failure", size);
+ tac_exit(1);
+ }
+ return (p);
+}
+
+char *
+tac_realloc(ptr, size)
+char *ptr;
+int size;
+{
+ char *p;
+
+ if (ptr == NULL) {
+ /* realloc(0, size) is not portable */
+ p = tac_malloc(size);
+ } else {
+ p = (char *)realloc(ptr, size);
+ }
+
+ if (p == NULL) {
+ report(LOG_ERR, "realloc %d failure", size);
+ tac_exit(1);
+ }
+ return (p);
+}
+
+tac_exit(status)
+int status;
+{
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "exit status=%d", status);
+ exit(status);
+}
+
+char *
+tac_strdup(p)
+char *p;
+{
+ char *n = strdup(p);
+
+ if (n == NULL) {
+ report(LOG_ERR, "strdup allocation failure");
+ tac_exit(1);
+ }
+ return (n);
+}
+
+char *
+tac_make_string(p, len)
+u_char *p;
+int len;
+{
+ char *string;
+ int new_len = len;
+
+ /*
+ * Add space for a null terminator if needed. Also, no telling
+ * what various mallocs will do when asked for a length of zero.
+ */
+ if (len == 0 || p[len - 1])
+ new_len++;
+
+ string = (char *) tac_malloc(new_len);
+
+ bzero(string, new_len);
+ bcopy(p, string, len);
+ return (string);
+}
+
+/* return a pointer to the end of substring in string, or NULL. Substring
+ must begin at start of string.
+*/
+char *
+tac_find_substring(substring, string)
+char *substring, *string;
+{
+ int len;
+
+ if (!(substring && string)) {
+ return(NULL);
+ }
+
+ len = strlen(substring);
+
+ if (len > (int) strlen(string)) {
+ return(NULL);
+ }
+
+ if (strncmp(substring, string, len)) {
+ /* no match */
+ return(NULL);
+ }
+ return(string + len);
+}
+
+#ifdef NEED_BZERO
+int
+bzero(p, len)
+ register char *p;
+ int len;
+{
+ register int n;
+
+ if (len <= 0) {
+ return;
+ }
+ for (n=0; n < len; n++) {
+ p[n] = 0;
+ }
+}
+
+int
+bcopy(s1, s2, len)
+ register char *s1, *s2;
+ int len;
+{
+ register int n;
+
+ if ((n = len) <= 0)
+ return;
+ do
+ *s2++ = *s1++;
+ while (--n);
+}
+
+int
+bcmp(s1,s2,n)
+ char *s1,*s2;
+ int n;
+{
+ while (n-- > 0) {
+ if (*s1++ != *s2++) {
+ return(1);
+ }
+ }
+ return 0;
+}
+#endif /* NEED_BZERO */
+
+/* Lock a file descriptor using fcntl. Returns 1 on successfully
+ acquiring the lock. The lock disappears when we close the file.
+
+ Note that if the locked file is on an NFS-mounted partition, you
+ are at the mercy of SUN's lockd, which is probably a bad idea
+*/
+
+int
+tac_lockfd (filename, lockfd)
+char *filename;
+int lockfd;
+{
+ int tries;
+ struct flock flock;
+ int status;
+
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET; /* relative to bof */
+ flock.l_start = 0L; /* from offset zero */
+ flock.l_len = 0L; /* lock to eof */
+
+ if (debug & DEBUG_LOCK_FLAG) {
+ syslog(LOG_ERR, "Attempting to lock %s fd %d", filename, lockfd);
+ }
+
+ for (tries = 0; tries < 10; tries++) {
+ errno = 0;
+ status = fcntl(lockfd, F_SETLK, &flock);
+ if (status == -1) {
+ if (errno == EACCES || errno == EAGAIN) {
+ sleep(1);
+ continue;
+ } else {
+ syslog(LOG_ERR, "fcntl lock error status %d on %s %d %s",
+ status, filename, lockfd, sys_errlist[errno]);
+ return(0);
+ }
+ }
+ /* successful lock */
+ break;
+ }
+
+ if (errno != 0) {
+ syslog(LOG_ERR, "Cannot lock %s fd %d in %d tries %s",
+ filename, lockfd, tries+1, sys_errlist[errno]);
+
+ /* who is hogging this lock */
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET; /* relative to bof */
+ flock.l_start = 0L; /* from offset zero */
+ flock.l_len = 0L; /* lock to eof */
+#ifdef HAS_FLOCK_SYSID
+ flock.l_sysid = 0L;
+#endif
+ flock.l_pid = 0;
+
+ status = fcntl(lockfd, F_GETLK, &flock);
+ if ((status == -1) || (flock.l_type == F_UNLCK)) {
+ syslog(LOG_ERR, "Cannot determine %s lockholder status=%d type=%d",
+ filename, status, flock.l_type);
+ return(0);
+ }
+
+ if (debug & DEBUG_LOCK_FLAG) {
+ syslog(LOG_ERR, "Lock on %s is being held by sys=%u pid=%d",
+ filename,
+#ifdef HAS_FLOCK_SYSID
+ flock.l_sysid,
+#else
+ 0,
+#endif
+ flock.l_pid);
+ }
+ return(0);
+ }
+
+ if (debug & DEBUG_LOCK_FLAG) {
+ syslog(LOG_ERR, "Successfully locked %s fd %d after %d tries",
+ filename, lockfd, tries+1);
+ }
+ return(1);
+}
+
+/* Unlock a file descriptor using fcntl. Returns 1 on successfully
+ releasing a lock. The lock dies when we close the file.
+
+ Note that if the locked file is on an NFS-mounted partition, you
+ are at the mercy of SUN's lockd, which is probably a bad idea
+*/
+
+int
+tac_unlockfd (filename,lockfd)
+char *filename;
+int lockfd;
+{
+ struct flock flock;
+ int status;
+
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET; /* relative to bof */
+ flock.l_start = 0L; /* from offset zero */
+ flock.l_len = 0L; /* lock to eof */
+
+ if (debug & DEBUG_LOCK_FLAG) {
+ syslog(LOG_ERR, "Attempting to unlock %s fd %d", filename, lockfd);
+ }
+
+ status = fcntl(lockfd, F_UNLCK, &flock);
+ if (status == -1) {
+ syslog(LOG_ERR, "fcntl unlock error status %d on %s %d %s",
+ status, filename, lockfd, sys_errlist[errno]);
+ return(1);
+ }
+
+ if (debug & DEBUG_LOCK_FLAG) {
+ syslog(LOG_ERR, "Successfully unlocked %s fd %d",
+ filename, lockfd);
+ }
+ return(0);
+}