+diff --git a/.exrc b/.exrc
+new file mode 100644
+index 0000000..d51ef7d
+--- /dev/null
++++ b/.exrc
+@@ -0,0 +1,2 @@
++set tabstop=8
++set shiftwidth=4
+diff --git a/Makefile.am b/Makefile.am
+new file mode 100644
+index 0000000..8d609b1
+--- /dev/null
++++ b/Makefile.am
+@@ -0,0 +1,126 @@
++# 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.
++
++
++AUTOMAKE_OPTIONS = foreign
++
++sbin_PROGRAMS = tac_plus
++bin_PROGRAMS = generate_passwd
++man_MANS = tac_plus.1
++noinst_MANS = tac_regexp.3
++tacacssysconfdir = $(sysconfdir)/tacacs
++tacacssysconf_DATA = tac_plus.cfg
++EXTRA_DIST = \
++ CHANGES \
++ README.LDAP \
++ README.PAM \
++ users_guide \
++ tac_plus.spec.in \
++ convert.pl \
++ tac_plus.cfg \
++ tac_plus.init \
++ tac_plus.rotate \
++ tac_plus.sql \
++ tac_plus.pam \
++ $(tacacssysconf_DATA) \
++ $(man_MANS) \
++ $(noinst_MANS)
++
++generate_passwd_SOURCES = generate_passwd.c
++
++tac_plus_SOURCES = \
++ acct.c acct.h \
++ authen.c authen.h \
++ author.c author.h \
++ cfgfile.c cfgfile.h \
++ cfgeval.c cfgeval.h \
++ choose_authen.c choose_authen.h \
++ default_fn.c default_fn.h \
++ default_v0_fn.c default_v0_fn.h \
++ do_acct.c do_acct.h \
++ do_author.c do_author.h \
++ dump.c dump.h \
++ enable.c enable.h \
++ encrypt.c encrypt.h \
++ expire.c expire.h \
++ hash.c hash.h \
++ main.c main.h \
++ md5.c md5.h \
++ packet.c packet.h \
++ parse.c parse.h \
++ programs.c programs.h \
++ pw.c pw.h \
++ pwlib.c pwlib.h \
++ report.c report.h \
++ sendauth.c sendauth.h \
++ sendpass.c sendpass.h \
++ time_limit.c time_limit.h \
++ utils.c utils.h \
++ mschap.h \
++ tac_regmagic.h \
++ tac_plus.h
++
++EXTRA_tac_plus_SOURCES = $(cond)
++tac_plus_LDFLAGS = $(conf_LDFLAGS)
++# $(use_o) has to be BEFORE $(conf_LDADD)! (for library dependencies)
++tac_plus_LDADD = $(use_o) $(conf_LDADD)
++tac_plus_DEPENDENCIES = $(use_o)
++use = @COND_USE@
++use_o = $(filter %.o,$(use:.c=.o))
++
++cond_DB = db.c db.h
++cond_DB_MYSQL = db_mysql.c db_mysql.h
++cond_DB_NULL = db_null.c db_null.h
++cond_DB_PGSQL = db_pgsql.c db_pgsql.h
++cond_USE_LDAP = ldap_author.c ldap_author.h
++cond_MAXSESS = maxsess.c maxsess.h
++cond_MSCHAP = md4.c md4.h
++cond_SKEY = skey_fn.c skey_fn.h
++cond_USE_PAM = tac_pam.c tac_pam.h
++cond_TCPWRAPPER = tcpwrap.c tcpwrap.h
++cond_WITH_INCLUDED_REGEX = \
++ tac_regexp.c tac_regexp.h
++
++cond = \
++ $(cond_DB) \
++ $(cond_DB_MYSQL) \
++ $(cond_DB_NULL) \
++ $(cond_DB_PGSQL) \
++ $(cond_USE_LDAP) \
++ $(cond_MAXSESS) \
++ $(cond_MSCHAP) \
++ $(cond_SKEY) \
++ $(cond_USE_PAM) \
++ $(cond_TCPWRAPPER) \
++ $(cond_WITH_INCLUDED_REGEX)
++
++
++# These rules were not migrated from Makefile.in as I don't have
++# (and I don't know) 'purify' tool:
++#
++# 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)
+diff --git a/README.LDAP b/README.LDAP
+new file mode 100644
+index 0000000..755b19e
+--- /dev/null
++++ b/README.LDAP
+@@ -0,0 +1,146 @@
++ LDAP Authentification with Tacacs+
++ ----------------------------------
++
++
++Author : Harpes Patrick (patrick.harpes@tudor.lu)
++ Jahnen Andreas (andreas.jahnen@tudor.lu)
++Date : 16.03.2001
++
++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.
++
++
++This document aim to describe how to perform LDAP authentification for tacacs+.
++
++
++Requirements:
++-------------
++
++1) tac_plus.F4.0.3-v8.alpha.tar.gz
++ This package includes the original CISCO tacacs+ package from http://www.gazi.edu.tr/tacacs/
++2) openldap package
++ This package has been developped using the openldap libraries version 2.0.7
++ OpenLDAP is available from www.openldap.org
++3) GCC and other GNU developpment tools (make...)
++4) A running LDAP server (test has been made using Lotus Domino LDAP server version 5.0.x and
++ OpenLDAP)
++
++Overview:
++---------
++ ------------ ----------------
++ - Server - - Notes DOMINO -
++ ---------------- - running -____LDAP____- LDAP Server -
++ - CISCO Dialup -__tacacs+_____- tacacs+ - - or -
++ - Router - - - - other LDAP -
++ ---------------- ------------ - Server -
++ ---------------
++
++The CISCO router sends tacacs+ request to the tacacs+ server. This one uses the LDAP
++server to authentificate the user.
++
++
++HowTo configure the CISCO router?
++---------------------------------
++
++There are good documentations available on how to set up the CISCO router for using
++tacacs+. This documents can be found on the tacacs+ homepage http://www.gazi.edu.tr/tacacs/
++
++HowTo install the tacacs+ package with LDAP support?
++----------------------------------------------------
++
++To enable the LDAP support on the tacacs+ package, you have to perform the following steps:
++
++ 1. Install the Open LDAP package (version 2.0.7) (www.openldap.org)
++ Refer to the INSTALL document to build this package.
++
++ 2. Unpack the tacacs+ package in /usr/local/src
++ # tar -zxvf tac_plus.F4.0.3-v8.alpha.tar.gz
++
++ 3. Use the configure script to create the Makefiles
++
++ # cd /usr/local/src/tac_plus.F5.0.0.alpha/
++ # ./configure --with-ldap
++
++ You can use ./configure --help to get more options
++
++ 4. Compile the package
++
++ # make tac_plus
++
++ 5. Set your LD_LIBRARY_PATH to include the LDAP libraries
++
++ # LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH
++
++
++HowTo configure tacacs+ for using the LDAP support
++--------------------------------------------------
++
++To use the LDAP authentification, use the following simple tacacs+ configuration file
++
++ key = "your tacacs key"
++ accounting file = /var/log/tac-plus/tac_plus.log
++ default authentification = ldap "ldap://<Hostname of your LDAP server>"
++ user=DEFAULT {
++ service = ppp protocol = ip {
++ }
++ }
++
++
++For more information on the configuration file please use the complete tacacs+ documentation.
++
++
++How to start the tacacs+ daemon
++-------------------------------
++
++Make sure your LD_LIBRARY_PATH includes the LDAP libraries.
++As root, start the tacacs daemon:
++ # /usr/local/src/tac_plus.F4.0.3-v8.alpha/tac_plus -C tac_plus.cfg
++
++
++How to configure the LDAP server
++--------------------------------
++
++a) Notes Domino LDAP server
++---------------------------
++
++You have to enable the Domino server task "LDAP" with the Administration Tool. You
++can do this with the command "laod ldap" at the server console or with the help of
++the Tools Menu of the server tab (Tools -> Task -> Start "LDAP Server").
++
++You can define which attributes of your Domino Directory are accessible by
++anonymous users and if it is allowed to write to your Domino Directory using LDAP in
++a Configuration document. You have to specify "Use these settings as the default
++settings for all servers" in the Basic tab of the Configuration document to display
++the LDAP options tab. There you are able to adjust the settings for a your LDAP server.
++
++For additional information see the IBM Red Book "Getting the most from your Domino
++Directory" (11/2000), which you can downlaod from http://www.redbooks.ibm.com.
++
++
++b) Open LDAP
++------------
++
++It is also possible to use OpenLDAP for this kind of authentification. Please look at
++the documentation at http://www.openldap.org for details how to install the server.
++
++
++Security
++---------
++
++The here described tacacs+ queries are not quering any of the fields stored in your LDAP
++server. We only try to log in and this is the "test" we perform here.
++
++Pleae note that the passwords are not send encrypted. You have to make sure that it is
++not possible to sniff them. In general is there no support from tacacs+ to support encrypted
++passwords.
++It is maybe possible to use OpenLDAP with TLS support to encrypt the passwords and use a
++secure LDAP server. This is also supported by Domino and OpenLDAP. But this is not implemented.
++
++Good luck,
++
++ Harpes Patrick (patrick.harpes@tudor.lu) and Jahnen Andreas (andreas.jahnen@tudor.lu)
+diff --git a/README.PAM b/README.PAM
+index 0c1d468..7cbd4b4 100644
+--- a/README.PAM
++++ b/README.PAM
+@@ -36,5 +36,3 @@ into tac_plus.conf.
+
+
+ Max Liccardo <ravel@tiscalinet.it>
+-
+-
+diff --git a/acconfig.h b/acconfig.h
+new file mode 100644
+index 0000000..72e7998
+--- /dev/null
++++ b/acconfig.h
+@@ -0,0 +1,97 @@
++/* acconfig.h
++ *
++ * $Id$
++ */
++
++#ifndef TAC_PLUS_CONFIG_H
++#define TAC_PLUS_CONFIG_H 1
++
++@TOP@
++
++
++/* --maintainer-mode
++ * Sets "/etc/tacacs/tac_plus.cfg" as default config file
++ */
++#undef MAINTAINER_MODE
++
++/* Missing socklen_t in <sys/socket.h>
++ * We don't use 'typedef' to not to yet require <stddef.h> included here.
++ */
++#undef socklen_t
++
++/* 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
++
++/* Check for some fields in <pwd.h>/struct passwd and <utmp.h>/struct utmp
++ */
++#undef HAVE_PASSWD_PW_AGE
++#undef HAVE_PASSWD_PW_COMMENT
++#undef HAVE_UTMP_UT_HOST
++
++/* All OSes detected by configure.in:
++ */
++#undef LINUX
++#undef GLIBC
++#undef SOLARIS
++#undef FREEBSD
++#undef HPUX
++#undef AIX
++#undef MIPS
++
++/* --with-pam */
++#undef USE_PAM
++/* --with-ldap */
++#undef USE_LDAP
++/* --with-db */
++#undef DB
++/* --with-db */
++#undef DB_NULL
++/* --with-mysql */
++#undef DB_MYSQL
++/* --with-pgsql */
++#undef DB_PGSQL
++/* --enable-maxsess */
++#undef MAXSESS
++/* --with-libwrap */
++#undef TCPWRAPPER
++/* --with-skey */
++#undef SKEY
++/* --with-mschap[=des] */
++#undef MSCHAP
++#undef MSCHAP_DES
++/* --with-tac[ug]id */
++#undef TACPLUS_USERID
++#undef TACPLUS_GROUPID
++/* --with-tacplus_pid */
++#undef TACPLUS_PIDFILE
++/* --with-included-regex */
++#undef WITH_INCLUDED_REGEX
++
++
++@BOTTOM@
++
++/* Keep in sync with configure.in */
++#define _XOPEN_SOURCE 1 /* for unistd.h/crypt() */
++#define _XOPEN_SOURCE_EXTENDED 1 /* for pwd.h/{set,end}pwent(), sys/wait.h/wait3() */
++#define _BSD_SOURCE 1 /* for u_{char,short,int} & string.h/bcopy() */
++#define _OSF_SOURCE 1 /* for u_{char,short,int} (on Alpha OSF1) */
++#define __EXTENSIONS__ 1 /* for u_{char,short,int} (on Sparc Solaris) */
++
++#if SIZEOF_UNSIGNED_SHORT == 4
++typedef unsigned short tac_uint32;
++#else
++#if SIZEOF_UNSIGNED_INT == 4
++typedef unsigned int tac_uint32;
++#else
++#if SIZEOF_UNSIGNED_LONG == 4
++typedef unsigned long tac_uint32;
++#else
++#error "Unable to find 32-bit unsigned int for TAC_UINT32 type"
++#endif /* SIZEOF_UNSIGNED_LONG */
++#endif /* SIZEOF_UNSIGNED_INT */
++#endif /* SIZEOF_UNSIGNED_SHORT */
++
++
++#endif /* TAC_PLUS_CONFIG_H */
+diff --git a/acct.c b/acct.c
+index 315064f..2145cef 100644
+--- a/acct.c
++++ b/acct.c
+@@ -17,18 +17,37 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include <stdlib.h>
++#include <netinet/in.h> /* for ntohl() */
++
++#include "acct.h"
++#include "report.h"
++#include "packet.h"
++#include "utils.h"
++#include "do_acct.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++#include "cfgfile.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++#ifdef DB
++#include "db.h"
++#endif
++
++
++static void account TAC_ARGS((u_char *pak));
++
++
+ /*
+ * Come here when we receive an Start Accounting packet
+ */
+
+-void account();
+-
+-/* For DB accounting */
+-#ifdef DB
+-int db_acct();
+-#endif /* DB */
++void accounting TAC_ARGS((u_char *pak));
+
+ void
+ accounting(pak)
+@@ -37,8 +56,8 @@ u_char *pak;
+ struct acct *acct_pak;
+ u_char *p;
+ HDR *hdr;
+- u_char *read_packet();
+- int i, len;
++ int i;
++ unsigned long len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "Start accounting request");
+@@ -59,7 +78,7 @@ u_char *pak;
+ len += p[i];
+ }
+
+- if (len != ntohl(hdr->datalength)) {
++ if (len != (unsigned long) ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_ACCT, NULL);
+ return;
+ }
+@@ -69,7 +88,9 @@ u_char *pak;
+ free(pak);
+ }
+
+-void
++static void account TAC_ARGS((u_char *pak));
++
++static void
+ account(pak)
+ u_char *pak;
+ {
+@@ -123,6 +144,8 @@ u_char *pak;
+
+ identity.priv_lvl = acct_pak->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ rec.identity = &identity;
+
+ /* Now process cmd args */
+diff --git a/acct.h b/acct.h
+new file mode 100644
+index 0000000..0640ae6
+--- /dev/null
++++ b/acct.h
+@@ -0,0 +1,12 @@
++#ifndef ACCT_H
++#define ACCT_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++extern void accounting TAC_ARGS((u_char *pak));
++
++
++#endif /* ACCT_H */
+diff --git a/authen.c b/authen.c
+index a12745e..7c873fe 100644
+--- a/authen.c
++++ b/authen.c
+@@ -17,16 +17,43 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
+-static int choose();
+-static void authenticate();
+-static void do_start();
++#include <stdlib.h>
++#include <netinet/in.h> /* for ntohl() */
++
++#include "authen.h"
++#include "packet.h"
++#include "report.h"
++#include "utils.h"
++#include "choose_authen.h"
++#include "do_author.h" /* for "struct identity" */
++#include "main.h"
++#include "cfgfile.h"
++
++#ifdef TCPWRAPPER
++#include "tcpwrap.h"
++#endif
++
++
++static void do_start TAC_ARGS((u_char *pak));
++static int choose TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++static void authenticate TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
++
++/* Configurable:
++ */
++
++#define TAC_PLUS_MAX_ITERATIONS 50
++
+
+ /*
+ * Come here when we receive an authentication START packet
+ */
+
++void authen TAC_ARGS((u_char *pak));
++
+ void
+ authen(pak)
+ u_char *pak;
+@@ -39,9 +66,9 @@ u_char *pak;
+ start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
+
+ if ((hdr->seq_no != 1) ||
+- (ntohl(hdr->datalength) != TAC_AUTHEN_START_FIXED_FIELDS_SIZE +
++ ((unsigned long) ntohl(hdr->datalength) != (unsigned long)(TAC_AUTHEN_START_FIXED_FIELDS_SIZE +
+ start->user_len + start->port_len + start->rem_addr_len +
+- start->data_len)) {
++ start->data_len))) {
+ send_authen_error("Invalid AUTHEN/START packet (check keys)");
+ return;
+ }
+@@ -65,6 +92,8 @@ u_char *pak;
+ * attempt to authenticate.
+ */
+
++static void do_start TAC_ARGS((u_char *pak));
++
+ static void
+ do_start(pak)
+ u_char *pak;
+@@ -109,6 +138,8 @@ u_char *pak;
+
+ identity.priv_lvl = start->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ /* The authen_data structure */
+
+ bzero(&authen_data, sizeof(struct authen_data));
+@@ -184,6 +215,8 @@ send_authen_error("You are not allowed to access here");
+ /* 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 TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
+ static int
+ choose(datap, typep)
+ struct authen_data *datap;
+@@ -293,6 +326,8 @@ struct authen_type *typep;
+ /* NOTREACHED */
+ }
+
++static void authenticate TAC_ARGS((struct authen_data *datap, struct authen_type *typep));
++
+ /* Perform authentication assuming we have successfully chosen an
+ authentication method */
+ static void
+@@ -303,7 +338,7 @@ struct authen_type *typep;
+ int iterations = 0;
+ u_char *reply, *p;
+ struct authen_cont *cont;
+- int (*func) ();
++ int (*func) TAC_ARGS((struct authen_data *data));
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "Calling authentication function");
+@@ -469,4 +504,3 @@ struct authen_type *typep;
+ /* NOTREACHED */
+ }
+ }
+-
+diff --git a/authen.h b/authen.h
+new file mode 100644
+index 0000000..d89d9cc
+--- /dev/null
++++ b/authen.h
+@@ -0,0 +1,12 @@
++#ifndef AUTHEN_H
++#define AUTHEN_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++extern void authen TAC_ARGS((u_char *pak));
++
++
++#endif /* AUTHEN_H */
+diff --git a/author.c b/author.c
+index a9f2277..7a05db5 100644
+--- a/author.c
++++ b/author.c
+@@ -17,12 +17,27 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include <stdlib.h>
++#include <netinet/in.h> /* for ntohl() */
++
++#include "author.h"
++#include "report.h"
++#include "packet.h"
++#include "utils.h"
++#include "do_author.h"
++#include "main.h"
++#include "cfgfile.h"
++
++
+ /*
+ * Come here when we receive an authorization START packet
+ */
+
++void author TAC_ARGS((u_char *pak));
++
+ void
+ author(pak)
+ u_char *pak;
+@@ -34,7 +49,8 @@ u_char *pak;
+ u_char *p;
+ u_char *argsizep;
+ char **cmd_argp;
+- int i, len;
++ int i;
++ unsigned long len;
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Start authorization request");
+@@ -58,7 +74,7 @@ u_char *pak;
+ len += p[i];
+ }
+
+- if (len != ntohl(hdr->datalength)) {
++ if (len != (unsigned long) ntohl(hdr->datalength)) {
+ send_error_reply(TAC_PLUS_AUTHOR, NULL);
+ return;
+ }
+@@ -95,6 +111,8 @@ u_char *pak;
+
+ identity.priv_lvl = apak->priv_lvl;
+
++ cfg_request_identity(&identity);
++
+ /* The author_data structure */
+
+ author_data.id = &identity; /* user id */
+diff --git a/author.h b/author.h
+new file mode 100644
+index 0000000..0e3a2ce
+--- /dev/null
++++ b/author.h
+@@ -0,0 +1,12 @@
++#ifndef AUTHOR_H
++#define AUTHOR_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++extern void author TAC_ARGS((u_char *pak));
++
++
++#endif /* AUTHOR_H */
+diff --git a/autogen b/autogen
+new file mode 100755
+index 0000000..83862fd
+--- /dev/null
++++ b/autogen
+@@ -0,0 +1,47 @@
++#! /bin/sh
++#
++# autogen
++#
++# Copyright (C) 1999, 2000, 2001
++# Partition Surprise Team <surprise-dev@lists.sourceforge.net>
++#
++# $Id$
++#
++
++
++# Run this to generate all the initial makefiles, etc.
++
++set -e
++if test "x$1" = "xBASH" ;then
++ shift
++else if test "x$BASH" = "x" ;then
++ for trypath in `echo "$PATH"|tr : ' '` /usr/local/bin;do
++ if test -x "$trypath/bash";then
++ "$trypath/bash" "$0" BASH "$@"
++ exit $?
++ fi
++ done
++ echo "ERROR: Unable to find 'bash' interpreter needed for self-execution!"
++ exit 1
++fi;fi
++
++defaultCONFDEFS="" # --enable-debug --without-efence
++project="tac_plus"
++automake_gnu=false
++want_tarZ=false
++want_gettext=false
++want_libtool=false
++#upload="vellum.cz:WWW/sw/"
++docdir=""
++subdirs=""
++
++CLEAN_LOCAL="
++ .print_userprogs
++ tac_plus
++ generate_passwd
++"
++
++ARGS_HELP_LOCAL="\
++"
++
++source ./autogen-body
+diff --git a/autogen-body b/autogen-body
+new file mode 100644
+index 0000000..6856444
+--- /dev/null
++++ b/autogen-body
+@@ -0,0 +1,259 @@
++#
++# autogen-body
++#
++# Placed into the public domain by
++# Partition Surprise Team <surprise-dev@lists.sourceforge.net>
++#
++# $Id$
++#
++
++
++# Executable code to be included from ./autogen script
++
++# Expected variables:
++# defaultCONFDEFS, project, want_tarZ, upload, docdir, subdirs, CLEAN_LOCAL
++# function PREP_LOCAL
++
++set -e
++t=/tmp/autogen.$$
++autogen_failed=true
++cleaup_dir="$PWD"
++automake_reqd=""
++function cleanup
++{
++ cd "$cleanup_dir"
++ rm -f "$t.*"
++ if $automake_gnu;then for i in $automake_reqd;do if [ '!' -s "$i" ];then rm -f "$i";fi;done;fi
++}
++EXITmsg_do=true
++trap '
++ cleanup
++ if $autogen_failed;then
++ if $EXITmsg_do;then
++ echo -e "\n$0 failed! Try the following command to debug it: set -x"
++ EXITmsg_do=false
++ fi
++ exit 1
++ fi
++ ' EXIT
++
++if [ "$1" = help -o "$1" = -h -o "$1" = --help ];then cat <<EOHELP
++Beware!: "autogen" is a tool only for maintainers.
++
++Supported parameters:
++ rpm: Build RPM packages locally (needs /usr/src/(redhat|packages)/ access)
++ rpmtest: Build RPM like "rpm" but w/o gpg/pgp signing
++ rpmup: Like rpm target but also uploads the result
++ tar: Build tar file (its basename dir-based) with only vital files incl. CVS dirs
++ clean: Standard cleanup method
++ fullclean: Like clean but even the .cvsignore files are removed
++ sym: Like clean but don't remove symbolic links, should be equal (check!)
++ copy: Behave exactly like in default mode but copy all instead of symlinks
++$ARGS_HELP_LOCAL
++EOHELP
++ autogen_failed=false
++ exit
++ fi
++
++
++if [ -f "$HOME/.$project.autogen" ];then
++ . "$HOME/.$project.autogen"
++ fi
++CONFDEFS="$defaultCONFDEFS $CONFDEFS"
++
++function funcdo
++{
++local func="$1";
++
++ shift
++ if declare -f "$func" >/dev/null;then
++local func_exit=false
++
++ "$func" "$@"
++ if $func_exit;then exit;fi
++ fi
++}
++funcdo ARGS_LOCAL "$@"
++
++if expr match "$1" "rpm" >/dev/null;then
++ builds="/usr/src/redhat /usr/src/packages"
++ for b in $builds X;do
++ if test -d $b;then break;fi
++ done
++ if [ $b = X ];then
++ echo Build directory not reachable, searched: $builds
++ exit 1
++ fi
++ rm -r -f /var/tmp/$project-*-root $b/BUILD/$project-*
++ specsrc="$project.spec.m4.in"
++ if [ '!' -f "$specsrc" ];then specsrc="$project.spec.in";fi
++ CONFDEFS="`awk '/^(.*)\\$/{x=x$1" ";next}{print x$0;x=""}' <$specsrc \
++ |sed -n 's/^.*\.\/configure \(.*\)$/\1/p'`" ./autogen copy
++ make dist $project.spec
++ cp $project-*.tar.gz $b/SOURCES
++ if [ "$1" = "rpmtest" ];then SIGNIT=;else SIGNIT=--sign;fi
++ rpm -ba $SIGNIT $project.spec
++ if $want_tarZ;then make dist-tarZ;fi
++ rm $b/SOURCES/$project-*.tar.gz
++ mv $b/SRPMS/$project-*.src.rpm .
++ mv $b/RPMS/i386/$project-*.i386.rpm .
++ ls -l $project-*
++ if [ "$1" = rpmup ];then
++ echo "Uploading $[`cat $project-*|wc -c`] bytes..."
++ if [ -n "$upload" ];then
++ scp -v $project-* "$upload"
++ else
++ echo "Upload not done."
++ fi
++ fi
++ autogen_failed=false
++ exit
++fi
++
++function subdo
++{
++ for i in _ $subdirs;do
++ if [ -d $i ];then
++ cd "$i"
++ ./autogen $subdir_args
++ cd ..
++ fi
++ done
++}
++subdir_args="${*:-dist}"
++if [ "$subdir_args" = "copy" ];then
++ subdir_args="copy dist"
++ fi
++
++# maintainer-clean hack is not safe, please list all files for 'rm'.
++# When the filename doesn't contain '/', it is applied to ALL directories.
++# Please note that files exactly in root dir MUST have ./ in the front
++# (to not to be considered as ALL-directories files).
++
++CLEANFILES="
++ *~ .#*
++ *.orig *.rej
++ core
++ Makefile Makefile.in
++ TAGS tags ID
++ .deps .libs
++ *.[oa] *.l[oa]
++
++ ./errs*
++ ./intl
++ ./configure ./configure.scan
++ ./config.guess ./config.status ./config.sub ./config.log ./config.cache
++ ./config.h ./config.h.in
++ ./confdefs.h ./conftest* ./autoh[0-9]* ./confcache
++ ./stamp-h ./stamp-h.in
++ ./install-sh
++ ./aclocal.m4
++ ./missing
++ ./mkinstalldirs
++ ./libtool ./ltconfig ./ltmain.sh
++ ./ChangeLog
++ ./ABOUT-NLS ./COPYING
++ ./$project-[0-9]* ./$project-devel-[0-9]*
++ ./$project.spec ./$project.m4 ./$project.spec.m4
++ macros/macros.dep
++ po/Makefile.in.in po/POTFILES* po/cat-id-tbl.c po/cat-id-tbl.tmp po/*.gmo po/*.mo po/stamp-cat-id po/$project.pot
++
++ $CLEAN_LOCAL
++ "
++if [ -n "$docdir" ];then
++CLEANFILES="
++ $docdir/*.html
++ $docdir/*.info*
++ $docdir/*.txt
++ $docdir/*.tex
++ $docdir/*.sgml
++ $CLEANFILES"
++ fi
++if [ "$1" != sym ];then CLEANFILES="$CLEANFILES `find . -type l`";fi
++CLEANFILES="`echo "$CLEANFILES"|tr ' ' '\n'|sed 's,^\./\(.*/.*\)$,\1,'|sort|uniq|grep -v '^ *$'`"
++true >"$t.find"
++rm -f "$t.discard"
++echo "$CLEANFILES"|while read -r fi;do
++ if [ "$fi" != "${fi#*/}" ];then
++ echo "$fi" >>"$t.discard"
++ else echo "-o -name $fi" >>"$t.find"
++ fi;done
++for dirpatt in `(find . -type d '!' \( -name CVS $(sed 's,[]*?[],\\&,g' <"$t.find") \)|sort|uniq;cat "$t.discard")|sort|uniq -u`;do
++ for dir in $dirpatt;do if test -d $dir;then
++ (cd $dir
++ (echo "$CLEANFILES" #ALL-dir files
++ echo "$CLEANFILES"|sed -n "s,^\\$(echo $dir|sed 's,^\./,,')/,,p" #THIS-dir files
++ echo .cvsignore #MUST be last!
++ )|grep -v / >.cvsignore
++ if [ "$1" = "${1#tar}" ];then
++ rm -rf `
++ if [ "$1" = fullclean ];then cat .cvsignore
++ else grep -v '^\.cvsignore' <.cvsignore
++ fi`
++ fi
++ )
++ fi;done
++ done
++rm -f "$t.find" "$t.discard"
++if [ "$1" = tarprep ];then
++ autogen_failed=false
++ exit
++ fi
++if [ "$1" = tar ];then
++ subdir_args=tarprep
++ subdo
++ mydir="$(basename `pwd`)"
++ cd ..
++ tar cf - \
++ $(for fi in `find "$mydir" -name .cvsignore`;do sed "s,^,--exclude=`dirname $fi`/," <$fi;done) \
++ "$mydir"
++ autogen_failed=false
++ exit
++ fi
++if [ "$1" != "${1#*clean}" ];then
++ subdo
++ autogen_failed=false
++ exit 0
++ fi
++
++funcdo PREP_LOCAL "$@"
++subdo
++
++if [ "$1" = copy ];then COPY=--copy;shift
++else unset COPY|cat # |cat construction is used to not fail in "set -e" state
++fi
++
++if test -d po;then
++ touch po/POTFILES.in
++fi
++aclocal_opts=""
++if test -d macros;then
++ aclocal_opts="-I macros $aclocal_opts"
++fi
++aclocal $aclocal_opts
++if $want_gettext;then
++ gettextize $COPY
++ rm -f aclocal.m4 # We delete created aclocal.m4 as it's just bug in gettextize. It shouldn't link that file here
++ aclocal $aclocal_opts # gettextize made some changes of files which need to be reflected
++fi
++if $want_libtool;then
++ libtoolize $COPY
++fi
++autoheader
++automake_opts=""
++if $automake_gnu;then
++ automake_reqd="$automake_reqd ChangeLog README"
++ automake_opts="$automake_opts --gnu"
++ for i in $automake_reqd;do if [ '!' -f "$i" ];then touch "$i";fi;done
++fi
++automake --add-missing $COPY $automake_opts
++cleanup
++autoheader
++autoconf
++
++if [ "$1" != dist ];then
++ # shared/static switching cannot be based on maintainer-mode in configure
++ ./configure --enable-maintainer-mode --enable-shared --disable-static $CONFDEFS
++ fi
++
++autogen_failed=false
+diff --git a/cfgeval.c b/cfgeval.c
+new file mode 100644
+index 0000000..f0aed72
+--- /dev/null
++++ b/cfgeval.c
+@@ -0,0 +1,1900 @@
++/*
++ * ???():
++ * If you belong to the group where do you belong only in the case you belong
++ * in that group, do you in fact belong to that group or don't?
++ * I've decided it is safer to decide that you DON'T belong to such group.
++ *
++ * expr_eval_notify_expr_remove():
++ * If someone was interested in what am I like but she is no longer
++ * interested in what am I like, because she already knows what is she like,
++ * then she should tell it me, as although I still may not know what am I
++ * like then in the case she was the last who wanted to know what am I like,
++ * I should tell everyone who I didn't know what they are like and I wanted
++ * to know what they are like that I no longer want to know what they are
++ * like, because it is no longer needed to know what am I like about myself.
++ *
++ * membership_eval_immediate():
++ * It is important to not only know, what do you want to know, but it is also
++ * important to know what do you now know but you still didn't utilize it.
++ */
++
++
++#include "tac_plus.h"
++
++#include <stdlib.h>
++
++#include "cfgeval.h"
++#include "cfgfile.h"
++#include "main.h"
++#include "parse.h"
++#include "report.h"
++#include "hash.h"
++
++
++/* Configurable:
++ */
++
++/* Whether to do sanity checks */
++#define SCAN_PARANOIA 1
++
++/* report even no-op scan up-to-date checks */
++#define REPORT_CHECK_SCAN_VERBOSE 0
++
++
++static void check_request_scan_membership TAC_ARGS((struct membership *membership, int flush));
++static void check_eval_scan_membership TAC_ARGS((struct membership *membership, int flush));
++static void check_eval_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++static void check_request_scan_expr TAC_ARGS((struct expr *expr, int flush));
++static void check_eval_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++
++static unsigned request_scan_seq = 0;
++static unsigned value_scan_seq = 0;
++static unsigned eval_scan_seq = 0;
++int request_scan_user_known;
++static struct tac_list eval_kicked_entity_list;
++static struct tac_list eval_destroy_entity_list;
++static struct tac_list eval_notified_expr_list;
++static struct tac_list request_virtual_membership_list;
++
++
++/* 'P[FA]_*' object printing section:
++ */
++
++static const char *eval_result_to_string TAC_ARGS((int valid, enum eval_result er));
++
++const char *
++eval_result_to_string(valid, er)
++int valid;
++enum eval_result er;
++{
++ if (!valid)
++ return ("!UPDATED");
++
++ switch (er) {
++ case ER_TRUE: return ("ER_TRUE" );
++ case ER_FALSE: return ("ER_FALSE" );
++ case ER_UNKNOWN: return ("ER_UNKNOWN" );
++ default: return ("*** INVALID ***");
++ }
++ /* NOTREACHED */
++}
++
++static const char *value_scan_func_result_to_string TAC_ARGS((enum value_scan_func_result vsfr));
++
++const char *
++value_scan_func_result_to_string(vsfr)
++enum value_scan_func_result vsfr;
++{
++ switch (vsfr) {
++ case VSFR_CONTINUE: return ("VSFR_CONTINUE");
++ case VSFR_FOUND: return ("VSFR_FOUND" );
++ case VSFR_STOP: return ("VSFR_STOP" );
++ default: return ("*** INVALID ***");
++ }
++ /* NOTREACHED */
++}
++
++/* These macros are VERY big overhead but it's primary negative effect
++ * is the size of executable. I've considered it as not much interesting,
++ * CPU overhead is hit only when DEBUG_CFGEVAL_FLAG (also not interesting).
++ */
++
++#define PF_VSFR "%s"
++#define PA_VSFR(vsfr) (value_scan_func_result_to_string((vsfr)))
++
++#define PF_RESULT "%s"
++#define PA_RESULT(result) (eval_result_to_string(1 /* valid */, (result)))
++
++#define PF_ERESULT_struct PF_RESULT
++#define PA_ERESULT_struct(entity, field) \
++ (!(entity) ? "*NULL*(=ER_TRUE)" : \
++ (eval_result_to_string((entity)->request_scan.seq == request_scan_seq, (entity)->request_scan.field)))
++#define PA_ERESULT_struct_NULL "*NULL*"
++
++#define PF_ERESULT_EXPR PF_ERESULT_struct
++#define PA_ERESULT_EXPR(expr) PA_ERESULT_struct((expr), result)
++#define PF_ERESULT_ENTITY PF_ERESULT_struct
++#define PA_ERESULT_ENTITY(entity) PA_ERESULT_struct((entity), belongs)
++#define PF_ERESULT_MEMBERSHIP PF_ERESULT_struct
++#define PA_ERESULT_MEMBERSHIP(membership) (!(membership) ? PA_ERESULT_struct_NULL : PA_ERESULT_EXPR((membership)->when))
++
++#define PF_EXPR_ "expr@%d{%s " PF_MEMBERSHIP "}"
++#define PA_EXPR_(expr) (!(expr) ? 0 : (expr)->line), \
++ (!(expr) ? "*NULL*" : "of"), \
++ PA_MEMBERSHIP((!(expr) ? NULL : (expr)->membership))
++
++#define PF_MEMBERSHIP_ "membership{child=" PF_ENTITY ",parent=" PF_ENTITY "}"
++#define PA_MEMBERSHIP_(membership) PA_ENTITY((!(membership) ? NULL : MEMBERSHIP_TO_CHILD_ENTITY( (membership)))), \
++ PA_ENTITY((!(membership) ? NULL : MEMBERSHIP_TO_PARENT_ENTITY((membership))))
++
++#define PF_ENTITY_ "{%s@%d \"%s\"}"
++#define PA_ENTITY_(entity) (!(entity) ? "*NULL* entity" : entity_type_to_string((entity)->type)), \
++ (!(entity) ? 0 : (entity)->line), \
++ (!(entity) ? "*NULL*" : (entity)->name)
++
++#define PF_EXPR PF_EXPR_ "=" PF_ERESULT_EXPR
++#define PA_EXPR(expr) PA_EXPR_(expr), PA_ERESULT_EXPR(expr)
++#define PF_MEMBERSHIP PF_MEMBERSHIP_ "=" PF_ERESULT_MEMBERSHIP
++#define PA_MEMBERSHIP(membership) PA_MEMBERSHIP_(membership), PA_ERESULT_MEMBERSHIP(membership)
++#define PF_ENTITY PF_ENTITY_ "=" PF_ERESULT_ENTITY
++#define PA_ENTITY(entity) PA_ENTITY_(entity), PA_ERESULT_ENTITY(entity)
++
++
++/* '{unlink,free}_*()' section:
++ */
++
++void unlink_expr TAC_ARGS((struct expr *expr));
++
++/* never depend on "->parent" as it may not yet been filled! */
++void
++unlink_expr(expr)
++struct expr *expr;
++{
++ if (!expr)
++ return; /* prevent possible DEBUG_CLEAN_FLAG report */
++
++ if (debug & DEBUG_CLEAN_FLAG) {
++ if (expr->membership)
++ report(LOG_DEBUG, "unlink_expr: (of membership): " PF_EXPR,
++ PA_EXPR(expr));
++ else
++ report(LOG_DEBUG, "unlink_expr: (standalone)");
++ }
++
++ while (expr) {
++
++ /* We need to at least unlink "eval_scan->u.entity.notify_expr_node": */
++ check_request_scan_expr(expr, 1); /* invalidate */
++ check_eval_scan_expr(expr, 1); /* invalidate */
++
++ switch (expr->type) {
++
++ case S_not:
++ unlink_expr(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ unlink_expr(expr->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for unlink_expr", expr->type);
++ return;
++ }
++
++ expr = expr->next;
++ }
++}
++
++void free_expr TAC_ARGS((struct expr *expr));
++
++/* given 'expr' memory WILL be freed! */
++/* never depend on "->parent" as it may not yet been filled! */
++void
++free_expr(expr)
++struct expr *expr;
++{
++struct expr *expr_next;
++
++ if (!expr)
++ return; /* prevent possible DEBUG_CLEAN_FLAG report */
++
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "free_expr: (may be unlinked)");
++
++ while (expr) {
++ expr_next = expr->next;
++ switch (expr->type) {
++
++ case S_not:
++ free_expr(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ free_expr(expr->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ if (expr->u.entity.name)
++ free((/* de-const */ char *)expr->u.entity.name);
++ /* "expr->u.entity.entity" will be freed from elsewhere */
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for free_expr", expr->type);
++ return;
++ }
++
++ free(expr);
++ expr=expr_next;
++ }
++}
++
++static void unlink_membership TAC_ARGS((struct membership *membership));
++
++static void
++unlink_membership(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "unlink_membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ /* 'unlink_expr()' may want a lot of existing (linked) resources */
++ unlink_expr(membership->when);
++
++ check_request_scan_membership(membership, 1); /* invalidate */
++ check_eval_scan_membership(membership, 1); /* invalidate */
++
++#ifdef SCAN_PARANOIA
++ if (!parent_entity->to_child_membership_num) {
++ report(LOG_ERR, "INTERNAL: to_child_membership_num-- == 0 in unlink_membership");
++ parent_entity->to_child_membership_num++;
++ }
++#endif
++ parent_entity->to_child_membership_num--;
++
++ tac_list_node_remove(&membership->parent_node);
++ tac_list_node_remove(&membership-> child_node);
++}
++
++/* given 'membership' memory WILL be freed! */
++
++static void free_membership TAC_ARGS((struct membership *membership));
++
++static void
++free_membership(membership)
++struct membership *membership;
++{
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "free_membership: (may be unlinked)");
++
++ free_expr(membership->when);
++ free(membership);
++}
++
++/* we are not allowed to free memory here, we are only 'scan_' additional 'free' */
++
++void scan_free_entity TAC_ARGS((ENTITY *entity));
++
++void
++scan_free_entity(entity)
++ENTITY *entity;
++{
++ struct tac_list_node *parent_membership_node, *next_parent_membership_node;
++ struct membership *parent_membership;
++ struct tac_list_node *child_membership_node, *next_child_membership_node;
++ struct membership *child_membership;
++
++ if (debug & DEBUG_CLEAN_FLAG)
++ report(LOG_DEBUG, "scan_free_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ /* Be careful to keep '->next' ptr before we destroy the structure! */
++
++ for (
++ parent_membership_node = tac_list_first_node(&entity->to_child_membership_list);
++ parent_membership_node;
++ parent_membership_node = next_parent_membership_node
++ ) {
++ parent_membership = PARENT_NODE_TO_MEMBERSHIP(parent_membership_node);
++ next_parent_membership_node = tac_list_node_next(parent_membership_node);
++ unlink_membership(parent_membership);
++ free_membership(parent_membership);
++ }
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ child_membership_node;
++ child_membership_node = next_child_membership_node
++ ) {
++ child_membership = CHILD_NODE_TO_MEMBERSHIP(child_membership_node);
++ next_child_membership_node = tac_list_node_next(child_membership_node);
++ unlink_membership(child_membership);
++ free_membership(child_membership);
++ }
++}
++
++struct expr *new_expr TAC_ARGS((int type));
++
++struct expr *
++new_expr(type)
++int type;
++{
++ struct expr *expr;
++
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->next = NULL;
++ expr->type = type;
++ switch (expr->type) {
++
++ case S_not:
++ expr->u.not.child = NULL;
++ break;
++
++ case S_and:
++ case S_or:
++ expr->u.and_or.child_first = NULL;
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr->u.entity.entity = NULL;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for new_expr", expr->type);
++ return (expr); /* it would be probably lethal to return NULL */
++ }
++ return (expr);
++}
++
++static int expr_sink_internal TAC_ARGS((struct expr *expr, struct membership *membership, struct expr *parent));
++
++static int
++expr_sink_internal(expr, membership, parent)
++struct expr *expr;
++struct membership *membership;
++struct expr *parent;
++{
++ for (;expr; expr=expr->next) {
++ expr->membership = membership;
++ expr->parent = parent;
++ expr->request_scan.seq = request_scan_seq-1;
++ expr-> eval_scan.seq = eval_scan_seq-1;
++ switch (expr->type) {
++
++ case S_not:
++ if (expr_sink_internal(expr->u.not.child, membership, expr /* parent */))
++ return (1);
++ break;
++
++ case S_and:
++ case S_or:
++ if (expr_sink_internal(expr->u.and_or.child_first, membership, expr /* parent */))
++ return (1);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ tac_list_node_init(&expr->eval_scan.u.entity.notify_expr_node);
++ expr->u.entity.entity = entity_lookup(expr->type, expr->u.entity.name);
++ if (!expr->u.entity.entity && expr->type == S_host) {
++ expr->u.entity.entity = new_entity(expr->type, (char *)expr->u.entity.name, expr->line);
++ if (!expr->u.entity.entity)
++ return (1);
++ expr->u.entity.name = NULL;
++ }
++ if (!expr->u.entity.entity) {
++ report(LOG_ERR, "referenced entity %s %s not found on line %d",
++ entity_type_to_string(expr->type), expr->u.entity.name, expr->line);
++ return (1);
++ }
++ /* already NULLed for not-yet-existing S_host */
++ free((char *) expr->u.entity.name);
++ expr->u.entity.name = NULL;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for expr_sink", expr->type);
++ return (1);
++ }
++ }
++ return (0);
++}
++
++static int expr_sink TAC_ARGS((struct expr *expr, struct membership *membership));
++
++static int
++expr_sink(expr, membership)
++struct expr *expr;
++struct membership *membership;
++{
++ return (expr_sink_internal(expr, membership, NULL /* parent */));
++}
++
++static struct expr *expr_sink_register_head = NULL;
++
++void expr_sink_register TAC_ARGS((struct expr *expr));
++
++void
++expr_sink_register(expr)
++struct expr *expr;
++{
++ if (!expr)
++ return;
++
++ expr->parent = expr_sink_register_head;
++ expr_sink_register_head = expr;
++}
++
++int expr_sink_commit TAC_ARGS((void));
++
++int
++expr_sink_commit()
++{
++ struct expr *expr;
++
++ while ((expr = expr_sink_register_head)) {
++ expr_sink_register_head = expr->parent;
++ /* 'expr->parent' not defined for 'expr_sink()' */
++
++ if (expr_sink(expr, NULL /* membership */))
++ return (1); /* failure */
++ }
++ return (0); /* success */
++}
++
++struct expr *dupl_expr TAC_ARGS((const struct expr *in));
++
++struct expr *
++dupl_expr(in)
++const struct expr *in;
++{
++ struct expr *expr_root = NULL;
++ struct expr **succ_exprp = &expr_root;
++ struct expr *expr;
++
++ for (;in; in=in->next) {
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = in->line;
++ expr->next = NULL;
++ expr->type = in->type;
++ switch (in->type) {
++
++ case S_not:
++ if (in->u.not.child && in->u.not.child->type==S_not) {
++ free(expr);
++ expr = dupl_expr(in->u.not.child->u.not.child);
++ } else
++ expr->u.not.child = dupl_expr(in->u.not.child);
++ break;
++
++ case S_and:
++ case S_or:
++ if (!in->u.and_or.child_first) {
++ free(expr);
++ continue;
++ } else if (!in->u.and_or.child_first->next) {
++ free(expr);
++ expr = dupl_expr(in->u.and_or.child_first);
++ } else
++ expr->u.and_or.child_first = dupl_expr(in->u.and_or.child_first);
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ if (in->u.entity.name)
++ expr->u.entity.name = tac_strdup(in->u.entity.name);
++ else
++ expr->u.entity.name = NULL;
++ expr->u.entity.entity = in->u.entity.entity;
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for dupl_expr", in->type);
++ free_expr(expr_root);
++ return (NULL);
++ }
++
++ *succ_exprp = expr;
++ succ_exprp = &expr->next;
++ }
++ return (expr_root);
++}
++
++
++/* 'check_*_scan_*()' section:
++ */
++
++static void check_request_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++static void
++check_request_scan_expr(expr, flush)
++struct expr *expr;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_expr: " PF_EXPR " (" PF_ERESULT_EXPR ")",
++ PA_EXPR(expr), PA_ERESULT_EXPR(expr));
++#endif
++
++ if (!flush && expr->request_scan.seq == request_scan_seq)
++ return; /* up to date */
++
++ expr->request_scan.result = ER_UNKNOWN;
++ expr->request_scan.loop_reported = 0;
++ expr->request_scan.seq = request_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_expr: done: " PF_EXPR,
++ PA_EXPR(expr));
++}
++
++static void check_eval_scan_expr TAC_ARGS((struct expr *expr, int flush));
++
++static void
++check_eval_scan_expr(expr, flush)
++struct expr *expr;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_expr: " PF_EXPR,
++ PA_EXPR(expr));
++#endif
++
++ if (!flush && expr->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_request_scan_expr(expr, 0);
++
++ switch (expr->type) {
++
++ case S_user:
++ case S_host:
++ case S_group: {
++#ifdef SCAN_PARANOIA
++ if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node) == &eval_notified_expr_list) {
++ report(LOG_ERR, "INTERNAL: expr still connected to eval_notified_expr_list in check_eval_scan_expr");
++ } else if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node)) {
++ ENTITY *notifying_entity = EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr);
++
++ if (notifying_entity != expr->u.entity.entity)
++ report(LOG_ERR, "INTERNAL: expr->notify_expr_node->list != expr->entity");
++ if (notifying_entity->eval_scan.seq != expr->eval_scan.seq)
++ report(LOG_ERR, "INTERNAL: entity seq != expr node seq");
++ tac_list_node_remove(&expr->eval_scan.u.entity.notify_expr_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_node_init(&expr->eval_scan.u.entity.notify_expr_node);
++#endif /* SCAN_PARANOIA */
++ } break;
++ }
++
++ expr->eval_scan.seq = eval_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_expr: done: " PF_EXPR,
++ PA_EXPR(expr));
++}
++
++static void check_request_scan_membership TAC_ARGS((struct membership *membership, int flush));
++
++static void
++check_request_scan_membership(membership, flush)
++struct membership *membership;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_membership: " PF_MEMBERSHIP " (" PF_ERESULT_MEMBERSHIP ")",
++ PA_MEMBERSHIP(membership), PA_ERESULT_MEMBERSHIP(membership));
++#endif
++
++ if (!flush && membership->request_scan.seq == request_scan_seq)
++ return; /* up to date */
++
++#ifdef SCAN_PARANOIA
++ {
++ struct tac_list *virtual_list = tac_list_node_get_list(&membership->request_scan.virtual_membership_node);
++
++ if (virtual_list && virtual_list != &request_virtual_membership_list)
++ report(LOG_ERR, "Illegal list in membership->virtual_membership_node.list");
++ if (virtual_list)
++ tac_list_node_remove(&membership->request_scan.virtual_membership_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_node_init(&membership->request_scan.virtual_membership_node);
++#endif /* SCAN_PARANOIA */
++
++ membership->request_scan.seq = request_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_membership: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++/* we are cross-checking (membership<->parent entity)! */
++
++static void check_eval_scan_membership TAC_ARGS((struct membership *membership, int flush));
++
++static void
++check_eval_scan_membership(membership, flush)
++struct membership *membership;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++#endif
++
++ if (!flush && membership->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_request_scan_membership(membership, 0);
++
++ membership->eval_scan.unsolved = 1;
++ membership->eval_scan.seq = eval_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_membership: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++static void check_request_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_request_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_entity: " PF_ENTITY " (" PF_ERESULT_ENTITY ")",
++ PA_ENTITY(entity), PA_ERESULT_ENTITY(entity));
++#endif
++
++ if (!flush && entity->request_scan.seq == request_scan_seq)
++ return;
++
++ entity->request_scan.belongs = ER_UNKNOWN;
++ entity->request_scan.seq = request_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_request_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++static void check_value_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_value_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_value_scan_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++#endif
++
++ if (!flush && entity->value_scan.seq == value_scan_seq)
++ return;
++ check_request_scan_entity(entity, 0);
++
++ entity->value_scan.seen = 0;
++ entity->value_scan.from = NULL;
++ entity->value_scan.seq = value_scan_seq;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_value_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++static void check_eval_scan_entity TAC_ARGS((ENTITY *entity, int flush));
++
++static void
++check_eval_scan_entity(entity, flush)
++ENTITY *entity;
++int flush;
++{
++ struct tac_list_node *child_membership_parent_node;
++
++#if REPORT_CHECK_SCAN_VERBOSE
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_entity: " PF_ENTITY,
++ PA_ENTITY(entity));
++#endif
++
++ if (!flush && entity->eval_scan.seq == eval_scan_seq)
++ return; /* up to date */
++ check_value_scan_entity(entity, 0);
++
++ entity->eval_scan.unsolved_to_child_membership_num = entity->to_child_membership_num;
++
++ if ((child_membership_parent_node = tac_list_first_node(&entity->to_child_membership_list))) {
++ struct membership *child_membership = PARENT_NODE_TO_MEMBERSHIP(child_membership_parent_node);
++
++ entity->eval_scan.unsolved_to_child_membership_first = child_membership;
++ } else
++ entity->eval_scan.unsolved_to_child_membership_first = NULL;
++
++#ifdef SCAN_PARANOIA
++ {
++ struct tac_list_node *notify_expr_node;
++
++ while ((notify_expr_node = tac_list_first_node(&entity->eval_scan.notify_expr_list))) {
++ struct expr *notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node);
++
++ if (notify_expr->u.entity.entity != entity)
++ report(LOG_ERR, "INTERNAL: notify_expr->entity != entity");
++ if (notify_expr->eval_scan.seq != entity->eval_scan.seq)
++ report(LOG_ERR, "INTERNAL: notify_expr seq != entity seq");
++ tac_list_node_remove(notify_expr_node);
++ }
++
++ if (tac_list_node_get_list(&entity->eval_scan.pending_entity_node))
++ tac_list_node_remove(&entity->eval_scan.pending_entity_node);
++ }
++#else /* SCAN_PARANOIA */
++ tac_list_init(&entity->eval_scan.notify_expr_list);
++ tac_list_node_init(&entity->eval_scan.pending_entity_node);
++#endif /* SCAN_PARANOIA */
++
++ entity->eval_scan.seq = eval_scan_seq; /* used above, keep as LAST! */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "check_eval_scan_entity: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++
++/* invalidation managing section (for '*_scan_begin()'):
++ */
++
++/* this will happen once upon 'unsigned' overflow, ehm */
++
++#define INVALIDATE_SEQ_PTR(object,ptr) \
++ (G_STRUCT_MEMBER(unsigned, ptr, invalidate_scan_##object##_table[what]) = (unsigned) -1)
++#define INVALIDATE_SEQ(object) \
++ (INVALIDATE_SEQ_PTR(object,object))
++
++static const long invalidate_scan_expr_table[IS_MAX]={
++ G_STRUCT_OFFSET(struct expr, request_scan.seq),
++ -1,
++ G_STRUCT_OFFSET(struct expr, eval_scan.seq)};
++
++static void invalidate_scan_expr TAC_ARGS((struct expr *expr,enum invalidate_scan what));
++
++static void
++invalidate_scan_expr(expr_single, what)
++struct expr *expr_single;
++enum invalidate_scan what;
++{
++ struct expr *expr_parent, *expr_child;
++
++ if (!expr_single) {
++ report(LOG_ERR, "INTERNAL: NULL input expressions not support by invalidate_scan_expr");
++ return;
++ }
++ if (expr_single->parent) {
++ report(LOG_ERR, "INTERNAL: non-root expressions not supported by invalidate_scan_expr");
++ return;
++ }
++
++ /* TOP->DOWN scanner: */
++top_down:
++ do {
++ INVALIDATE_SEQ_PTR(expr,expr_single);
++ expr_parent = expr_single;
++
++ switch (expr_parent->type) {
++
++ case S_not:
++ expr_child = expr_parent->u.not.child;
++ continue;
++
++ case S_and:
++ case S_or:
++ expr_child = expr_parent->u.and_or.child_first;
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr_child = NULL; /* no child exists */
++ break;
++
++ default:
++ report(LOG_ERR, "Illegal child node type %d for invalidate_scan_expr", expr_parent->type);
++ return;
++ }
++ } while ((expr_single = expr_child));
++ /* expr_child==NULL, we have only expr_parent: */
++
++ expr_child = expr_parent;
++
++ /* we have only expr_child: */
++ /* BOTTOM->UP scanner */
++ do {
++ if ((expr_single = expr_child->next))
++ goto top_down;
++ expr_parent = expr_child->parent;
++ } while ((expr_child = expr_parent));
++}
++
++static const long invalidate_scan_membership_table[IS_MAX]={
++ G_STRUCT_OFFSET(struct membership, request_scan.seq),
++ -1,
++ G_STRUCT_OFFSET(struct membership, eval_scan.seq)};
++
++static void invalidate_scan_membership TAC_ARGS((struct membership *membership,enum invalidate_scan what));
++
++static void
++invalidate_scan_membership(membership, what)
++struct membership *membership;
++enum invalidate_scan what;
++{
++ INVALIDATE_SEQ(membership);
++
++ if (membership->when)
++ invalidate_scan_expr(membership->when, what);
++}
++
++static const long invalidate_scan_entity_table[IS_MAX]={
++ G_STRUCT_OFFSET(ENTITY, request_scan.seq),
++ G_STRUCT_OFFSET(ENTITY, value_scan.seq),
++ G_STRUCT_OFFSET(ENTITY, eval_scan.seq)};
++
++static void invalidate_scan_entity TAC_ARGS((ENTITY *entity,enum invalidate_scan what));
++
++static void
++invalidate_scan_entity(entity, what)
++ENTITY *entity;
++enum invalidate_scan what;
++{
++ struct tac_list_node *child_membership_node;
++ struct membership *child_membership;
++
++ INVALIDATE_SEQ(entity);
++
++ if (what==IS_VALUE) /* optimalization */
++ return;
++
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_child_membership_list);
++ child_membership_node;
++ child_membership_node = tac_list_node_next(&child_membership->parent_node)
++ ) {
++ child_membership = PARENT_NODE_TO_MEMBERSHIP(child_membership_node);
++ invalidate_scan_membership(child_membership, what);
++ }
++}
++
++void scan_invalidate_entities_hashtable TAC_ARGS((void **hashtable, enum invalidate_scan what));
++
++void
++scan_invalidate_entities_hashtable(hashtable, what)
++void **hashtable;
++enum invalidate_scan what;
++{
++ int i;
++ ENTITY *entity;
++
++ for (i = 0; i < HASH_TAB_SIZE; i++)
++ for (entity = (ENTITY *) hashtable[i]; entity; entity = entity->hash)
++ invalidate_scan_entity(entity, what);
++}
++
++/* '*_scan_begin()' section:
++ */
++
++void request_scan_begin TAC_ARGS((void));
++
++void
++request_scan_begin()
++{
++#ifdef SCAN_PARANOIA
++ static int inited = 0;
++#endif /* SCAN_PARANOIA */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "request_scan_begin:");
++
++ request_scan_user_known = 0;
++
++ if (!++request_scan_seq)
++ scan_invalidate_entities(IS_REQUEST);
++
++#ifdef SCAN_PARANOIA
++ if (!inited) {
++#endif /* SCAN_PARANOIA */
++ tac_list_init(&request_virtual_membership_list);
++#ifdef SCAN_PARANOIA
++ inited = 1;
++ } else {
++ struct tac_list_node *virtual_membership_node;
++
++ while ((virtual_membership_node = tac_list_first_node(&request_virtual_membership_list))) {
++ struct membership *virtual_membership = VIRTUAL_MEMBERSHIP_NODE_TO_MEMBERSHIP(virtual_membership_node);
++
++ if (virtual_membership->request_scan.seq == request_scan_seq)
++ report(LOG_ERR, "INTERNAL: request_virtual_membership_list membership seq == ++request_scan_seq in request_scan_begin");
++ tac_list_node_remove(virtual_membership_node);
++ unlink_membership(virtual_membership);
++ free_membership(virtual_membership);
++ }
++ }
++#endif /* SCAN_PARANOIA */
++}
++
++static void value_scan_begin TAC_ARGS((ENTITY *entity));
++
++static void
++value_scan_begin(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_begin:");
++
++ if (!++value_scan_seq)
++ scan_invalidate_entities(IS_VALUE);
++
++ check_value_scan_entity(entity, 0); /* sure as seq invalidated */
++ /* assumed (entity->value_scan.from == NULL) */
++}
++
++#ifdef SCAN_PARANOIA
++
++static void eval_scan_begin_pending_entity_node TAC_ARGS((struct tac_list_node *pending_entity_node));
++
++static void
++eval_scan_begin_pending_entity_node(pending_entity_node)
++struct tac_list_node *pending_entity_node;
++{
++ ENTITY *pending_entity = PENDING_ENTITY_NODE_TO_ENTITY(pending_entity_node);
++
++ if (pending_entity->eval_scan.seq == eval_scan_seq)
++ report(LOG_ERR, "INTERNAL: eval_{pending}_entity_list entity seq == ++eval_scan_seq in eval_scan_begin");
++
++ tac_list_node_remove(pending_entity_node);
++}
++
++#endif /* SCAN_PARANOIA */
++
++static void eval_scan_begin TAC_ARGS((void));
++
++static void
++eval_scan_begin()
++{
++#ifdef SCAN_PARANOIA
++ static int inited = 0;
++#endif /* SCAN_PARANOIA */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "eval_scan_begin:");
++
++ if (!++eval_scan_seq)
++ scan_invalidate_entities(IS_EVAL);
++
++#ifdef SCAN_PARANOIA
++ if (!inited) {
++#endif /* SCAN_PARANOIA */
++ tac_list_init(&eval_kicked_entity_list);
++ tac_list_init(&eval_destroy_entity_list);
++ tac_list_init(&eval_notified_expr_list);
++#ifdef SCAN_PARANOIA
++ inited = 1;
++ } else {
++ struct tac_list_node *pending_entity_node;
++ struct tac_list_node *notify_expr_node;
++
++ while ((pending_entity_node = tac_list_first_node(&eval_kicked_entity_list)))
++ eval_scan_begin_pending_entity_node(pending_entity_node);
++ while ((pending_entity_node = tac_list_first_node(&eval_destroy_entity_list)))
++ eval_scan_begin_pending_entity_node(pending_entity_node);
++
++ while ((notify_expr_node = tac_list_first_node(&eval_notified_expr_list))) {
++ struct expr *notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node);
++
++ if (notify_expr->eval_scan.seq == eval_scan_seq)
++ report(LOG_ERR, "INTERNAL: eval_notified_expr_list expr seq == ++eval_scan_seq in eval_scan_begin");
++
++ tac_list_node_remove(notify_expr_node);
++ }
++ }
++#endif /* SCAN_PARANOIA */
++}
++
++/* 'priority=0' => addtail - used for WANTED entities
++ * 'priority=1' => addhead - used for SOLVED entities
++ * It may be better to do insert it AFTER all currently solved
++ * entities but may be not but who cares...
++ */
++
++static void register_kicked_entity TAC_ARGS((ENTITY *entity, int priority));
++
++static void register_kicked_entity(entity, priority)
++ENTITY *entity;
++int priority;
++{
++ struct tac_list_node *pending_entity_node = &entity->eval_scan.pending_entity_node;
++
++ check_eval_scan_entity(entity, 0);
++
++ if (tac_list_node_get_list(pending_entity_node) == &eval_destroy_entity_list) {
++ tac_list_node_remove (pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "register_kicked_entity: REMOVED " PF_ENTITY " from eval_DESTROY_entity_list",
++ PA_ENTITY(entity));
++ }
++ if (tac_list_node_get_list(pending_entity_node) == NULL) {
++ if (priority)
++ tac_list_addhead(&eval_kicked_entity_list, pending_entity_node);
++ else
++ tac_list_addtail(&eval_kicked_entity_list, pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "register_kicked_entity: REGISTERED " PF_ENTITY " to eval_KICKED_entity_list (priority=%s)",
++ PA_ENTITY(entity), (priority ? "YES" : "NO"));
++ }
++#ifdef SCAN_PARANOIA
++ if ((tac_list_node_get_list(pending_entity_node) != &eval_kicked_entity_list)) {
++ report(LOG_ERR, "Illegal list in entity->pending_entity_node.list");
++ return;
++ }
++#endif
++}
++
++/* check_eval_scan_*() is assumed both for "expr" and for "entity" ! */
++
++static void expr_eval_notify_expr TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr(expr)
++struct expr *expr;
++{
++ ENTITY *entity = expr->u.entity.entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REGISTERED notify " PF_EXPR " when " PF_ENTITY " is known",
++ PA_EXPR(expr), PA_ENTITY(entity));
++
++ if (tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node)) {
++#ifdef SCAN_PARANOIA
++ if (&entity->eval_scan.notify_expr_list
++ != tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node))
++ report(LOG_ERR, "Another " PF_ENTITY " already registered in notify node of " PF_EXPR,
++ PA_ENTITY(EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr)), PA_EXPR(expr));
++#endif
++ return;
++ }
++
++ tac_list_addtail(&entity->eval_scan.notify_expr_list,
++ &expr->eval_scan.u.entity.notify_expr_node);
++
++ register_kicked_entity(entity, 0 /* priority */);
++}
++
++/* check_eval_scan_*() is assumed for "expr" ! */
++
++static void expr_eval_notify_expr_remove_internal TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr_remove_internal(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_remove_internal: no longer interested in " PF_EXPR,
++ PA_EXPR(expr));
++
++ if (!expr)
++ return;
++ if (expr->eval_scan.seq != eval_scan_seq)
++ return;
++ if (expr->request_scan.result != ER_UNKNOWN)
++ return;
++
++ switch (expr->type) {
++
++ case S_not:
++ expr_eval_notify_expr_remove_internal(expr->u.not.child);
++ break;
++
++ case S_and:
++ case S_or: {
++ struct expr *child;
++
++ for (child=expr->u.and_or.child_first; child; child=child->next)
++ expr_eval_notify_expr_remove_internal(child);
++ } break;
++
++ case S_user:
++ case S_host:
++ case S_group: {
++ ENTITY *entity = expr->u.entity.entity;
++ struct tac_list_node *pending_entity_node = &entity->eval_scan.pending_entity_node;
++
++ if (!tac_list_node_get_list(&expr->eval_scan.u.entity.notify_expr_node))
++ break;
++
++ tac_list_node_remove(&expr->eval_scan.u.entity.notify_expr_node);
++ if (tac_list_first_node(&entity->eval_scan.notify_expr_list))
++ break;
++ /* no one is further interested in "entity" */
++
++ if ((tac_list_node_get_list(pending_entity_node) == &eval_kicked_entity_list)) {
++ tac_list_node_remove (pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REMOVED " PF_ENTITY " from eval_KICKED_entity_list",
++ PA_ENTITY(entity));
++ }
++ if (tac_list_node_get_list(pending_entity_node) == NULL) {
++ tac_list_addtail(&eval_destroy_entity_list, pending_entity_node);
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr: REGISTERED " PF_ENTITY " to eval_DESTROY_entity_list",
++ PA_ENTITY(entity));
++ }
++#ifdef SCAN_PARANOIA
++ if (tac_list_node_get_list(pending_entity_node) != &eval_destroy_entity_list) {
++ report(LOG_ERR, "Illegal list in entity->pending_entity_node.list");
++ return;
++ }
++#endif
++
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal node type %d for expr_eval_notify_expr_remove", expr->type);
++ return;
++ }
++}
++
++static void expr_eval_notify_expr_flush_destroy_entity_list TAC_ARGS((void));
++
++static void expr_eval_notify_expr_flush_destroy_entity_list()
++{
++struct tac_list_node *destroy_entity_node;
++
++ while ((destroy_entity_node = tac_list_first_node(&eval_destroy_entity_list))) {
++ ENTITY *destroy_entity = PENDING_ENTITY_NODE_TO_ENTITY(destroy_entity_node);
++ struct tac_list_node *destroy_notify_expr_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_flush_destroy_entity_list: PROCESSING " PF_ENTITY " from eval_DESTROY_entity_list",
++ PA_ENTITY(destroy_entity));
++
++ while ((destroy_notify_expr_node = tac_list_first_node(&destroy_entity->eval_scan.notify_expr_list))) {
++ struct expr *destroy_notify_expr = NOTIFY_EXPR_NODE_TO_EXPR(destroy_notify_expr_node);
++
++ expr_eval_notify_expr_remove_internal(destroy_notify_expr);
++ }
++ }
++}
++
++static void expr_eval_notify_expr_remove TAC_ARGS((struct expr *expr));
++
++static void
++expr_eval_notify_expr_remove(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_notify_expr_remove: no longer interested in " PF_EXPR,
++ PA_EXPR(expr));
++
++ expr_eval_notify_expr_remove_internal(expr);
++ expr_eval_notify_expr_flush_destroy_entity_list();
++}
++
++/* It would be very nice to try to optimize the expression before evaluation.
++
++ We are not interested in some CPU time complexity of the expression.
++ But we would be very happy to discard any user/host/group membership
++ dependencies (our 'variables'). Unfortunately such optimization is
++ NP problem (classification by courtesy of Daniel Kral) so it is considered
++ too expensive for us.
++
++ TODO in future: Full NP optimization for small number of variables and/or
++ heuristic optimizations for complex expressions.
++*/
++
++static enum eval_result expr_eval_immediate TAC_ARGS((struct expr *expr_single));
++
++static enum eval_result
++expr_eval_immediate(expr_single)
++struct expr *expr_single;
++{
++ struct expr *expr_child, *expr_parent;
++ enum eval_result result_child, result_parent = 0 /* GCC paranoia */;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ if (!expr_single)
++ return (ER_TRUE);
++
++ /* TOP->DOWN scanner: */
++top_down:
++ while (1) {
++ enum eval_result result_single;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: top_down start: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ check_eval_scan_expr(expr_single, 0);
++ result_single = expr_single->request_scan.result;
++ if (result_single != ER_UNKNOWN)
++ break;
++ switch (expr_single->type) {
++
++ case S_not:
++ expr_single = expr_single->u.not.child;
++ continue;
++
++ case S_and:
++ case S_or:
++ expr_single = expr_single->u.and_or.child_first;
++ continue;
++
++ case S_user:
++ case S_host:
++ case S_group: {
++ ENTITY *entity = expr_single->u.entity.entity;
++
++ check_eval_scan_entity(entity, 0);
++
++ if (entity->request_scan.belongs == ER_UNKNOWN)
++ expr_eval_notify_expr(expr_single);
++ else
++ result_single = entity->request_scan.belongs;
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal child node type %d for expr_eval", expr_single->type);
++ return (ER_UNKNOWN);
++ }
++
++ expr_single->request_scan.result = result_single;
++ break;
++ }
++
++ /* BOTTOM->UP scanner: */
++ do {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up start: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ expr_parent = expr_single->parent;
++ if (!expr_parent)
++ break;
++ if (expr_parent->eval_scan.seq != eval_scan_seq) {
++ report(LOG_ERR, "INTERNAL: Parent expr node eval_scan NOT up-to-date");
++ return (ER_UNKNOWN);
++ }
++ if (expr_parent->request_scan.seq != request_scan_seq) {
++ report(LOG_ERR, "INTERNAL: Parent expr node request_scan NOT up-to-date");
++ return (ER_UNKNOWN);
++ }
++ if (expr_parent->request_scan.result != ER_UNKNOWN) {
++ report(LOG_WARNING, "INTERNAL-WARNING: Parent expr node already known, wasteful eval occured");
++ return (ER_UNKNOWN);
++ }
++
++ expr_child = expr_single;
++ result_child = expr_child->request_scan.result;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up switch: child=" PF_EXPR ",parent=" PF_EXPR,
++ PA_EXPR(expr_child), PA_EXPR(expr_parent));
++
++ switch (expr_parent->type) {
++
++ case S_not:
++ switch (result_child) {
++ case ER_UNKNOWN: result_parent = ER_UNKNOWN; break;
++ case ER_FALSE: result_parent = ER_TRUE; break;
++ case ER_TRUE: result_parent = ER_FALSE; break;
++ }
++ break;
++
++ case S_and:
++ case S_or: {
++ enum eval_result veto = (expr_parent->type==S_and ? ER_FALSE : ER_TRUE );
++ enum eval_result consent = (expr_parent->type==S_and ? ER_TRUE : ER_FALSE);
++
++ if (result_child == veto)
++ result_parent = veto;
++ else if (result_child == ER_UNKNOWN || result_child == consent) {
++ if (expr_child->next) {
++ expr_single = expr_child->next;
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up and_or: traversed to and_or next: " PF_EXPR,
++ PA_EXPR(expr_single));
++ goto top_down;
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up and_or: full scan: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ /* It would be nice to pretend that all 'veto' decisions already made in the child
++ * had to set our 'result' to the correct value. But 'consent' decisions don't set
++ * us and the behaviour of auto-set from the child in 'veto' case may get changed
++ * in the future versions.
++ * So we rather don't depend on it.
++ * This overhead doesn't change altgorithmic complexity anyway.
++ */
++ result_parent = consent;
++ for (expr_child = expr_parent->u.and_or.child_first; expr_child; expr_child = expr_child->next)
++ {
++ check_eval_scan_expr(expr_child, 0); /* shouldn't be needed */
++ if (expr_child->request_scan.result == ER_UNKNOWN)
++ result_parent = ER_UNKNOWN; /* assumed (result_parent != veto) */
++ else if (expr_child->request_scan.result == veto) {
++ result_parent = veto;
++ break;
++ }
++ }
++ break;
++ }
++ } break;
++
++ default:
++ report(LOG_ERR, "Illegal parent node type %d for expr_eval", expr_parent->type);
++ return (ER_UNKNOWN);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: bottom_up end: child=" PF_EXPR ",parent=" PF_EXPR,
++ PA_EXPR(expr_child), PA_EXPR(expr_parent));
++
++ if (result_parent != ER_UNKNOWN) {
++ expr_parent->request_scan.result = result_parent;
++ /* we no longer need any notifications from entities to solve sub-expression */
++ expr_eval_notify_expr_remove(expr_parent);
++ }
++
++ expr_single = expr_parent;
++ } while (0);
++ /* The whole expression has been scanned to its root, we have "expr_single" */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval_immediate: done: " PF_EXPR,
++ PA_EXPR(expr_single));
++
++ return (expr_single->request_scan.result);
++}
++
++static void membership_solved TAC_ARGS((struct membership *membership));
++
++static void
++membership_solved(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ check_request_scan_entity(parent_entity, 0);
++
++#ifdef SCAN_PARANOIA
++ if (!membership->eval_scan.unsolved) {
++ report(LOG_ERR, "INTERNAL: membership already solved in membership_solved");
++ return;
++ }
++#endif
++
++ membership->eval_scan.unsolved = 0;
++
++#ifdef SCAN_PARANOIA
++ if (!parent_entity->eval_scan.unsolved_to_child_membership_num) {
++ report(LOG_ERR, "INTERNAL: unsolved_to_child_membership_num-- == 0 in membership_solved");
++ parent_entity->eval_scan.unsolved_to_child_membership_num++;
++ }
++#endif
++ parent_entity->eval_scan.unsolved_to_child_membership_num--;
++
++ if (!parent_entity->eval_scan.unsolved_to_child_membership_num
++ && parent_entity->request_scan.belongs == ER_UNKNOWN) {
++ parent_entity->request_scan.belongs = ER_FALSE;
++ register_kicked_entity(parent_entity, 1 /* priority */);
++ }
++}
++
++static void membership_parent_solve TAC_ARGS((struct membership *membership, enum eval_result how));
++
++static void
++membership_parent_solve(membership, how)
++struct membership *membership;
++enum eval_result how;
++{
++ enum eval_result negative = (how == ER_TRUE ? ER_FALSE : ER_TRUE);
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ check_request_scan_entity(parent_entity, 0);
++
++ if (parent_entity->request_scan.belongs == negative)
++ report(LOG_ERR, "INTERNAL: parent " PF_ENTITY "already negative to what says membership " PF_MEMBERSHIP "in membership_eval_immediate",
++ PA_ENTITY(parent_entity), PA_MEMBERSHIP(membership));
++
++ parent_entity->request_scan.belongs = how;
++ register_kicked_entity(parent_entity, 1 /* priority */);
++
++ membership_solved(membership);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_parent_solve: " PF_MEMBERSHIP " marked parent " PF_ENTITY,
++ PA_MEMBERSHIP(membership), PA_ENTITY(parent_entity));
++}
++
++static void membership_eval_immediate TAC_ARGS((struct membership *membership));
++
++static void
++membership_eval_immediate(membership)
++struct membership *membership;
++{
++ enum eval_result membership_valid;
++ ENTITY *child_entity, *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_eval_immediate: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ check_eval_scan_membership(membership, 0);
++
++ if (!membership->eval_scan.unsolved)
++ return;
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++ check_request_scan_entity(parent_entity, 0);
++ if (parent_entity->request_scan.belongs != ER_UNKNOWN) /* why to solve this membership? */
++ return;
++
++ membership_valid = expr_eval_immediate(membership->when);
++
++ child_entity = MEMBERSHIP_TO_CHILD_ENTITY( membership);
++ check_request_scan_entity( child_entity, 0);
++
++#if 0 /* non-valid membership doesn't YET solve the parent! */
++ if (child_entity->request_scan.belongs == ER_FALSE || membership_valid == ER_FALSE) {
++ membership_parent_solve(membership, ER_FALSE);
++ return;
++ }
++#endif
++
++ if (child_entity->request_scan.belongs == ER_TRUE && membership_valid == ER_TRUE ) {
++ membership_parent_solve(membership, ER_TRUE );
++ return;
++ }
++
++ if ( child_entity->request_scan.belongs == ER_UNKNOWN)
++ register_kicked_entity( child_entity, 0 /* priority */);
++ if (parent_entity->request_scan.belongs == ER_UNKNOWN)
++ register_kicked_entity(parent_entity, 0 /* priority */);
++
++ if (parent_entity->request_scan.belongs != ER_UNKNOWN
++ || (child_entity->request_scan.belongs == ER_FALSE || membership_valid == ER_FALSE))
++ membership_solved(membership);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "membership_eval_immediate: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++}
++
++static void entity_eval_immediate TAC_ARGS((ENTITY *entity));
++
++static void
++entity_eval_immediate(entity)
++ENTITY *entity;
++{
++ struct tac_list_node *notified_expr_node;
++ struct tac_list_node *child_membership_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ check_eval_scan_entity(entity, 0);
++
++ if (!request_scan_user_known) {
++#ifdef SCAN_PARANOIA
++ if (entity->request_scan.belongs != ER_UNKNOWN)
++ report(LOG_ERR, "INTERNAL: belonging known while still !request_scan_user_known for " PF_ENTITY " in entity_eval_immediate",
++ PA_ENTITY(entity));
++#endif
++ return;
++ }
++
++ if (entity->request_scan.belongs == ER_UNKNOWN) {
++ if (entity->eval_scan.unsolved_to_child_membership_first) {
++ struct membership *order_membership = entity->eval_scan.unsolved_to_child_membership_first;
++ struct tac_list_node *next_membership_parent_node = tac_list_node_next(&order_membership->parent_node);
++
++ membership_eval_immediate(order_membership);
++ if (next_membership_parent_node)
++ entity->eval_scan.unsolved_to_child_membership_first = PARENT_NODE_TO_MEMBERSHIP(next_membership_parent_node);
++ else
++ entity->eval_scan.unsolved_to_child_membership_first = NULL;
++
++ register_kicked_entity(entity, 0 /* priority */);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: finishing as we ordered child membership: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(order_membership));
++ return;
++ }
++
++ if (!entity->eval_scan.unsolved_to_child_membership_num)
++ entity->request_scan.belongs = ER_FALSE;
++ else {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: finishing as unsolved child memberships still available and I'm still clueless");
++ return;
++ }
++ }
++ /* belonging is known here */
++
++ /* recheck all memberships we may decide */
++ for (
++ child_membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ child_membership_node;
++ child_membership_node = tac_list_node_next(child_membership_node)
++ ) {
++ struct membership *child_membership = CHILD_NODE_TO_MEMBERSHIP(child_membership_node);
++
++ membership_eval_immediate(child_membership);
++ }
++
++ /* notify all exprs which are interested in us */
++ while ((notified_expr_node = tac_list_first_node(&entity->eval_scan.notify_expr_list))) {
++ tac_list_node_remove(notified_expr_node);
++ tac_list_addtail(&eval_notified_expr_list, notified_expr_node);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "entity_eval_immediate: done: " PF_ENTITY,
++ PA_ENTITY(entity));
++}
++
++
++enum eval_result expr_eval TAC_ARGS((struct expr *expr));
++
++enum eval_result
++expr_eval(expr)
++struct expr *expr;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: top level order for " PF_EXPR,
++ PA_EXPR(expr));
++
++ if (!expr)
++ return (ER_TRUE);
++
++ eval_scan_begin();
++ if (expr_eval_immediate(expr) != ER_UNKNOWN)
++ return (expr->request_scan.result);
++
++ /* all 'solved' nodes MUST be removed BEFORE '*_immediate()' has been called,
++ * otherwise we may have no longer valid node!
++ */
++ for (;;) {
++ struct tac_list_node *notified_expr_node, *kicked_entity_node;
++
++ /* check it rather always, checking just on notifications looks too complex.
++ */
++ if (expr->request_scan.result != ER_UNKNOWN) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: finishing as ordered " PF_EXPR " got known",
++ PA_EXPR(expr));
++ return (expr->request_scan.result);
++ }
++
++#if 0 /* not needed as it is now always called after any destroy */
++ expr_eval_notify_expr_flush_destroy_entity_list(); /* eval_destroy_entity_list */
++#endif /* not needed */
++
++ if ((notified_expr_node = tac_list_first_node(&eval_notified_expr_list))) {
++ struct expr *notified_expr = NOTIFY_EXPR_NODE_TO_EXPR(notified_expr_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: PROCESSING " PF_EXPR " from eval_NOTIFIED_expr_list",
++ PA_EXPR(notified_expr));
++
++ tac_list_node_remove(notified_expr_node);
++ expr_eval_immediate(notified_expr);
++
++ if (notified_expr->membership)
++ membership_eval_immediate(notified_expr->membership);
++
++ continue; /* shortcut */
++ }
++
++ if ((kicked_entity_node = tac_list_first_node(&eval_kicked_entity_list))) {
++ ENTITY *kicked_entity = PENDING_ENTITY_NODE_TO_ENTITY(kicked_entity_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "expr_eval: PROCESSING " PF_ENTITY " from eval_KICKED_entity_list",
++ PA_ENTITY(kicked_entity));
++
++ tac_list_node_remove(kicked_entity_node);
++ entity_eval_immediate(kicked_entity);
++ continue; /* shortcut */
++ }
++
++ break; /* nothing done yet, all lists are empty! */
++ }
++
++ if (!expr->request_scan.loop_reported) {
++ report(LOG_WARNING, "Unable to resolve expression from line %d, some looping occured", expr->line);
++ expr->request_scan.loop_reported = 1;
++ }
++ return (ER_UNKNOWN);
++}
++
++
++void eval_force_belong_entity TAC_ARGS((ENTITY *entity));
++
++void eval_force_belong_entity(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "eval_force_belong_entity: " PF_ENTITY " (before check_scan " PF_ERESULT_ENTITY ")",
++ PA_ENTITY(entity), PA_ERESULT_ENTITY(entity));
++
++ check_request_scan_entity(entity, 0);
++
++ if (entity->request_scan.belongs == ER_FALSE)
++ report(LOG_ERR, "Dangerous force of TRUE to FALSE-determined entity in eval_force_belong_entity");
++
++ entity->request_scan.belongs = ER_TRUE;
++}
++
++void scan_init_entity TAC_ARGS((ENTITY *entity));
++
++void
++scan_init_entity(entity)
++ENTITY *entity;
++{
++ entity->request_scan.seq = request_scan_seq-1; /* invalidate */
++ entity-> value_scan.seq = value_scan_seq-1; /* invalidate */
++ entity-> eval_scan.seq = eval_scan_seq-1; /* invalidate */
++ tac_list_init(&entity->eval_scan.notify_expr_list);
++ tac_list_node_init(&entity->eval_scan.pending_entity_node);
++}
++
++struct membership *enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child, struct expr *when));
++
++struct membership *
++enlist_entity_direct(parent, child, when)
++ENTITY *parent;
++ENTITY *child;
++struct expr *when;
++{
++ struct membership *membership = (struct membership *) tac_malloc(sizeof(struct membership));
++
++ tac_list_node_init(&membership->parent_node);
++ tac_list_node_init(&membership->child_node);
++ membership->request_scan.seq = request_scan_seq-1;
++ tac_list_node_init(&membership->request_scan.virtual_membership_node);
++ membership->eval_scan.seq = eval_scan_seq-1;
++
++ tac_list_addtail(&parent->to_child_membership_list , &membership->parent_node);
++ parent->to_child_membership_num++;
++ tac_list_addtail(& child->to_parent_membership_list, &membership-> child_node);
++ membership->when = when;
++ if (expr_sink(membership->when, membership)) {
++ unlink_membership(membership);
++ free_membership(membership);
++ return (NULL);
++ }
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "enlist_entity_direct: done: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ return (membership);
++}
++
++struct membership *virtual_enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child));
++
++struct membership *
++virtual_enlist_entity_direct(parent, child)
++ENTITY *parent;
++ENTITY *child;
++{
++ struct membership *membership;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "virtual_enlist_entity_direct: the following enlist will be VIRTUAL...");
++
++ membership = enlist_entity_direct(parent, child, NULL /* when */);
++ if (!membership)
++ return (NULL);
++
++ check_request_scan_membership(membership, 0);
++ tac_list_addtail(&request_virtual_membership_list, &membership->request_scan.virtual_membership_node);
++
++ return (membership);
++}
++
++/* returns given 'entity' or NULL if already visited */
++
++void (*value_scan_forward_seen_hook) TAC_ARGS((struct membership *membership));
++
++static ENTITY *value_scan_forward TAC_ARGS((struct membership *membership));
++
++static ENTITY *
++value_scan_forward(membership)
++struct membership *membership;
++{
++ ENTITY *parent_entity;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: from " PF_MEMBERSHIP " try forward...",
++ PA_MEMBERSHIP(membership));
++
++ parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ if (ER_TRUE != expr_eval(membership->when)) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward NOT successful due to failed 'when' evaluation.");
++ return (NULL);
++ }
++ check_value_scan_entity(parent_entity, 0);
++ if (parent_entity->value_scan.seen) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward NOT successful as the parent " PF_ENTITY " was already seen this value scan.",
++ PA_ENTITY(parent_entity));
++ if (value_scan_forward_seen_hook)
++ (*value_scan_forward_seen_hook)(membership);
++ return (NULL);
++ }
++ parent_entity->value_scan.seen = 1;
++ parent_entity->value_scan.from = membership;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_forward: forward SUCCESSFUL to parent " PF_ENTITY,
++ PA_ENTITY(parent_entity));
++ return (parent_entity);
++}
++
++struct membership *value_scan_backward TAC_ARGS((ENTITY *entity));
++
++struct membership *
++value_scan_backward(entity)
++ENTITY *entity;
++{
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_backward: from " PF_ENTITY " went back to " PF_MEMBERSHIP,
++ PA_ENTITY(entity), PA_MEMBERSHIP(entity->value_scan.from));
++
++#ifdef SCAN_PARANOIA
++ if (entity->value_scan.seq != value_scan_seq) {
++ report(LOG_ERR, "entity value_scan NOT up-to-date in value_scan_backward");
++ return (NULL);
++ }
++#endif
++
++ return (entity->value_scan.from);
++}
++
++/* Scan the entity graph and return each node found.
++ 'when' conditions for graph connections are respected,
++ looping is correctly prevented.
++*/
++
++enum value_scan_func_result value_scan_entity TAC_ARGS((ENTITY *entity, int recurse, value_scan_func_t func, void *func_data));
++
++enum value_scan_func_result
++value_scan_entity(entity, recurse, func, func_data)
++ENTITY *entity;
++int recurse;
++value_scan_func_t func;
++void *func_data;
++{
++ enum value_scan_func_result vsfr;
++ struct tac_list_node *membership_node;
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: " PF_ENTITY ", recurse=%d",
++ PA_ENTITY(entity), recurse);
++
++ vsfr=(*func)(entity,func_data);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: root func-> " PF_VSFR,
++ PA_VSFR(vsfr));
++
++ if (vsfr != VSFR_CONTINUE) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as root func didn't return VSFR_CONTINUE");
++ return (vsfr);
++ }
++ if (!recurse ) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as recurse not ordered");
++ return (VSFR_STOP);
++ }
++
++ value_scan_begin(entity);
++ membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ if (!membership_node) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as no parent entities of root");
++ return (VSFR_CONTINUE); /* no parent entities */
++ }
++
++ while (1) {
++ struct membership *membership = CHILD_NODE_TO_MEMBERSHIP(membership_node);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: trace loop start: " PF_MEMBERSHIP,
++ PA_MEMBERSHIP(membership));
++
++ entity = value_scan_forward(membership);
++ if (entity) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: successful recurse to " PF_ENTITY,
++ PA_ENTITY(entity));
++
++ vsfr=(*func)(entity,func_data);
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: func(" PF_ENTITY ")-> " PF_VSFR,
++ PA_ENTITY(entity), PA_VSFR(vsfr));
++
++ if (vsfr == VSFR_FOUND) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as func returned VSFR_FOUND");
++ return (vsfr);
++ }
++ if (vsfr == VSFR_CONTINUE)
++ membership_node = tac_list_first_node(&entity->to_parent_membership_list);
++ }
++ if (!entity || vsfr == VSFR_STOP) {
++ ENTITY *parent_entity = MEMBERSHIP_TO_PARENT_ENTITY(membership);
++
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership); /* for retreat from the LAST membership */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: unsuccessful recurse to " PF_ENTITY ", tracing back through child " PF_ENTITY,
++ PA_ENTITY(parent_entity), PA_ENTITY(entity));
++
++ membership_node = tac_list_node_next(&membership->child_node);
++ }
++
++ while (!membership_node) {
++ membership = value_scan_backward(entity);
++ if (!membership) {
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: finishing as all nodes were scanned");
++ return (VSFR_CONTINUE); /* FINISH */
++ }
++
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership); /* for retreat from the LAST membership */
++
++ if (debug & DEBUG_CFGEVAL_FLAG)
++ report(LOG_DEBUG, "value_scan_entity: backward retreat ('next' chase) "
++ "through " PF_MEMBERSHIP " to child " PF_ENTITY,
++ PA_MEMBERSHIP(membership), PA_ENTITY(entity));
++
++ membership_node = tac_list_node_next(&membership->child_node);
++ }
++ }
++ /* NOTREACHED */
++}
+diff --git a/cfgeval.h b/cfgeval.h
+new file mode 100644
+index 0000000..63f33cb
+--- /dev/null
++++ b/cfgeval.h
+@@ -0,0 +1,134 @@
++#ifndef CFGEVAL_H
++#define CFGEVAL_H 1
++
++#include "tac_plus.h"
++
++#include "utils.h"
++
++
++struct entity;
++typedef struct entity ENTITY;
++enum invalidate_scan {
++ IS_REQUEST = 0,
++ IS_VALUE = 1,
++ IS_EVAL = 2,
++ IS_MAX = 3
++};
++
++enum eval_result {
++ ER_UNKNOWN,
++ ER_FALSE,
++ ER_TRUE
++};
++
++enum value_scan_func_result {
++ VSFR_CONTINUE, /* for value_scan_entity() it means 'not found' */
++ VSFR_FOUND, /* immediately return from the whole value_scan_entity() */
++ VSFR_STOP /* backtrack (stop) this branch, scan further; nevere returned by value_scan_entity() */
++};
++
++struct membership {
++ struct tac_list_node parent_node; /* to_child_membership_list , AKA legacy "member" */
++ struct tac_list_node child_node; /* to_parent_membership_list */
++
++ struct {
++ unsigned seq; /* corresponds to global request_scan_seq */
++ struct tac_list_node virtual_membership_node;
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ unsigned unsolved:1; /* we haven't yet decreased
++ * parent_entity->eval_scan.unsolved_to_child_membership_num */
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++
++ struct expr *when;
++};
++#define MEMBERSHIP_TO_PARENT_ENTITY(membership) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(membership)->parent_node), to_child_membership_list ))
++#define MEMBERSHIP_TO_CHILD_ENTITY(membership) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(membership)-> child_node), to_parent_membership_list))
++#define PARENT_NODE_TO_MEMBERSHIP(parent_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (parent_node_), parent_node))
++#define CHILD_NODE_TO_MEMBERSHIP( child_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, ( child_node_), child_node))
++#define UNSOLVED_CHILD_NODE_TO_MEMBERSHIP(unsolved_child_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (unsolved_child_node_), eval_scan.unsolved_child_node))
++#define VIRTUAL_MEMBERSHIP_NODE_TO_MEMBERSHIP(virtual_membership_node_) \
++ (&TAC_MEMBER_STRUCT(struct membership, (virtual_membership_node_), request_scan.virtual_membership_node))
++
++struct expr {
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ enum eval_result result; /* known result of this whole branch */
++ unsigned loop_reported:1; /* prevent excessive logging */
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ union {
++
++ struct { /* for S_host, S_user or S_group */
++ /* connected to either "entity->eval_scan.notify_expr_list"
++ * or to global "eval_notified_expr_list"
++ */
++ struct tac_list_node notify_expr_node; /* gets removed on this expr->seq!= */
++ } entity;
++
++ } u;
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++
++ struct membership *membership; /* our owner */
++ struct expr *parent; /* NULL if we are the root expr */
++ /* "parent" also (mis)used by expr_sink_{register,commit}() ! */
++ struct expr *next; /* used in childs of S_and / S_or */
++ int type; /* S_not, S_and, S_or, S_host, S_user or S_group */
++ int line;
++ union {
++
++ struct {
++ struct expr *child;
++ } not;
++
++ struct {
++ struct expr *child_first; /* linked by expr->next */
++ } and_or;
++
++ struct { /* for S_host, S_user or S_group */
++ const char *name;
++ ENTITY *entity;
++ } entity;
++
++ } u;
++};
++#define EXPR_ENTITY_TO_NOTIFYING_ENTITY(expr_) \
++ (&TAC_MEMBER_STRUCT(ENTITY, tac_list_node_get_list(&(expr_)->eval_scan.u.entity.notify_expr_node), eval_scan.notify_expr_list))
++#define NOTIFY_EXPR_NODE_TO_EXPR(notify_expr_node_) \
++ (&TAC_MEMBER_STRUCT(struct expr, (notify_expr_node_), eval_scan.u.entity.notify_expr_node))
++
++typedef enum value_scan_func_result (*value_scan_func_t) TAC_ARGS((ENTITY *entity,void *func_data));
++extern void (*value_scan_forward_seen_hook) TAC_ARGS((struct membership *membership));
++
++
++extern int request_scan_user_known; /* have we allowed to 'solve' S_user entities at all? */
++
++
++extern void unlink_expr TAC_ARGS((struct expr *expr));
++extern void free_expr TAC_ARGS((struct expr *expr));
++extern void scan_free_entity TAC_ARGS((ENTITY *entity));
++extern struct expr *new_expr TAC_ARGS((int type));
++extern enum value_scan_func_result value_scan_entity TAC_ARGS((ENTITY *entity, int recurse, value_scan_func_t func, void *func_data));
++extern struct membership *value_scan_backward TAC_ARGS((ENTITY *entity));
++extern enum eval_result expr_eval TAC_ARGS((struct expr *expr_single));
++extern struct expr *dupl_expr TAC_ARGS((const struct expr *in));
++extern void scan_init_entity TAC_ARGS((ENTITY *entity));
++extern struct membership *enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child, struct expr *when));
++extern struct membership *virtual_enlist_entity_direct TAC_ARGS((ENTITY *parent, ENTITY *child));
++extern void scan_invalidate_entities_hashtable TAC_ARGS((void **hashtable, enum invalidate_scan what));
++extern void request_scan_begin TAC_ARGS((void));
++extern void eval_force_belong_entity TAC_ARGS((ENTITY *entity));
++extern void expr_sink_register TAC_ARGS((struct expr *expr));
++extern int expr_sink_commit TAC_ARGS((void));
++
++
++#endif /* CFGEVAL_H */
+diff --git a/cfgfile.c b/cfgfile.c
+index 6eaef53..041993c 100644
+--- a/cfgfile.c
++++ b/cfgfile.c
+@@ -17,38 +17,87 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <errno.h>
+-#include "regexp.h"
++#include <string.h>
++
++#ifndef WITH_INCLUDED_REGEX
++#ifdef HAVE_REGEX_H
++#include <regex.h>
++#endif
++#endif
++
++#include "cfgfile.h"
++#include "report.h"
++#include "utils.h"
++#include "hash.h"
++#include "parse.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++
++#ifdef WITH_INCLUDED_REGEX
++#include "tac_regexp.h"
++#endif
++
++
++static void sym_get TAC_ARGS((void));
++static void when_expr_root_init TAC_ARGS((void));
++static void rch TAC_ARGS((void));
++static int parse_entity TAC_ARGS((int entity_type));
++static NODE *parse_svcs TAC_ARGS((void));
++static int parse_conditional_block TAC_ARGS((ENTITY *entity));
++static NODE *parse_cmd_matches TAC_ARGS((void));
++static NODE *parse_attrs TAC_ARGS((void));
++static void getsym TAC_ARGS((void));
++static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity));
++
+
+ /*
+ <config> := <decl>*
+
+- <decl> := <top_level_decl> | <user_decl>
++ <decl> := <top_level_decl> | <entity_decl>
+
+ <top_level_decl> := <authen_default> |
+- accounting file = <string>
++ accounting file = <string> |
+ default authorization = permit |
+- key = <string>
++ key = <string> |
++ authorization = ( first | recursive )
+
+ <authen_default> := default authentication = file <filename>
+ #if defined(DB)
+- | db <string> )
++ | db <string>
+ #endif
+
+-<permission> := permit | deny
++ <permission> := permit | deny
+
+ <filename> := <string>
+
+ <password> := <string>
+
+ <user_decl> := user = <string> {
+- [ default service = [ permit | deny ] ]
++ [ <service_default> ]
+ <user_attr>*
+ <svc>*
+ }
+
++ <host_decl> := host = <string> {
++ [ <service_default> ]
++ <host_attr>*
++ <svc>*
++ }
++
++ <group_decl> := group = <string> {
++ [ <service_default> ]
++ <group_attr>*
++ <svc>*
++ }
++
++ <service_default> := default service = ( permit | deny | default )
++
+ <password_spec> := file <filename> |
+ skey |
+ cleartext <password> |
+@@ -80,23 +129,66 @@
+ global = cleartext <string> |
+ msg = <string>
+ before authorization = <string> |
+- after authorization = <string>
++ after authorization = <string> |
++ <when_attr>
++
++ <host_attr> := key = <string> |
++ <user_attr>
++
++ <group_attr> := enlist = <entity_spec> |
++ key = <string> |
++ <user_attr>
++
++ <when_attr> := member = <string> |
++ enlist = <entity_spec> |
++ <svc> |
++ <when_attr_block>
++
++ <when_attr_block> := <when_decl> {
++ <when_attr>*
++ }
+
+ <svc> := <svc_auth> | <cmd_auth>
+
+ <cmd_auth> := cmd = <string> {
+- <cmd-match>*
++ <when_match>*
+ }
+
+- <cmd-match> := <permission> <string>
++ <when_match> := <permission> <string> |
++ <when_match_block>
++
++ <when_match_block> := <when_decl> {
++ <when_match>*
++ }
+
+- <svc_auth> := service = ( exec | arap | slip | ppp protocol = <string> {
++ <svc_auth> := service = ( exec | arap | slip | ppp protocol = <string> ) {
++# first matching <svc_auth> is the FINAL one, no further graph scanning occurs!
+ [ default attribute = permit ]
+- <attr_value_pair>*
++ <when_AV_pair>*
++ }
++
++ <when_AV_pair> := [ optional ] <string> = <string> |
++ <when_AV_pair_block>
++
++ <when_AV_pair_block> := <when_decl> {
++ <when_AV_pair>*
+ }
+
+- <attr_value_pair> := [ optional ] <string> = <string>
++ <when_decl> := when = <expr>
+
++# to avoid ambiguous precence by forbid of "or" & "and" without parentheses:
++ <expr> := <entity_spec> |
++ not <expr> |
++ '(' <expr_or> ')' |
++ '(' <expr_and> ')'
++
++ <expr_or> := <expr> |
++ <expr> or <expr_or>
++
++ <expr_and> := <expr> |
++ <expr> and <expr_and>
++
++ <entity_spec> := ( user | host | group ) <string>
+ */
+
+ static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */
+@@ -107,102 +199,54 @@ 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 */
++ /* ='default authorization': 0/S_permit */
++static int algorithm_recursive = 0; /* use newer authorization alogrithm? */
++ /* 1 if 'authorization = recursive' */
+ static char *authen_default = NULL; /* top level authentication default */
+-static int authen_default_method = 0; /*For method check */
++ /* ='default authentication' */
++static int authen_default_method = 0; /* For method check */
++ /* ='default authentication' symbol */
+ 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;
++static void *grouptable[HASH_TAB_SIZE]; /* Table of group declarations */
++static void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
++static void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
+-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 */
+
++struct enlist_entity_item {
++ struct enlist_entity_item *next;
++ int parent_type; char *parent;
++ int child_type; char * child; /* will be created when not found (for "enlist") */
++ struct expr *when;
++ int line;
++};
++static struct enlist_entity_item * enlist_entity_list;
++static struct enlist_entity_item **enlist_entity_list_tailp = &enlist_entity_list;
+
+-static void
+- sym_get();
+
++static void parse_error TAC_ARGS((char *fmt,...)) G_GNUC_PRINTF(1, 2);
+
+ #ifdef __STDC__
++
+ #include <stdarg.h> /* ANSI C, variable length args */
+ static void
+ parse_error(char *fmt,...)
+-#else
++
++#else /* __STDC__ */
++
+ #include <varargs.h> /* has 'vararg' definitions */
+ /* VARARGS2 */
+ static void
+ parse_error(fmt, va_alist)
+ char *fmt;
+-
+ va_dcl /* no terminating semi-colon */
+-#endif
++
++#endif /* __STDC__ */
+ {
+ char msg[256]; /* temporary string */
+ va_list ap;
+@@ -220,7 +264,9 @@ va_dcl /* no terminating semi-colon */
+ tac_exit(1);
+ }
+
+-char *
++const char *cfg_nodestring TAC_ARGS((int type));
++
++const char *
+ cfg_nodestring(type)
+ int type;
+ {
+@@ -250,6 +296,54 @@ cfg_nodestring(type)
+ }
+ }
+
++const char *entity_type_to_string TAC_ARGS((int entity_type));
++
++const char *
++entity_type_to_string(entity_type)
++int entity_type;
++{
++ switch (entity_type) {
++ case S_user:
++ return ("user");
++ case S_host:
++ return ("host");
++ case S_group:
++ return ("group");
++ }
++ return (NULL);
++}
++
++static void **entity_type_to_hashtable TAC_ARGS((int entity_type));
++
++static void **
++entity_type_to_hashtable(entity_type)
++int entity_type;
++{
++ switch (entity_type) {
++ case S_user:
++ return (usertable);
++ case S_host:
++ return (hosttable);
++ case S_group:
++ return (grouptable);
++ }
++ return (NULL);
++}
++
++void scan_invalidate_entities TAC_ARGS((enum invalidate_scan what));
++
++void
++scan_invalidate_entities(what)
++enum invalidate_scan what;
++{
++ scan_invalidate_entities_hashtable( usertable, what);
++ scan_invalidate_entities_hashtable( hosttable, what);
++ scan_invalidate_entities_hashtable(grouptable, what);
++}
++
++
++static void free_attrs TAC_ARGS((NODE *node));
++
+ static void
+ free_attrs(node)
+ NODE *node;
+@@ -257,13 +351,17 @@ NODE *node;
+ NODE *next;
+
+ while (node) {
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
++
+ 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);
++ (const char *) node->value);
+ break;
+ default:
+ report(LOG_ERR, "Illegal node type %s for free_attrs",
+@@ -278,6 +376,8 @@ NODE *node;
+ }
+ }
+
++static void free_cmd_matches TAC_ARGS((NODE *node));
++
+ static void
+ free_cmd_matches(node)
+ NODE *node;
+@@ -288,16 +388,32 @@ NODE *node;
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+- node->value);
++ (const char *) node->value);
++
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
+
+ free(node->value); /* text */
+- free(node->value1); /* regexp compiled text */
++
++#ifdef WITH_INCLUDED_REGEX
++
++ free(node->value1); /* tac_regexp compiled text */
++
++#else /* WITH_INCLUDED_REGEX */
++
++ regfree((regex_t *) node->value1); /* POSIX regex compiled text */
++
++#endif /* WITH_INCLUDED_REGEX */
++
+ next = node->next;
+ free(node);
+ node = next;
+ }
+ }
+
++static void free_svcs TAC_ARGS((NODE *node));
++
+ static void
+ free_svcs(node)
+ NODE *node;
+@@ -305,12 +421,16 @@ NODE *node;
+ NODE *next;
+
+ while (node) {
++ unlink_expr(node->when);
++ free_expr(node->when);
++ node->when = NULL;
+
+ switch (node->type) {
+ case N_svc_cmd:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+- cfg_nodestring(node->type), node->value);
++ cfg_nodestring(node->type),
++ (const char *) node->value);
+ free(node->value); /* cmd name */
+ free_cmd_matches(node->value1);
+ next = node->next;
+@@ -340,85 +460,107 @@ NODE *node;
+ }
+ }
+
++static void free_enlist_entity_item TAC_ARGS((struct enlist_entity_item *item));
++
++static void
++free_enlist_entity_item(item)
++struct enlist_entity_item *item;
++{
++ free(item->parent);
++ free(item->child);
++ free_expr(item->when);
++}
++
++
++static void free_entity TAC_ARGS((ENTITY *entity));
++
+ static void
+-free_userstruct(user)
+-USER *user;
++free_entity(entity)
++ENTITY *entity;
+ {
+ 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);
++ entity_type_to_string(entity->type), entity->name);
++
++ /* function MUST be called while the whole entity is still VALID! */
++ scan_free_entity(entity);
++
++ if (entity->name)
++ free(entity->name);
++ if (entity->full_name)
++ free(entity->full_name);
++ if (entity->login)
++ free(entity->login);
++ if (entity->expires)
++ free(entity->expires);
++ if (entity->time)
++ free(entity->time);
++ if (entity->arap)
++ free(entity->arap);
++ if (entity->chap)
++ free(entity->chap);
+ #ifdef MSCHAP
+- if (user->mschap)
+- free(user->mschap);
++ if (entity->mschap)
++ free(entity->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);
++ if (entity->pap)
++ free(entity->pap);
++ if (entity->opap)
++ free(entity->opap);
++ if (entity->global)
++ free(entity->global);
++ if (entity->msg)
++ free(entity->msg);
++ if (entity->before_author)
++ free(entity->before_author);
++ if (entity->after_author)
++ free(entity->after_author);
++ if (entity->key)
++ free(entity->key);
++ free_svcs(entity->svcs);
+ }
+
++static void free_hashtable TAC_ARGS((void **hashtable));
++
+ static void
+-free_hoststruct(host)
+-HOST *host;
++free_hashtable(hashtable)
++void **hashtable;
+ {
+- if (debug & DEBUG_CLEAN_FLAG)
+- report(LOG_DEBUG, "free %s",
+- host->name);
+-
+- if (host->name)
+- free(host->name);
+-
+- if (host->key)
+- free(host->key);
++ int i;
++ ENTITY *entity,**entityp;
+
+- if (host->type)
+- free(host->type);
++ for (i = 0; i < HASH_TAB_SIZE; i++) {
++ entityp = (ENTITY **) (hashtable+i);
++ while ((entity = *entityp)) {
++ *entityp = entity->hash;
++ free_entity(entity);
++ free(entity);
++ }
++ }
+ }
+
++
+ /*
+ * Exported routines
+ */
+
++void cfg_clean_config TAC_ARGS((void));
++
+ /* Free all allocated structures preparatory to re-reading the config file */
+ void
+ cfg_clean_config()
+ {
+- int i;
+- USER *entry, *next;
+- HOST *host_entry,*hn;
++ struct enlist_entity_item *enlist_entity_item;
+
+ if (authen_default) {
+ free(authen_default);
+ authen_default = NULL;
+ }
+
++ if (algorithm_recursive) {
++ algorithm_recursive = 0;
++ }
++
+ if (authen_default_method) {
+ authen_default_method = 0;
+ }
+@@ -438,43 +580,21 @@ cfg_clean_config()
+ 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;
+- }
++ free_hashtable( usertable);
++ free_hashtable( hosttable);
++ free_hashtable(grouptable);
+
+- /* 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;
++ while (enlist_entity_list) {
++ enlist_entity_item = enlist_entity_list;
++ enlist_entity_list = enlist_entity_item->next;
++ free_enlist_entity_item(enlist_entity_item);
++ free(enlist_entity_item);
+ }
++ enlist_entity_list_tailp = &enlist_entity_list;
+ }
+
++static int parse_permission TAC_ARGS((void));
++
+ static int
+ parse_permission()
+ {
+@@ -490,6 +610,8 @@ parse_permission()
+ return (symbol);
+ }
+
++static int parse TAC_ARGS((int symbol));
++
+ static int
+ parse(symbol)
+ int symbol;
+@@ -505,9 +627,13 @@ int symbol;
+ return (0);
+ }
+
++static int parse_opt_svc_default TAC_ARGS((void));
++
+ static int
+ parse_opt_svc_default()
+ {
++ int retval;
++
+ if (sym_code != S_default) {
+ return (0);
+ }
+@@ -515,14 +641,30 @@ parse_opt_svc_default()
+ parse(S_default);
+ parse(S_svc);
+ parse(S_separator);
+- if (sym_code == S_permit) {
+- parse(S_permit);
+- return (S_permit);
++
++ switch (sym_code) {
++ default:
++ parse_error("expecting 'permit', 'deny' or 'default' but found '%s' on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_default:
++ if (!algorithm_recursive) {
++ parse_error("'default service = %s' supported only if set top level 'authorization = recursive', on line %d",
++ sym_buf, sym_line);
++ return (1);
++ }
++ /* FALLTHRU */
++ case S_permit:
++ case S_deny:
++ retval = sym_code;
++ sym_get();
++ return (retval);
+ }
+- parse(S_deny);
+- return (S_deny);
+ }
+
++static int parse_opt_attr_default TAC_ARGS((void));
++
+ static int
+ parse_opt_attr_default()
+ {
+@@ -536,20 +678,19 @@ parse_opt_attr_default()
+ 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 TAC_ARGS((void));
++
+ static int
+ parse_decls()
+ {
+ no_user_dflt = 0; /* default if user doesn't exist */
++ algorithm_recursive = 0; /* use backward compatible alg. by default */
++ when_expr_root_init();
++ enlist_entity_list_tailp = &enlist_entity_list;
+
+ sym_code = 0;
+ rch();
+@@ -614,7 +755,7 @@ parse_decls()
+ case S_db:
+ #endif
+ #ifdef USE_LDAP
+- case S_ldap;
++ case S_ldap:
+ #endif
+ #ifdef USE_PAM
+ case S_pam:
+@@ -642,6 +783,26 @@ parse_decls()
+ continue;
+ }
+
++ case S_authorization:
++ sym_get();
++ parse(S_separator);
++ switch (sym_code) {
++ default:
++ parse_error("expecting 'first' or 'recursive' but found '%s' on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_first:
++ parse(S_first);
++ algorithm_recursive = 0;
++ continue;
++
++ case S_recursive:
++ parse(S_recursive);
++ algorithm_recursive = 1;
++ continue;
++ }
++
+ case S_key:
+ /* Process a key declaration. */
+ sym_get();
+@@ -656,17 +817,12 @@ parse_decls()
+ sym_get();
+ continue;
+
+- case S_host:
+- parse_host();
+- continue;
+-
+ case S_user:
++ case S_host:
+ case S_group:
+- parse_user();
++ parse_entity(sym_code);
+ continue;
+
+- /* case S_host: parse_host(); continue; */
+-
+ default:
+ parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
+ return (1);
+@@ -674,8 +830,6 @@ parse_decls()
+ }
+ }
+
+-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 */
+@@ -688,147 +842,511 @@ sym_get(); parse(S_separator); if (field) { \
+ } \
+ field = tac_strdup(sym_buf);
+
+-static int
+-parse_host()
++static struct expr *when_expr_root;
++#define WHEN_EXPR_ROOT_SANE() \
++ (when_expr_root && !when_expr_root->next && when_expr_root->type==S_and)
++#define WHEN_EXPR_ROOT_EMPTY() \
++ (WHEN_EXPR_ROOT_SANE() && !when_expr_root->u.and_or.child_first)
++
++static struct expr *parse_expr_node TAC_ARGS((int single_item));
++
++static struct expr *
++parse_expr_node(single_item)
++int single_item;
+ {
+- HOST *h;
+- HOST *host = (HOST *) tac_malloc(sizeof(HOST));
+- int save_sym;
+- char buf[MAX_INPUT_LINE_LEN];
++ struct expr *expr_root = NULL;
++ struct expr **succ_exprp = &expr_root;
++ struct expr *expr;
+
+- bzero(host, sizeof(HOST));
++ while (1) {
++ switch (sym_code) {
+
++ case S_not:
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ *succ_exprp = expr;
++ expr->next = NULL;
++ succ_exprp = &expr->next;
++ expr->type = S_not;
+ sym_get();
+- parse(S_separator);
+- host->name = tac_strdup(sym_buf);
+- host->line = sym_line;
++ expr->u.not.child = parse_expr_node(1 /* single_item */);
++ if (!expr->u.not.child) {
++ free_expr(expr_root);
++ return (NULL);
++ }
++ break;
++
++ case S_user:
++ case S_host:
++ case S_group:
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ *succ_exprp = expr;
++ expr->next = NULL;
++ succ_exprp = &expr->next;
++ expr->type = sym_code;
++ sym_get();
++ expr->u.entity.name = tac_strdup(sym_buf);
++ sym_get();
++ expr->u.entity.entity = NULL; /* not known yet */
++ break;
+
+- h = hash_add_entry(hosttable, (void *) host);
++ case S_openparen:
++ sym_get();
++ expr = parse_expr_node(0 /* single_item */);
++ *succ_exprp = expr;
++ parse(S_closeparen);
+
+- if (h) {
+- parse_error("multiply defined %s on lines %d and %d",
+- host->name, h->line, sym_line);
+- return (1);
++ if (expr->next) {
++ report(LOG_ERR, "Illegal filled next field of parsed parenthesed expr");
++ free_expr(expr_root);
++ return (NULL);
+ }
++ succ_exprp = &expr->next;
++ break;
+
+- sym_get();
+- parse(S_openbra);
++ default:
++ parse_error("expecting 'not', 'user', 'host', 'group' or '(' but found '%s' on line %d",
++ sym_buf, sym_line);
++ free_expr(expr_root);
++ return (NULL);
++ }
++
++ if (single_item) /* used by 'not' operator with high precedence */
++ return (expr_root);
+
+- 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);
++
++ case S_and:
++ case S_or:
++ if (expr_root->type == (sym_code==S_and ? S_or : S_and)) {
++ parse_error("ambiguous use of 'and' together with 'or', parentheses required on line %d",
++ sym_line);
++ free_expr(expr_root);
++ return (NULL);
++ }
++ if (expr_root->type != sym_code) {
++ expr = (struct expr *) tac_malloc(sizeof(struct expr));
++ expr->line = sym_line;
++ expr->next = NULL;
++ expr->type = sym_code;
++ expr->u.and_or.child_first = expr_root;
++ expr_root = expr;
++ }
+ sym_get();
+ continue;
++ }
+
+- case S_closebra:
+- parse(S_closebra);
+- return (0);
++ return(expr_root);
++ }
++}
+
+- default:
+- parse_error("Unrecognised keyword %s for host %s on line %d",
+- sym_buf, host->name,sym_line);
++static struct expr *parse_when_decl TAC_ARGS((void));
+
+- return (0);
++static struct expr *
++parse_when_decl()
++{
++ parse(S_when);
++ if (!algorithm_recursive) {
++ parse_error("'when' conditionals supported only if set top level 'authorization = recursive', on line %d",
++ sym_line);
++ return (NULL);
+ }
+- } /* while */
+-} /* finish parse_host */
++ parse(S_separator);
++ return (parse_expr_node(0 /* single_item */));
++}
++
++static void when_expr_root_init TAC_ARGS((void));
++
++static void
++when_expr_root_init()
++{
++ free_expr(when_expr_root);
++ when_expr_root = new_expr(S_and);
++}
+
++static int push_parsed_when_decl TAC_ARGS((void));
+
+ static int
+-parse_user()
++push_parsed_when_decl()
+ {
+- USER *n;
+- int isuser;
+- USER *user = (USER *) tac_malloc(sizeof(USER));
+- int save_sym;
+- char **fieldp;
+- char buf[MAX_INPUT_LINE_LEN];
++ struct expr *parsed_expr;
+
+- bzero(user, sizeof(USER));
++ parsed_expr = parse_when_decl();
++ if (!parsed_expr)
++ return (1);
++ if (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_ERR, "INTERNAL: when_expr_root not valid during push_parsed_when_decl()!");
++ free_expr(parsed_expr);
++ return (1);
++ }
++ if (parsed_expr->next) {
++ report(LOG_ERR, "INTERNAL: Illegal filled next field of parsed expr");
++ free_expr(parsed_expr);
++ return (1);
++ }
++ parsed_expr->next = when_expr_root->u.and_or.child_first;
++ when_expr_root->u.and_or.child_first = parsed_expr;
++ when_expr_root->line = parsed_expr->line;
++ return (0);
++}
+
+- isuser = (sym_code == S_user);
++static int pop_when_decl TAC_ARGS((void));
+
+- sym_get();
+- parse(S_separator);
+- user->name = tac_strdup(sym_buf);
+- user->line = sym_line;
++static int
++pop_when_decl()
++{
++ struct expr *first_expr;
+
+- 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 (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_ERR, "INTERNAL: when_expr_root not valid during pop_when_decl()!");
++ return (1);
+ }
+-
+- if (n) {
+- parse_error("multiply defined %s %s on lines %d and %d",
+- isuser ? "user" : "group",
+- user->name, n->line, sym_line);
++ first_expr = when_expr_root->u.and_or.child_first;
++ if (!first_expr) {
++ report(LOG_ERR, "No expr in stack and pop_when_decl() called");
+ return (1);
+ }
+- sym_get();
+- parse(S_openbra);
++ when_expr_root->u.and_or.child_first = first_expr->next;
++ first_expr->next = NULL;
++ free_expr(first_expr);
++ return (0);
++}
+
+- /* Is the default deny for svcs or cmds to be overridden? */
+- user->svc_dflt = parse_opt_svc_default();
++static struct expr *copy_current_when_decl TAC_ARGS((void));
+
+- while (1) {
+- switch (sym_code) {
+- case S_eof:
+- return (0);
++static struct expr *
++copy_current_when_decl()
++{
++ return (dupl_expr(when_expr_root));
++}
+
+- case S_time:
+- ASSIGN(user->time);
+- sym_get();
+- continue;
++static struct expr *when_expr_dungeon;
+
+- case S_before:
+- sym_get();
+- parse(S_authorization);
+- if (user->before_author)
+- free(user->before_author);
+- user->before_author = tac_strdup(sym_buf);
++static void starve_when_decl TAC_ARGS((void));
++
++static void
++starve_when_decl()
++{
++ if (!WHEN_EXPR_ROOT_SANE()) {
++ report(LOG_WARNING, "INTERNAL: when_expr_root not sane during starve_when_decl!");
++ }
++ when_expr_root->next = when_expr_dungeon;
++ when_expr_dungeon = when_expr_root;
++ when_expr_root = NULL;
++ when_expr_root_init();
++}
++
++static int feed_when_decl TAC_ARGS((void));
++
++static int
++feed_when_decl()
++{
++ if (!when_expr_dungeon) {
++ report(LOG_ERR, "INTERNAL: No expr in dungeon and feed_when_decl() called");
++ return (1);
++ }
++ if (!WHEN_EXPR_ROOT_EMPTY()) {
++ report(LOG_WARNING, "INTERNAL: Some 'when' expression found still pushed in dungeon during feed_when_decl()!");
++ }
++ free_expr(when_expr_root);
++ when_expr_root = when_expr_dungeon;
++ when_expr_dungeon = when_expr_dungeon->next;
++ when_expr_root->next = NULL;
++ return (0);
++}
++
++ENTITY *entity_lookup TAC_ARGS((int type, const char *name));
++
++ENTITY *
++entity_lookup(type, name)
++int type;
++const char *name;
++{
++ return (hash_lookup(entity_type_to_hashtable(type), name));
++}
++
++static int enlist_entity_connect TAC_ARGS((void));
++
++static int
++enlist_entity_connect()
++{
++ struct enlist_entity_item *item;
++ ENTITY *parent_entity, *child_entity;
++
++ while ((item=enlist_entity_list)) {
++
++ parent_entity = entity_lookup(item->parent_type, item->parent);
++ if (!parent_entity) {
++ parse_error("Entity %s %s not defined, referenced as parent on line %d",
++ entity_type_to_string(item->parent_type), item->parent, item->line);
++ return (1);
++ }
++ child_entity = entity_lookup(item-> child_type, item-> child);
++ if (!child_entity) {
++ child_entity = new_entity(item->child_type, item->child, item->line);
++ if (!child_entity)
++ return (1); /* 'hash_add_entry()' conflict */
++ item->child = NULL; /* don't free string ref'ed from 'child_entity'! */
++ }
++
++ if (!enlist_entity_direct(parent_entity, child_entity, item->when))
++ return (1); /* entities not found */
++
++ enlist_entity_list = item->next;
++ item->when = NULL;
++ free_enlist_entity_item(item);
++ free(item);
++ }
++ enlist_entity_list_tailp = &enlist_entity_list;
++ return (0);
++}
++
++static void enlist_entity TAC_ARGS((int parent_type, const char *parent, int child_type, const char *child));
++
++static void
++enlist_entity(parent_type, parent, child_type, child)
++int parent_type;
++const char *parent;
++int child_type;
++const char *child;
++{
++ struct enlist_entity_item *item =
++ (struct enlist_entity_item *) tac_malloc(sizeof(struct enlist_entity_item));
++
++ item->next = NULL;
++ *enlist_entity_list_tailp = item;
++ enlist_entity_list_tailp = &item->next;
++
++ item->parent_type = parent_type;
++ item->parent = tac_strdup(parent);
++ item-> child_type = child_type;
++ item->child = tac_strdup(child);
++ item->when = copy_current_when_decl();
++ item->line = sym_line;
++}
++
++static int parse_entity_spec TAC_ARGS((void));
++
++/* returns 0 for error, otherwise S_user, S_host or S_group; sym_buf filled */
++static int
++parse_entity_spec()
++{
++ int retval;
++
++ if (sym_code != S_user
++ && sym_code != S_host
++ && sym_code != S_group
++ ) {
++ parse_error("Expecting 'user', 'host' or ' group' as entity specification, found %s on line %d",
++ sym_buf, sym_line);
++ return (0);
++ }
++
++ retval = sym_code;
+ sym_get();
+- continue;
+
+- case S_after:
++ return (retval);
++}
++
++static int parse_conditional_block_item TAC_ARGS((ENTITY *entity));
++
++static int
++parse_conditional_block_item(entity)
++ENTITY *entity;
++{
++ switch (sym_code) {
++ case S_eof:
++ return (1);
++
++ /* case S_closebra: not needed, handled by our caller parse_conditional_block() */
++
++ default:
++ parse_error("Unrecognised keyword %s for entity on line %d",
++ sym_buf, sym_line);
++ return (1);
++
++ case S_member:
+ sym_get();
+- parse(S_authorization);
+- if (user->after_author)
+- free(user->after_author);
+- user->after_author = tac_strdup(sym_buf);
++ parse(S_separator);
++ enlist_entity(S_group, sym_buf, entity->type, entity->name);
+ sym_get();
+- continue;
++ break;
++
++ case S_enlist: {
++ int parsed_entity_type;
++
++ if (entity->type != S_group) {
++ parse_error("'enlist' keyword allowed only in 'group' section on line %d",
++ sym_line);
++ return (1);
++ }
++ sym_get();
++ parse(S_separator);
++ parsed_entity_type = parse_entity_spec();
++ if (!parsed_entity_type)
++ return (1);
++ enlist_entity(entity->type, entity->name, parsed_entity_type, sym_buf);
++ sym_get();
++ break;
++ }
+
+ case S_svc:
+ case S_cmd:
+
+- if (user->svcs) {
++ if (entity->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)
++ for (p=entity->svcs; p->next; p=p->next)
+ /* NULL STMT */;
+ p->next = parse_svcs();
+ } else {
+- user->svcs = parse_svcs();
++ entity->svcs = parse_svcs();
++ }
++ break;
++
++ case S_when:
++ if (parse_conditional_block(entity))
++ return (1);
++ break;
++ }
++
++ return (0);
++}
++
++static int parse_conditional_block TAC_ARGS((ENTITY *entity));
++
++static int
++parse_conditional_block(entity)
++ENTITY *entity;
++{
++ int retval = -1 /* GCC paranoia */;
++
++ if (push_parsed_when_decl())
++ return (1);
++ parse(S_openbra);
++
++ while (1) {
++ if (sym_code == S_closebra) {
++ sym_get();
++ retval = 0; /* success */
++ break;
++ }
++
++ if (parse_conditional_block_item(entity)) {
++ retval = 1; /* failure */
++ break;
++ }
++ }
++
++ if (pop_when_decl())
++ return (1);
++
++ return (retval);
++}
++
++/* passed 'name' WILL be directly stored to returned ENTITY, don't touch it! */
++
++ENTITY *new_entity TAC_ARGS((int type, char *name, int line));
++
++ENTITY *
++new_entity(type, name, line)
++int type;
++char *name;
++int line;
++{
++ ENTITY *entity = (ENTITY *) tac_malloc(sizeof(ENTITY));
++ ENTITY *hash_conflict;
++
++ bzero(entity, sizeof(ENTITY));
++ tac_list_init(&entity->to_parent_membership_list);
++ tac_list_init(&entity->to_child_membership_list );
++ entity->to_child_membership_num = 0;
++ scan_init_entity(entity);
++
++ entity->type = type;
++ entity->name = name;
++ entity->line = line;
++
++ hash_conflict = hash_add_entry(entity_type_to_hashtable(type), (void *) entity);
++ if (hash_conflict) {
++ parse_error("multiply defined %s %s on lines %d and %d",
++ entity_type_to_string(type),
++ entity->name, hash_conflict->line, sym_line);
++ free (entity);
++ return (NULL);
++ }
++
++ return (entity);
++}
++
++static int parse_entity TAC_ARGS((int entity_type));
++
++static int
++parse_entity(entity_type)
++int entity_type;
++{
++ ENTITY *entity;
++ int save_sym;
++ char **fieldp = NULL /* GCC paranoia */;
++ char buf[MAX_INPUT_LINE_LEN];
++
++ sym_get();
++ parse(S_separator);
++
++ entity = new_entity(entity_type, tac_strdup(sym_buf) /* name */, sym_line /* line */);
++ if (!entity)
++ return (1); /* 'hash_add_entry()' conflict, 'tac_strdup(sym_buf)' leaked! */
++
++ sym_get();
++ parse(S_openbra);
++
++ /* Is the default deny for svcs or cmds to be overridden? */
++ entity->svc_dflt = parse_opt_svc_default();
++
++ while (1) {
++ if (entity_type != S_user)
++ switch (sym_code) {
++ case S_key:
++ ASSIGN(entity->key);
++ sym_get();
++ continue;
+ }
++
++ switch (sym_code) {
++ case S_eof:
++ return (0);
++
++ case S_time:
++ ASSIGN(entity->time);
++ sym_get();
++ continue;
++
++ case S_before:
++ sym_get();
++ parse(S_authorization);
++ if (entity->before_author)
++ free(entity->before_author);
++ entity->before_author = tac_strdup(sym_buf);
++ sym_get();
++ continue;
++
++ case S_after:
++ sym_get();
++ parse(S_authorization);
++ if (entity->after_author)
++ free(entity->after_author);
++ entity->after_author = tac_strdup(sym_buf);
++ sym_get();
+ continue;
+
+ case S_login:
+- if (user->login) {
++ if (entity->login) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+- codestring(sym_code), user->login,
++ codestring(sym_code), entity->login,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+@@ -837,15 +1355,15 @@ parse_user()
+ switch(sym_code) {
+
+ case S_skey:
+- user->login = tac_strdup(sym_buf);
++ entity->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;
++ entity->login = tac_strdup(nopasswd_str);
++ entity->nopasswd = 1;
+ break;
+
+ case S_file:
+@@ -860,7 +1378,7 @@ parse_user()
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+- user->login = tac_strdup(buf);
++ entity->login = tac_strdup(buf);
+ break;
+
+ default:
+@@ -878,9 +1396,9 @@ parse_user()
+ continue;
+
+ case S_pap:
+- if (user->pap) {
++ if (entity->pap) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+- codestring(sym_code), user->pap,
++ codestring(sym_code), entity->pap,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+@@ -896,11 +1414,11 @@ parse_user()
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+- user->pap = tac_strdup(buf);
++ entity->pap = tac_strdup(buf);
+ break;
+
+ sprintf(buf, "%s ", sym_buf);
+- user->pap = tac_strdup(buf);
++ entity->pap = tac_strdup(buf);
+ break;
+
+ default:
+@@ -918,23 +1436,17 @@ parse_user()
+ continue;
+
+ case S_name:
+- ASSIGN(user->full_name);
+- sym_get();
+- continue;
+-
+- case S_member:
+- ASSIGN(user->member);
++ ASSIGN(entity->full_name);
+ sym_get();
+ continue;
+
+-
+ case S_expires:
+- ASSIGN(user->expires);
++ ASSIGN(entity->expires);
+ sym_get();
+ continue;
+
+ case S_message:
+- ASSIGN(user->msg);
++ ASSIGN(entity->msg);
+ sym_get();
+ continue;
+
+@@ -952,20 +1464,32 @@ parse_user()
+ parse(S_cleartext);
+ strcat(buf, sym_buf);
+
+- if (save_sym == S_arap)
+- fieldp = &user->arap;
+- if (save_sym == S_chap)
+- fieldp = &user->chap;
++ switch (save_sym) {
++ case S_arap:
++ fieldp = &entity->arap;
++ break;
++ case S_chap:
++ fieldp = &entity->chap;
++ break;
+ #ifdef MSCHAP
+- if (save_sym == S_mschap)
+- fieldp = &user->mschap;
++ case S_mschap:
++ fieldp = &entity->mschap;
++ break;
+ #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;
++ case S_pap:
++ fieldp = &entity->pap;
++ break;
++ case S_opap:
++ fieldp = &entity->opap;
++ break;
++ case S_global:
++ fieldp = &entity->global;
++ break;
++ default:
++ report(LOG_ERR, "INTERNAL: fieldp not recognized (on line %d)",
++ sym_line);
++ continue;
++ }
+
+ if (*fieldp) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+@@ -984,7 +1508,7 @@ parse_user()
+ case S_maxsess:
+ sym_get();
+ parse(S_separator);
+- if (sscanf(sym_buf, "%d", &user->maxsess) != 1) {
++ if (sscanf(sym_buf, "%d", &entity->maxsess) != 1) {
+ parse_error("expecting integer, found '%s' on line %d",
+ sym_buf, sym_line);
+ }
+@@ -997,16 +1521,14 @@ parse_user()
+ 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);
++ if (parse_conditional_block_item(entity))
++ return (0); /* error message already printed */
+ }
+ }
+ }
+
+-static NODE *parse_attrs();
+-static NODE *parse_cmd_matches();
++static NODE *parse_svcs TAC_ARGS((void));
+
+ static NODE *
+ parse_svcs()
+@@ -1034,11 +1556,16 @@ parse_svcs()
+
+ sym_get();
+ parse(S_openbra);
+-
++ starve_when_decl();
+ result->value1 = parse_cmd_matches();
++ parse(S_closebra);
++ if (feed_when_decl())
++ tac_exit(1); /* no error return possibility */
++
+ result->type = N_svc_cmd;
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
+
+- parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+ }
+@@ -1075,25 +1602,52 @@ parse_svcs()
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ }
++
+ sym_get();
+ parse(S_openbra);
++ starve_when_decl();
++
+ result->dflt = parse_opt_attr_default();
+ result->value = parse_attrs();
++
+ parse(S_closebra);
++ feed_when_decl();
++
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
++
+ result->next = parse_svcs();
+ return (result);
+ }
+
+-/* <cmd-match> := <permission> <string> */
++/* <cmd_match> := <permission> <string> */
++
++static NODE *parse_cmd_matches TAC_ARGS((void));
+
+ static NODE *
+ parse_cmd_matches()
+ {
++ NODE *retval = NULL, **succp = &retval;
+ NODE *result;
+
+- if (sym_code != S_permit && sym_code != S_deny) {
+- return (NULL);
+- }
++ for (;;) {
++ switch (sym_code) {
++ default:
++ return (retval);
++
++ case S_when:
++ if (push_parsed_when_decl())
++ tac_exit(1); /* no error return possibility */
++ parse(S_openbra);
++ result = parse_cmd_matches();
++ parse(S_closebra);
++ if (pop_when_decl())
++ tac_exit(1); /* no error return possibility */
++ break;
++
++ case S_permit:
++ case S_deny:
++
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+@@ -1102,7 +1656,20 @@ parse_cmd_matches()
+ result->type = (parse_permission() == S_permit) ? N_permit : N_deny;
+ result->value = tac_strdup(sym_buf);
+
+- result->value1 = (void *) regcomp(result->value);
++#ifdef WITH_INCLUDED_REGEX
++
++ result->value1 = (void *) tac_regcomp(result->value);
++
++#else /* WITH_INCLUDED_REGEX */
++
++ result->value1 = tac_malloc(sizeof(regex_t));
++ if (regcomp(result->value1, result->value /* regex */, REG_NOSUB /* cflags */)) {
++ free(result->value1);
++ result->value1 = NULL;
++ }
++
++#endif /* WITH_INCLUDED_REGEX */
++
+ if (!result->value1) {
+ report(LOG_ERR, "in regular expression %s on line %d",
+ sym_buf, sym_line);
+@@ -1110,30 +1677,56 @@ parse_cmd_matches()
+ }
+ sym_get();
+
+- result->next = parse_cmd_matches();
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
+
+- return (result);
++ result->next = NULL;
++ }
++ *succp = result;
++ while (result->next)
++ result = result->next; /* skip parsed chain from parse_cmd_matches() */
++ succp = &result->next;
++ }
++ /* NOTREACHED */
+ }
+
++static NODE *parse_attrs TAC_ARGS((void));
++
+ static NODE *
+ parse_attrs()
+ {
++ NODE *retval = NULL, **succp = &retval;
+ NODE *result;
+ char buf[MAX_INPUT_LINE_LEN];
+- int optional = 0;
++ int optional;
+
+- if (sym_code == S_closebra) {
+- return (NULL);
+- }
++ for (;;) {
++ optional = 0;
++
++ switch (sym_code) {
++ case S_closebra:
++ return (retval);
++
++ case S_when:
++ if (push_parsed_when_decl())
++ tac_exit(1); /* no error return possibility */
++ parse(S_openbra);
++ result = parse_attrs();
++ parse(S_closebra);
++ if (pop_when_decl())
++ tac_exit(1); /* no error return possibility */
++ break;
++
++ case S_optional:
++ optional = 1;
++ sym_get();
++ /* FALLTHRU */
++ default:
+ 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);
+@@ -1144,13 +1737,22 @@ parse_attrs()
+ parse(S_string);
+
+ result->value = tac_strdup(buf);
+- result->next = parse_attrs();
+- return (result);
++
++ result->when = copy_current_when_decl();
++ expr_sink_register(result->when);
++
++ result->next = NULL;
++ }
++ *succp = result;
++ while (result->next)
++ result = result->next; /* skip parsed chain from parse_attrs() */
++ succp = &result->next;
++ }
++ /* NOTREACHED */
+ }
+
+
+-static void
+- getsym();
++static void sym_get TAC_ARGS((void));
+
+ static void
+ sym_get()
+@@ -1163,9 +1765,11 @@ sym_get()
+ }
+ }
+
++static char *sym_buf_add TAC_ARGS((int c));
++
+ static char *
+ sym_buf_add(c)
+-char c;
++int c; /* promoted "char" type */
+ {
+ if (sym_pos >= MAX_INPUT_LINE_LEN) {
+ sym_buf[MAX_INPUT_LINE_LEN-1] = '\0';
+@@ -1180,6 +1784,8 @@ char c;
+ return(sym_buf);
+ }
+
++static void getsym TAC_ARGS((void));
++
+ static void
+ getsym()
+ {
+@@ -1220,6 +1826,18 @@ next:
+ rch();
+ return;
+
++ case '(':
++ strcpy(sym_buf, "(");
++ sym_code = S_openparen;
++ rch();
++ return;
++
++ case ')':
++ strcpy(sym_buf, ")");
++ sym_code = S_closeparen;
++ rch();
++ return;
++
+ case '#':
+ while ((sym_ch != '\n') && (sym_ch != EOF))
+ rch();
+@@ -1304,6 +1922,8 @@ next:
+ }
+ }
+
++static void rch TAC_ARGS((void));
++
+ static void
+ rch()
+ {
+@@ -1318,201 +1938,208 @@ rch()
+ }
+
+
+-/* For a user or group, find the value of a field. Does not recurse. */
+-VALUE
+-get_value(user, field)
+-USER *user;
++static VALUE get_value TAC_ARGS((ENTITY *entity, int field));
++
++/* Find the value of a field. Does not recurse. */
++static VALUE
++get_value(entity, field)
++ENTITY *entity;
+ int field;
+ {
+ VALUE v;
+
++ v.pval = NULL; /* do both just for sure... */
+ v.intval = 0;
+
+- if (!user) {
+- parse_error("get_value: illegal user");
++ if (!entity) {
++ parse_error("get_value: illegal entity");
+ return (v);
+ }
+ switch (field) {
+
+ case S_name:
+- v.pval = user->name;
++ v.pval = entity->name;
+ break;
+
+ case S_login:
+- v.pval = user->login;
++ v.pval = entity->login;
+ break;
+
+ case S_global:
+- v.pval = user->global;
+- break;
+-
+- case S_member:
+- v.pval = user->member;
++ v.pval = entity->global;
+ break;
+
+ case S_expires:
+- v.pval = user->expires;
++ v.pval = entity->expires;
+ break;
+
+ case S_arap:
+- v.pval = user->arap;
++ v.pval = entity->arap;
+ break;
+
+ case S_chap:
+- v.pval = user->chap;
++ v.pval = entity->chap;
+ break;
+
+ #ifdef MSCHAP
+ case S_mschap:
+- v.pval = user->mschap;
++ v.pval = entity->mschap;
+ break;
+ #endif /* MSCHAP */
+
+ case S_pap:
+- v.pval = user->pap;
++ v.pval = entity->pap;
+ break;
+
+ case S_opap:
+- v.pval = user->opap;
++ v.pval = entity->opap;
+ break;
+
+ case S_message:
+- v.pval = user->msg;
++ v.pval = entity->msg;
+ break;
+
+ case S_svc:
+- v.pval = user->svcs;
++ v.pval = entity->svcs;
+ break;
+
+ case S_before:
+- v.pval = user->before_author;
++ v.pval = entity->before_author;
+ break;
+
+ case S_after:
+- v.pval = user->after_author;
++ v.pval = entity->after_author;
+ break;
+
+ case S_svc_dflt:
+- v.intval = user->svc_dflt;
++ v.intval = entity->svc_dflt;
+ break;
+
+ #ifdef MAXSESS
+ case S_maxsess:
+- v.intval = user->maxsess;
++ v.intval = entity->maxsess;
+ break;
+ #endif
+
+ case S_nopasswd:
+- v.intval = user->nopasswd;
++ v.intval = entity->nopasswd;
+ break;
+
+ case S_time:
+- v.pval = user->time;
++ v.pval = entity->time;
+ break;
+
+- default:
++ case S_key:
++ if (entity->type == S_user) {
++ report(LOG_ERR, "get_value: S_key field not supported in %s %s",
++ entity_type_to_string(entity->type), entity->name);
++ v.pval = NULL;
++ return(v);
++ }
++ v.pval = entity->key;
++ 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;
++
++/* Internal graph scanning routines */
++
++static enum value_scan_func_result value_scan TAC_ARGS((int type, const char *name, int recurse, value_scan_func_t func, void *func_data));
++
++static enum value_scan_func_result
++value_scan(type, name, recurse, func, func_data)
++int type;
++const char *name;
++int recurse;
++value_scan_func_t func;
++void *func_data;
+ {
+- 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;
++ ENTITY *entity;
+
+- case S_key:
+- v.pval = host->key;
+- break;
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "value_scan: find %s %s, recurse=%d",
++ entity_type_to_string(type), name, recurse);
+
+- default:
+- report(LOG_ERR, "get_value: unknown field %d", field);
+- break;
++ entity = entity_lookup(type, name);
++ if (!entity) {
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "value_scan: no %s named %s",
++ entity_type_to_string(type), name);
++ return (VSFR_CONTINUE);
+ }
+- return (v);
+-}
+
++ return (value_scan_entity(entity, recurse, func, func_data));
++}
+
+ /* 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;
++static int circularity_check_failed;
+
+- if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "circularity_check: user=%s", user->name);
++static void circularity_check_fail TAC_ARGS((struct membership *membership));
++
++static void
++circularity_check_fail(membership)
++struct membership *membership;
++{
++ ENTITY *entity;
++
++ circularity_check_failed = 1;
+
+- /* Initialise all groups "seen" flags to zero */
+- for (q = groups; *q; q++) {
+- group = *q;
+- group->flags &= ~FLAG_SEEN;
++ report(LOG_ERR, "recursively defined groups:");
++ while (membership) {
++ entity = MEMBERSHIP_TO_CHILD_ENTITY(membership);
++ report(LOG_ERR, "%s %s",
++ entity_type_to_string(entity->type), entity->name);
++ membership = value_scan_backward(entity);
+ }
++}
+
+- entry = user;
++static enum value_scan_func_result circularity_check_func TAC_ARGS((ENTITY *entity, void *func_data));
+
+- while (entry) {
+- /* check groups we are a member of */
+- char *groupname = entry->member;
++static enum value_scan_func_result
++circularity_check_func(entity, func_data /* unused */)
++ENTITY *entity;
++void *func_data;
++{
++ /* only useful to speedup case of failure */
++ if (circularity_check_failed)
++ return (VSFR_FOUND);
+
+- if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "\tmember of group %s",
+- groupname ? groupname : "<none>");
++ return (VSFR_CONTINUE);
++}
+
++static int circularity_check TAC_ARGS((void));
+
+- /* if not a member of any groups, go on to next user */
+- if (!groupname)
+- break;
++static int
++circularity_check()
++{
++ ENTITY *entity;
++ ENTITY **users_base = (ENTITY **) hash_get_entries(usertable);
++ ENTITY **users;
+
+- 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");
++ /* users */
++ for (users = users_base; *users; users++) {
++ entity = *users;
+
+- /* 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;
+- }
++ if (debug & DEBUG_PARSE_FLAG)
++ report(LOG_DEBUG, "circularity_check: user=%s", entity->name);
++
++ circularity_check_failed = 0;
++ value_scan_forward_seen_hook = circularity_check_fail;
++ value_scan_entity(entity, TAC_PLUS_RECURSE,
++ (value_scan_func_t) circularity_check_func, NULL /* func_data-unused */);
++ value_scan_forward_seen_hook = NULL;
++ if (circularity_check_failed)
++ break;
+ }
+- free(users);
+- free(groups);
+- return (0);
++ free(users_base);
++ return (circularity_check_failed);
+ }
+
+
+@@ -1525,147 +2152,85 @@ circularity_check()
+ 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;
++static VALUE cfg_get_value_VALUE; /* private */
+
+- 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);
+- }
++static enum value_scan_func_result cfg_get_value_func TAC_ARGS((ENTITY *entity, int *attrp));
+
++static enum value_scan_func_result
++cfg_get_value_func(entity,attrp /* func_data */)
++ENTITY *entity;
++int *attrp;
++{
+ /* found the entry. Lookup value from attr=value */
+- value = get_value(user, attr);
++ cfg_get_value_VALUE = get_value(entity, *attrp);
++ if (cfg_get_value_VALUE.pval)
++ return (VSFR_FOUND);
+
+- if (value.pval || !recurse) {
+- return (value);
+- }
+- /* no value. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++ return (VSFR_CONTINUE);
++}
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_value: recurse group = %s",
+- group->name);
++static VALUE cfg_get_value TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+- value = get_value(group, attr);
++static VALUE
++cfg_get_value(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
++{
++ if (debug & DEBUG_CONFIG_FLAG)
++ report(LOG_DEBUG, "cfg_get_value: type=%s name=%s attr=%s recurse=%d",
++ entity_type_to_string(type), name, codestring(attr), recurse);
+
+- if (value.pval) {
+- return (value);
+- }
+- /* still nothing. Check containing group and so on */
++ cfg_get_value_VALUE.pval = NULL;
++ value_scan(type, name, recurse,
++ (value_scan_func_t) cfg_get_value_func, &attr /* func_data */);
++ return (cfg_get_value_VALUE);
++}
+
+- 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 TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+-/* Wrappers for cfg_get_value */
+ int
+-cfg_get_intvalue(name, isuser, attr, recurse)
+-char *name;
+-int isuser, attr, recurse;
++cfg_get_intvalue(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
+ {
+- int val = cfg_get_value(name, isuser, attr, recurse).intval;
++ int val = cfg_get_value(type, name, 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 */
++const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
+
+- 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;
++const char *
++cfg_get_pvalue(type, name, attr, recurse)
++int type;
++const char *name;
++int attr, recurse;
+ {
+- char *p = cfg_get_hvalue(name, attr).pval;
++ char *p = cfg_get_value(type, name, attr, recurse).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_phvalue: returns %s",
++ report(LOG_DEBUG, "cfg_get_pvalue: 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. */
++/* Read the config file and do some basic sanity checking on
++ * it. Return 1 if we find any errors.
++ */
++int cfg_read_config TAC_ARGS((const char *cfile));
+
++int
+ cfg_read_config(cfile)
+-char *cfile;
++const char *cfile;
+ {
+ sym_line = 1;
+
+@@ -1679,7 +2244,17 @@ char *cfile;
+ return (1);
+ }
+
+- if (circularity_check()) {
++ if (0
++ || enlist_entity_connect()
++ || expr_sink_commit()
++ /* circularity is allowed in the new fully-recursive algorithm */
++ || (!algorithm_recursive && circularity_check())
++ ) {
++ fclose(cf);
++ return (1);
++ }
++ if (!WHEN_EXPR_ROOT_EMPTY() || when_expr_dungeon) {
++ report(LOG_ERR, "Some 'when' expression found still pushed on stack");
+ fclose(cf);
+ return (1);
+ }
+@@ -1688,346 +2263,443 @@ char *cfile;
+ return (0);
+ }
+
+-/* return 1 if user exists, 0 otherwise */
++/* return 1 if user exists, 0 otherwise
++ */
++int cfg_user_exists TAC_ARGS((const char *username));
++
+ int
+ cfg_user_exists(username)
+-char *username;
++const char *username;
+ {
+- USER *user = (USER *) hash_lookup(usertable, username);
+-
+- return (user != NULL);
++ return (NULL != hash_lookup(usertable, username));
+ }
+
+ /* 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;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_expires TAC_ARGS((const char *username, int recurse));
+
++const char *
++cfg_get_expires(username, recurse)
++const char *username;
++int recurse;
+ {
+- return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse));
++ return (cfg_get_pvalue(S_user, username, 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 *
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse));
++
++const char *
+ cfg_get_timestamp(username, recurse)
+-char *username;
++const char *username;
++int recurse;
+ {
+- return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse));
++ return (cfg_get_pvalue(S_user, username, 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;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_login_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse));
++ return (cfg_get_pvalue(S_user, 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 */
++ * on, and so on, recursively if recurse is non-zero
++ */
++int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse));
++
+ int
+ cfg_get_user_nopasswd(user, recurse)
+- char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse));
++ return (cfg_get_intvalue(S_user, 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;
++ * on, and so on, recursively if recurse is non-zero
++ */
++const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_arap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_arap, recurse));
+ }
+
+-char *
+-cfg_get_chap_secret(user, recurse)
+-char *user;
++const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_chap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_chap, recurse));
+ }
+
+ #ifdef MSCHAP
+-char *
+-cfg_get_mschap_secret(user, recurse)
+-char *user;
+
++const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
++cfg_get_mschap_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_mschap, recurse));
+ }
++
+ #endif /* MSCHAP */
+
+-char *
++const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
+ cfg_get_pap_secret(user, recurse)
+-char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_pap, recurse));
+ }
+
+-char *
++const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse));
++
++const char *
+ cfg_get_opap_secret(user, recurse)
+-char *user;
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse));
++ return (cfg_get_pvalue(S_user, user, S_opap, recurse));
+ }
+
+ /* return the global password for the user (or the group, etc.) */
+
+-char *
+-cfg_get_global_secret(user, recurse)
+-char *user;
++const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse));
+
++const char *
++cfg_get_global_secret(user, recurse)
++const char *user;
++int recurse;
+ {
+- return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse));
++ return (cfg_get_pvalue(S_user, 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;
+
++const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse));
++
++const char *
++cfg_get_pam_service(user, recurse)
++const char *user;
++int recurse;
+ {
+- char *cfg_passwd;
+- char *p;
++ const char *cfg_passwd;
++ const char *p;
+
+-cfg_passwd = cfg_get_pap_secret(user, recurse);
++ cfg_passwd = cfg_get_pap_secret(user, recurse);
+
+-if (!cfg_passwd) {
++ if (!cfg_passwd)
+ cfg_passwd = cfg_get_global_secret(user, recurse);
+-}
+
+-if (!cfg_passwd && !cfg_user_exists(user)) {
++ 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);
++ p = tac_find_substring("pam ", cfg_passwd);
+
+-if(p) { /* We find PAM services */
++ if(p) { /* We find PAM services */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I get PAM sevice:%s",p);
+-return (p);
+-}
++ return (p);
++ }
+
+-if (debug & DEBUG_AUTHOR_FLAG)
++ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "No any PAM Sevice");
+
+-return(NULL);
++ 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 only read if the svctype 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);
++struct cfg_get_svc_node_param {
++ int svctype;
++ const char *protocol, *svcname;
++ NODE *node;
++ int retval;
++};
+
+- if (!user) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username);
+- return (NULL);
+- }
++static enum value_scan_func_result cfg_get_svc_node_func TAC_ARGS((ENTITY *entity, struct cfg_get_svc_node_param *param));
+
+- /* found the user entry. Find svc node */
+- for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
++static enum value_scan_func_result
++cfg_get_svc_node_func(entity, param /* func_data */)
++ENTITY *entity;
++struct cfg_get_svc_node_param *param;
++{
++ NODE *svc;
++ enum eval_result svc_default;
+
+- if (svc->type != type)
++ for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) {
++ if (svc->type != param->svctype)
+ continue;
+-
+- if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
++ if (param->svctype == N_svc_ppp && param->protocol && !STREQ(svc->value1, param->protocol))
+ continue;
+- }
+-
+- if (type == N_svc && !STREQ(svc->value1, svcname)) {
++ if (param->svctype == N_svc && param->svcname && !STREQ(svc->value1, param->svcname ))
++ continue;
++ if (expr_eval(svc->when) != ER_TRUE) /* expensive */
+ 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 : "");
++ cfg_nodestring(param->svctype),
++ param->protocol ? param->protocol : "",
++ param->svcname ? param->svcname : "");
+
+- return(svc);
++ param->node = svc;
++ param->retval = 1;
++ return (VSFR_FOUND);
+ }
+
+- if (!recurse) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+- return (NULL);
+- }
++ /* look at 'default service' settings */
++ svc_default = entity_svc_default(entity);
++ switch (svc_default) {
+
+- /* no matching node. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++ case ER_TRUE:
++ case ER_FALSE:
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG,
++ "cfg_get_svc_node: svc=%s protocol=%s svcname=%s forced %s by default service",
++ cfg_nodestring(param->svctype),
++ param->protocol ? param->protocol : "",
++ param->svcname ? param->svcname : "",
++ (svc_default == ER_TRUE ? "permit" : "deny"));
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s",
+- group->name);
++ param->retval = (svc_default == ER_TRUE);
++ return (VSFR_FOUND);
+
+- for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
++ default: /* shouldn't happen */
++ case ER_UNKNOWN:
++ return (VSFR_CONTINUE);
++ }
++ /* NOTREACHED */
++}
+
+- if (svc->type != type)
+- continue;
++int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep));
+
+- if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+- continue;
+- }
++int
++cfg_get_svc_node(username, svctype, protocol, svcname, recurse, nodep)
++const char *username;
++int svctype;
++const char *protocol;
++const char *svcname;
++int recurse;
++NODE **nodep;
++{
++ struct cfg_get_svc_node_param param;
++ enum value_scan_func_result vsfr;
+
+- if (type == N_svc && !STREQ(svc->value1, svcname)) {
+- continue;
+- }
++ param.svctype = svctype;
++ param.protocol = protocol;
++ param.svcname = svcname;
++ param.node = NULL;
++ param.retval = 0;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+- "cfg_get_svc_node: found %s proto=%s svcname=%s",
+- cfg_nodestring(type),
++ "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d",
++ username,
++ cfg_nodestring(svctype),
+ protocol ? protocol : "",
+- svcname ? svcname : "");
+-
+- return(svc);
+- }
++ svcname ? svcname : "",
++ recurse);
+
+- /* still nothing. Check containing group and so on */
++ vsfr = value_scan(S_user, username, recurse,
++ (value_scan_func_t) cfg_get_svc_node_func, ¶m /* func_data */);
++ if (nodep)
++ *nodep = param.node;
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
+- }
++ if (vsfr == VSFR_FOUND)
++ return (param.retval);
+
+- 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);
++ /* The service does not exist. Do the default */
++ return (cfg_no_user_permitted() ? 1 : 0);
+ }
+
+-/* Return a pointer to the node representing a set of command regexp
++/* Return a pointer to the node representing a set of command tac_regexp
+ matches for a user and command, handling recursion issues correctly */
+-NODE *
+-cfg_get_cmd_node(name, cmdname, recurse)
+-char *name, *cmdname;
+-int recurse;
+
++struct cfg_authorize_cmd_param {
++ const char *cmd;
++ const char *args;
++ enum eval_result result;
++};
++
++static enum value_scan_func_result cfg_authorize_cmd_func TAC_ARGS((ENTITY *entity, struct cfg_authorize_cmd_param *param));
++
++static enum value_scan_func_result
++cfg_authorize_cmd_func(entity, param /* func_data */)
++ENTITY *entity;
++struct cfg_authorize_cmd_param *param;
+ {
+- 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);
++ for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) {
++ NODE *node;
+
+- /* find the user/group entry */
+- user = (USER *) hash_lookup(usertable, name);
++ if (svc->type != N_svc_cmd)
++ continue;
++ if (!STREQ(svc->value, param->cmd))
++ continue;
++ if (expr_eval(svc->when) != ER_TRUE) /* expensive */
++ continue;
+
+- if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name);
+- return (NULL);
++ report(LOG_DEBUG, "cfg_authorize_cmd: found cmd %s %s node",
++ param->cmd, cfg_nodestring(svc->type));
++
++ /* we have 'cmd <openbra>' point, now traverse through its 'permit'/'deny' pairs: */
++
++ for (node = svc->value1; node; node = node->next) {
++ int match;
++
++ if (expr_eval(node->when) != ER_TRUE) /* expensive */
++ continue;
++
++#ifdef WITH_INCLUDED_REGEX
++
++ match = tac_regexec((tac_regexp *) node->value1, param->args);
++
++#else /* WITH_INCLUDED_REGEX */
++
++ match = !regexec((const regex_t *) node->value1, param->args /* string */,
++ 0 /* nmatch */, NULL /* pmatch */, 0 /* eflags */);
++
++#endif /* WITH_INCLUDED_REGEX */
++
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch",
++ node->line, param->cmd,
++ node->type == N_permit ? "permit" : "deny",
++ (const char *) node->value, param->args, (match ? "" : "no "));
+ }
+- /* 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);
++ if (!match)
++ continue;
++
++ switch (node->type) {
++ case N_permit:
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_DEBUG, "%s %s permitted by line %d",
++ param->cmd, param->args, node->line);
+ }
+- svc = svc->next;
++ param->result = ER_TRUE;
++ return (VSFR_FOUND);
++ break;
++ case N_deny:
++ if (debug & DEBUG_AUTHOR_FLAG) {
++ report(LOG_DEBUG, "%s %s denied by line %d",
++ param->cmd, param->args, node->line);
++ }
++ param->result = ER_FALSE;
++ return (VSFR_FOUND);
++ break;
++ default:
++ report(LOG_ERR, "INTERNAL: illegal configuration node: %s: %s %s",
++ session.peer, param->cmd, param->args);
++ param->result = ER_UNKNOWN; /* error */
++ return (VSFR_FOUND);
++ }
++ }
++ if (!algorithm_recursive) { /* compatibility mode: */
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s exists, but no args match, denied (as no 'authorization = recursive' found)",
++ param->cmd);
++ param->result = ER_FALSE; /* emulate last "deny .*" */
++ return (VSFR_FOUND);
+ }
+-
+- 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);
++ /* look at 'default service' settings */
++ param->result = entity_svc_default(entity);
++ switch (param->result) {
+
+- svc = get_value(group, S_svc).pval;
++ case ER_TRUE:
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s does not exist, permitted by default", param->cmd);
++ return (VSFR_FOUND);
+
+- 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;
+- }
++ case ER_FALSE:
+
+- /* still nothing. Check containing group and so on */
++ if (debug & DEBUG_AUTHOR_FLAG)
++ report(LOG_DEBUG, "cmd %s does not exist, denied by default", param->cmd);
++ return (VSFR_FOUND);
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
++ default: /* shouldn't happen */
++ case ER_UNKNOWN:
++ return (VSFR_CONTINUE);
+ }
++ /* NOTREACHED */
++}
++
++enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args));
++
++enum eval_result
++cfg_authorize_cmd(username, cmd, args)
++const char *username;
++const char *cmd;
++const char *args;
++{
++ struct cfg_authorize_cmd_param param;
++
++ param.cmd = cmd;
++ param.args = args;
++ param.result = ER_UNKNOWN; /* error */
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
++ report(LOG_DEBUG, "cfg_authorize_cmd: name=%s cmdname=%s args=%s",
++ username, cmd, args);
+
+- /* no matching cmd node for this user or her containing groups */
+- return (NULL);
++ value_scan(S_user, username, TAC_PLUS_RECURSE,
++ (value_scan_func_t) cfg_authorize_cmd_func, ¶m /* func_data */);
++
++ if (param.result != ER_UNKNOWN)
++ return (param.result);
++
++ /* The command does not exist. Do the default */
++ return (cfg_no_user_permitted() ? ER_TRUE : ER_FALSE);
+ }
+
+ /* Return an array of character strings representing configured AV
+@@ -2039,6 +2711,8 @@ int recurse;
+ * Lastly, indicate what default permission was configured by setting
+ * denyp */
+
++char **cfg_get_svc_attrs TAC_ARGS((NODE *svcnode, int *denyp));
++
+ char **
+ cfg_get_svc_attrs(svcnode, denyp)
+ NODE *svcnode;
+@@ -2063,9 +2737,14 @@ int *denyp;
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next) {
+- char *arg = tac_strdup(node->value);
+- char *p = index(arg, '=');
++ char *arg;
++ char *p;
+
++ if (expr_eval(node->when) != ER_TRUE) /* expensive */
++ continue; /* ignore this node */
++
++ arg = tac_strdup(node->value);
++ p = index(arg, '=');
+ if (p && node->type == N_optarg)
+ *p = '*';
+ args[i++] = arg;
+@@ -2075,23 +2754,28 @@ int *denyp;
+ }
+
+
+-int
+-cfg_user_svc_default_is_permit(user)
+-char *user;
++static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity));
+
++static enum eval_result
++entity_svc_default(entity)
++ENTITY *entity;
+ {
+- 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);
++ switch (entity->svc_dflt) {
+ case S_permit:
+- return (1);
++ return (ER_TRUE);
++ case S_deny:
++ return (ER_FALSE);
++ case S_default:
++ case 0: /* not specified */
++ return (ER_UNKNOWN);
++ default:
++ report(LOG_ERR, "INTERNAL: invalid entity svc_dflt (%d)", entity->svc_dflt);
++ return (ER_UNKNOWN);
+ }
+ }
+
++int cfg_no_user_permitted TAC_ARGS((void));
++
+ int
+ cfg_no_user_permitted()
+ {
+@@ -2101,12 +2785,16 @@ cfg_no_user_permitted()
+ }
+
+
+-char *
++const char *cfg_get_authen_default TAC_ARGS((void));
++
++const char *
+ cfg_get_authen_default()
+ {
+ return (authen_default);
+ }
+
++int cfg_get_authen_default_method TAC_ARGS((void));
++
+ /* For describe authentication method(pam,file,db..etc) */
+ int
+ cfg_get_authen_default_method()
+@@ -2115,92 +2803,123 @@ cfg_get_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;
++/* Host entity management:
++ */
++
++const char *cfg_get_host_key TAC_ARGS((const char *host));
++
++/* For getting host key */
++const char *
++cfg_get_host_key(host)
++const char *host;
+ {
+- USER *user, *group;
+- NODE *svc;
++ return (cfg_get_pvalue(S_host, host, S_key, algorithm_recursive /* recurse */));
++}
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d",
+- username, recurse);
++static ENTITY *force_belong_entity TAC_ARGS((int type, const char *name));
++
++static ENTITY *
++force_belong_entity(type, name)
++int type;
++const char *name;
++{
++ ENTITY *entity = entity_lookup(type, name);
+
+- /* find the user/group entry */
+- user = (USER *) hash_lookup(usertable, username);
++ if (entity)
++ eval_force_belong_entity(entity);
+
+- if (!user) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s",
+- username);
+- return (0);
+- }
++ return (entity);
++}
+
+- /* found the user entry. Find svc node */
+- for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
++/* assumed existing initialized "session.peer*" */
+
+- if (svc->type != N_svc_ppp)
+- continue;
++static ENTITY *request_peer_addr;
++static ENTITY *request_peer;
++static ENTITY *request_DEFAULT_group;
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+- svc->value1);
++static void enlist_request_peer TAC_ARGS((const char *hostname, ENTITY **entityp));
+
+- return(1);
+- }
++static void
++enlist_request_peer(hostname, entityp)
++const char *hostname;
++ENTITY **entityp;
++{
++ *entityp = NULL;
++ if (!hostname)
++ return;
+
+- if (!recurse) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+- return (0);
+- }
++ *entityp = force_belong_entity(S_host, hostname);
++ if (*entityp && request_DEFAULT_group)
++ virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, *entityp /* child */);
++}
+
+- /* no matching node. Check containing group */
+- if (user->member)
+- group = (USER *) hash_lookup(grouptable, user->member);
+- else
+- group = NULL;
++/* Try to build the following scenery:
++ *
++ * host <session.peer_addr> [=ER_TRUE]
++ * |
++ * +-- group <DEFAULT_GROUPNAME>
++ *
++ * host <session.peer > [=ER_TRUE]
++ * |
++ * +-- group <DEFAULT_GROUPNAME>
++ */
+
+- while (group) {
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: recurse group = %s",
+- group->name);
++void cfg_request_scan_begin TAC_ARGS((void));
+
+- for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
++void
++cfg_request_scan_begin()
++{
++ request_scan_begin();
+
+- if (svc->type != N_svc_ppp)
+- continue;
++ request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME);
+
+- if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+- svc->value1);
++ if (session.peer_addr != session.peer)
++ enlist_request_peer(session.peer_addr, &request_peer_addr);
++ enlist_request_peer(session.peer, &request_peer);
++}
+
+- return(1);
+- }
++/* Try to build the following scenery:
++ *
++ * ( user <identity->username> | user <DEFAULT_USERNAME> ) [=ER_TRUE]
++ * |
++ * +-- host <session.peer_addr> [=ER_TRUE]
++ * | |
++ * | +- group <DEFAULT_GROUPNAME>
++ * |
++ * +-- host <session.peer > [=ER_TRUE]
++ * | |
++ * | +-- group <DEFAULT_GROUPNAME>
++ * |
++ * +-- group <DEFAULT_GROUPNAME>
++ */
+
+- /* still nothing. Check containing group and so on */
++void cfg_request_identity TAC_ARGS((const struct identity *identity));
+
+- if (group->member)
+- group = (USER *) hash_lookup(grouptable, group->member);
+- else
+- group = NULL;
+- }
++void
++cfg_request_identity(identity)
++const struct identity *identity;
++{
++ ENTITY *user_entity,*request_DEFAULT_group;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+- report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
++ report(LOG_DEBUG, "cfg_request_identity: username=%s, NAS_name=%s, NAS_port=%s, NAC_address=%s, priv_lvl=%d",
++ identity->username, identity->NAS_name, identity->NAS_port, identity->NAC_address, identity->priv_lvl);
+
+- /* no PPP svc nodes for this user or her containing groups */
+- return (0);
+-}
++ user_entity = force_belong_entity(S_user, identity->username);
++ request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME);
+
+-/* For getting host key */
+-char *
+-cfg_get_host_key(host)
+-char *host;
+-{
+- return (cfg_get_phvalue(host, S_key));
+-}
++ if (!user_entity)
++ user_entity = force_belong_entity(S_user, DEFAULT_USERNAME);
++
++ request_scan_user_known = 1;
+
++ if (!user_entity)
++ return;
++
++ if (request_peer_addr)
++ virtual_enlist_entity_direct(request_peer_addr /* parent */, user_entity /* child */);
++ if (request_peer )
++ virtual_enlist_entity_direct(request_peer /* parent */, user_entity /* child */);
++ if (request_DEFAULT_group)
++ virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, user_entity /* child */);
++}
+diff --git a/cfgfile.h b/cfgfile.h
+new file mode 100644
+index 0000000..3669996
+--- /dev/null
++++ b/cfgfile.h
+@@ -0,0 +1,164 @@
++#ifndef CFGFILE_H
++#define CFGFILE_H 1
++
++#include "tac_plus.h"
++
++#include "utils.h"
++#include "cfgeval.h"
++
++
++/* Configurable:
++ */
++
++#define DEFAULT_USERNAME "DEFAULT"
++#define DEFAULT_GROUPNAME "DEFAULT"
++
++
++#define TAC_PLUS_RECURSE 1
++#define TAC_PLUS_NORECURSE 0
++
++/* 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
++
++typedef struct node NODE;
++
++/* A parse tree node */
++struct node {
++ int type; /* node type (arg, svc, proto) */
++ NODE *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 */
++ struct expr *when; /* conditions needed to respect this NODE */
++};
++
++union v {
++ int intval;
++ void *pval;
++};
++
++typedef union v VALUE;
++
++/* A user, host 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 entity {
++ char *name; /* username/groupname/hostname */
++ void *hash; /* hash table next pointer */
++ int line; /* line number defined on */
++ int type; /* set to S_user, S_host or S_group */
++
++ 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 *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 */
++ char *key; /* host spesific key (N/A for S_user) */
++ int svc_dflt; /* default authorization behaviour for svc or
++ * cmd */
++ /* =S_permit, S_deny or S_default */
++ NODE *svcs; /* pointer to svc nodes */
++#ifdef MAXSESS
++ int maxsess; /* Max sessions/user */
++#endif /* MAXSESS */
++ char *time; /* Timestamp */
++
++ struct tac_list to_parent_membership_list; /* ordered list of memberships to groups owning us: */
++ struct tac_list to_child_membership_list; /* ordered list of memberships to entities in this group: */
++ unsigned to_child_membership_num; /* # of 'to_child_membership_list' items */
++
++ struct {
++ unsigned seq; /* corresponds to global request_scan_seq */
++ enum eval_result belongs; /* whether this ENTITY 'belongs' */
++ } request_scan; /* cfg_request() scanning */
++
++ struct {
++ unsigned seq; /* corresponds to global value_scan_seq */
++ unsigned seen:1;
++ struct membership *from; /* from which we got to this one or NULL */
++ } value_scan; /* cfg_get_value() scanning, many per request_scan */
++
++ struct {
++ unsigned seq; /* corresponds to global eval_scan_seq */
++ struct tac_list notify_expr_list; /* contains expr.u.waiting_expr_node */
++ /* may be from any of: eval_{want,solved,destroy}_entity_list: */
++ struct tac_list_node pending_entity_node; /* we are interested in this entity */
++ /* child memberships which are not yet check_eval-ed are NOT present here,
++ * although when check_eval-entity finishes, all will be added here.
++ * List refilling driven by check_eval_scan_entity(),
++ * although each unsolved_child_node is added in check_eval_scan_membership().
++ */
++ unsigned unsolved_to_child_membership_num; /* when 0, we know we are ER_FALSE */
++ struct membership *unsolved_to_child_membership_first;
++ } eval_scan; /* expr_eval() scanning, many per value_scan */
++};
++#define PENDING_ENTITY_NODE_TO_ENTITY(pending_entity_node_) \
++ (&TAC_MEMBER_STRUCT(ENTITY, (pending_entity_node_), eval_scan.pending_entity_node))
++
++
++struct identity;
++
++extern const char *cfg_nodestring TAC_ARGS((int type));
++extern void cfg_clean_config TAC_ARGS((void));
++extern int cfg_get_intvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
++extern const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse));
++extern int cfg_read_config TAC_ARGS((const char *cfile));
++extern int cfg_user_exists TAC_ARGS((const char *username));
++extern const char *cfg_get_expires TAC_ARGS((const char *username, int recurse));
++extern const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse));
++extern const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse));
++extern int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse));
++#ifdef MSCHAP
++extern const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse));
++#endif /* MSCHAP */
++extern const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse));
++extern const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse));
++#ifdef USE_PAM
++extern const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse));
++#endif /* PAM */
++extern int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep));
++extern char **cfg_get_svc_attrs TAC_ARGS((NODE *svcnode, int *denyp));
++extern int cfg_no_user_permitted TAC_ARGS((void));
++extern const char *cfg_get_authen_default TAC_ARGS((void));
++extern int cfg_get_authen_default_method TAC_ARGS((void));
++extern const char *cfg_get_host_key TAC_ARGS((const char *host));
++extern void cfg_request_scan_begin TAC_ARGS((void));
++extern void cfg_request_identity TAC_ARGS((const struct identity *identity));
++extern enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args));
++
++/* for use by cfgeval.c: */
++extern ENTITY *new_entity TAC_ARGS((int type, char *name, int line));
++extern const char *entity_type_to_string TAC_ARGS((int entity_type));
++extern void scan_invalidate_entities TAC_ARGS((enum invalidate_scan what));
++extern ENTITY *entity_lookup TAC_ARGS((int type, const char *name));
++
++
++#endif /* CFGFILE_H */
+diff --git a/choose_authen.c b/choose_authen.c
+index 9329b73..d6b3062 100644
+--- a/choose_authen.c
++++ b/choose_authen.c
+@@ -17,24 +17,47 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include "choose_authen.h"
+ #include "expire.h"
++#include "enable.h"
++#include "report.h"
++#include "cfgfile.h"
++#include "default_fn.h"
++#include "default_v0_fn.h"
++#include "sendauth.h"
++#include "sendpass.h"
++#include "packet.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
+
+-static int choose_login();
+-static int choose_sendpass();
+-static int choose_sendauth();
++#ifdef SKEY
++#include "skey_fn.h"
++#endif
+
+-int
++
++static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type));
++static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type));
++static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
++
++#if 0 /* unused */
++static int
+ get_minor_version()
+ {
+ return(session.version & ~TAC_PLUS_MAJOR_VER_MASK);
+ }
++#endif /* unused */
+
+ /*
+ * Choose an authentication function. Return CHOOSE_OK if chosen,
+ * CHOOSE_GETUSER if we need a username, CHOOSE_FAILED on failure
+ */
+
++int choose_authen TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ int
+ choose_authen(data, type)
+ struct authen_data *data;
+@@ -84,14 +107,16 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ /* 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;
++ const char *name = data->NAS_id->username;
++ const char *cfg_passwd;
+
+ switch(type->authen_type) {
+ case TAC_PLUS_AUTHEN_TYPE_ASCII:
+@@ -193,6 +218,8 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ static int
+ choose_sendauth(data, type)
+ struct authen_data *data;
+@@ -247,6 +274,8 @@ struct authen_type *type;
+ return(CHOOSE_FAILED);
+ }
+
++static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
+ /* Compatibility routine for (obsolete) minor version == 0 */
+ static int
+ choose_sendpass(data, type)
+@@ -291,4 +320,3 @@ struct authen_type *type;
+
+ return(CHOOSE_FAILED);
+ }
+-
+diff --git a/choose_authen.h b/choose_authen.h
+new file mode 100644
+index 0000000..1276494
+--- /dev/null
++++ b/choose_authen.h
+@@ -0,0 +1,63 @@
++#ifndef CHOOSE_AUTHEN_H
++#define CHOOSE_AUTHEN_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++/*
++ * 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_data;
++
++struct authen_type {
++ char authen_name[AUTHEN_NAME_SIZE];
++ int (*authen_func) TAC_ARGS((struct authen_data *data));
++ int authen_type;
++};
++
++/*
++ * 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 */
++ unsigned 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 */
++
++
++extern int choose_authen TAC_ARGS((struct authen_data *data, struct authen_type *type));
++
++
++#endif /* CHOOSE_AUTHEN_H */
+diff --git a/configure.in b/configure.in
+index 6b3eb13..0b8fd72 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1,53 +1,101 @@
+ dnl This file writen by Devrim SERAL for tac_plus daemon
+
+ AC_INIT()
++dnl Check for Host information
++dnl AC_CANONICAL_HOST()
++AC_CANONICAL_SYSTEM()
++AM_INIT_AUTOMAKE(tac_plus, F4.0.3.alpha.8.gts4)
+
+ 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"
++ AC_DEFINE(LINUX)
++ AC_DEFINE(GLIBC)
+ ;;
+ *solaris)
+- OS="-DSOLARIS"
++ AC_DEFINE(SOLARIS)
+ ;;
+ *freebsd)
+- OS="-DFREEBSD"
++ AC_DEFINE(FREEBSD)
+ ;;
+ *hpux)
+- OS="-DHPUX"
++ AC_DEFINE(HPUX)
+ ;;
+ *aix)
+- OS="-DAIX"
++ AC_DEFINE(AIX)
++ AC_MSG_WARN([See /usr/lpp/bos/bsdport on your system for details of how to define bsdcc])
++ # CC="bsdcc"
++ ;;
++ *mips)
++ AC_DEFINE(MIPS)
+ ;;
+ *)
+ ;;
+ esac
+
++dnl Devrim Added
++AM_CONFIG_HEADER(config.h)
++AM_MAINTAINER_MODE
++
++if test "x$USE_MAINTAINER_MODE" = "xyes"; then
++ AC_DEFINE(MAINTAINER_MODE)
++fi
++
++if test "x$USE_MAINTAINER_MODE" = "xyes" -a "x$GCC" = "xyes"; then
++ CFLAGS="$CFLAGS -ggdb3 -Wall -Wstrict-prototypes -pedantic -Wsign-compare"
++fi
++
++# Set these options as otherwise some autoconf tests give different results:
++final_CFLAGS="$CFLAGS"
++CFLAGS="$CFLAGS -D_XOPEN_SOURCE=1 -D_XOPEN_SOURCE_EXTENDED=1 -D_BSD_SOURCE=1 -D_OSF_SOURCE=1 -D__EXTENSIONS__=1"
++
++COND_USE=""
++AC_SUBST(COND_USE)
++conf_LDFLAGS=""
++AC_SUBST(conf_LDFLAGS)
++conf_LDADD=""
++AC_SUBST(conf_LDADD)
++
++
+ dnl Checks for libraries.
+ dnl Replace `main' with a function in -lnsl:
+-AC_CHECK_LIB(nsl, main)
++AC_CHECK_LIB(nsl, main, [ conf_LDADD="-lnsl $conf_LDADD" ] )
+ 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)
++AC_CHECK_LIB(og, main, [ conf_LDADD="-log $conf_LDADD" ] )
+ dnl Replace `main' with a function in -lsocket:
+-AC_CHECK_LIB(socket, main)
++AC_CHECK_LIB(socket, main, [ conf_LDADD="-lsocket $conf_LDADD" ] )
+ dnl Check for Crypt function
++dnl Never use CONF_LDADD here as it is used also for "generate_passwd"
+ AC_CHECK_LIB(crypt, crypt)
+ AC_CHECK_LIB(c,printf)
+
++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 sys/syslog.h unistd.h regex.h sys/param.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_TYPE_SIZE_T
++AC_CHECK_FUNCS(select socket strcspn strdup strtol siginterrupt)
++AC_CHECK_SIZEOF(unsigned short,2)
++AC_CHECK_SIZEOF(unsigned int,4)
++AC_CHECK_SIZEOF(unsigned long,4)
+
+-dnl Devrim Added
+-AC_CONFIG_HEADER(config.h)
+
+ dnl For PAM support
+ AC_MSG_CHECKING(for PAM support:)
+@@ -55,9 +103,10 @@ 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_CHECK_LIB(dl, dlopen, [ conf_LDADD="-ldl $conf_LDADD" ] )
++ AC_CHECK_LIB(pam, pam_start, [ conf_LDADD="-lpam $conf_LDADD" ] )
++ AC_DEFINE(USE_PAM)
++ COND_USE="$COND_USE "'$(cond_USE_PAM)'
+ AC_MSG_RESULT(Pam support... yes)
+ else
+ AC_MSG_RESULT(Pam support... no)
+@@ -70,10 +119,15 @@ 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"
++ dnl Replace `main' with a function in -llber:
++ AC_CHECK_LIB(lber, main, [ conf_LDADD="-llber $conf_LDADD"; liblber="-llber" ], [ liblber="" ] )
++ dnl Replace `main' with a function in -lldap:
++ AC_CHECK_LIB(ldap, ldap_simple_bind_s, [ conf_LDADD="-lldap $conf_LDADD" ],
++ [
++ AC_CHECK_LIB(ldap, ldap_init, [ conf_LDADD="-lldap $conf_LDADD" ],, $liblber)
++ ], $liblber )
++ AC_DEFINE(USE_LDAP)
++ COND_USE="$COND_USE "'$(cond_USE_LDAP)'
+ AC_MSG_RESULT(LDAP support... yes)
+ else
+ AC_MSG_RESULT(LDAP support... no)
+@@ -85,7 +139,10 @@ echo
+ AC_ARG_WITH(db,
+ [ --with-db For DB Support ],,)
+ if test "x$with_db" = "xyes";then
+- DB="$DB -DDB -DDB_NULL"
++ AC_DEFINE(DB)
++ AC_DEFINE(DB_NULL)
++ COND_USE="$COND_USE "'$(cond_DB)'
++ COND_USE="$COND_USE "'$(cond_DB_NULL)'
+ AC_MSG_RESULT(DB support... yes)
+ else
+ AC_MSG_RESULT(DB support... no)
+@@ -108,14 +165,15 @@ AC_ARG_WITH(mysql-prefix,
+
+ if test "x$with_mysql" = "xyes";then
+
+- LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $LDFLAGS"
+- LDFLAGS="-I$MYSQL_PREFIX/include/mysql $LDFLAGS"
++ conf_LDFLAGS="-L$MYSQL_PREFIX/lib/mysql $conf_LDFLAGS"
++ CPPFLAGS="-I$MYSQL_PREFIX/include/mysql $CPPFLAGS"
+ AC_CHECK_LIB(mysqlclient, mysql_init,
+- LIBS="-lmysqlclient -lm $LIBS",
++ conf_LDADD="-lmysqlclient -lm $conf_LDADD",
+ AC_MSG_ERROR(*** couldn't find libmysqlclient),
+ -lm)
+
+- DB="$DB -DDB_MYSQL";
++ AC_DEFINE(DB_MYSQL)
++ COND_USE="$COND_USE "'$(cond_DB_MYSQL)'
+ AC_MSG_RESULT(Mysql support... yes)
+ else
+ AC_MSG_RESULT(Mysql support... no)
+@@ -140,13 +198,14 @@ AC_ARG_WITH(pgsql-prefix,
+
+ if test "x$with_pgsql" = "xyes";then
+
+- LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $LDFLAGS"
+- LDFLAGS="-I$PGSQL_PREFIX/include/pgsql $LDFLAGS"
++ conf_LDFLAGS="-L$PGSQL_PREFIX/lib/pgsql $conf_LDFLAGS"
++ CPPFLAGS="-I$PGSQL_PREFIX/include/pgsql $CPPFLAGS"
+ AC_CHECK_LIB(pq,PQconnectdb ,
+- LIBS="-lpq $LIBS",
++ conf_LDADD="-lpq $conf_LDADD",
+ AC_MSG_ERROR(*** couldn't find libpq))
+
+- DB="$DB -DDB_PGSQL";
++ AC_DEFINE(DB_PGSQL)
++ COND_USE="$COND_USE "'$(cond_DB_PGSQL)'
+ AC_MSG_RESULT(Pgsql support... yes)
+ else
+ AC_MSG_RESULT(Pgsql support... no)
+@@ -164,7 +223,8 @@ AC_ARG_WITH(tacgid,
+
+ 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_DEFINE_UNQUOTED(TACPLUS_USERID, $with_tacuid)
++ AC_DEFINE_UNQUOTED(TACPLUS_GROUPID, $with_tacgid)
+ AC_MSG_RESULT(tacacs+ work with given user and group id)
+ fi
+
+@@ -173,8 +233,9 @@ AC_ARG_ENABLE(maxsess,
+ [ --enable-maxsess Enable maxsess feature ],
+ [
+ if test "$enableval" = "yes";then
+- DEFINES="-DMAXSESS $DEFINES";
++ AC_DEFINE(MAXSESS)
+ AC_MSG_RESULT(yes)
++ COND_USE="$COND_USE "'$(cond_MAXSESS)'
+ else
+ AC_MSG_RESULT(no)
+ fi
+@@ -184,16 +245,18 @@ fi
+ ])
+
+ 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\\\""
++ [ pidfile="$withval" ],
++ [ pidfile="" ]
+ )
++if test "x$pidfile" '!=' "x"; then
++ AC_DEFINE_UNQUOTED(TACPLUS_PIDFILE, "$pidfile/tac_plus.pid")
++fi
+
+ dnl For libwrap check
+-AC_MSG_CHECKING(whether to enable the libwrap feture)
+-
++AC_MSG_CHECKING(whether to enable the libwrap feature)
++cond=false
+ AC_ARG_WITH(libwrap,
+ [ --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support.],
+ [ case "$withval" in
+@@ -203,48 +266,221 @@ AC_ARG_WITH(libwrap,
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_CHECK_LIB(wrap, request_init, [
+- LIBS="-lwrap $LIBS"
+- DEFINES="-DTCPWRAPPER $DEFINES"])
++ conf_LDADD="-lwrap $conf_LDADD"
++ cond=true
++ ])
+ ;;
+ *)
+ 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.) ])
++ cond=true
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+ )
++if $cond; then
++ AC_DEFINE(TCPWRAPPER)
++ COND_USE="$COND_USE "'$(cond_TCPWRAPPER)'
++fi
+
+-dnl insert defines to Makefile
+-AC_SUBST(DEFINES)
+-AC_SUBST(PIDFILE)
+-AC_SUBST(DB)
+-AC_SUBST(OS)
++dnl For SKEY check
++AC_MSG_CHECKING(whether to use SKEY security feature)
++cond=false
++AC_ARG_WITH(skey,
++[ --with-skey[=LIBPATH] Compile with SKEY support (also use -I in CPPFLAGS var).],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT(yes)
++ cond=true
++ ;;
++ *)
++ AC_MSG_RESULT(yes)
++ if test '!' -f "$withval";then
++ AC_MSG_ERROR([Cannot find $withval library file, you may wish to use LIBS variable instead.])
++ fi
++ conf_LDADD="$withval $conf_LDADD"
++ cond=true
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++if $cond; then
++ AC_DEFINE(SKEY)
++ COND_USE="$COND_USE "'$(cond_SKEY)'
++fi
+
+-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)
++dnl For MSCHAP and also MSCHAP_DES
++AC_MSG_CHECKING(whether to compile with Microsoft CHAP)
++cond=false
++AC_ARG_WITH(mschap,
++[ --with-mschap[=des] Compile with Microsoft CHAP (optionally including MSCHAP_DES).],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT([yes, without DES])
++ cond=true
++ ;;
++ des)
++ AC_MSG_RESULT([yes, including DES])
++ AC_DEFINE(MSCHAP_DES)
++ cond=true
++ ;;
++ *)
++ AC_MSG_ERROR(Unknown --with-mschap argument $withval, use: no, yes or des)
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++if $cond; then
++ AC_DEFINE(MSCHAP)
++ COND_USE="$COND_USE "'$(cond_MSCHAP)'
++fi
++
++dnl For SunOS encryption compatibility
++dnl Never use CONF_LDADD here as it is used also for "generate_passwd"
++AC_MSG_CHECKING(whether to use SunOS encryption compatibility)
++cond=false
++AC_ARG_WITH(descrypt,
++[ --with-descrypt Be password encryption compatible with SunOS.],
++[ case "$withval" in
++ no)
++ AC_MSG_RESULT(no)
++ ;;
++ yes)
++ AC_MSG_RESULT(yes)
++ LIBS="-ldescrypt $LIBS"
++ ;;
++ *)
++ AC_MSG_RESULT(yes - $withval)
++ LIBS="$withval $LIBS"
++ ;;
++ esac ],
++ AC_MSG_RESULT(no)
++)
++
++AC_ARG_WITH(efence,
++[ --with-efence compile with efence support (for debugging)],,[
++ if test "x$USE_MAINTAINER_MODE" = "xyes"; then
++ with_efence=auto
++ else
++ with_efence=no
+ fi
+- ],)
+-dnl Checks for typedefs, structures, and compiler characteristics.
+-AC_C_CONST
+-AC_HEADER_TIME
++])
++if test "$with_efence" != no; then
++ AC_CHECK_LIB(efence,malloc,,[
++ if test "$with_efence" = yes; then
++ AC_MSG_ERROR(Unable to find efence library.)
++ fi
++ ])
++fi
+
+-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)
++dnl Check for type in sys/socket.h
++AC_MSG_CHECKING([for parameter type of 3rd accept() arg])
++AC_CACHE_VAL(tac_plus_cv_accept_type, [
++ check_ok=false
++ for type in socklen_t size_t int; do
++ AC_TRY_COMPILE([
++#include <sys/types.h>
++#include <sys/socket.h>
++#if STDC_HEADERS
++#include <stdlib.h>
++#include <stddef.h>
++#endif
++],[
++ return 0;}
++ int accept(int s, struct sockaddr *addr, ]$type[ *addrlen)
++ { return 0; }
++ int discarded_main() {
++],
++ [check_ok=true;break],continue)
++ done
++ if $check_ok
++ then
++ tac_plus_cv_accept_type=$type
++ else
++ tac_plus_cv_accept_type=no
++ fi
++ ])
++if test "x$tac_plus_cv_accept_type" = "xno"
++then
++ AC_DEFINE(socklen_t,int)
++ AC_MSG_RESULT([failed to detect, will try int])
++else
++ AC_MSG_RESULT($tac_plus_cv_accept_type)
++ if test "x$tac_plus_cv_accept_type" != "xsocklen_t"
++ then
++ AC_DEFINE_UNQUOTED(socklen_t,$tac_plus_cv_accept_type)
++ fi
++fi
++
++dnl Check for system regex (stolen from "mutt" package)
++AC_ARG_WITH(included-regex, [ --with-included-regex Use the included regex library ],
++ [tac_plus_cv_included_regex=yes],
++ [AC_CHECK_FUNCS(regcomp, tac_plus_cv_included_regex=no, tac_plus_cv_included_regex=yes)])
++
++if test $tac_plus_cv_included_regex = no ; then
++AC_CACHE_CHECK([whether your system's regexp library is completely broken],
++ [tac_plus_cv_included_regex_broken],
++ AC_TRY_RUN([
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_REGEX_H
++#include <regex.h>
++#endif
++main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
++ tac_plus_cv_included_regex_broken=no, tac_plus_cv_included_regex_broken=yes, tac_plus_cv_included_regex_broken=yes))
++ if test $tac_plus_cv_included_regex_broken = yes ; then
++ echo "Using the included regex instead." >&AC_FD_MSG
++ tac_plus_cv_included_regex=yes
++ fi
++fi
++if test $tac_plus_cv_included_regex = yes; then
++ AC_DEFINE(WITH_INCLUDED_REGEX)
++ COND_USE="$COND_USE "'$(cond_WITH_INCLUDED_REGEX)'
++fi
++
++dnl Check for "struct passwd.pw_{age,comment}"
++dnl Stolen from Julianne Frances Haugh's login replacement.
++AC_CACHE_CHECK(for pw_age in struct passwd,
++tac_plus_cv_struct_passwd_pw_age, AC_TRY_COMPILE([#include <pwd.h>],
++[ struct passwd pw; pw.pw_age = "" ],
++tac_plus_cv_struct_passwd_pw_age=yes, tac_plus_cv_struct_passwd_pw_age=no))
++
++if test "$tac_plus_cv_struct_passwd_pw_age" = "yes"; then
++ AC_DEFINE(HAVE_PASSWD_PW_AGE)
++fi
++AC_CACHE_CHECK(for pw_comment in struct passwd,
++tac_plus_cv_struct_passwd_pw_comment, AC_TRY_COMPILE([#include <pwd.h>],
++[ struct passwd pw; pw.pw_comment = "" ],
++tac_plus_cv_struct_passwd_pw_comment=yes, tac_plus_cv_struct_passwd_pw_comment=no))
++
++if test "$tac_plus_cv_struct_passwd_pw_comment" = "yes"; then
++ AC_DEFINE(HAVE_PASSWD_PW_COMMENT)
++fi
++AC_CACHE_CHECK(for ut_host in struct utmp,
++tac_plus_cv_struct_utmp_ut_host, AC_TRY_COMPILE([#include <utmp.h>],
++[ struct utmp ut; ut.ut_host = "" ],
++tac_plus_cv_struct_utmp_ut_host=yes, tac_plus_cv_struct_utmp_ut_host=no))
++
++if test "$tac_plus_cv_struct_utmp_ut_host" = "yes"; then
++ AC_DEFINE(HAVE_UTMP_UT_HOST)
++fi
++
++CFLAGS="$final_CFLAGS"
+
+-AC_OUTPUT(Makefile,echo timestamp > stamp-h)
++AC_OUTPUT([
++ Makefile
++ tac_plus.spec
++ ])
+diff --git a/convert.pl b/convert.pl
+index eb0b5c2..83cbdcc 100755
+--- a/convert.pl
++++ b/convert.pl
+@@ -141,6 +141,3 @@ exit 0 if ($acl_valid);
+ foreach $group (keys %groups) {
+ print "group = $group { }\n";
+ }
+-
+-
+-
+diff --git a/db.c b/db.c
+index 9cbbbe1..b5c156c 100644
+--- a/db.c
++++ b/db.c
+@@ -44,18 +44,47 @@
+ devrim(devrim@gazi.edu.tr)
+ */
+
+-#if defined(DB)
+-#include <stdio.h>
++
+ #include "tac_plus.h"
++
++#ifdef DB
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "db.h"
++#include "report.h"
++#include "do_acct.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++#include "utils.h"
++
++
++#ifdef DB_MYSQL
++#include "db_mysql.h"
++#endif
++#ifdef DB_NULL
++#include "db_null.h"
++#endif
++#ifdef DB_PGSQL
++#include "db_pgsql.h"
++#endif
++
++
++static int check_db_type TAC_ARGS((char *db_type));
++
++
+ /* The databases recognized by this function */
+ #define DEFINED_DB {"null","mysql","pgsql"}
+
+-char *find_attr_value();
++int db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
+
+ int
+ db_verify(user, users_passwd, str_conn)
+-char *user, *users_passwd; /* Username and gived password */
+-char *str_conn; /* String connection to database */
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *str_conn; /* string connection to database */
+ {
+ char *buffer;
+ char *db_pref, *db_user, *db_password;
+@@ -65,11 +94,7 @@ char *str_conn; /* String connection to database */
+ 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);
+- }
++ buffer = db_pref = (char *) tac_malloc( strlen(str_conn) + 1 );
+
+ strcpy( buffer, str_conn );
+
+@@ -216,6 +241,8 @@ char *str_conn; /* String connection to database */
+ }
+
+
++int db_acct TAC_ARGS((struct acct_rec *rec));
++
+ /* Db accounting routine */
+ int
+ db_acct(rec)
+@@ -227,12 +254,7 @@ struct acct_rec *rec;
+ 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);
+- }
++ buffer = db_pref = (char *) tac_malloc( strlen(session.db_acct) + 1 );
+
+ strcpy( buffer, session.db_acct);
+
+@@ -384,8 +406,10 @@ struct acct_rec *rec;
+
+ }
+
++static int check_db_type TAC_ARGS((char *db_type));
++
+ /* For checking DB type */
+-int
++static int
+ check_db_type(db_type)
+ char *db_type;
+ {
+@@ -400,4 +424,9 @@ for (i=0; dbp[i] ; i++ ) {
+ }
+ return ret;
+ }
++
++#else /* DB */
++
++TAC_SOURCEFILE_EMPTY
++
+ #endif /* DB */
+diff --git a/db.h b/db.h
+new file mode 100644
+index 0000000..dfc0c23
+--- /dev/null
++++ b/db.h
+@@ -0,0 +1,17 @@
++#ifndef DB_H
++#define DB_H 1
++
++#include "tac_plus.h"
++
++#ifdef DB
++
++
++struct acct_rec;
++
++extern int db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
++extern int db_acct TAC_ARGS((struct acct_rec *rec));
++
++
++#endif /* DB */
++
++#endif /* DB_H */
+diff --git a/db_mysql.c b/db_mysql.c
+index 8aef6d4..4c09f48 100644
+--- a/db_mysql.c
++++ b/db_mysql.c
+@@ -1,88 +1,88 @@
+-#if defined(DB_MYSQL) && defined(DB)
+-
+ /*
+-Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+-*/
++ * Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
++ */
++
+
+ #include "tac_plus.h"
++
++#if defined(DB_MYSQL) && defined(DB)
++
+ #include <stdio.h>
+-#include "mysql.h"
++#include <stdlib.h>
++#include <mysql.h>
++
++#include "db_mysql.h"
++#include "report.h"
++#include "pwlib.h"
++#include "main.h"
++#include "utils.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)
++static MYSQL mysqldb;
++static MYSQL_RES *res;
++static MYSQL_ROW row;
+
+
+-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;
++int mysql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
+
++int mysql_db_verify(user, users_passwd, db_user, db_password,
++ db_hostname, db_name, db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
+-
+-char *real_passwd;
+-char *mysqlcmd;
+-int sql_len;
++ char *real_passwd;
++ char *mysqlcmd;
++ int sql_len;
+
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: verify %s", user);
+
+-/* Connect database server */
++ /* Connect database server */
+
+- if ( !( mysql_connect(&mysqldb,db_hostname,db_user,db_password) ) )
+- {
++ 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 */
++ /* Select tacacs db */
+
+- if ( mysql_select_db(&mysqldb,db_name) )
+- {
++ 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 */
++ /* Check select string length */
+
+-sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
++ sql_len = strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+- if ( sql_len> SQLCMDL )
+- {
++ if ( sql_len> SQLCMDL ) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Sql cmd exceed alowed limits");
+ return(0);
+ }
+
+-/* Prepare select string */
++ /* 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);
+-}
++ mysqlcmd = (char *) tac_malloc(sql_len);
+
+-sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
++ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+-/* Query database */
++ /* Query database */
+
+- if (mysql_query(&mysqldb,mysqlcmd))
+- {
++ if (mysql_query(&mysqldb,mysqlcmd)) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database ");
+ free(mysqlcmd);
+@@ -91,32 +91,29 @@ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+ free(mysqlcmd);
+
+- if (!(res = mysql_store_result(&mysqldb)))
+- {
++ 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 (!(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 (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);
++ real_passwd=(char *) tac_malloc(strlen(row[0])+1);
+ strcpy(real_passwd,row[0]);
+
+- if (!mysql_eof(res))
+- {
++ if (!mysql_eof(res)) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "MySQL: Result not end!!");
+ return(0);
+@@ -125,7 +122,7 @@ sprintf(mysqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+ mysql_free_result(res);
+ mysql_close(&mysqldb);
+
+-if (debug & DEBUG_AUTHEN_FLAG)
++ 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 */
+@@ -133,69 +130,65 @@ if (debug & DEBUG_AUTHEN_FLAG)
+ 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;
++int mysql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
+
++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)
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const char *bytes_out;
+ {
++ char *mysqlcmd;
++ int sql_len;
+
+-char *mysqlcmd;
+-int sql_len;
+-
+-/* Connect database server */
++ /* Connect database server */
+
+- if (!(mysql_connect(&mysqldb,db_hostname,db_user,db_password)))
+- {
++ 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 */
++ /*Select tacacs db */
+
+- if (mysql_select_db(&mysqldb,db_name))
+- {
++ 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);
++ /* 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 ( 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);
++ /* Prepare select string */
++ mysqlcmd=(char *) tac_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);
++ sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+-/* Query database */
++ /* Query database */
+
+- if (mysql_query(&mysqldb,mysqlcmd))
+- {
++ if (mysql_query(&mysqldb,mysqlcmd)) {
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "MySQL: cannot query database");
+ free(mysqlcmd);
+@@ -204,13 +197,18 @@ sprintf(mysqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in
+
+ free(mysqlcmd);
+
+-/* Check if accounting is sucess */
+- if ( mysql_affected_rows( &mysqldb ) < 0 )
+- {
++ /* 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
++
++#else /* defined(DB_MYSQL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_MYSQL) && defined(DB) */
+diff --git a/db_mysql.h b/db_mysql.h
+new file mode 100644
+index 0000000..7e806b6
+--- /dev/null
++++ b/db_mysql.h
+@@ -0,0 +1,15 @@
++#ifndef DB_MYSQL_H
++#define DB_MYSQL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_MYSQL) && defined(DB)
++
++
++extern int mysql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int mysql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_MYSQL) && defined(DB) */
++
++#endif /* DB_MYSQL_H */
+diff --git a/db_null.c b/db_null.c
+index 40252d8..9892ef0 100644
+--- a/db_null.c
++++ b/db_null.c
+@@ -6,22 +6,30 @@
+ ** 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)
++#if defined(DB_NULL) && defined(DB)
++
++#include "db_null.h"
++#include "report.h"
++#include "main.h"
+
+-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;
+
++int null_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++
++int null_db_verify(user, users_passwd, db_user, db_password, db_hostname,
++ db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parametr's */
++const char *db_password;
++const char *db_hostname;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
+-//report(LOG_DEBUG, "DB_NULL(%u) - ok", __LINE__);
++/* report(LOG_DEBUG, "DB_NULL(%u) - ok", __LINE__); */
+
+ /* Try to verify the password
+ Successful if username and password equal */
+@@ -36,22 +44,30 @@ char *dbfield_passwd;
+
+ /* Null Database Accounting */
+
++int null_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
+ 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;
++const char *db_user; /* db's parametr's */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const 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);
++ 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
+
++#else /* defined(DB_NULL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_NULL) && defined(DB) */
+diff --git a/db_null.h b/db_null.h
+new file mode 100644
+index 0000000..4e82047
+--- /dev/null
++++ b/db_null.h
+@@ -0,0 +1,15 @@
++#ifndef DB_NULL_H
++#define DB_NULL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_NULL) && defined(DB)
++
++
++extern int null_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int null_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_NULL) && defined(DB) */
++
++#endif /* DB_NULL_H */
+diff --git a/db_pgsql.c b/db_pgsql.c
+index 22725fe..321133c 100644
+--- a/db_pgsql.c
++++ b/db_pgsql.c
+@@ -1,5 +1,3 @@
+-#if defined(DB_PGSQL) && defined(DB)
+-
+ /*
+ Writen by Devrim SERAL(devrim@tef.gazi.edu.tr)
+ For PostgreSQL Authentication And Accounting
+@@ -7,80 +5,87 @@ For PostgreSQL Authentication And Accounting
+ This program protected with GPL License.
+ */
+
++
+ #include "tac_plus.h"
++
++#if defined(DB_PGSQL) && defined(DB)
++
+ #include <stdio.h>
+-#include "libpq-fe.h"
++#include <stdlib.h>
++#include <libpq-fe.h>
++#include <string.h>
++
++#include "db_pgsql.h"
++#include "main.h"
++#include "report.h"
++#include "utils.h"
++#include "pwlib.h"
++
++
++static void exit_nicely TAC_ARGS((PGconn *cn, PGresult *r));
++
++
+ #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)
++static PGconn *conn;
++static PGresult *res;
+
+
+-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;
++int pgsql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
+
++int pgsql_db_verify(user, users_passwd, db_user, db_password,
++ db_hostname, db_name, db_table, dbfield_name, dbfield_passwd)
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *dbfield_name;
++const char *dbfield_passwd;
+ {
++ char *real_passwd;
++ char *pgsqlcmd;
++ int sql_len;
++ int nrow;
+
+-char *real_passwd;
+-char *pgsqlcmd;
+-int sql_len;
+-int nrow;
+-
+-if (debug & DEBUG_AUTHEN_FLAG)
++ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "PGSQL: verify %s", user);
+
+-/* Connect database server */
++ /* Connect database server */
+
+-conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
++ conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+-if ( PQstatus(conn) == CONNECTION_BAD )
+-{
++ 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 */
++ /* Check select string length */
+
+-sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
++ sql_len=strlen(dbfield_passwd)+strlen(dbfield_name)+strlen(db_table)+strlen(user)+strlen(AUTHSQL);
+
+-if ( sql_len> SQLCMDL )
+-{
++ 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);
++ /* Prepare select string */
+
+-if(pgsqlcmd==NULL)
+-{
+- if (debug & DEBUG_AUTHEN_FLAG)
+- report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+- return(0);
+-}
++ pgsqlcmd=(char *) tac_malloc(sql_len);
+
+-sprintf(pgsqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
++ sprintf(pgsqlcmd,AUTHSQL,dbfield_passwd,db_table,dbfield_name,user);
+
+-/* Query database */
+-res=PQexec(conn,pgsqlcmd);
++ /* Query database */
++ res=PQexec(conn,pgsqlcmd);
+
+-if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+-{
++ 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) );
+@@ -88,119 +93,109 @@ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ free(pgsqlcmd);
+ exit_nicely(conn,res);
+ return(0);
+-}
++ }
+
+-free(pgsqlcmd);
++ free(pgsqlcmd);
+
+-if( nrow=PQntuples(res)!=1)
+-{
++ 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 ( 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);
++ real_passwd=(char *) tac_malloc(PWLEN+1);
+ strncpy(real_passwd,PQgetvalue(res,0,PQfnumber(res,dbfield_passwd)),PWLEN);
+ real_passwd[PWLEN]='\0';
+
+-exit_nicely(conn,res);
++ exit_nicely(conn,res);
+
+-if (debug & DEBUG_AUTHEN_FLAG)
++ 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;
++int pgsql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
+
++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)
++const char *db_user; /* db's parameters */
++const char *db_password;
++const char *db_hostname;
++const char *db_name;
++const char *db_table;
++const char *s_name;
++const char *c_name;
++const char *a_username;
++const char *elapsed_time;
++const char *bytes_in;
++const char *bytes_out;
+ {
+-
+-char *pgsqlcmd;
+-int sql_len;
++ char *pgsqlcmd;
++ int sql_len;
+
+ if (debug & DEBUG_ACCT_FLAG)
+ report(LOG_DEBUG, "PGSQL: Accounting for %s begin", a_username);
+
+-/* Connect database server */
++ /* Connect database server */
+
+-conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
++ conn=PQsetdbLogin(db_hostname,NULL,NULL,NULL,db_name,db_user,db_password);
+
+-if ( PQstatus(conn) == CONNECTION_BAD )
+-{
++ 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 */
++ /* 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);
++ 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 ( 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);
++ /* Prepare select string */
+
+-if(pgsqlcmd==NULL)
+-{
+-if (debug & DEBUG_ACCT_FLAG)
+- report(LOG_ERR, "pgsql_db_verify: pgsqlcmd malloc error");
+- return(0);
+-}
++ pgsqlcmd=(char *) tac_malloc(sql_len);
+
+-sprintf(pgsqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
++ sprintf(pgsqlcmd,ACCTSQL,db_table,a_username,s_name,c_name,elapsed_time,bytes_in,bytes_out);
+
+-/* Query database */
+-res=PQexec(conn,pgsqlcmd);
++ /* Query database */
++ res=PQexec(conn,pgsqlcmd);
+
+-if (!res || PQresultStatus(res) != PGRES_COMMAND_OK )
+-{
++ 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);
++ free(pgsqlcmd);
+
+-/* Flush all result and close connection */
+-exit_nicely(conn,res);
++ /* 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);
+@@ -208,11 +203,20 @@ exit_nicely(conn,res);
+ return (1); /* Return 1 if verified, 0 otherwise. */
+ }
+
+-int
+-exit_nicely(PGconn *cn,PGresult *r)
++
++static void exit_nicely TAC_ARGS((PGconn *cn, PGresult *r));
++
++static void
++exit_nicely(cn, r)
++PGconn *cn;
++PGresult *r;
+ {
+ PQclear(r);
+ PQfinish(cn);
+ }
+
+-#endif
++#else /* defined(DB_PGSQL) && defined(DB) */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* defined(DB_PGSQL) && defined(DB) */
+diff --git a/db_pgsql.h b/db_pgsql.h
+new file mode 100644
+index 0000000..b966bfc
+--- /dev/null
++++ b/db_pgsql.h
+@@ -0,0 +1,15 @@
++#ifndef DB_PGSQL_H
++#define DB_PGSQL_H 1
++
++#include "tac_plus.h"
++
++#if defined(DB_PGSQL) && defined(DB)
++
++
++extern int pgsql_db_verify TAC_ARGS((const char *user, const char *users_passwd, const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *dbfield_name, const char *dbfield_passwd));
++extern int pgsql_db_acct TAC_ARGS((const char *db_user, const char *db_password, const char *db_hostname, const char *db_name, const char *db_table, const char *s_name, const char *c_name, const char *a_username, const char *elapsed_time, const char *bytes_in, const char *bytes_out));
++
++
++#endif /* defined(DB_PGSQL) && defined(DB) */
++
++#endif /* DB_PGSQL_H */
+diff --git a/default_fn.c b/default_fn.c
+index f97e9e2..d6196cf 100644
+--- a/default_fn.c
++++ b/default_fn.c
+@@ -17,23 +17,50 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++
++#include "default_fn.h"
+ #include "expire.h"
+ #include "md5.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
+
+ #ifdef MSCHAP
+ #include "md4.h"
+ #include "mschap.h"
+-
+ #ifdef MSCHAP_DES
+ #include "arap_des.h"
+-#endif
++#endif /* MSCHAP_DES */
+ #endif /* MSCHAP */
+
+ #ifdef ARAP_DES
+ #include "arap_des.h"
+ #endif
+
++
++struct private_data;
++
++static void chap_verify TAC_ARGS((struct authen_data *data));
++static void arap_verify TAC_ARGS((struct authen_data *data));
++static void pap_verify TAC_ARGS((struct authen_data *data));
++static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
++
++#ifdef MSCHAP
++static void mschap_verify TAC_ARGS((struct authen_data *data));
++#endif
++
++
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+ #define STATE_AUTHEN_GETUSER 1 /* username has been requested */
+@@ -44,13 +71,6 @@ struct private_data {
+ 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
+@@ -67,6 +87,8 @@ static void tac_login();
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int default_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ default_fn(data)
+ struct authen_data *data;
+@@ -210,6 +232,8 @@ struct authen_data *data;
+ *
+ */
+
++static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
++
+ static void
+ tac_login(data, p)
+ struct authen_data *data;
+@@ -278,6 +302,8 @@ struct private_data *p;
+ * the START packet.
+ */
+
++static void pap_verify TAC_ARGS((struct authen_data *data));
++
+ static void
+ pap_verify(data)
+ struct authen_data *data;
+@@ -305,6 +331,8 @@ struct authen_data *data;
+ }
+
+
++static void chap_verify TAC_ARGS((struct authen_data *data));
++
+ /* Verify the challenge and id against the response by looking up the
+ * chap secret in the config file. Set data->status appropriately.
+ */
+@@ -312,8 +340,9 @@ static void
+ chap_verify(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal, digest[MD5_LEN];
+- char *exp_date, *p;
++ char *name, *chal, digest[MD5_LEN];
++ const char *secret;
++ const char *exp_date, *p;
+ u_char *mdp;
+ char id;
+ int chal_len, inlen;
+@@ -398,13 +427,15 @@ struct authen_data *data;
+ }
+
+
++static void pw_bitshift TAC_ARGS((char *pw));
++
+ /*
+ * 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
++static void
+ pw_bitshift(pw)
+ char *pw;
+ {
+@@ -423,12 +454,14 @@ char *pw;
+ }
+
+
++static void arap_verify TAC_ARGS((struct authen_data *data));
++
+ 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;
++ const char *name, *cfg_secret, *exp_date, *p;
+
+ if (!(char) data->NAS_id->username[0]) {
+ report(LOG_ERR, "%s %s: no username for arap_verify",
+@@ -511,9 +544,12 @@ struct authen_data *data;
+ #ifdef MSCHAP
+
+ /* Following code is added for ms-chap */
++
++static void mschap_desencrypt TAC_ARGS((const char *clear, unsigned char *str, unsigned char *cypher));
++
+ static void
+ mschap_desencrypt(clear, str, cypher)
+-char *clear;
++const char *clear;
+ unsigned char *str;
+ unsigned char *cypher;
+ {
+@@ -576,19 +612,23 @@ unsigned char *cypher;
+ }
+
+
++static void mschap_deshash TAC_ARGS((unsigned char *clear, unsigned char *cypher));
++
+ static void
+ mschap_deshash(clear, cypher)
+-char *clear;
+-char *cypher;
++unsigned char *clear;
++unsigned char *cypher;
+ {
+ mschap_desencrypt(MSCHAP_KEY, clear, cypher);
+ }
+
+
++static void mschap_lmpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
++
+ static void
+ mschap_lmpasswordhash(password, passwordhash)
+-char *password;
+-char *passwordhash;
++const char *password;
++unsigned char *passwordhash;
+ {
+ unsigned char upassword[15];
+ int i = 0;
+@@ -604,13 +644,15 @@ char *passwordhash;
+ }
+
+
++static void mschap_challengeresponse TAC_ARGS((const char *challenge, unsigned char *passwordhash, unsigned char *response));
++
+ static void
+ mschap_challengeresponse(challenge, passwordhash, response)
+-char *challenge;
+-char *passwordhash;
+-char *response;
++const char *challenge;
++unsigned char *passwordhash;
++unsigned char *response;
+ {
+- char zpasswordhash[21];
++ unsigned char zpasswordhash[21];
+
+ memset(zpasswordhash, 0, 21);
+ memcpy(zpasswordhash, passwordhash, 16);
+@@ -621,22 +663,26 @@ char *response;
+ }
+
+
++void mschap_lmchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++
+ void
+ mschap_lmchallengeresponse(challenge, password, response)
+-char *challenge;
+-char *password;
+-char *response;
++const char *challenge;
++const char *password;
++unsigned char *response;
+ {
+- char passwordhash[16];
++ unsigned char passwordhash[16];
+
+ mschap_lmpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+ }
+
+
++static int mschap_unicode_len TAC_ARGS((unsigned char *password));
++
+ static int
+ mschap_unicode_len(password)
+-char *password;
++unsigned char *password;
+ {
+ int i;
+
+@@ -649,14 +695,16 @@ char *password;
+ }
+
+
++static void mschap_ntpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
++
+ static void
+ mschap_ntpasswordhash(password, passwordhash)
+-char *password;
+-char *passwordhash;
++const char *password;
++unsigned char *passwordhash;
+ {
+ MD4_CTX context;
+ int i;
+- char *cp;
++ const char *cp;
+ unsigned char unicode_password[512];
+
+ memset(unicode_password, 0, 512);
+@@ -676,15 +724,15 @@ char *passwordhash;
+ }
+
+
++void mschap_ntchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++
+ void
+-mschap_ntchallengeresponse(challenge,
+- password,
+- response)
+-char *challenge;
+-char *password;
+-char *response;
++mschap_ntchallengeresponse(challenge, password, response)
++const char *challenge;
++const char *password;
++unsigned char *response;
+ {
+- char passwordhash[16];
++ unsigned char passwordhash[16];
+
+ mschap_ntpasswordhash(password, passwordhash);
+ mschap_challengeresponse(challenge, passwordhash, response);
+@@ -694,16 +742,19 @@ char *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 TAC_ARGS((struct authen_data *data));
++
+ static void
+ mschap_verify(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal, *resp;
+- char *exp_date, *p;
++ const char *name, *secret, *chal, *resp;
++ const char *exp_date, *p;
+ char id;
+ int chal_len;
+- char lmresponse[24];
+- char ntresponse[24];
++ unsigned char lmresponse[24];
++ unsigned char ntresponse[24];
+ int bcmp_status;
+
+ if (!(char) data->NAS_id->username[0]) {
+diff --git a/default_fn.h b/default_fn.h
+new file mode 100644
+index 0000000..20e0e0f
+--- /dev/null
++++ b/default_fn.h
+@@ -0,0 +1,16 @@
++#ifndef DEFAULT_FN_H
++#define DEFAULT_FN_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int default_fn TAC_ARGS((struct authen_data *data));
++#ifdef MSCHAP
++extern void mschap_lmchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++extern void mschap_ntchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
++#endif
++
++
++#endif /* DEFAULT_FN_H */
+diff --git a/default_v0_fn.c b/default_v0_fn.c
+index 96e0c1d..7c4c9cb 100644
+--- a/default_v0_fn.c
++++ b/default_v0_fn.c
+@@ -17,8 +17,23 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "default_v0_fn.h"
+ #include "expire.h"
++#include "utils.h"
++#include "report.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
++#include "cfgfile.h"
++
+
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+@@ -45,6 +60,8 @@ struct private_data {
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int default_v0_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ default_v0_fn(data)
+ struct authen_data *data;
+@@ -185,4 +202,3 @@ struct authen_data *data;
+ return (1);
+ }
+ }
+-
+diff --git a/default_v0_fn.h b/default_v0_fn.h
+new file mode 100644
+index 0000000..1165ee4
+--- /dev/null
++++ b/default_v0_fn.h
+@@ -0,0 +1,12 @@
++#ifndef DEFAULT_V0_FN_H
++#define DEFAULT_V0_FN_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int default_v0_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* DEFAULT_V0_FN_H */
+diff --git a/do_acct.c b/do_acct.c
+index dee8eaa..287b0dc 100644
+--- a/do_acct.c
++++ b/do_acct.c
+@@ -17,18 +17,46 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include <stdlib.h>
++#include <errno.h>
++#include <time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
++#endif
++#include <string.h>
++#include <utmp.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++#include "do_acct.h"
++#include "report.h"
++#include "utils.h"
++#include "main.h"
++#include "do_author.h" /* for "struct identity" */
++
++
++char *wtmpfile = NULL; /* for wtmp file logging */
++
++
++static int wtmpfd = 0;
+ static int acctfd = 0;
+
+ /* Make a acct entry into the accounting file for accounting.
+ Return 1 on error */
+
++static int acct_write TAC_ARGS((char *string));
++
+ static int
+ acct_write(string)
+- char *string;
++char *string;
+ {
+- if (write(acctfd, string, strlen(string)) != strlen(string)) {
++ if ((unsigned long)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]);
+@@ -41,11 +69,13 @@ acct_write(string)
+ return(0);
+ }
+
++static int acct_write_field TAC_ARGS((char *string));
++
+ /* Write a string or "unknown" into the accounting file.
+ Return 1 on error */
+ static int
+ acct_write_field(string)
+- char *string;
++char *string;
+ {
+ if (string && string[0]) {
+ if (acct_write(string))
+@@ -57,6 +87,8 @@ acct_write_field(string)
+ return(0);
+ }
+
++int do_acct TAC_ARGS((struct acct_rec *rec));
++
+ int
+ do_acct(rec)
+ struct acct_rec *rec;
+@@ -131,10 +163,12 @@ struct acct_rec *rec;
+ return (0);
+ }
+
+-int
++static int wtmp_entry TAC_ARGS((char *line, char *name, char *host, time_t utime));
++
++static int
+ wtmp_entry (line, name, host, utime)
+- char *line, *name, *host;
+- time_t utime;
++char *line, *name, *host;
++time_t utime;
+ {
+ struct utmp entry;
+
+@@ -152,7 +186,7 @@ wtmp_entry (line, name, host, utime)
+ strcpy(entry.ut_name, name);
+ else bcopy(name, entry.ut_name, sizeof entry.ut_name);
+
+-#ifndef SOLARIS
++#ifdef HAVE_UTMP_UT_HOST
+ if (strlen(host) < sizeof entry.ut_host)
+ strcpy(entry.ut_host, host);
+ else bcopy(host, entry.ut_host, sizeof entry.ut_host);
+@@ -180,16 +214,18 @@ wtmp_entry (line, name, host, utime)
+ close(wtmpfd);
+
+ if (debug & DEBUG_ACCT_FLAG) {
+- report(LOG_DEBUG, "wtmp: %s, %s %s %d", line, name, host, utime);
++ report(LOG_DEBUG, "wtmp: %s, %s %s %ld", line, name, host, (long)utime);
+ }
+
+ return(0);
+ }
+
++char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt));
++
+ char *
+ find_attr_value (attr, args, cnt)
+- char *attr, **args;
+- int cnt;
++char *attr, **args;
++int cnt;
+ {
+ int i;
+
+@@ -208,9 +244,11 @@ find_attr_value (attr, args, cnt)
+ return(NULL);
+ }
+
++int do_wtmp TAC_ARGS((struct acct_rec *rec));
++
+ int
+ do_wtmp(rec)
+- struct acct_rec *rec;
++struct acct_rec *rec;
+ {
+ time_t now = time(NULL);
+ char *service;
+diff --git a/do_acct.h b/do_acct.h
+new file mode 100644
+index 0000000..5e6e025
+--- /dev/null
++++ b/do_acct.h
+@@ -0,0 +1,33 @@
++#ifndef DO_ACCT_H
++#define DO_ACCT_H 1
++
++#include "tac_plus.h"
++
++
++/* 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;
++};
++
++extern char *wtmpfile; /* for wtmp file logging */
++
++
++extern int do_acct TAC_ARGS((struct acct_rec *rec));
++extern char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt));
++extern int do_wtmp TAC_ARGS((struct acct_rec *rec));
++
++
++#endif /* DO_ACCT_H */
+diff --git a/do_author.c b/do_author.c
+index 574736f..3f23602 100644
+--- a/do_author.c
++++ b/do_author.c
+@@ -17,15 +17,39 @@
+ 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();
++#include <stdlib.h>
++#include <string.h>
++
++#include "do_author.h"
++#include "tac_regexp.h"
++#include "cfgfile.h"
++#include "report.h"
++#include "utils.h"
++#include "programs.h"
++#include "main.h"
++#include "parse.h"
++#include "cfgeval.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++#ifdef USE_PAM
++#include "tac_pam.h"
++#endif
++
++
++static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
++static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
++static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
++static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
++static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
++static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
++
++
++int do_author TAC_ARGS((struct author_data *data));
+
+ /* Return 0 is data->status is valid */
+ int
+@@ -37,7 +61,7 @@ struct author_data *data;
+ int svc;
+ char *cmd, *protocol, *svcname;
+ #ifdef USE_PAM
+- char *pam_service= NULL;
++ const char *pam_service= NULL;
+ #endif
+ protocol = NULL;
+
+@@ -48,7 +72,8 @@ struct author_data *data;
+
+ /* If this user doesn't exist in our configs, do the default */
+
+- if (!cfg_user_exists(username) && !cfg_user_exists(DEFAULT_USERNAME)) {
++ if (!cfg_user_exists(username)) {
++ if (!cfg_user_exists(DEFAULT_USERNAME)) {
+
+ if (cfg_no_user_permitted()) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+@@ -70,7 +95,8 @@ struct author_data *data;
+ return (0);
+ }
+
+- if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) {
++ /* assumed (cfg_user_exists(DEFAULT_USERNAME)): */
++
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'",
+ DEFAULT_USERNAME, username);
+@@ -127,19 +153,20 @@ struct author_data *data;
+ #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 ((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 (tac_pam_authorization(data->id->username, data, pam_service)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "PAM Authorization Fail");
+ return(0);
+ }
+-} /* Pam_service */
++ } /* Pam_service */
++
+ default:
+ break;
+ }
+@@ -181,15 +208,17 @@ if (pam_service=cfg_get_pam_service(data->id->username,TAC_PLUS_RECURSE) ) {
+ A return value of 1 means no further authorization is required
+ */
+
++static int pre_authorization TAC_ARGS((const char *username, struct author_data *data));
++
+ static int
+ pre_authorization(username, data)
+-char *username;
++const char *username;
+ struct author_data *data;
+ {
+ int status;
+ char **out_args;
+ int out_cnt, i;
+- char *cmd;
++ const char *cmd;
+ char error_str[255];
+ int error_len = 255;
+
+@@ -199,7 +228,7 @@ struct author_data *data;
+ /* If a before-authorization program exists, call it to see how to
+ proceed */
+
+- cmd = cfg_get_pvalue(username, TAC_IS_USER,
++ cmd = cfg_get_pvalue(S_user, username,
+ S_before, TAC_PLUS_RECURSE);
+ if (!cmd)
+ return(0);
+@@ -303,16 +332,17 @@ struct author_data *data;
+ change the authorization status by calling an external program.
+ */
+
++static void post_authorization TAC_ARGS((const char *username, struct author_data *data));
++
+ static void
+ post_authorization(username, data)
+- char *username;
+- struct author_data *data;
++const 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);
++ const char *after = cfg_get_pvalue(S_user, username, S_after, TAC_PLUS_RECURSE);
+ if (!after)
+ return;
+
+@@ -378,6 +408,8 @@ post_authorization(username, data)
+ }
+
+
++static char *value TAC_ARGS((char *s));
++
+ /* Return a pointer to the value part of an attr=value string */
+ static char *
+ value(s)
+@@ -393,6 +425,8 @@ char *s;
+ /* 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 TAC_ARGS((struct author_data *data));
++
+ static char *
+ assemble_args(data)
+ struct author_data *data;
+@@ -445,30 +479,27 @@ struct author_data *data;
+ Otherwise, we return 1, indicating no further processing is
+ required for this request. */
+
++static int authorize_exec TAC_ARGS((const char *user, struct author_data *data));
++
+ static int
+ authorize_exec(user, data)
+-char *user;
++const 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 (cfg_get_svc_node(user, N_svc_exec, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+- report(LOG_DEBUG, "exec is explicitly permitted by line %d",
+- svc->line);
++ report(LOG_DEBUG, "exec is explicitly permitted");
+ 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 (cfg_get_svc_node(user, N_svc_cmd, NULL, NULL, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "exec permitted because commands are configured");
+
+@@ -478,18 +509,6 @@ struct author_data *data;
+ 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");
+
+@@ -501,16 +520,15 @@ struct author_data *data;
+ /* Is an exec command authorized per our database(s)?
+ Return 0 if status is valid */
+
++static int authorize_cmd TAC_ARGS((const char *user, const char *cmd, struct author_data *data));
++
+ static int
+ authorize_cmd(user, cmd, data)
+-char *user, *cmd;
++const char *user;
++const char *cmd;
+ struct author_data *data;
+ {
+- NODE *node;
+ char *args;
+- int match;
+-
+- args = assemble_args(data);
+
+ if (!cmd) {
+ data->status = AUTHOR_STATUS_ERROR;
+@@ -520,93 +538,41 @@ struct author_data *data;
+ 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;
++ args = assemble_args(data);
++ switch (cfg_authorize_cmd(user, cmd, args)) {
+
+- switch (node->type) {
+- case N_permit:
+- if (debug & DEBUG_AUTHOR_FLAG) {
+- report(LOG_DEBUG, "%s %s permitted by line %d",
+- cmd, args, node->line);
+- }
++ case ER_TRUE:
+ 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);
+- }
++
++ case ER_FALSE:
+ data->status = AUTHOR_STATUS_FAIL;
+ data->num_out_args = 0;
+ break;
+- default:
++
++ case ER_UNKNOWN:
+ 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);
++ data->admin_msg = tac_strdup("Server error illegal configuration");
+ break;
+ }
++
+ if (args)
+ free(args);
+- args = NULL;
+- return (0);
+- }
+- if (args)
+- free(args);
+- return (0);
++ return(0);
+ }
+
++static int is_separator TAC_ARGS((int ch));
++
+ static int
+ is_separator(ch)
+-char ch;
++int ch; /* promoted "char" type */
+ {
+ return (ch == '=' || ch == '*');
+ }
+
++static int arg_ok TAC_ARGS((char *arg));
++
+ /* check an attr=value pair for well-formedness */
+ static int
+ arg_ok(arg)
+@@ -630,6 +596,8 @@ char *arg;
+ }
+
+
++static int match_attrs TAC_ARGS((char *nas_arg, char *server_arg));
++
+ /* return 1 if attrs match, 0 otherwise */
+ static int
+ match_attrs(nas_arg, server_arg)
+@@ -647,6 +615,8 @@ char *nas_arg, *server_arg;
+ return (0);
+ }
+
++static int match_values TAC_ARGS((char *nas_arg, char *server_arg));
++
+ /* return 1 if values match, 0 otherwise */
+ static int
+ match_values(nas_arg, server_arg)
+@@ -671,6 +641,8 @@ char *nas_arg, *server_arg;
+ return(STREQ(nas_arg, server_arg));
+ }
+
++static int mandatory TAC_ARGS((char *arg));
++
+ /* Return 1 if arg is mandatory, 0 otherwise */
+ static int
+ mandatory(arg)
+@@ -690,6 +662,8 @@ char *arg;
+ return (*p == '=');
+ }
+
++static int optional TAC_ARGS((char *arg));
++
+ static int
+ optional(arg)
+ char *arg;
+@@ -699,21 +673,16 @@ char *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 */
++ we admit (return 1) any PPP-LCP request */
++
++static int ppp_lcp_allowed TAC_ARGS((const char *user));
+
+ static int
+-ppp_lcp_allowed(svc, protocol, user)
+- int svc;
+- char *user, *protocol;
++ppp_lcp_allowed(user)
++const char *user;
+ {
+- /* 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)) {
++ /* It is an LCP request. Are there PPP services configured? */
++ if (cfg_get_svc_node(user, N_svc_ppp, NULL /* protocol */, NULL /* svcname */, TAC_PLUS_RECURSE, NULL /* nodep */)) {
+ if (debug & DEBUG_AUTHOR_FLAG) {
+ report(LOG_DEBUG,
+ "ppp/lcp request permitted (ppp is configured for %s)",
+@@ -730,14 +699,16 @@ ppp_lcp_allowed(svc, protocol, user)
+ return(0);
+ }
+
++static int authorize_svc TAC_ARGS((const char *user, int svc, const char *protocol, const char *svcname, struct author_data *data));
++
+ /* 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 */
++const char *user;
++int svc;
++const char *svcname;
++struct author_data *data;
++const char *protocol; /* valid only if svc == ppp */
+ {
+ int max_args;
+ char **out_args, **outp;
+@@ -747,20 +718,23 @@ authorize_svc(user, svc, protocol, svcname, data)
+ char **cfg_argp;
+ int deny_by_default;
+ NODE *node;
++ int service_permit;
+
+ 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);
++ service_permit = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE, &node);
+
+- 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. */
++ /* Now we may have three cases:
++ * service_permit == 1 && node != NULL
++ * service_permit == 1 && node == NULL
++ * service_permit == 0 (BTW node == NULL)
++ */
+
+- if (cfg_user_svc_default_is_permit(user)) {
++ if (service_permit && !node) {
++ /* Service not found and the default is permit, we'll allow it. */
+
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG,
+@@ -775,7 +749,13 @@ authorize_svc(user, svc, protocol, svcname, data)
+ return(0);
+ }
+
+- if (ppp_lcp_allowed(svc, protocol, user)) {
++ if (!service_permit) {
++ /* Service not found, if this is an
++ PPP/LCP request and other ppp services are configured,
++ we'll allow it. */
++
++ if (svc == N_svc_ppp && protocol && STREQ(protocol, "lcp")
++ && ppp_lcp_allowed(user)) {
+ data->status = AUTHOR_STATUS_PASS_ADD;
+ data->num_out_args = 0;
+ data->output_args = NULL;
+@@ -1200,6 +1180,8 @@ authorize_svc(user, svc, protocol, svcname, data)
+ name in svcname.
+ */
+
++static int get_nas_svc TAC_ARGS((struct author_data *data, char **cmdname, char **protocol, char **svcname));
++
+ static int
+ get_nas_svc(data, cmdname, protocol, svcname)
+ struct author_data *data;
+diff --git a/do_author.h b/do_author.h
+new file mode 100644
+index 0000000..08bea16
+--- /dev/null
++++ b/do_author.h
+@@ -0,0 +1,64 @@
++#ifndef DO_AUTHOR_H
++#define DO_AUTHOR_H 1
++
++#include "tac_plus.h"
++
++
++/*
++ * 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;
++};
++
++
++/*
++ * 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 */
++
++};
++
++
++extern int do_author TAC_ARGS((struct author_data *data));
++
++
++#endif /* DO_AUTHOR_H */
+diff --git a/dump.c b/dump.c
+index ae58e5e..511085b 100644
+--- a/dump.c
++++ b/dump.c
+@@ -17,8 +17,21 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include <netinet/in.h> /* for ntohl() */
++
++#include "dump.h"
++#include "report.h"
++#include "utils.h"
++#include "packet.h"
++#include "do_author.h"
++#include "main.h"
++
++
++char *summarise_outgoing_packet_type TAC_ARGS((u_char *pak));
++
+ /* Routines for dumping packets to stderr */
+ char *
+ summarise_outgoing_packet_type(pak)
+@@ -90,7 +103,9 @@ u_char *pak;
+ return (p);
+ }
+
+-void
++static void dump_header TAC_ARGS((u_char *pak));
++
++static void
+ dump_header(pak)
+ u_char *pak;
+ {
+@@ -117,7 +132,10 @@ u_char *pak;
+ }
+
+
++void dump_nas_pak TAC_ARGS((u_char *pak));
++
+ /* Dump packets originated by a NAS */
++void
+ dump_nas_pak(pak)
+ u_char *pak;
+ {
+@@ -376,6 +394,9 @@ u_char *pak;
+
+ /* Dump packets originated by Tacacsd */
+
++void dump_tacacs_pak TAC_ARGS((u_char *pak));
++
++void
+ dump_tacacs_pak(pak)
+ u_char *pak;
+ {
+@@ -482,6 +503,8 @@ u_char *pak;
+ report(LOG_DEBUG, "End packet");
+ }
+
++char *summarise_incoming_packet_type TAC_ARGS((u_char *pak));
++
+ /* summarise packet types for logging routines. */
+ char *
+ summarise_incoming_packet_type(pak)
+diff --git a/dump.h b/dump.h
+new file mode 100644
+index 0000000..bddce30
+--- /dev/null
++++ b/dump.h
+@@ -0,0 +1,15 @@
++#ifndef DUMP_H
++#define DUMP_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++extern char *summarise_outgoing_packet_type TAC_ARGS((u_char *pak));
++extern void dump_nas_pak TAC_ARGS((u_char *pak));
++extern void dump_tacacs_pak TAC_ARGS((u_char *pak));
++extern char *summarise_incoming_packet_type TAC_ARGS((u_char *pak));
++
++
++#endif /* DUMP_H */
+diff --git a/enable.c b/enable.c
+index 63a17eb..3e56c92 100644
+--- a/enable.c
++++ b/enable.c
+@@ -17,8 +17,23 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "enable.h"
+ #include "expire.h"
++#include "utils.h"
++#include "report.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
++#include "cfgfile.h"
++
+
+ /* internal state variables */
+ #define STATE_AUTHEN_START 0 /* no requests issued */
+@@ -30,6 +45,8 @@ struct private_data {
+ int state;
+ };
+
++static void enable TAC_ARGS((char *passwd, struct authen_data *data));
++
+ static void
+ enable(passwd, data)
+ char *passwd;
+@@ -84,6 +101,8 @@ struct authen_data *data;
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int enable_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ enable_fn(data)
+ struct authen_data *data;
+diff --git a/enable.h b/enable.h
+new file mode 100644
+index 0000000..10a4e8a
+--- /dev/null
++++ b/enable.h
+@@ -0,0 +1,12 @@
++#ifndef ENABLE_H
++#define ENABLE_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int enable_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* ENABLE_H */
+diff --git a/encrypt.c b/encrypt.c
+index 60ba5e9..2840916 100644
+--- a/encrypt.c
++++ b/encrypt.c
+@@ -17,8 +17,19 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
++#include <netinet/in.h> /* for ntohl() */
++
++#include "encrypt.h"
+ #include "md5.h"
++#include "utils.h"
++#include "report.h"
++#include "packet.h"
++#include "main.h"
++
+
+ /*
+ * create_md5_hash(): create an md5 hash of the "session_id", "the user's
+@@ -36,21 +47,25 @@
+ *
+ */
+
+-void
++static void create_md5_hash TAC_ARGS((int session_id, const char *key, unsigned version, unsigned seq_no, u_char *prev_hash, u_char *hash));
++
++static 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;
++const char *key;
++unsigned version; /* promoted "u_char" type */
++unsigned seq_no; /* promoted "u_char" type */
+ u_char *prev_hash;
+ u_char *hash;
+ {
+ u_char *md_stream, *mdp;
+ int md_len;
+ MD5_CTX mdcontext;
++ u_char version_uchar = version;
++ u_char seq_no_uchar = seq_no;
+
+- md_len = sizeof(session_id) + strlen(key) + sizeof(version) +
+- sizeof(seq_no);
++ md_len = sizeof(session_id) + strlen(key) + sizeof(version_uchar) +
++ sizeof(seq_no_uchar);
+
+ if (prev_hash) {
+ md_len += MD5_LEN;
+@@ -62,11 +77,11 @@ u_char *hash;
+ bcopy(key, mdp, strlen(key));
+ mdp += strlen(key);
+
+- bcopy(&version, mdp, sizeof(version));
+- mdp += sizeof(version);
++ bcopy(&version_uchar, mdp, sizeof(version_uchar));
++ mdp += sizeof(version_uchar);
+
+- bcopy(&seq_no, mdp, sizeof(seq_no));
+- mdp += sizeof(seq_no);
++ bcopy(&seq_no_uchar, mdp, sizeof(seq_no_uchar));
++ mdp += sizeof(seq_no_uchar);
+
+ if (prev_hash) {
+ bcopy(prev_hash, mdp, MD5_LEN);
+@@ -90,10 +105,13 @@ u_char *hash;
+ * Return 0 on success, -1 on failure.
+ */
+
++int md5_xor TAC_ARGS((HDR *hdr, u_char *data, const char *key));
++
++int
+ md5_xor(hdr, data, key)
+ HDR *hdr;
+ u_char *data;
+-char *key;
++const char *key;
+ {
+ int i, j;
+ u_char hash[MD5_LEN]; /* the md5 hash */
+diff --git a/encrypt.h b/encrypt.h
+new file mode 100644
+index 0000000..9c7ae53
+--- /dev/null
++++ b/encrypt.h
+@@ -0,0 +1,14 @@
++#ifndef ENCRYPT_H
++#define ENCRYPT_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++#include "packet.h" /* for "HDR" */
++
++
++extern int md5_xor TAC_ARGS((HDR *hdr, u_char *data, const char *key));
++
++
++#endif /* ENCRYPT_H */
+diff --git a/expire.c b/expire.c
+index 1ef745c..3aed354 100644
+--- a/expire.c
++++ b/expire.c
+@@ -17,9 +17,17 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <ctype.h>
++#include <time.h>
++#include <string.h>
++#include <stdio.h>
++
+ #include "expire.h"
+
++
+ /*
+ * check a date for expiry. If the field specifies
+ * a shell return PW_OK
+@@ -37,9 +45,11 @@ static char *monthname[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+ static long days_ere_month[] = {0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334};
+
++int check_expiration TAC_ARGS((const char *date));
++
+ int
+ check_expiration(date)
+-char *date;
++const char *date;
+ {
+ long day, month, year, leaps, now, expiration, warning;
+ char monthstr[10];
+@@ -52,11 +62,11 @@ char *date;
+ return (PW_OK);
+
+ /* Parse date string. Fail it upon error. */
+- if (sscanf(date, "%s %d %d", monthstr, &day, &year) != 3)
++ if (sscanf(date, "%s %ld %ld", monthstr, &day, &year) != 3)
+ return (PW_EXPIRED);
+
+ for(i=0; i < 3; i++) {
+- monthstr[i] = toupper(monthstr[i]);
++ monthstr[i] = toupper((int) monthstr[i]);
+ }
+
+ /* Compute the expiration date in days. */
+diff --git a/expire.h b/expire.h
+index c7df48d..6e7fc5e 100644
+--- a/expire.h
++++ b/expire.h
+@@ -1,3 +1,8 @@
++#ifndef EXPIRE_H
++#define EXPIRE_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,10 +22,15 @@
+ 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();
++
++extern int check_expiration TAC_ARGS((const char *date));
++
++
++#endif /* EXPIRE_H */
+diff --git a/generate_passwd.c b/generate_passwd.c
+index 509e48a..a21098d 100644
+--- a/generate_passwd.c
++++ b/generate_passwd.c
+@@ -23,12 +23,41 @@
+ Usage: a.out [salt]
+ */
+
+-#define NULL 0
+
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <stdlib.h>
++#include <time.h>
++#include <stdio.h>
++#include <string.h>
++
++
++#ifndef NULL
++#define NULL ((void *) 0)
++#endif
++
++/* Stolen from pbmplus.h of netpbm: */
++
++#if __STDC__
++#define TAC_ARGS(alist) alist
++#else /*__STDC__*/
++#define TAC_ARGS(alist) ()
++#endif /*__STDC__*/
++
++
++int main TAC_ARGS((int argc, char **argv));
++
++int
+ main(argc, argv)
++int argc;
+ char **argv;
+ {
+- char *crypt();
+ char pass[25], *salt, buf[24];
+ char *result;
+ int n;
+@@ -42,10 +71,10 @@ char **argv;
+
+ write(1, prompt, strlen(prompt));
+ n = read(0, pass, sizeof(pass));
+- pass[n-1] = NULL;
++ pass[n-1] = 0;
+
+ if (!salt) {
+- int i, r, r1, r2;
++ int i, r, r1 = 0 /* GCC paranoia */, r2 = 0 /* GCC paranoia */;
+
+ srand(time(0));
+
+@@ -82,9 +111,6 @@ char **argv;
+
+ write(1, result, strlen(result));
+ write(1, "\n", 1);
+-}
+-
+-
+-
+-
+
++ return (EXIT_SUCCESS);
++}
+diff --git a/hash.c b/hash.c
+index 443d42b..2db807b 100644
+--- a/hash.c
++++ b/hash.c
+@@ -17,19 +17,24 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include "hash.h"
++#include "utils.h"
++
++
+ struct entry {
+ char *name;
+ void *hash;
+ };
+
+-typedef struct entry ENTRY;
++static int calculate_hash TAC_ARGS((const char *name));
+
+ /* Calculate hash value from a string */
+ static int
+ calculate_hash(name)
+-char *name;
++const char *name;
+ {
+ int i;
+ int len = strlen(name);
+@@ -43,12 +48,14 @@ char *name;
+ return (hashval);
+ }
+
++void *hash_lookup TAC_ARGS((void **hashtab, const char *name));
++
+ /* Lookup a name in a hash table. Return its node if it exists, NULL
+ otherwise */
+ void *
+ hash_lookup(hashtab, name)
+ void **hashtab;
+-char *name;
++const char *name;
+ {
+ ENTRY *entry;
+ int hashval = calculate_hash(name);
+@@ -64,6 +71,8 @@ char *name;
+ return (NULL);
+ }
+
++void *hash_add_entry TAC_ARGS((void **hashtab, ENTRY *newentry));
++
+ /* Add a node to a hash table. Return node if it exists, NULL
+ otherwise */
+ void *
+@@ -86,6 +95,8 @@ ENTRY *newentry;
+ }
+
+
++void **hash_get_entries TAC_ARGS((void **hashtab));
++
+ /* Return an array of pointers to all the entries in a hash table */
+ void **
+ hash_get_entries(hashtab)
+diff --git a/hash.h b/hash.h
+new file mode 100644
+index 0000000..a808f05
+--- /dev/null
++++ b/hash.h
+@@ -0,0 +1,17 @@
++#ifndef HASH_H
++#define HASH_H 1
++
++#include "tac_plus.h"
++
++
++#define HASH_TAB_SIZE 157 /* user and group hash table sizes */
++
++typedef struct entry ENTRY;
++
++
++extern void *hash_lookup TAC_ARGS((void **hashtab, const char *name));
++extern void *hash_add_entry TAC_ARGS((void **hashtab, ENTRY *newentry));
++extern void **hash_get_entries TAC_ARGS((void **hashtab));
++
++
++#endif /* HASH_H */
+diff --git a/ldap_author.c b/ldap_author.c
+index ca1a8ef..c788e82 100644
+--- a/ldap_author.c
++++ b/ldap_author.c
+@@ -36,21 +36,30 @@ Port name isn't required.. I would like to change format with :
+ */
+
+
+-#if defined(USE_LDAP)
++#include "tac_plus.h"
++
++#ifdef USE_LDAP
++
+ #include <stdio.h>
+ #include <string.h>
++#include <stdlib.h>
+ #include <lber.h>
+ #include <ldap.h>
+ #include <ldap_cdefs.h>
+
+-#include "tac_plus.h"
+-#include "ldap.h"
++#include "ldap_author.h"
++#include "main.h"
++#include "utils.h"
++#include "report.h"
++
+
++int ldap_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
+
+ int
+ ldap_verify(user, users_passwd, str_conn)
+-char *user, *users_passwd; /* Username and gived password */
+-char *str_conn; /* String connection to database */
++const char *user; /* username ... */
++const char *users_passwd; /* ... and given password */
++const char *str_conn; /* string connection to database */
+ {
+ char *buf;
+ char *ldapServer;
+@@ -62,11 +71,7 @@ char *str_conn; /* String connection to database */
+ /* 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);
+- }
++ buf = (char *) tac_malloc(strlen(str_conn)+1);
+
+ strcpy(buf,str_conn);
+ ldapServer=strstr(buf, "://");
+@@ -99,7 +104,7 @@ char *str_conn; /* String connection to database */
+ return 1;
+ }
+
+- err=ldap_simple_bind_s(ld, user, users_passwd);
++ err=ldap_simple_bind_s(ld, (/* de-const */ char *) user, (/* de-const */ char *) users_passwd);
+
+ if(err != LDAP_SUCCESS)
+ {
+@@ -116,4 +121,9 @@ char *str_conn; /* String connection to database */
+ return 0;
+ }
+ }
+-#endif /* LDAP */
++
++#else /* USE_LDAP */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* USE_LDAP */
+diff --git a/ldap_author.h b/ldap_author.h
+index 6479426..ecd71dd 100644
+--- a/ldap_author.h
++++ b/ldap_author.h
+@@ -1 +1,14 @@
+-int ldap_verify();
++#ifndef LDAP_AUTHOR_H
++#define LDAP_AUTHOR_H 1
++
++#include "tac_plus.h"
++
++#ifdef USE_LDAP
++
++
++extern int ldap_verify TAC_ARGS((const char *user, const char *users_passwd, const char *str_conn));
++
++
++#endif /* USE_LDAP */
++
++#endif /* LDAP_AUTHOR_H */
+diff --git a/main.c b/main.c
+index cf0ffd8..c356b75 100644
+--- a/main.c
++++ b/main.c
+@@ -14,7 +14,7 @@
+ * 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,
+@@ -22,37 +22,87 @@
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+-#include "sys/wait.h"
+-#include "signal.h"
++
++#include <stdlib.h>
++#include <sys/wait.h>
++#include <signal.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <netdb.h>
++#include <errno.h>
++#ifdef HAVE_SYS_TIME_H
++#include <sys/time.h>
++#endif
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_SYSLOG_H
++#include <syslog.h>
++#endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include <sys/syslog.h>
++#endif
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
++#endif
++#ifdef HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++
++#include "main.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "packet.h"
++#include "dump.h"
++#include "author.h"
++#include "acct.h"
++#include "authen.h"
++#include "do_acct.h"
++#include "parse.h"
++
++#ifdef MAXSESS
++#include "maxsess.h"
++#endif
++
++
++static void version TAC_ARGS((void));
++static void start_session TAC_ARGS((void));
++
++
++/* Configurable:
++ */
++
++#ifndef TAC_PLUS_PORT
++#define TAC_PLUS_PORT 49
++#endif
++
+
+ 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 */
++static 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()
++static RETSIGTYPE reapchild TAC_ARGS((int signo));
++
++static RETSIGTYPE
++reapchild(signo)
++int signo;
+ {
+ #ifdef UNIONWAIT
+ union wait status;
+@@ -61,6 +111,8 @@ reapchild()
+ #endif
+ int pid;
+
++ signal(SIGCHLD, reapchild);
++
+ for (;;) {
+ pid = wait3(&status, WNOHANG, 0);
+ if (pid <= 0)
+@@ -71,6 +123,8 @@ reapchild()
+ }
+ #endif /* REAPCHILD */
+
++static void die TAC_ARGS((int signum));
++
+ static void
+ die(signum)
+ int signum;
+@@ -80,6 +134,8 @@ int signum;
+ tac_exit(0);
+ }
+
++static void init TAC_ARGS((void));
++
+ static void
+ init()
+ {
+@@ -104,26 +160,59 @@ init()
+
+ initialised++;
+
+- report(LOG_INFO, "Version %s Initialized %d", VERSION, initialised);
++ report(LOG_INFO, "Version %s%s Initialized %d", VERSION, VERSION_TAIL, initialised);
+
+ }
+
+-static void
++/* 'handler()' will be called during initialization to setup signal handler,
++ * keep it in mind when modifying it!
++ */
++
++static int handler_occured = 0;
++
++static RETSIGTYPE handler TAC_ARGS((int signum));
++
++static RETSIGTYPE
+ handler(signum)
+ int signum;
+ {
+- report(LOG_INFO, "Received signal %d", signum);
+- init();
+-#ifdef REARMSIGNAL
++ /* never execute any non-trivial (=system-call) commands here
++ * as it may immediately abort the whole 'handler()' (SYSV signal).
++ * We hope that SA_RESTART is NOT set for our signals
++ */
++ handler_occured = 1;
++ /* It is never wrong to reinstall 'handler' just to be safe */
+ signal(SIGUSR1, handler);
+- signal(SIGHUP, handler);
+-#endif REARMSIGNAL
++ signal(SIGHUP , handler);
++
++ /* DON'T interrupt! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 0 /* flag */);
++ siginterrupt(SIGHUP , 0 /* flag */);
++#endif
++}
++
++static void check_handler_occured TAC_ARGS((void));
++
++static RETSIGTYPE
++check_handler_occured()
++{
++
++ if (!handler_occured)
++ return;
++
++ handler_occured = 0;
++ report(LOG_INFO, "Signal detected, reloading configuration");
++ init();
+ }
+
+ /*
+ * Return a socket bound to an appropriate port number/address. Exits
+ * the program on failure */
+
++static int get_socket TAC_ARGS((void));
++
++static int
+ get_socket()
+ {
+ int s;
+@@ -171,6 +260,8 @@ get_socket()
+ return (s);
+ }
+
++static void open_logfile TAC_ARGS((void));
++
+ static void
+ open_logfile()
+ {
+@@ -182,6 +273,29 @@ open_logfile()
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ }
+
++static void prep_session_peer TAC_ARGS((const struct sockaddr_in *from));
++
++static void
++prep_session_peer(from)
++const struct sockaddr_in *from;
++{
++ struct hostent *hp;
++
++ if (session.peer_addr && session.peer_addr != session.peer)
++ free(session.peer_addr);
++ if (session.peer)
++ free(session.peer);
++
++ session.peer_addr = tac_strdup( (char *) inet_ntoa(from->sin_addr) );
++
++ hp = gethostbyaddr((char *) &from->sin_addr.s_addr, sizeof(from->sin_addr.s_addr), AF_INET);
++
++ if (hp)
++ session.peer = tac_strdup(hp->h_name);
++ else
++ session.peer = session.peer_addr;
++}
++
+ /*
+ * main
+ *
+@@ -189,6 +303,9 @@ open_logfile()
+ * Parse arguments and act appropiately.
+ */
+
++int main TAC_ARGS((int argc, char **argv));
++
++int
+ main(argc, argv)
+ int argc;
+ char **argv;
+@@ -214,6 +331,10 @@ char **argv;
+ port = TAC_PLUS_PORT;
+ #endif
+
++#ifdef MAINTAINER_MODE
++ session.cfgfile = "/etc/tacacs/tac_plus.cfg";
++#endif
++
+ if (argc <= 1) {
+ fprintf(stderr, "Usage: tac_plus -C <configuration file>\n");
+ fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p <port> ]\n");
+@@ -284,8 +405,9 @@ char **argv;
+
+ init();
+
+- signal(SIGUSR1, handler);
+- signal(SIGHUP, handler);
++ handler(-1 /* signum */); /* connect to the signals */
++ handler_occured = 0; /* post-fix cludge */
++
+ signal(SIGTERM, die);
+ signal(SIGPIPE, SIG_IGN);
+
+@@ -293,31 +415,27 @@ char **argv;
+ tac_exit(0);
+
+ if (debug)
+- report(LOG_DEBUG, "tac_plus server %s starting", VERSION);
++ report(LOG_DEBUG, "tac_plus server %s%s starting", VERSION, VERSION_TAIL);
+
+ if (!standalone) {
+ /* running under inetd */
+ struct sockaddr_in name;
+- int name_len;
+- int on = 1;
++ socklen_t name_len;
++#ifdef FIONBIO
++ int fionbio_on = 1;
++#endif
+
+ 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));
+- }
++ prep_session_peer(NULL);
++ } else
++ prep_session_peer(&name);
++
++ session.sock = 0;
+ #ifdef FIONBIO
+- if (ioctl(session.sock, FIONBIO, &on) < 0) {
++ if (ioctl(session.sock, FIONBIO, &fionbio_on) < 0) {
+ report(LOG_ERR, "ioctl(FIONBIO) %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+@@ -351,23 +469,29 @@ char **argv;
+
+ #ifndef REAPCHILD
+
+-#ifdef LINUX
++#ifdef SETPGRP_VOID
+ if (setpgrp() == -1)
+-#else /* LINUX */
++#else /* SETPGRP_VOID */
+ if (setpgrp(0, getpid()) == -1)
+-#endif /* LINUX */
++#endif /* SETPGRP_VOID */
+ report(LOG_ERR, "Can't change process group");
+
++#ifdef TIOCNOTTY
+ c = open("/dev/tty", O_RDWR);
+ if (c >= 0) {
+ ioctl(c, TIOCNOTTY, (char *) 0);
+ (void) close(c);
+ }
++#endif
+ signal(SIGCHLD, reapchild);
+
+ #else /* REAPCHILD */
+
++#ifdef SETPGRP_VOID
+ if (setpgrp() == 1)
++#else /* SETPGRP_VOID */
++ if (setpgrp(0, getpid()) == 1)
++#endif /* SETPGRP_VOID */
+ report(LOG_ERR, "Can't change process group");
+
+ signal(SIGHUP, SIG_IGN);
+@@ -423,7 +547,7 @@ char **argv;
+
+ /* write process id to pidfile */
+ if ((fp = fopen(pidfilebuf, "w")) != NULL) {
+- fprintf(fp, "%d\n", getpid());
++ fprintf(fp, "%d\n", (int) getpid());
+ fclose(fp);
+ } else
+ report(LOG_ERR, "Cannot write pid to %s %s",
+@@ -446,38 +570,45 @@ char **argv;
+ #endif /* MAXSESS */
+
+ report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
+- getuid(), geteuid(), getgid(), getegid(), s);
++ (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid(), s);
+
+ for (;;) {
+ int pid;
+ struct sockaddr_in from;
+- int from_len;
++ socklen_t from_len;
+ int newsockfd;
+- struct hostent *hp = NULL;
++
++ check_handler_occured();
+
+ bzero((char *) &from, sizeof(from));
+ from_len = sizeof(from);
+
++ /* PERMIT interrupt of accept()! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 1 /* flag */);
++ siginterrupt(SIGHUP , 1 /* flag */);
++#endif
++
++ errno = 0;
+ newsockfd = accept(s, (struct sockaddr *) &from, &from_len);
+
++ /* DON'T interrupt! */
++#ifdef HAVE_SIGINTERRUPT
++ siginterrupt(SIGUSR1, 0 /* flag */);
++ siginterrupt(SIGHUP , 0 /* flag */);
++#endif
++
++ check_handler_occured();
++
+ if (newsockfd < 0) {
+- if (errno == EINTR)
++ /* sometimes we may get even 'errno==0' when 'handler()' signal occured */
++ if (errno == EINTR || errno == 0)
+ 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));
++ prep_session_peer(&from);
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "session request from %s sock=%d",
+@@ -521,8 +652,10 @@ getdtablesize()
+ }
+ #endif /* GETDTABLESIZE */
+
++static int bad_version_check TAC_ARGS((u_char *pak));
++
+ /* Make sure version number is kosher. Return 0 if it is */
+-int
++static int
+ bad_version_check(pak)
+ u_char *pak;
+ {
+@@ -555,17 +688,21 @@ u_char *pak;
+ *
+ */
+
+-void
++static void start_session TAC_ARGS((void));
++
++static void
+ start_session()
+ {
+- u_char *pak, *read_packet();
++ u_char *pak;
+ HDR *hdr;
+- void authen();
+
+ session.seq_no = 0;
+ session.aborted = 0;
+ session.version = 0;
+
++ /* Now we are starting our new 'request' cycle */
++ cfg_request_scan_begin();
++
+ pak = read_packet();
+ if (!pak) {
+ return;
+@@ -608,9 +745,12 @@ start_session()
+ }
+ }
+
++static void version TAC_ARGS((void));
++
++static void
+ version()
+ {
+- fprintf(stdout, "tac_plus version %s\n", VERSION);
++ fprintf(stdout, "tac_plus version %s%s\n", VERSION, VERSION_TAIL);
+ #ifdef AIX
+ fprintf(stdout,"AIX\n");
+ #endif
+@@ -644,9 +784,6 @@ version()
+ #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
+@@ -662,15 +799,9 @@ version()
+ #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
+@@ -692,14 +823,8 @@ version()
+ #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");
++#ifdef HAVE_STRCSPN
++ fprintf(stdout,"HAVE_STRCSPN\n");
+ #endif
+ #ifdef SYSV
+ fprintf(stdout,"SYSV\n");
+@@ -713,15 +838,9 @@ version()
+ #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
+diff --git a/main.h b/main.h
+new file mode 100644
+index 0000000..79c643a
+--- /dev/null
++++ b/main.h
+@@ -0,0 +1,59 @@
++#ifndef MAIN_H
++#define MAIN_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++#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 *peer_addr; /* numerical name of connected peer */
++ /* it MAY (peer==peer_addr)! */
++ 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 int debug; /* debugging flag */
++extern int single; /* do not fork (for debugging) */
++extern int console; /* log to console */
++extern int parse_only; /* exit after parsing verbosely */
++extern int sendauth_only; /* don't do sendauth */
++
++/* 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_CFGEVAL_FLAG 16384
++#define DEBUG_MAXSESS_FLAG 32768
++#define DEBUG_LOCK_FLAG 65536
++
++
++extern struct session session; /* the session */
++extern int main TAC_ARGS((int argc, char **argv));
++
++
++#endif /* MAIN_H */
+diff --git a/maxsess.c b/maxsess.c
+index 83e0815..aa81a9a 100644
+--- a/maxsess.c
++++ b/maxsess.c
+@@ -17,9 +17,62 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
+ #ifdef MAXSESS
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
++#endif
++#include <string.h>
++#include <errno.h>
++#ifdef HAVE_SYS_TIME_H
++#include <sys/time.h>
++#endif
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <netdb.h>
++
++#include "maxsess.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "main.h"
++#include "do_acct.h"
++#include "parse.h"
++
++
++void maxsess_loginit TAC_ARGS((void));
++
++
++/* Configurable:
++ */
++
++/* This is a shared file used to maintain a record of who's on
++ */
++#define WHOLOG_DEFAULT "/var/log/tac_who.log"
++
++
++/*
++ * 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 */
++};
++
++
+ char *wholog = WHOLOG_DEFAULT;
+ /*
+ * initialize wholog file for tracking of user logins/logouts from
+@@ -41,6 +94,8 @@ maxsess_loginit()
+ }
+ }
+
++static char *portname TAC_ARGS((char *oldport));
++
+ /*
+ * Given a port description, return it in a canonical format.
+ *
+@@ -67,6 +122,8 @@ char *oldport;
+ return (p);
+ }
+
++static void write_record TAC_ARGS((char *name, FILE *fp, void *buf, int size, long int offset));
++
+ /*
+ * Seek to offset and write a buffer into the file pointed to by fp
+ */
+@@ -79,7 +136,7 @@ void *buf;
+ char *name;
+ {
+ if (fseek(fp, offset, SEEK_SET) < 0) {
+- report(LOG_ERR, "%s fd=%d Cannot seek to %d %s",
++ report(LOG_ERR, "%s fd=%d Cannot seek to %ld %s",
+ name, fileno(fp), offset, sys_errlist[errno]);
+ }
+ if (fwrite(buf, size, 1, fp) != 1) {
+@@ -88,6 +145,8 @@ char *name;
+ }
+ }
+
++static void process_stop_record TAC_ARGS((struct identity *idp));
++
+ static void
+ process_stop_record(idp)
+ struct identity *idp;
+@@ -133,6 +192,8 @@ struct identity *idp;
+ fclose(fp);
+ }
+
++static void process_start_record TAC_ARGS((struct identity *idp));
++
+ static void
+ process_start_record(idp)
+ struct identity *idp;
+@@ -215,10 +276,13 @@ struct identity *idp;
+ }
+
+
++void loguser TAC_ARGS((struct acct_rec *rec));
++
+ /*
+ * Given a start or a stop accounting record, update the file of
+ * records which tracks who's logged on and where.
+ */
++void
+ loguser(rec)
+ struct acct_rec *rec;
+ {
+@@ -260,10 +324,12 @@ struct acct_rec *rec;
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes read. */
+
+-int
++static int timed_read TAC_ARGS((int fd, void *ptr, int nbytes, int timeout));
++
++static int
+ timed_read(fd, ptr, nbytes, timeout)
+ int fd;
+-u_char *ptr;
++void *ptr;
+ int nbytes;
+ int timeout;
+ {
+@@ -348,6 +414,8 @@ int timeout;
+ * with a maximum possible width of 10.
+ */
+
++static int ckfinger TAC_ARGS((char *user, char *nas, struct identity *idp));
++
+ static int
+ ckfinger(user, nas, idp)
+ char *user, *nas;
+@@ -372,7 +440,7 @@ struct identity *idp;
+
+ /* Get IP addr for the NAS */
+ inaddr = inet_addr(nas);
+- if (inaddr != -1) {
++ if (inaddr != (u_long)-1) {
+ /* A dotted decimal address */
+ bcopy(&inaddr, &sin.sin_addr, sizeof(inaddr));
+ sin.sin_family = AF_INET;
+@@ -492,6 +560,8 @@ struct identity *idp;
+ return (count);
+ }
+
++static int countusers_by_finger TAC_ARGS((struct identity *idp));
++
+ /*
+ * Verify how many sessions a user has according to the wholog file.
+ * Use finger to contact each NAS that wholog says has this user
+@@ -565,6 +635,8 @@ struct identity *idp;
+ return (nsess);
+ }
+
++static int countuser TAC_ARGS((struct identity *idp));
++
+ /*
+ * Estimate how many sessions a named user currently owns by looking in
+ * the wholog file.
+@@ -604,6 +676,8 @@ struct identity *idp;
+ return (nsess);
+ }
+
++static int is_async TAC_ARGS((char *portname));
++
+ /*
+ * is_async()
+ * Tell if the named NAS port is an async-like device.
+@@ -622,6 +696,8 @@ char *portname;
+ return (0);
+ }
+
++int maxsess_check_count TAC_ARGS((char *user, struct author_data *data));
++
+ /*
+ * See if this user can have more sessions.
+ */
+@@ -636,7 +712,7 @@ struct author_data *data;
+ /* No max session configured--don't check */
+ id = data->id;
+
+- maxsess = cfg_get_intvalue(user, TAC_IS_USER, S_maxsess, TAC_PLUS_RECURSE);
++ maxsess = cfg_get_intvalue(S_user, 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",
+@@ -680,13 +756,6 @@ struct author_data *data;
+
+ #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;
++TAC_SOURCEFILE_EMPTY
+
+ #endif /* MAXSESS */
+diff --git a/maxsess.h b/maxsess.h
+new file mode 100644
+index 0000000..c96d2b8
+--- /dev/null
++++ b/maxsess.h
+@@ -0,0 +1,25 @@
++#ifndef MAXSESS_H
++#define MAXSESS_H 1
++
++#include "tac_plus.h"
++
++#ifdef MAXSESS
++
++#include "do_author.h"
++
++
++/* This is a shared file used to maintain a record of who's on
++ */
++extern char *wholog;
++
++
++struct acct_rec;
++
++extern void maxsess_loginit TAC_ARGS((void));
++extern void loguser TAC_ARGS((struct acct_rec *rec));
++extern int maxsess_check_count TAC_ARGS((char *user, struct author_data *data));
++
++
++#endif /* MAXSESS */
++
++#endif /* MAXSESS_H */
+diff --git a/md4.c b/md4.c
+index d99c84c..5ac377d 100644
+--- a/md4.c
++++ b/md4.c
+@@ -43,19 +43,27 @@
+ */
+
+
++#include "tac_plus.h"
++
++#ifdef MSCHAP
++
+ #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
++static void MD4Transform TAC_ARGS((UINT4 state[4], const unsigned char block[64]));
++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len));
++static void Decode TAC_ARGS((UINT4 *output, const unsigned char *input, unsigned int len));
++
+
+ /* Constants for MD4Transform routine.
+ */
+@@ -72,12 +80,6 @@ typedef unsigned long int UINT4;
+ #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,
+@@ -111,6 +113,9 @@ static unsigned char PADDING[64] = {
+
+ /* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
++
++void MD4Init TAC_ARGS((MD4_CTX *context));
++
+ void MD4Init (context)
+ MD4_CTX *context; /* context */
+ {
+@@ -128,6 +133,9 @@ MD4_CTX *context; /* context */
+ operation, processing another message block, and updating the
+ context.
+ */
++
++void MD4Update TAC_ARGS((MD4_CTX *context, const unsigned char *input, unsigned int inputLen));
++
+ void MD4Update (context, input, inputLen)
+ MD4_CTX *context; /* context */
+ const unsigned char *input; /* input block */
+@@ -168,6 +176,9 @@ unsigned int inputLen; /* length of input block */
+ /* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
++
++void MD4Final TAC_ARGS((unsigned char digest[16], MD4_CTX *context));
++
+ void MD4Final (digest, context)
+ unsigned char digest[16]; /* message digest */
+ MD4_CTX *context; /* context */
+@@ -196,6 +207,9 @@ MD4_CTX *context; /* context */
+
+ /* MD4 basic transformation. Transforms state based on block.
+ */
++
++static void MD4Transform TAC_ARGS((UINT4 state[4], const unsigned char block[64]));
++
+ static void MD4Transform (state, block)
+ UINT4 state[4];
+ const unsigned char block[64];
+@@ -271,6 +285,9 @@ const unsigned char block[64];
+ /* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
++
++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len));
++
+ static void Encode (output, input, len)
+ unsigned char *output;
+ UINT4 *input;
+@@ -289,8 +306,10 @@ unsigned int len;
+ /* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+-static void Decode (output, input, len)
+
++static void Decode TAC_ARGS((UINT4 *output, const unsigned char *input, unsigned int len));
++
++static void Decode (output, input, len)
+ UINT4 *output;
+ const unsigned char *input;
+ unsigned int len;
+@@ -301,3 +320,9 @@ unsigned int len;
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+ }
++
++#else /* MSCHAP */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* MSCHAP */
+diff --git a/md4.h b/md4.h
+index d821229..38d0a81 100644
+--- a/md4.h
++++ b/md4.h
+@@ -1,3 +1,10 @@
++#ifndef MD4_H
++#define MD4_H 1
++
++#include "tac_plus.h"
++
++#ifdef MSCHAP
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -42,8 +49,6 @@
+ documentation and/or software.
+ */
+
+-#ifndef _MD4_H_
+-#define _MD4_H_
+ /* MD4 context. */
+ typedef struct MD4Context {
+ unsigned long int state[4]; /* state (ABCD) */
+@@ -51,11 +56,12 @@ typedef struct MD4Context {
+ unsigned char buffer[64]; /* input buffer */
+ } MD4_CTX;
+
+-void MD4Init();
+-void MD4Update();
+-void MD4Final();
+-char * MD4End();
+-char * MD4File();
+-char * MD4Data();
+
+-#endif /* _MD4_H_ */
++extern void MD4Init TAC_ARGS((MD4_CTX *context));
++extern void MD4Update TAC_ARGS((MD4_CTX *context, const unsigned char *input, unsigned int inputLen));
++extern void MD4Final TAC_ARGS((unsigned char digest[16], MD4_CTX *context));
++
++
++#endif /* MSCHAP */
++
++#endif /* MD4_H */
+diff --git a/md5.c b/md5.c
+index 06225b0..32ca404 100644
+--- a/md5.c
++++ b/md5.c
+@@ -47,12 +47,20 @@
+ * to contain all the information that RFC 1321's global.h contains.
+ */
+
++
++#include "tac_plus.h"
++
+ #include "md5.h"
+
++
++static void MD5Transform TAC_ARGS((UINT4 *state, unsigned char *block));
++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len));
++static void Decode TAC_ARGS((UINT4 *output, unsigned char *input, unsigned int len));
++
++
+ /* Constants for MD5Transform routine.
+ */
+
+-
+ #define S11 7
+ #define S12 12
+ #define S13 17
+@@ -70,24 +78,6 @@
+ #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,
+@@ -129,6 +119,8 @@ Rotation is separate from addition to prevent recomputation.
+ (a) += (b); \
+ }
+
++void MD5Init TAC_ARGS((MD5_CTX *context));
++
+ /* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+ void
+@@ -143,6 +135,8 @@ MD5_CTX *context; /* context */
+ context->state[3] = 0x10325476;
+ }
+
++void MD5Update TAC_ARGS((MD5_CTX *context, unsigned char *input, unsigned int inputLen));
++
+ /* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+@@ -168,8 +162,7 @@ unsigned int inputLen; /* length of input block */
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen) {
+- MD5_memcpy
+- ((POINTER) & context->buffer[index], (POINTER) input, partLen);
++ memcpy((POINTER) & context->buffer[index], (POINTER) input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+@@ -180,11 +173,12 @@ unsigned int inputLen; /* length of input block */
+ i = 0;
+
+ /* Buffer remaining input */
+- MD5_memcpy
+- ((POINTER) & context->buffer[index], (POINTER) & input[i],
++ memcpy((POINTER) & context->buffer[index], (POINTER) & input[i],
+ inputLen - i);
+ }
+
++void MD5Final TAC_ARGS((unsigned char *digest, MD5_CTX *context));
++
+ /* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+@@ -211,9 +205,11 @@ MD5_CTX *context; /* context */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+- MD5_memset((POINTER) context, 0, sizeof(*context));
++ memset((POINTER) context, 0, sizeof(*context));
+ }
+
++static void MD5Transform TAC_ARGS((UINT4 *state, unsigned char *block));
++
+ /* MD5 basic transformation. Transforms state based on block.
+ */
+ static void
+@@ -303,9 +299,11 @@ unsigned char block[64];
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+- MD5_memset((POINTER) x, 0, sizeof(x));
++ memset((POINTER) x, 0, sizeof(x));
+ }
+
++static void Encode TAC_ARGS((unsigned char *output, UINT4 *input, unsigned int len));
++
+ /* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+@@ -325,6 +323,8 @@ unsigned int len;
+ }
+ }
+
++static void Decode TAC_ARGS((UINT4 *output, unsigned char *input, unsigned int len));
++
+ /* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+@@ -340,36 +340,3 @@ unsigned int len;
+ 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) */
+diff --git a/md5.h b/md5.h
+index 097156e..90f9021 100644
+--- a/md5.h
++++ b/md5.h
+@@ -1,3 +1,8 @@
++#ifndef MD5_H
++#define MD5_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -44,8 +49,6 @@
+ * documentation and/or software.
+ */
+
+-#ifndef _MD5_H
+-#define _MD5_H
+
+ /* delineate the cisco changes to the RSA supplied module */
+ #define CISCO_MD5_MODS
+@@ -53,16 +56,11 @@
+ #if defined(CISCO_MD5_MODS)
+
+ /* typedef a 32-bit type */
+-typedef unsigned long int UINT4;
++typedef tac_uint32 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. */
+@@ -72,10 +70,10 @@ typedef struct {
+ 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 *));
++
++extern void MD5Init TAC_ARGS((MD5_CTX *context));
++extern void MD5Update TAC_ARGS((MD5_CTX *context, unsigned char *input, unsigned int inputLen));
++extern void MD5Final TAC_ARGS((unsigned char *digest, MD5_CTX *context));
+
+
+-#endif /* _MD5_H */
++#endif /* MD5_H */
+diff --git a/mschap.h b/mschap.h
+index 192d9d2..d98b84b 100644
+--- a/mschap.h
++++ b/mschap.h
+@@ -1,3 +1,8 @@
++#ifndef MSCHAP_H
++#define MSCHAP_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,4 +22,8 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #define MSCHAP_KEY "Contact Microsoft for the MSCHAP key"
++
++
++#endif /* MSCHAP_H */
+diff --git a/packet.c b/packet.c
+index f3f7023..2e003d1 100644
+--- a/packet.c
++++ b/packet.c
+@@ -17,14 +17,51 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+
++#include <stdlib.h>
++#include <netinet/in.h> /* for ntohl() */
++#include <errno.h>
++#include <time.h>
++#ifdef HAVE_SYS_TIME_H
++#include <sys/time.h>
++#endif
++#include <sys/types.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++#include "packet.h"
++#include "utils.h"
++#include "report.h"
++#include "dump.h"
++#include "cfgfile.h"
++#include "encrypt.h"
++#include "main.h"
++#include "do_author.h"
++
++
++static int write_packet TAC_ARGS((u_char *pak));
++
++
++/* Configurable:
++ */
++
++#define TAC_PLUS_READ_TIMEOUT 180 /* seconds */
++#define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */
++
++
+ /* Everything to do with reading and writing packets */
+
++void send_acct_reply TAC_ARGS((unsigned status, const char *msg, const char *data));
++
+ /* send an accounting response packet */
++void
+ send_acct_reply(status, msg, data)
+- u_char status;
+- char *msg, *data;
++unsigned status; /* promoted "u_char" type */
++const char *msg;
++const char *data;
+ {
+ u_char *pak, *p;
+ HDR *hdr;
+@@ -73,13 +110,16 @@ send_acct_reply(status, msg, data)
+ free(pak);
+ }
+
++void send_author_reply TAC_ARGS((unsigned status, const char *msg, const char *data, int arg_cnt, /* const */ char **args));
++
+ /* send an authorization reply packet */
++void
+ send_author_reply(status, msg, data, arg_cnt, args)
+-u_char status;
+-char *msg;
+-char *data;
++unsigned status; /* promoted "u_char" type */
++const char *msg;
++const char *data;
+ int arg_cnt;
+-char **args;
++/* const */ char **args;
+ {
+ u_char *pak, *p;
+ HDR *hdr;
+@@ -159,8 +199,11 @@ char **args;
+ /* Send an authentication reply packet indicating an error has
+ occurred. msg is a null terminated character string */
+
++void send_authen_error TAC_ARGS((const char *msg));
++
++void
+ send_authen_error(msg)
+-char *msg;
++const char *msg;
+ {
+ char buf[255];
+
+@@ -176,13 +219,16 @@ char *msg;
+
+ /* create and send an authentication reply packet from tacacs+ to a NAS */
+
++void send_authen_reply TAC_ARGS((int status, const char *msg, unsigned msg_len, const unsigned char *data, unsigned data_len, unsigned flags));
++
++void
+ 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;
++const char *msg;
++unsigned msg_len; /* promoted "u_short" type */
++const unsigned char *data;
++unsigned data_len; /* promoted "u_short" type */
++unsigned flags; /* promoted "u_char" type */
+ {
+ u_char *pak, *p;
+ HDR *hdr;
+@@ -228,12 +274,14 @@ u_char flags;
+ }
+
+
++u_char *get_authen_continue TAC_ARGS((void));
++
+ /* read an authentication GETDATA packet from a NAS. Return 0 on failure */
+ u_char *
+ get_authen_continue()
+ {
+ HDR *hdr;
+- u_char *pak, *read_packet();
++ u_char *pak;
+ struct authen_cont *cont;
+ char msg[255];
+
+@@ -255,10 +303,10 @@ get_authen_continue()
+ 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 +
++ if ((unsigned long)(TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE +
+ cont->user_msg_len +
+- cont->user_data_len !=
+- ntohl(hdr->datalength)) {
++ cont->user_data_len) !=
++ (unsigned long) ntohl(hdr->datalength)) {
+ char *m = "Illegally sized authentication cont packet";
+ report(LOG_ERR, "%s: %s", session.peer, m);
+ send_authen_error(m);
+@@ -278,7 +326,9 @@ get_authen_continue()
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes read. */
+
+-int
++static int sockread TAC_ARGS((int fd, u_char *ptr, int nbytes, int timeout));
++
++static int
+ sockread(fd, ptr, nbytes, timeout)
+ int fd;
+ u_char *ptr;
+@@ -354,7 +404,9 @@ int timeout;
+ * Return -1 on error, eof or timeout. Otherwise return number of
+ * bytes written. */
+
+-int
++static int sockwrite TAC_ARGS((int fd, u_char *ptr, int bytes, int timeout));
++
++static int
+ sockwrite(fd, ptr, bytes, timeout)
+ int fd;
+ u_char *ptr;
+@@ -416,16 +468,32 @@ int timeout;
+ return (bytes - remaining);
+ }
+
++static const char *get_session_key TAC_ARGS((void));
++
++static const char *
++get_session_key()
++{
++ const char *retval = NULL;
++
++ if ((retval = cfg_get_host_key(session.peer_addr)))
++ return (retval);
++ if (session.peer_addr != session.peer
++ && (retval = cfg_get_host_key(session.peer )))
++ return (retval);
++ return (session.key);
++}
++
+ /* read a packet from the wire, and decrypt it. Increment the global
+ seq_no return NULL on failure */
+
++u_char *read_packet TAC_ARGS((void));
++
+ 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");
+@@ -451,7 +519,7 @@ read_packet()
+ len < TAC_PLUS_HDR_SIZE || len > 0x10000) {
+ report(LOG_ERR,
+ "%s: Illegal data size: %lu\n",
+- session.peer, ntohl(hdr.datalength));
++ session.peer, (unsigned long) ntohl(hdr.datalength));
+ return(NULL);
+ }
+ pkt = (u_char *) tac_malloc(len);
+@@ -463,9 +531,9 @@ read_packet()
+ data = pkt + TAC_PLUS_HDR_SIZE;
+
+ /* read the rest of the packet data */
+- if (sockread(session.sock, data, ntohl(hdr.datalength),
++ if ((unsigned long)sockread(session.sock, data, ntohl(hdr.datalength),
+ TAC_PLUS_READ_TIMEOUT) !=
+- ntohl(hdr.datalength)) {
++ (unsigned long) ntohl(hdr.datalength)) {
+ report(LOG_ERR, "%s: start_session: bad socket read", session.peer);
+ return (NULL);
+ }
+@@ -480,10 +548,7 @@ read_packet()
+ }
+
+ /* decrypt the data portion */
+- if ( !(tkey=(char *)cfg_get_host_key(session.peer)) )
+- tkey = session.key;
+-
+- if (md5_xor((HDR *)pkt, data, tkey)) {
++ if (md5_xor((HDR *)pkt, data, get_session_key())) {
+ report(LOG_ERR, "%s: start_session error decrypting data",
+ session.peer);
+ return (NULL);
+@@ -498,14 +563,16 @@ read_packet()
+ return (pkt);
+ }
+
++static int write_packet TAC_ARGS((u_char *pak));
++
+ /* write a packet to the wire, encrypting it */
++static int
+ 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);
+
+@@ -513,10 +580,7 @@ u_char *pak;
+ 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)) {
++ if (md5_xor((HDR *)pak, data, get_session_key())) {
+ report(LOG_ERR, "%s: write_packet: error encrypting data", session.peer);
+ return (-1);
+ }
+@@ -528,6 +592,9 @@ u_char *pak;
+ return (0);
+ }
+
++void send_error_reply TAC_ARGS((int type, char *msg));
++
++void
+ send_error_reply(type, msg)
+ int type;
+ char *msg;
+diff --git a/packet.h b/packet.h
+new file mode 100644
+index 0000000..83a4d9c
+--- /dev/null
++++ b/packet.h
+@@ -0,0 +1,217 @@
++#ifndef PACKET_H
++#define PACKET_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++/* 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 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
++
++
++extern void send_acct_reply TAC_ARGS((unsigned status, const char *msg, const char *data));
++extern void send_author_reply TAC_ARGS((unsigned status, const char *msg, const char *data, int arg_cnt, /* const */ char **args));
++extern void send_authen_error TAC_ARGS((const char *msg));
++extern void send_authen_reply TAC_ARGS((int status, const char *msg, unsigned msg_len, const unsigned char *data, unsigned data_len, unsigned flags));
++extern u_char *get_authen_continue TAC_ARGS((void));
++extern u_char *read_packet TAC_ARGS((void));
++extern void send_error_reply TAC_ARGS((int type, char *msg));
++
++
++#endif /* PACKET_H */
+diff --git a/parse.c b/parse.c
+index 6ac6908..b9f3360 100644
+--- a/parse.c
++++ b/parse.c
+@@ -19,8 +19,15 @@
+
+ /* Keywords of the configuration language */
+
++
+ #include "tac_plus.h"
+
++#include "parse.h"
++#include "utils.h"
++#include "report.h"
++#include "hash.h"
++
++
+ static void *wordtable[HASH_TAB_SIZE]; /* Table of keyword declarations */
+
+ struct keyword {
+@@ -31,6 +38,8 @@ struct keyword {
+
+ typedef struct keyword KEYWORD;
+
++static void declare TAC_ARGS((char *name, int value));
++
+ static void
+ declare(name, value)
+ char *name;
+@@ -53,6 +62,8 @@ declare(name, value)
+
+ /* Declare keywords of the "configuration language". */
+
++void parser_init TAC_ARGS((void));
++
+ void
+ parser_init()
+ {
+@@ -85,7 +96,6 @@ parser_init()
+ 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);
+@@ -115,12 +125,23 @@ parser_init()
+ declare("service", S_svc);
+ declare("user", S_user);
+ declare("time", S_time);
++ declare("and", S_and);
++ declare("closeparen", S_closeparen);
++ declare("enlist", S_enlist);
++ declare("first", S_first);
++ declare("not", S_not);
++ declare("openparen", S_openparen);
++ declare("or", S_or);
++ declare("recursive", S_recursive);
++ declare("when", S_when);
+ }
+
++int keycode TAC_ARGS((const char *keyword));
++
+ /* Return a keyword code if a keyword is recognized. 0 otherwise */
+ int
+ keycode(keyword)
+-char *keyword;
++const char *keyword;
+ {
+ KEYWORD *k = hash_lookup(wordtable, keyword);
+
+@@ -129,7 +150,9 @@ char *keyword;
+ return (S_unknown);
+ }
+
+-char *
++const char *codestring TAC_ARGS((int type));
++
++const char *
+ codestring(type)
+ int type;
+ {
+@@ -156,8 +179,6 @@ int type;
+ return ("group");
+ case S_host:
+ return ("host");
+- case S_type:
+- return ("type");
+ case S_file:
+ return ("file");
+ case S_skey:
+@@ -250,5 +271,23 @@ int type;
+ return("lcp");
+ case S_time:
+ return("time");
++ case S_and:
++ return("and");
++ case S_closeparen:
++ return(")");
++ case S_enlist:
++ return("enlist");
++ case S_first:
++ return("first");
++ case S_not:
++ return("not");
++ case S_openparen:
++ return("(");
++ case S_or:
++ return("or");
++ case S_recursive:
++ return("recursive");
++ case S_when:
++ return("when");
+ }
+ }
+diff --git a/parse.h b/parse.h
+index e5be7f7..34d72ee 100644
+--- a/parse.h
++++ b/parse.h
+@@ -1,3 +1,8 @@
++#ifndef PARSE_H
++#define PARSE_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,8 +22,6 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+-/* Dummy password, if nopasswd is specified */
+-extern char *nopasswd_str;
+
+ /* Keywords & values */
+
+@@ -82,9 +85,24 @@ extern char *nopasswd_str;
+ #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
++#define S_and 49
++#define S_closeparen 50
++#define S_enlist 51
++#define S_first 52
++#define S_not 53
++#define S_openparen 54
++#define S_or 55
++#define S_recursive 56
++#define S_when 57
++
++
++extern void parser_init TAC_ARGS((void));
++extern int keycode TAC_ARGS((const char *keyword));
++extern const char *codestring TAC_ARGS((int type));
++
+
++#endif /* PARSE_H */
+diff --git a/programs.c b/programs.c
+index bce0178..ac42b23 100644
+--- a/programs.c
++++ b/programs.c
+@@ -19,10 +19,29 @@
+
+ /* Routines to fork children and communicate with them via pipes */
+
++
+ #include "tac_plus.h"
+-#include "sys/wait.h"
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <sys/wait.h>
++#ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+-#include "signal.h"
++#endif
++#include <signal.h>
++#ifdef HAVE_SYSLOG_H
++#include <syslog.h>
++#endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include <sys/syslog.h>
++#endif
++
++#include "programs.h"
++#include "utils.h"
++#include "report.h"
++#include "do_author.h" /* for "struct author_data" */
++#include "main.h"
++
+
+ /* Support for dollar variables. Look in the authorization data and
+ return strings representing values found there. If not found, return
+@@ -38,6 +57,8 @@ type -- (1 to 4)
+ service -- (1 to 7)
+ status -- (pass, fail, error, unknown) */
+
++static char *lookup TAC_ARGS((char *sym, struct author_data *data));
++
+ static char *
+ lookup(sym, data)
+ char *sym;
+@@ -96,12 +117,14 @@ struct author_data *data;
+ values for the various $ variables by looking in the authorization
+ data */
+
++static char *substitute TAC_ARGS((const char *string, struct author_data *data));
++
+ static char *
+ substitute(string, data)
+-char *string;
++const char *string;
+ struct author_data *data;
+ {
+- char *cp;
++ const char *cp;
+ char out[MAX_INPUT_LINE_LEN], *outp;
+ char sym[MAX_INPUT_LINE_LEN], *symp;
+ char *value, *valuep;
+@@ -129,7 +152,7 @@ struct author_data *data;
+
+ } else {
+ /* copy symbol into sym */
+- while (*cp && isalpha(*cp))
++ while (*cp && isalpha((int) *cp))
+ *symp++ = *cp++;
+ }
+
+@@ -160,6 +183,8 @@ struct author_data *data;
+ /* Wait for a (child) pid to terminate. Return its status. Probably
+ horribly implementation dependent. */
+
++static int waitfor TAC_ARGS((int pid));
++
+ static int
+ waitfor(pid)
+ int pid;
+@@ -189,6 +214,8 @@ int pid;
+ return (WEXITSTATUS(status));
+ }
+
++static int write_args TAC_ARGS((int fd, char **args, int arg_cnt));
++
+ /* Write an argv array of strings to fd, adding a newline to each one */
+ static int
+ write_args(fd, args, arg_cnt)
+@@ -211,6 +238,8 @@ char **args;
+ return (0);
+ }
+
++static void close_fds TAC_ARGS((int fd1, int fd2, int fd3));
++
+ /* Close the three given file-descruptors */
+ static void
+ close_fds(fd1, fd2, fd3)
+@@ -230,6 +259,8 @@ close_fds(fd1, fd2, 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 TAC_ARGS((char *cmd, int *readfdp, int *writefdp, int *errorfdp));
++
+ static int
+ my_popen(cmd, readfdp, writefdp, errorfdp)
+ char *cmd;
+@@ -297,6 +328,8 @@ int *readfdp, *writefdp, *errorfdp;
+ return(0); /* keep Codecenter quiet */
+ }
+
++static int read_string TAC_ARGS((int fd, char *string, int len));
++
+ /* read the file descriptor and stuff the data into the given array for
+ * the number of bytes given. Throw the rest away.
+ */
+@@ -305,7 +338,7 @@ read_string (fd, string, len)
+ int fd, len;
+ char *string;
+ {
+- uint i, ret;
++ int i, ret;
+ char c;
+
+ i=0;
+@@ -324,6 +357,8 @@ char *string;
+ the count of lines seen so far. When eof is read, the array is
+ allocated, and the recursion unravels */
+
++static char **read_args TAC_ARGS((int n, int fd));
++
+ static char **
+ read_args(n, fd)
+ int n, fd;
+@@ -356,12 +391,16 @@ int n, fd;
+ standard input and read its standard output into outarray. Return
+ the commands final status when it terminates */
+
++int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len));
++
+ int
+ call_pre_process(string, data, outargsp, outargs_cntp, error, err_len)
+-char *string, *error;
++const char *string;
+ struct author_data *data;
+ char ***outargsp;
+-int *outargs_cntp, err_len;
++int *outargs_cntp;
++char *error;
++int err_len;
+ {
+ char **new_args;
+ int readfd, writefd, errorfd;
+@@ -402,8 +441,8 @@ int *outargs_cntp, err_len;
+
+ read_string(errorfd, error, err_len);
+ if (error[0] != '\0') {
+- report(LOG_ERR, "Error from program (%d): \"%s\" ",
+- strlen(error), error);
++ report(LOG_ERR, "Error from program (%u): \"%s\" ",
++ (unsigned) strlen(error), error);
+ }
+
+ /* count the args */
+@@ -422,9 +461,11 @@ int *outargs_cntp, err_len;
+ standard input and read its standard output into outarray. Return
+ the commands final status when it terminates */
+
++int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp));
++
+ int
+ call_post_process(string, data, outargsp, outargs_cntp)
+-char *string;
++const char *string;
+ struct author_data *data;
+ char ***outargsp;
+ int *outargs_cntp;
+diff --git a/programs.h b/programs.h
+new file mode 100644
+index 0000000..31ebef3
+--- /dev/null
++++ b/programs.h
+@@ -0,0 +1,13 @@
++#ifndef PROGRAMS_H
++#define PROGRAMS_H 1
++
++#include "tac_plus.h"
++
++
++struct author_data;
++
++extern int call_pre_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp, char *error, int err_len));
++extern int call_post_process TAC_ARGS((const char *string, struct author_data *data, char ***outargsp, int *outargs_cntp));
++
++
++#endif /* PROGRAMS_H */
+diff --git a/pw.c b/pw.c
+index 96c761e..a749262 100644
+--- a/pw.c
++++ b/pw.c
+@@ -20,15 +20,26 @@
+ /* Tacacs+ password lookup routine for those systems which don't have
+ setpwfile. Not for use on /etc/passwd files */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
+ #include <pwd.h>
+ #include <string.h>
+
++#include "pw.h"
++#include "report.h"
++#include "main.h"
++
++
+ static struct passwd pw_passwd;
+
++struct passwd *tac_passwd_lookup TAC_ARGS((const char *name, const char *file));
++
+ struct passwd *
+ tac_passwd_lookup(name, file)
+- char *name, *file;
++const char *name;
++const char *file;
+ {
+ FILE *passwd_fp = NULL;
+
+@@ -113,10 +124,12 @@ tac_passwd_lookup(name, file)
+
+ pw_passwd.pw_name = uname;
+ pw_passwd.pw_passwd = password;
+-#ifndef NO_PWAGE
++#ifdef HAVE_PASSWD_PW_AGE
+ pw_passwd.pw_age = NULL;
++#endif
++#ifdef HAVE_PASSWD_PW_COMMENT
+ pw_passwd.pw_comment = NULL;
+-#endif /* NO_PWAGE */
++#endif
+ pw_passwd.pw_gecos = gecos;
+ pw_passwd.pw_dir = homedir;
+ pw_passwd.pw_shell = shell;
+diff --git a/pw.h b/pw.h
+new file mode 100644
+index 0000000..44dc425
+--- /dev/null
++++ b/pw.h
+@@ -0,0 +1,10 @@
++#ifndef PW_H
++#define PW_H 1
++
++#include "tac_plus.h"
++
++
++extern struct passwd *tac_passwd_lookup TAC_ARGS((const char *name, const char *file));
++
++
++#endif /* PW_H */
+diff --git a/pwlib.c b/pwlib.c
+index 75f90b1..70a468e 100644
+--- a/pwlib.c
++++ b/pwlib.c
+@@ -17,39 +17,61 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+-#include "expire.h"
+-#include "time_limit.h"
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <pwd.h>
++#include <sys/types.h>
++#include <string.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <time.h>
+
+ #ifdef SHADOW_PASSWORDS
++#ifdef HAVE_SHADOW_H
+ #include <shadow.h>
+ #endif
++#endif
+
+-#ifdef USE_PAM
+-int
+-tac_pam_auth(char *UserName,char *Password,struct authen_data *data,char *Service);
+-#endif /* USE_PAM */
++#include "pwlib.h"
++#include "expire.h"
++#include "time_limit.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "pw.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "packet.h"
++#include "main.h"
++#include "parse.h"
+
+-/* For database verification */
++#ifdef USE_PAM
++#include "tac_pam.h"
++#endif
+ #ifdef DB
+-int db_verify();
+-#endif /* DB */
+-
+-/* For LDAP verification */
++#include "db.h" /* For database verification */
++#endif
+ #ifdef USE_LDAP
+-#include "ldap.h"
+-#endif /* LDAP */
++#include "ldap_author.h" /* For LDAP verification */
++#endif
++
++
++static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename));
++
+
+ /* 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 TAC_ARGS((const char *exp_date, struct authen_data *data));
++
+ void
+ set_expiration_status(exp_date, data)
+-char *exp_date;
++const char *exp_date;
+ struct authen_data *data;
+ {
+ int expired;
+@@ -105,18 +127,18 @@ struct authen_data *data;
+
+ Return 1 if password is valid */
+
++int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse));
++
+ int
+ verify(name, passwd, data, recurse)
+-char *name, *passwd;
++const char *name;
++const char *passwd;
+ struct authen_data *data;
+ int recurse;
+ {
+- char *exp_date;
+- char *timestamp;
+- char *cfg_passwd;
+- char *p;
++ const char *exp_date, *cfg_passwd, *p, *timestamp;
+
+- timestamp = (char *)cfg_get_timestamp(name, recurse);
++ timestamp = cfg_get_timestamp(name, recurse);
+ if ( timestamp != NULL ) {
+ if( time_limit_process(timestamp) == 0 ) {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+@@ -144,18 +166,18 @@ int recurse;
+ has been issued, attempt to use this password file */
+
+ if (!cfg_passwd) {
+- char *file = cfg_get_authen_default();
++ const char *file = cfg_get_authen_default();
+ switch (cfg_get_authen_default_method()) {
+- case (S_file):
+
+- if (file) {
++ 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( 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)) {
+@@ -188,8 +210,8 @@ int recurse;
+ #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)) {
++ report(LOG_DEBUG, "PAM verify daemon [PAM] == NAS %s", 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;
+@@ -210,9 +232,8 @@ int recurse;
+ /* otherwise, we fail */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+-
+ }
+-}
++ }
+
+ /* We have a configured password. Deal with it depending on its
+ type */
+@@ -323,11 +344,15 @@ int recurse;
+ return (0);
+ }
+
++static int etc_passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data));
++
+ /* verify that this user/password is valid per /etc/passwd.
+- Return 0 if invalid. */
++ * Return 0 if invalid.
++ */
+ static int
+ etc_passwd_file_verify(user, supplied_passwd, data)
+-char *user, *supplied_passwd;
++const char *user;
++const char *supplied_passwd;
+ struct authen_data *data;
+ {
+ struct passwd *pw;
+@@ -409,11 +434,14 @@ struct authen_data *data;
+ /* verify that this user/password is valid per a passwd(5) style
+ database. Return 0 if invalid. */
+
++static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename));
++
+ static int
+ passwd_file_verify(user, supplied_passwd, data, filename)
+-char *user, *supplied_passwd;
++const char *user;
++const char *supplied_passwd;
+ struct authen_data *data;
+-char *filename;
++const char *filename;
+ {
+ struct passwd *pw;
+ char *exp_date;
+@@ -467,9 +495,12 @@ char *filename;
+ * return 1 if verified, 0 otherwise.
+ */
+
++int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd));
++
+ int
+ des_verify(users_passwd, encrypted_passwd)
+-char *users_passwd, *encrypted_passwd;
++const char *users_passwd;
++const char *encrypted_passwd;
+ {
+ char *ep;
+
+diff --git a/pwlib.h b/pwlib.h
+new file mode 100644
+index 0000000..92b54af
+--- /dev/null
++++ b/pwlib.h
+@@ -0,0 +1,14 @@
++#ifndef PWLIB_H
++#define PWLIB_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern void set_expiration_status TAC_ARGS((const char *exp_date, struct authen_data *data));
++extern int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse));
++extern int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd));
++
++
++#endif /* PWLIB_H */
+diff --git a/report.c b/report.c
+index a458617..d46abfd 100644
+--- a/report.c
++++ b/report.c
+@@ -17,20 +17,37 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
+-#include <stdio.h>
+
+-#ifdef AIX
++#include <stdio.h>
+ #include <sys/types.h>
+-#else
+ #include <time.h>
++#include <sys/stat.h>
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
+ #endif
+-
+-#ifdef __STDC__
+-#include <stdarg.h> /* ANSI C, variable length args */
+-#else
+-#include <varargs.h> /* has 'vararg' definitions */
++#include <string.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_SYSLOG_H
++#include <syslog.h>
+ #endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include <sys/syslog.h>
++#endif
++
++#include "report.h"
++#include "utils.h"
++#include "main.h"
++
++
++/* Configurable:
++ */
++
++#define LOGFILE_DEFAULT "/var/log/tac_plus.log"
++
+
+ FILE *ostream = NULL;
+
+@@ -45,21 +62,30 @@ char *logfile = LOGFILE_DEFAULT;
+ * All other priorities are always logged to syslog.
+ */
+
++void report TAC_ARGS((int priority, const char *fmt, ...)) G_GNUC_PRINTF(2, 3);
++
+ #ifdef __STDC__
++
++#include <stdarg.h> /* ANSI C, variable length args */
+ void
+-report(int priority, char *fmt,...)
+-#else
++report(int priority, const char *fmt,...)
++
++#else /* __STDC__ */
++
++#include <varargs.h> /* has 'vararg' definitions */
+ /* VARARGS2 */
+ void
+ report(priority, fmt, va_alist)
+ int priority;
+-char *fmt;
++const char *fmt;
+ va_dcl /* no terminating semi-colon */
+-#endif
++
++#endif /* __STDC__ */
+ {
+ char msg[255]; /* temporary string */
+- char *fp, *bufp, *charp;
+- int len, m, i, n;
++ const char *fp;
++ char *bufp, *charp = NULL /* GCC paranoia */;
++ int len, m = 0 /* GCC paranoia */, i, n;
+ char digits[16];
+ va_list ap;
+
+@@ -119,6 +145,9 @@ va_dcl /* no terminating semi-colon */
+ m = strlen(digits);
+ charp = digits;
+ break;
++ default:
++ syslog(LOG_ERR, "Unknown format character '%c', ignoring it", *fp);
++ continue;
+ }
+
+ if ((len + m + 1) >= n) {
+@@ -167,7 +196,7 @@ va_dcl /* no terminating semi-colon */
+
+ ct[24] = '\0';
+ tac_lockfd(logfile, logfd);
+- sprintf(buf, "%s [%d]: ", ct, getpid());
++ sprintf(buf, "%s [%d]: ", ct, (int) getpid());
+ write(logfd, buf, strlen(buf));
+ if (priority == LOG_ERR)
+ write(logfd, "Error ", 6);
+@@ -190,6 +219,8 @@ va_dcl /* no terminating semi-colon */
+ syslog(priority, "%s", msg);
+ }
+
++void report_hex TAC_ARGS((int priority, u_char *p, int len));
++
+ /* format a hex dump for syslog */
+ void
+ report_hex(priority, p, len)
+@@ -225,6 +256,8 @@ int len;
+ }
+
+
++void report_string TAC_ARGS((int priority, u_char *p, int len));
++
+ /* format a non-null terminated string for syslog */
+ void
+ report_string(priority, p, len)
+@@ -251,10 +284,11 @@ int len;
+ report(priority, "%s", buf);
+ }
+
++void tac_regerror TAC_ARGS((const char *s));
++
+ void
+-regerror(s)
+-char *s;
++tac_regerror(s)
++const char *s;
+ {
+ report(LOG_ERR, "in regular expression %s", s);
+ }
+-
+diff --git a/report.h b/report.h
+new file mode 100644
+index 0000000..5e5d1bb
+--- /dev/null
++++ b/report.h
+@@ -0,0 +1,26 @@
++#ifndef REPORT_H
++#define REPORT_H 1
++
++#include "tac_plus.h"
++
++#include <stdio.h>
++#include <sys/types.h> /* for u_* */
++#ifdef HAVE_SYSLOG_H
++#include <syslog.h> /* for LOG_* level values */
++#endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include <sys/syslog.h> /* for LOG_* level values */
++#endif
++
++
++extern FILE *ostream; /* for logging to console */
++extern char *logfile;
++
++
++extern void report TAC_ARGS((int priority, const char *fmt, ...)) G_GNUC_PRINTF(2, 3);
++extern void report_hex TAC_ARGS((int priority, u_char *p, int len));
++extern void report_string TAC_ARGS((int priority, u_char *p, int len));
++extern void tac_regerror TAC_ARGS((const char *s));
++
++
++#endif /* REPORT_H */
+diff --git a/sendauth.c b/sendauth.c
+index 68e5482..2076dc9 100644
+--- a/sendauth.c
++++ b/sendauth.c
+@@ -17,21 +17,43 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include <stdlib.h>
++
++#include "sendauth.h"
+ #include "expire.h"
+ #include "md5.h"
++#include "report.h"
++#include "cfgfile.h"
++#include "utils.h"
++#include "pwlib.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "packet.h"
++#include "main.h"
+
+-static int do_sendauth_fn();
+-static void outbound_chap();
+ #ifdef MSCHAP
+-static void outbound_mschap();
+-#endif /* MSCHAP */
+-void outbound_pap();
++#include "default_fn.h"
++#endif
++
++
++static int do_sendauth_fn TAC_ARGS((struct authen_data *data));
++static void outbound_chap TAC_ARGS((struct authen_data *data));
++static void outbound_pap TAC_ARGS((struct authen_data *data));
++
++#ifdef MSCHAP
++static void outbound_mschap TAC_ARGS((struct authen_data *data));
++#endif
++
++
++int sendauth_fn TAC_ARGS((struct authen_data *data));
+
+ int sendauth_fn(data)
+ struct authen_data *data;
+ {
+- int status;
++ int retval;
+ char *name, *p;
+
+ name = data->NAS_id->username;
+@@ -39,8 +61,9 @@ struct authen_data *data;
+ if (STREQ(name, DEFAULT_USERNAME)) {
+ /* This username is only valid for authorization */
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
++ retval = 0;
+ } else {
+- status = do_sendauth_fn(data);
++ retval = do_sendauth_fn(data);
+ }
+
+ if (debug) {
+@@ -71,7 +94,7 @@ struct authen_data *data;
+ (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
+ "accepted" : "rejected");
+ }
+- return(status);
++ return (retval);
+ }
+
+ /*
+@@ -84,11 +107,13 @@ struct authen_data *data;
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++static int do_sendauth_fn TAC_ARGS((struct authen_data *data));
++
+ static int
+ do_sendauth_fn(data)
+ struct authen_data *data;
+ {
+- char *name, *exp_date;
++ const char *name, *exp_date;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+@@ -129,11 +154,13 @@ struct authen_data *data;
+ return (0);
+ }
+
+-void
++static void outbound_pap TAC_ARGS((struct authen_data *data));
++
++static void
+ outbound_pap(data)
+ struct authen_data *data;
+ {
+- char *secret, *p, *name;
++ const char *secret, *p, *name;
+
+ name = data->NAS_id->username;
+
+@@ -163,17 +190,19 @@ struct authen_data *data;
+ return;
+ }
+
+- data->server_data = tac_strdup(p);
+- data->server_dlen = strlen(data->server_data);
++ data->server_data = (unsigned char *) tac_strdup(p);
++ data->server_dlen = strlen((char *) data->server_data);
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
++static void outbound_chap TAC_ARGS((struct authen_data *data));
++
+ static void
+ outbound_chap(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal, digest[MD5_LEN];
+- char *p;
++ const char *name, *secret, *chal, *p;
++ char digest[MD5_LEN];
+ u_char *mdp;
+ char id;
+ int chal_len, inlen;
+@@ -262,12 +291,13 @@ struct authen_data *data;
+
+ #ifdef MSCHAP
+
++static void outbound_mschap TAC_ARGS((struct authen_data *data));
++
+ static void
+ outbound_mschap(data)
+ struct authen_data *data;
+ {
+- char *name, *secret, *chal;
+- char *p;
++ const char *name, *secret, *chal, *p;
+ char id;
+ int chal_len;
+
+diff --git a/sendauth.h b/sendauth.h
+new file mode 100644
+index 0000000..d3e2fbc
+--- /dev/null
++++ b/sendauth.h
+@@ -0,0 +1,12 @@
++#ifndef SENDAUTH_H
++#define SENDAUTH_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int sendauth_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* SENDAUTH_H */
+diff --git a/sendpass.c b/sendpass.c
+index f7f0b3c..9d8ab00 100644
+--- a/sendpass.c
++++ b/sendpass.c
+@@ -17,11 +17,24 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ #include "tac_plus.h"
++
++#include "sendpass.h"
+ #include "expire.h"
++#include "report.h"
++#include "utils.h"
++#include "cfgfile.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "main.h"
++#include "packet.h"
+
+-static int
+-do_sendpass_fn();
++
++static int do_sendpass_fn TAC_ARGS((struct authen_data *data));
++
++
++int sendpass_fn TAC_ARGS((struct authen_data *data));
+
+ int sendpass_fn(data)
+ struct authen_data *data;
+@@ -63,17 +76,17 @@ struct authen_data *data;
+ * 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 */
++ * Return 0 if data->status is valid, otherwise 1
++ */
++
++static int do_sendpass_fn TAC_ARGS((struct authen_data *data));
+
+ static int
+ do_sendpass_fn(data)
+ struct authen_data *data;
+ {
+- char *name;
+- char *p;
++ const char *name, *exp_date, *secret, *p;
+ int expired;
+- char *exp_date;
+- char *secret;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+@@ -159,8 +172,8 @@ struct authen_data *data;
+ return(0);
+ }
+
+- data->server_data = tac_strdup(p);
+- data->server_dlen = strlen(data->server_data);
++ data->server_data = (unsigned char *) tac_strdup(p);
++ data->server_dlen = strlen((char *) data->server_data);
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ if (expired == PW_EXPIRING) {
+ data->server_msg = tac_strdup("Secret will expire soon");
+diff --git a/sendpass.h b/sendpass.h
+new file mode 100644
+index 0000000..7481cff
+--- /dev/null
++++ b/sendpass.h
+@@ -0,0 +1,12 @@
++#ifndef SENDPASS_H
++#define SENDPASS_H 1
++
++#include "tac_plus.h"
++
++
++struct authen_data;
++
++extern int sendpass_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* SENDPASS_H */
+diff --git a/skey_fn.c b/skey_fn.c
+index d3c9860..0a74fa8 100644
+--- a/skey_fn.c
++++ b/skey_fn.c
+@@ -17,17 +17,22 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+-#ifdef SKEY
++
+ #include "tac_plus.h"
++
++#ifdef SKEY
++
++#include <skey.h>
++
++#include "skey_fn.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];
+@@ -37,6 +42,8 @@ struct private_data {
+ /* Use s/key to verify a supplied password using state set up earlier
+ when the username was supplied */
+
++static int skey_verify TAC_ARGS((char *passwd, struct authen_data *data));
++
+ static int
+ skey_verify(passwd, data)
+ char *passwd;
+@@ -73,6 +80,8 @@ struct authen_data *data;
+ * Return 0 if data->status is valid, otherwise 1
+ */
+
++int skey_fn TAC_ARGS((struct authen_data *data));
++
+ int
+ skey_fn(data)
+ struct authen_data *data;
+@@ -222,12 +231,9 @@ struct authen_data *data;
+ 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 */
++#else /* SKEY */
+
+-static int dummy = 0;
++TAC_SOURCEFILE_EMPTY
+
+ #endif /* SKEY */
+diff --git a/skey_fn.h b/skey_fn.h
+new file mode 100644
+index 0000000..1b7474a
+--- /dev/null
++++ b/skey_fn.h
+@@ -0,0 +1,14 @@
++#ifndef SKEY_FN_H
++#define SKEY_FN_H 1
++
++#include "tac_plus.h"
++
++#ifdef SKEY
++
++
++extern int skey_fn TAC_ARGS((struct authen_data *data));
++
++
++#endif /* SKEY */
++
++#endif /* SKEY_FN_H */
+diff --git a/tac_pam.c b/tac_pam.c
+index bf1b215..4f088a0 100644
+--- a/tac_pam.c
++++ b/tac_pam.c
+@@ -1,12 +1,10 @@
+-#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]
+
+@@ -14,23 +12,39 @@
+ # 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 "tac_plus.h"
++
++#ifdef USE_PAM
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <security/pam_appl.h>
+-#include "tac_plus.h"
+
+-typedef struct
+-{
+- char *UserName;
+- char *Passwd;
++#include "tac_pam.h"
++#include "report.h"
++#include "utils.h"
++#include "choose_authen.h" /* for "struct authen_data" */
++#include "do_author.h" /* for "struct identity" */
++#include "main.h"
++
++
++typedef struct {
++ const char *UserName;
++ const char *Passwd;
+ } UserCred;
+
+
+-static int fconv(int num_msg, const struct pam_message **msg,
+- struct pam_response **resp,void *appdata_ptr)
++static int fconv TAC_ARGS((int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr));
++
++static int fconv(num_msg, msg, resp, appdata_ptr)
++int num_msg;
++const struct pam_message **msg;
++struct pam_response **resp;
++void *appdata_ptr;
+ {
+ int i;
+ UserCred *lUserCred;
+@@ -38,19 +52,16 @@ static int fconv(int num_msg, const struct pam_message **msg,
+
+ lUserCred = appdata_ptr;
+
+- if(lUserCred == NULL)
+- {
++ if(lUserCred == NULL) {
+ report(LOG_ERR,"argh....maybe a SunOs 5.6 ???");
+ return(PAM_CONV_ERR);
+ }
+
++ *resp = (struct pam_response *) tac_malloc(num_msg * sizeof(struct pam_response));
+
+- *resp = (struct pam_response *) calloc(num_msg,sizeof(struct pam_response));
++ for (i=0; i<num_msg; i++) {
++ switch(msg[i]->msg_style) {
+
+- for(i=0;i<num_msg;i++)
+- {
+- switch(msg[i]->msg_style)
+- {
+ case PAM_PROMPT_ECHO_OFF:
+ resp[i]->resp = strdup(lUserCred->Passwd);
+ break;
+@@ -60,6 +71,7 @@ static int fconv(int num_msg, const struct pam_message **msg,
+ break;
+
+ default:
++ resp[i]->resp = NULL;
+ report(LOG_DEBUG,"conv default");
+ break;
+ }
+@@ -70,12 +82,16 @@ static int fconv(int num_msg, const struct pam_message **msg,
+ }
+
+
+-
++int tac_pam_auth TAC_ARGS((const char *aszUserName, const char *aszPassword, struct authen_data *data, const char *aszService));
+
+ int
+-tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char *aszService)
++tac_pam_auth(aszUserName, aszPassword, data, aszService)
++const char *aszUserName;
++const char *aszPassword;
++struct authen_data *data;
++const char *aszService;
+ {
+- pam_handle_t *pamh=NULL;
++ pam_handle_t *pamh = NULL;
+ int retval;
+ char *lpszRemoteUser; /* Username/NAC address */
+ struct pam_conv s_conv;
+@@ -89,17 +105,13 @@ tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char *
+ 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);
+- }
++ lpszRemoteUser = tac_malloc((strlen(aszUserName)+1+strlen(data->NAS_id->NAC_address)+1) * sizeof(char));
+
+ retval = pam_start(aszService,aszUserName , &s_conv, &pamh);
+
+- if (retval != PAM_SUCCESS)
+- {
++ if (retval != PAM_SUCCESS) {
+ report(LOG_ERR, "cannot start pam-authentication");
++ free(lpszRemoteUser);
+ pamh = NULL;
+ return(1);
+ }
+@@ -130,10 +142,15 @@ tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,char *
+ * Devrim SERAL <devrim@tef.gazi.edu.tr>
+ */
+
++int tac_pam_authorization TAC_ARGS((const char *aszUserName, struct author_data *data, const char *aszService));
++
+ int
+-tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszService)
++tac_pam_authorization(aszUserName, data, aszService)
++const char *aszUserName;
++struct author_data *data;
++const char *aszService;
+ {
+- pam_handle_t *pamh=NULL;
++ pam_handle_t *pamh = NULL;
+ int retval;
+ char *lpszRemoteUser; /* Username/NAC address */
+ struct pam_conv s_conv;
+@@ -145,24 +162,18 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi
+ s_conv.conv = fconv;
+ s_conv.appdata_ptr = (void *) &s_UserCred;
+
+- if (aszService== NULL)
+- {
++ 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);
+- }
++ lpszRemoteUser = tac_malloc((strlen(aszUserName)+strlen(data->id->NAC_address)+2) * sizeof(char));
+
+ retval = pam_start(aszService,aszUserName , &s_conv, &pamh);
+
+- if (retval != PAM_SUCCESS)
+- {
++ if (retval != PAM_SUCCESS) {
+ report(LOG_ERR, "cannot start pam-authentication");
++ free(lpszRemoteUser);
+ pamh = NULL;
+ return(1);
+ }
+@@ -177,11 +188,12 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi
+
+ retval = pam_acct_mgmt(pamh, 0); /* Is user permit to gain access system */
+
+- if(retval != PAM_SUCCESS)
++ if (retval != PAM_SUCCESS)
+ report(LOG_ERR, "Pam Account Managment:%s",pam_strerror(pamh,retval));
+- else
++ 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;
+@@ -191,9 +203,8 @@ tac_pam_authorization (char *aszUserName,struct author_data *data,char *aszServi
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+ }
+
++#else /* USE_PAM */
+
+-#endif /* USE_PAM */
+-
+-
+-
++TAC_SOURCEFILE_EMPTY
+
++#endif /* USE_PAM */
+diff --git a/tac_pam.h b/tac_pam.h
+new file mode 100644
+index 0000000..c8306ba
+--- /dev/null
++++ b/tac_pam.h
+@@ -0,0 +1,18 @@
++#ifndef TAC_PAM_H
++#define TAC_PAM_H 1
++
++#include "tac_plus.h"
++
++#ifdef USE_PAM
++
++
++struct authen_data;
++struct author_data;
++
++extern int tac_pam_auth TAC_ARGS((const char *aszUserName, const char *aszPassword, struct authen_data *data, const char *aszService));
++extern int tac_pam_authorization TAC_ARGS((const char *aszUserName, struct author_data *data, const char *aszService));
++
++
++#endif /* USE_PAM */
++
++#endif /* TAC_PAM_H */
+diff --git a/tac_plus.1 b/tac_plus.1
+index 579c7ee..69f3502 100644
+--- a/tac_plus.1
++++ b/tac_plus.1
+@@ -111,6 +111,8 @@ the following values are recognised:
+ .nf
+
+ Value Meaning
++2 config file parsing debugging
++4 process forking debugging
+ 8 authorisation debugging
+ 16 authentication debugging
+ 32 password file processing debugging
+@@ -120,6 +122,11 @@ Value Meaning
+ 512 encryption/decryption
+ 1024 MD5 hash algorithm debugging
+ 2048 very low level encryption/decryption
++4096 config file memory allocation freeing
++8192 pre/post authorization program arguments substitutions
++16384 config file expressions with entity tracing
++32768 maxsess (concurrent logins) debugging
++65536 file locking progress reporting
+
+ .fi
+ .TP
+diff --git a/tac_plus.cfg b/tac_plus.cfg
+index 31e53f5..d217258 100644
+--- a/tac_plus.cfg
++++ b/tac_plus.cfg
+@@ -51,4 +51,3 @@ user = DEFAULT {
+ #group = staff {
+ # time = "Wd1800-1817|!Wd1819-2000"
+ #}
+-
+diff --git a/tac_plus.h b/tac_plus.h
+index 99d95e0..1bb658f 100644
+--- a/tac_plus.h
++++ b/tac_plus.h
+@@ -1,3 +1,6 @@
++#ifndef TAC_PLUS_H
++#define TAC_PLUS_H 1
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -16,8 +19,12 @@
+ WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+-/* For autoconfig */
++
++
++#ifdef HAVE_CONFIG_H
+ #include "config.h"
++#endif
++
+
+ /*
+ * If you are defining a system from scratch, the following may be useful.
+@@ -33,25 +40,9 @@
+ /* 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 */
+
+@@ -64,23 +55,14 @@
+ */
+ /* #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)"
++#define VERSION_TAIL " (Extended Tac_plus)"
+
+ /*
+ * System definitions.
+ */
+
+ #ifdef NETBSD
+-#define STDLIB_MALLOC
+-#define NO_PWAGE
+ #define CONST_SYSERRLIST
+-#define VOIDSIG
+ #endif
+
+ #ifdef AIX
+@@ -97,15 +79,10 @@
+ #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
+@@ -122,30 +99,17 @@
+ #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
+@@ -153,36 +117,26 @@
+ #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
++#ifdef HAVE_FCNTL_H
+ #include <fcntl.h>
++#endif
+ #define index strchr
+-#else /* ! SYSV */
+-#include <strings.h>
+ #endif /* SYSV */
+
++/* Sometimes are bzero/bcopy/bcmp declared as prototypes with non-void argument
++ * pointers. In such case we would generate too much warnings.
++ */
++#include <string.h>
++#ifdef HAVE_STRINGS_H
++#include <strings.h>
++#endif
++
++#define bzero(s, n) bzero((void *)(s), (size_t)(n))
++#define bcopy(src, dest, n) bcopy((const void *)(src), (void *)(dest), (size_t)(n))
++#define bcmp(s1, s2, n) bcmp((const void *)(s1), (const void *)(s2), (size_t)(n))
++
++
+ #ifndef TACPLUS_PIDFILE
+ #define TACPLUS_PIDFILE "/var/run/tac_plus.pid"
+ #endif
+@@ -193,559 +147,94 @@
+ * 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
+- */
++/* Stolen from pbmplus.h of netpbm: */
+
+-#define AUTHEN_NAME_SIZE 128
++#if __STDC__
++#define TAC_ARGS(alist) alist
++#else /*__STDC__*/
++#define TAC_ARGS(alist) ()
++#endif /*__STDC__*/
+
+-struct authen_type {
+- char authen_name[AUTHEN_NAME_SIZE];
+- int (*authen_func)();
+- int authen_type;
+-};
++/* Stolen from glib of Gnome/GTK+ environment: */
+
+-/*
+- * 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.
++/* Provide macros to feature the GCC function attribute.
+ */
+-
+-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).
++#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
++#define G_GNUC_PRINTF( format_idx, arg_idx ) \
++ __attribute__((format (printf, format_idx, arg_idx)))
++#define G_GNUC_NORETURN \
++ __attribute__((noreturn))
++#define G_GNUC_CONST \
++ __attribute__((const))
++#define G_GNUC_UNUSED \
++ __attribute__((unused))
++#else /* !__GNUC__ */
++#define G_GNUC_PRINTF( format_idx, arg_idx )
++#define G_GNUC_NORETURN
++#define G_GNUC_CONST
++#define G_GNUC_UNUSED
++#endif /* !__GNUC__ */
++
++/* Provide convenience macros for handling structure
++ * fields through their offsets.
++ * Project tac_plus changed "gulong" to "glong" to be able to recover
++ * original structure base from pointer to its member.
+ */
++#define G_STRUCT_OFFSET(struct_type, member) \
++ ((long) ((char*) &((struct_type*) 0)->member))
++#define G_STRUCT_MEMBER_P(struct_p, struct_offset) \
++ ((void*) ((char*) (struct_p) + (long) (struct_offset)))
++#define G_STRUCT_MEMBER(member_type, struct_p, struct_offset) \
++ (*(member_type*) G_STRUCT_MEMBER_P ((struct_p), (struct_offset)))
+
+-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 */
++/* reversed, in fact: for inside "member_p" calculate "struct" address */
++#define TAC_MEMBER_STRUCT(struct_type, member_p, member) \
++ (G_STRUCT_MEMBER(struct_type,member_p,-G_STRUCT_OFFSET(struct_type,member)))
+
+- 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()).
++/* <sys/param.h> may define MAX & MIN without checking whether they
++ * were previously already defined:
+ */
+-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
++#ifdef HAVE_SYS_PARAM_H
++#include <sys/param.h>
+ #endif
+
+-/* Define tac_plus name for hosts.* files */
+-#ifdef TCPWRAPPER
+-#define TACNAME "tac_plus"
++/* Provide definitions for some commonly used macros.
++ * Some of them are only provided if they haven't already
++ * been defined. It is assumed that if they are already
++ * defined then the current definition is correct.
++ */
++#ifndef NULL
++#define NULL ((void*) 0)
+ #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;
++#undef MAX
++#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+-#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
++#undef MIN
++#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+- u_char priv_lvl;
+
+-#define TAC_PLUS_PRIV_LVL_MIN 0x0
+-#define TAC_PLUS_PRIV_LVL_MAX 0xf
++/* 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 */
+
+- u_char authen_type;
++#define TAC_SOURCEFILE_EMPTY \
++ static int dummy_unused G_GNUC_UNUSED = 0;
+
+-#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
++#define DOLLARSIGN '$'
+
+ /* 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;
++#endif /* TAC_PLUS_H */
+diff --git a/tac_plus.pam b/tac_plus.pam
+new file mode 100644
+index 0000000..fc135fb
+--- /dev/null
++++ b/tac_plus.pam
+@@ -0,0 +1,5 @@
++#%PAM-1.0
++auth required /lib/security/pam_pwdb.so shadow
++account required /lib/security/pam_pwdb.so
++password required /lib/security/pam_pwdb.so nullok use_authtok shadow
++session required /lib/security/pam_pwdb.so
+diff --git a/tac_plus.spec.in b/tac_plus.spec.in
+new file mode 100644
+index 0000000..6df6899
+--- /dev/null
++++ b/tac_plus.spec.in
+@@ -0,0 +1,145 @@
++# This is tac_plus rpm spec file
++
++%define ver @VERSION@
++%define rel 1
++%define prefix /usr
++
++Summary: Cisco Tacacs+ Daemon
++Name: tac_plus
++Version: %ver
++Release: %rel
++Copyright: Cisco systems, Inc.
++Group: Networking/Daemons
++Source: http://www.gazi.edu.tr/tacacs/src/tac_plus-%{ver}.tar.gz
++Url: http://www.gazi.edu.tr/tacacs/
++Packager: Jan Kratochvil <short@ucw.cz>
++BuildRoot: /var/tmp/@PACKAGE@-%{ver}-%{rel}-root
++#Docdir: %{prefix}/doc
++
++%define __libtoolize true # we don't need it, is is otherwise run automatically
++ # don't %undefine it, there is expansion bug at least in rpm-4.0-4
++
++%description
++TACACS+ daemon using with Cisco's NASs (Or other vendors) for AAA (Authentication , Authorization and Accounting) propose.
++
++
++%prep
++%setup
++
++%build
++# configure script have some options describe below
++# --with-pam : For PAM support
++# --with-db : If you like to use db feature you must enable it
++# --with-mysql: For MySQL database support
++# --with-mysql-prefix: If you install MySQL libs other than /usr/lib
++# --enable-maxsess: For check concurrent logins (It's uses finger!!)
++# --with-pgsql With PgSQL Support
++# --with-pgsql-prefix=PREFIX PgSQL prefix [default=/usr]
++# --with-tacuid: If you like to run tac_plus specify UID
++# --with-tacgid: If you like to run tac_plus specify GID
++# --with-tacplus_pid=PREFIX Tac_plus pid file location [default=/var/run]
++# --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support
++
++%configure --with-pam --with-db
++make
++
++%install
++rm -rf "$RPM_BUILD_ROOT"
++%makeinstall
++install -d "$RPM_BUILD_ROOT"/%{_sysconfdir}/{tacacs,logrotate.d,pam.d,rc.d/{init.d,rc{0,1,2,3,4,5,6}.d}}
++install -c -m 0755 tac_plus.init "$RPM_BUILD_ROOT"/etc/rc.d/init.d/tac_plus
++install -b -c -m 0644 tac_plus.pam "$RPM_BUILD_ROOT"/etc/pam.d/pap
++install -b -c -m 0644 tac_plus.rotate "$RPM_BUILD_ROOT"/etc/logrotate.d/tac_plus
++
++%clean
++rm -rf "$RPM_BUILD_ROOT"
++
++%post
++/sbin/chkconfig --add tac_plus
++
++%preun
++if [ $1 = 0 ]; then
++ if [ -f /var/lock/subsys/tac_plus ]; then
++ %{_sysconfdir}/rc.d/init.d/tac_plus stop
++ fi
++ /sbin/chkconfig --del tac_plus
++fi
++
++%files
++%defattr(-, root, root)
++%config %{_sysconfdir}/tacacs/tac_plus.cfg
++%config %{_sysconfdir}/pam.d/pap
++%config %{_sysconfdir}/logrotate.d/tac_plus
++%doc users_guide CHANGES convert.pl
++%doc README.LDAP README.PAM tac_plus.sql
++%dir %{_sysconfdir}/tacacs
++%attr(750,root,root) %{_sysconfdir}/rc.d/init.d/tac_plus
++%attr(750,root,root) %{_bindir}/generate_passwd
++%attr(750,root,root) %{_sbindir}/tac_plus
++%attr(644,root,root) %{_mandir}/man1/*
++
++%changelog
++* Mon Jul 9 2001 Jan Kratochvil <short@ucw.cz>
++- following changes supported by GTS (www.gts.com), cooperation by:
++ Pavel Ruzicka <pavel.ruzicka@gtsgroup.cz>
++ Michael Macek <michael.macek@gtsgroup.cz>
++- multiple "member" keyword memberships supported
++- "enlist" keyword supported to specify reverse memberships
++- "host" entity unified with "user"/"group" entities
++- "when" blocks implemented for NAS host based configuration
++- "authorization = recursive" implemented for full recursivity
++- line-trailing white spaces removed
++- function prototypes cleanup and K&R C compatibility
++- maintainer compilation is pedantic now, compiler warnings cleanup
++- uncomplete transition from system-name conditions to autoconf style
++- all Makefile options moved to configure.in
++- Makefile.in rewritten to automake Makefile.am
++- autogen script included for easy maintainer rebuilds
++- tac_plus.h split to headers for each particular source file
++- system regex is now preferred, own regex is just fallback
++- several files renamed to prevent auto*/system headers conflicts
++
++* Sun Mar 25 2001 Devrim SERAL<devrim@gazi.edu.tr>
++- Added PostgreSQL authentication and accounting function
++- Added tcpwrapper feature
++- Added LDAP Authentication from Harpes Patrick (patrick.harpes@tudor.lu)
++- Added more options to configure script
++- Added time_limit function for control user loging time
++- And more control for buffer overflow
++
++* Fri Nov 17 2000 Devrim SERAL<devrim@tef.gazi.edu.tr>
++- packet.c is pached for overflow problem
++- Fix some log files name
++- Add new config parameters for database accounting
++- MySQL authentication code is functional
++- MySQL accounting code ready but not well tested
++
++* Mon Mar 10 2000 Devrim SERAL<devrim@tef.gazi.edu.tr>
++- I am add PAM patch from Max Liccardo <ravel@tiscalinet.it>
++- Change PAM code to authorize user
++- Add db support from fil@artelecom.ru
++- I am write MySQL authentication code
++- MySQL code is still experimental
++
++* Tue Nov 15 1999 Devrim SERAL<devrim@tef.gazi.edu.tr>
++- Take out documentation
++- Add more functional parameters tac_plus script
++- Change some code to authenticate with /etc/shadow
++- Fix some file permissions (Like accounting logs file)
++
++* Sun Oct 24 1999 D'mon <dimone@ikar.ugol.ru>
++- I moved to RedHat 6.0 =)
++- changes of the package internals!
++
++* Mon Oct 18 1999 D'mon <dimone@ikar.ugol.ru>
++- massive remake to suit RedHat 5.2 standard
++- patch for RedHat 5.2
++
++* Wed Aug 4 1999 Erhan Bilgili <erhan@altay.adm.deu.edu.tr>
++- fixes for the RPM_OPT_FLAGS
++- change the buildroot to /var/tmp/tacacsd
++
++* Wed Aug 4 1999 Devrim SERAL<devrim@tef.gazi.edu.tr>
++- I just re-did the spec file
++- And added Tacac FAQ
++
+diff --git a/tac_regexp.c b/tac_regexp.c
+index 9c0357a..390c5f8 100644
+--- a/tac_regexp.c
++++ b/tac_regexp.c
+@@ -42,9 +42,23 @@
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
++
++
++#include "tac_plus.h"
++
++#ifdef WITH_INCLUDED_REGEX
++
+ #include <stdio.h>
+-#include "regexp.h"
+-#include "regmagic.h"
++#include <string.h>
++#include <stdlib.h> /* malloc() can be found in <stdlib.h> OR <malloc.h> */
++#ifdef HAVE_MALLOC_H
++#include <malloc.h>
++#endif
++
++#include "tac_regexp.h"
++#include "tac_regmagic.h"
++#include "report.h" /* for regerror() */
++
+
+ /*
+ * The "internal use only" fields in regexp.h are present to pass info from
+@@ -149,7 +163,7 @@
+ #define UCHARAT(p) ((int)*(p)&CHARBITS)
+ #endif
+
+-#define FAIL(m) { regerror(m); return(NULL); }
++#define FAIL(m) { tac_regerror(m); return(NULL); }
+ #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+ #define META "^$.[()|?+*\\"
+
+@@ -164,7 +178,7 @@
+ /*
+ * Global work variables for regcomp().
+ */
+-static char *regparse; /* Input-scan pointer. */
++static const char *regparse; /* Input-scan pointer. */
+ static int regnpar; /* () count. */
+ static char regdummy;
+ static char *regcode; /* Code-emit pointer; ®dummy = don't. */
+@@ -173,23 +187,24 @@ 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();
++static char *reg TAC_ARGS((int paren, int *flagp));
++static char *regbranch TAC_ARGS((int *flagp));
++static char *regpiece TAC_ARGS((int *flagp));
++static char *regatom TAC_ARGS((int *flagp));
++static char *regnode TAC_ARGS((int op));
++static void regc TAC_ARGS((int b));
++static void reginsert TAC_ARGS((int op, char *opnd));
++static void regtail TAC_ARGS((char *p, char *val));
++static void regoptail TAC_ARGS((char *p, char *val));
++static int regtry TAC_ARGS((tac_regexp *prog, const char *string));
++static int regmatch TAC_ARGS((char *prog));
++static int regrepeat TAC_ARGS((char *p));
++static char *regnext TAC_ARGS((register char *p));
++#ifndef HAVE_STRCSPN
++static int strcspn TAC_ARGS((char *s1, char *s2));
+ #endif
+
++
+ /*
+ - regcomp - compile a regular expression into internal code
+ *
+@@ -205,16 +220,18 @@ STATIC int strcspn();
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+-regexp *
+-regcomp(exp)
+-char *exp;
++
++tac_regexp *tac_regcomp TAC_ARGS((const char *exp));
++
++tac_regexp *
++tac_regcomp(exp)
++const char *exp;
+ {
+- register regexp *r;
++ register tac_regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+- extern char *malloc();
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+@@ -233,7 +250,7 @@ char *exp;
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+- r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
++ r = (tac_regexp *)malloc(sizeof(tac_regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+@@ -272,7 +289,7 @@ char *exp;
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+- if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= (unsigned)len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+@@ -293,6 +310,9 @@ char *exp;
+ * 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 TAC_ARGS((int paren, int *flagp));
++
+ static char *
+ reg(paren, flagp)
+ int paren; /* Parenthesized? */
+@@ -301,7 +321,7 @@ int *flagp;
+ register char *ret;
+ register char *br;
+ register char *ender;
+- register int parno;
++ register int parno = 0 /* GCC paranoia */;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+@@ -365,6 +385,9 @@ int *flagp;
+ *
+ * Implements the concatenation operator.
+ */
++
++static char *regbranch TAC_ARGS((int *flagp));
++
+ static char *
+ regbranch(flagp)
+ int *flagp;
+@@ -404,6 +427,9 @@ int *flagp;
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
++
++static char *regpiece TAC_ARGS((int *flagp));
++
+ static char *
+ regpiece(flagp)
+ int *flagp;
+@@ -468,6 +494,9 @@ int *flagp;
+ * 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 TAC_ARGS((int *flagp));
++
+ static char *
+ regatom(flagp)
+ int *flagp;
+@@ -577,9 +606,12 @@ int *flagp;
+ /*
+ - regnode - emit a node
+ */
++
++static char *regnode TAC_ARGS((int op));
++
+ static char * /* Location. */
+ regnode(op)
+-char op;
++int op; /* promoted "char" type */
+ {
+ register char *ret;
+ register char *ptr;
+@@ -602,9 +634,12 @@ char op;
+ /*
+ - regc - emit (if appropriate) a byte of code
+ */
++
++static void regc TAC_ARGS((int b));
++
+ static void
+ regc(b)
+-char b;
++int b; /* promoted "char" type */
+ {
+ if (regcode != ®dummy)
+ *regcode++ = b;
+@@ -617,9 +652,12 @@ char b;
+ *
+ * Means relocating the operand.
+ */
++
++static void reginsert TAC_ARGS((int op, char *opnd));
++
+ static void
+ reginsert(op, opnd)
+-char op;
++int op; /* promoted "char" type */
+ char *opnd;
+ {
+ register char *src;
+@@ -646,6 +684,9 @@ char *opnd;
+ /*
+ - regtail - set the next-pointer at the end of a node chain
+ */
++
++static void regtail TAC_ARGS((char *p, char *val));
++
+ static void
+ regtail(p, val)
+ char *p;
+@@ -678,6 +719,9 @@ char *val;
+ /*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
++
++static void regoptail TAC_ARGS((char *p, char *val));
++
+ static void
+ regoptail(p, val)
+ char *p;
+@@ -696,44 +740,40 @@ char *val;
+ /*
+ * 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. */
++static const char *reginput; /* String-input pointer. */
++static const char *regbol; /* Beginning of input, for ^ check. */
++static const char **regstartp; /* Pointer to startp array. */
++static const 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();
++static char *regprop TAC_ARGS((char *op));
++static void regdump TAC_ARGS((tac_regexp *r));
+ #endif
+
+ /*
+ - regexec - match a regexp against a string
+ */
++
++int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string));
++
+ int
+-regexec(prog, string)
+-register regexp *prog;
+-register char *string;
++tac_regexec(prog, string)
++register tac_regexp *prog;
++register const char *string;
+ {
+- register char *s;
+- extern char *strchr();
++ register const char *s;
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+- regerror("NULL parameter");
++ tac_regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+- regerror("corrupted program");
++ tac_regerror("corrupted program");
+ return(0);
+ }
+
+@@ -779,14 +819,17 @@ register char *string;
+ /*
+ - regtry - try match at specific point
+ */
++
++static int regtry TAC_ARGS((tac_regexp *prog, const char *string));
++
+ static int /* 0 failure, 1 success */
+ regtry(prog, string)
+-regexp *prog;
+-char *string;
++tac_regexp *prog;
++const char *string;
+ {
+ register int i;
+- register char **sp;
+- register char **ep;
++ register const char **sp;
++ register const char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+@@ -816,13 +859,15 @@ char *string;
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
++
++static int regmatch TAC_ARGS((char *prog));
++
+ 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
+@@ -888,7 +933,7 @@ char *prog;
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+- register char *save;
++ register const char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+@@ -916,7 +961,7 @@ char *prog;
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+- register char *save;
++ register const char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+@@ -935,7 +980,7 @@ char *prog;
+ }
+ break;
+ case BRANCH: {
+- register char *save;
++ register const char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+@@ -956,7 +1001,7 @@ char *prog;
+ case PLUS: {
+ register char nextch;
+ register int no;
+- register char *save;
++ register const char *save;
+ register int min;
+
+ /*
+@@ -985,7 +1030,7 @@ char *prog;
+ return(1); /* Success! */
+ break;
+ default:
+- regerror("memory corruption");
++ tac_regerror("memory corruption");
+ return(0);
+ break;
+ }
+@@ -997,21 +1042,23 @@ char *prog;
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+- regerror("corrupted pointers");
++ tac_regerror("corrupted pointers");
+ return(0);
+ }
+
+ /*
+ - regrepeat - repeatedly match something simple, report how many
+ */
++
++static int regrepeat TAC_ARGS((char *p));
++
+ static int
+ regrepeat(p)
+ char *p;
+ {
+ register int count = 0;
+- register char *scan;
++ register const char *scan;
+ register char *opnd;
+- extern char *strchr();
+
+ scan = reginput;
+ opnd = OPERAND(p);
+@@ -1039,7 +1086,7 @@ char *p;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+- regerror("internal foulup");
++ tac_regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+@@ -1051,6 +1098,9 @@ char *p;
+ /*
+ - regnext - dig the "next" pointer out of a node
+ */
++
++static char *regnext TAC_ARGS((register char *p));
++
+ static char *
+ regnext(p)
+ register char *p;
+@@ -1072,19 +1122,19 @@ register char *p;
+
+ #ifdef DEBUG
+
+-STATIC char *regprop();
+-
+ /*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+-void
++
++static void regdump TAC_ARGS((tac_regexp *r));
++
++static void
+ regdump(r)
+-regexp *r;
++tac_regexp *r;
+ {
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+- extern char *strchr();
+
+
+ s = r->program + 1;
+@@ -1121,6 +1171,9 @@ regexp *r;
+ /*
+ - regprop - printable representation of opcode
+ */
++
++static char *regprop TAC_ARGS((char *op));
++
+ static char *
+ regprop(op)
+ char *op;
+@@ -1192,7 +1245,7 @@ char *op;
+ p = "PLUS";
+ break;
+ default:
+- regerror("corrupted opcode");
++ tac_regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+@@ -1207,12 +1260,14 @@ char *op;
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+-#ifdef STRCSPN
++#ifndef HAVE_STRCSPN
+ /*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
++static int strcspn TAC_ARGS((char *s1, char *s2));
++
+ static int
+ strcspn(s1, s2)
+ char *s1;
+@@ -1231,4 +1286,10 @@ char *s2;
+ }
+ return(count);
+ }
+-#endif
++#endif /* HAVE_STRCSPN */
++
++#else /* WITH_INCLUDED_REGEX */
++
++TAC_SOURCEFILE_EMPTY
++
++#endif /* WITH_INCLUDED_REGEX */
+diff --git a/tac_regexp.h b/tac_regexp.h
+index b23d97e..7a1a884 100644
+--- a/tac_regexp.h
++++ b/tac_regexp.h
+@@ -1,3 +1,10 @@
++#ifndef TAC_REGEXP_H
++#define TAC_REGEXP_H 1
++
++#include "tac_plus.h"
++
++#ifdef WITH_INCLUDED_REGEX
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,6 +24,7 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
++
+ /*
+ * Definitions etc. for regexp(3) routines.
+ *
+@@ -24,17 +32,21 @@
+ * not the System V one.
+ */
+ #define NSUBEXP 10
+-typedef struct regexp {
+- char *startp[NSUBEXP];
+- char *endp[NSUBEXP];
++typedef struct tac_regexp {
++ const char *startp[NSUBEXP];
++ const 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;
++} tac_regexp;
++
++
++extern tac_regexp *tac_regcomp TAC_ARGS((const char *exp));
++extern int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string));
++
++
++#endif /* WITH_INCLUDED_REGEX */
+
+-extern regexp *regcomp();
+-extern int regexec();
+-extern void regsub();
+-extern void regerror();
++#endif /* TAC_REGEXP_H */
+diff --git a/tac_regmagic.h b/tac_regmagic.h
+index 0b18b0a..ff321df 100644
+--- a/tac_regmagic.h
++++ b/tac_regmagic.h
+@@ -1,3 +1,8 @@
++#ifndef REGMAGIC_H
++#define REGMAGIC_H 1
++
++#include "tac_plus.h"
++
+ /*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+@@ -17,8 +22,12 @@
+ 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
++
++
++#endif /* REGMAGIC_H */
+diff --git a/tcpwrap.c b/tcpwrap.c
+index ef3d3ad..744a1df 100644
+--- a/tcpwrap.c
++++ b/tcpwrap.c
+@@ -4,21 +4,41 @@
+ Writen by Devrim SERAL<devrim@gazi.edu.tr>. This file protected by
+ GNU Copyright agreement.
+ */
++
++
++#include "tac_plus.h"
++
+ #ifdef TCPWRAPPER
++
+ #include <tcpd.h>
+-#include "tac_plus.h"
+
+-int allow_severity = LOG_INFO;
++#include "tcpwrap.h"
++#include "report.h"
++#include "packet.h"
++#include "do_author.h" /* for "struct identity" */
++#include "main.h"
++
++
++int allow_severity = LOG_INFO; /* *_severity accessed from libwrap */
+ int deny_severity = LOG_WARNING;
+
+
++/* Configurable:
++ */
++
++/* Define tac_plus name for hosts.* files */
++#define TACNAME "tac_plus"
++
++
++int check_from_wrap TAC_ARGS((struct identity *datap));
++
+ 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);
++ request_init(&req, RQ_FILE,session.sock,RQ_DAEMON,TACNAME,RQ_CLIENT_ADDR,datap->NAS_name , NULL);
+ fromhost(&req); /* validate client host info */
+ if (!hosts_access(&req))
+ {
+@@ -34,4 +54,9 @@ struct identity *datap;
+ return 1;
+
+ }
++
++#else /* TCPWRAPPER */
++
++TAC_SOURCEFILE_EMPTY
++
+ #endif /* TCPWRAPPER */
+diff --git a/tcpwrap.h b/tcpwrap.h
+new file mode 100644
+index 0000000..e8e9455
+--- /dev/null
++++ b/tcpwrap.h
+@@ -0,0 +1,16 @@
++#ifndef TCPWRAP_H
++#define TCPWRAP_H 1
++
++#include "tac_plus.h"
++
++#ifdef TCPWRAPPER
++
++
++struct identity;
++
++extern int check_from_wrap TAC_ARGS((struct identity *datap));
++
++
++#endif /* TCPWRAPPER */
++
++#endif /* TCPWRAP_H */
+diff --git a/time_limit.c b/time_limit.c
+index 08f6c55..75062f2 100644
+--- a/time_limit.c
++++ b/time_limit.c
+@@ -25,180 +25,224 @@ Software Foundation; either version 2, or (at your option) any later version.
+
+ */
+
+-#include"time_limit.h"
++
+ #include "tac_plus.h"
+
+-int problem=0;
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <time.h>
++
++#include "time_limit.h"
++#include "report.h"
++#include "main.h"
++#include "utils.h"
++
++
++static int str_token_proc TAC_ARGS((char *str));
++static int process TAC_ARGS((char *str));
++static int time_calc TAC_ARGS((char *str, int lct));
++static int antoi TAC_ARGS((char *str, int n));
++
++
++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};
++
++static int problem = 0;
++
++
++int time_limit_process TAC_ARGS((const char *str));
+
+ int
+ time_limit_process(str)
+-char *str;
++const char *str;
+ {
+-int ret=0;
+-char *tmp_str;
++ int ret=0;
++ char *tmp_str, *str_copy;
+
+-tmp_str=(char *)strtok(str,",|");
+-while ( tmp_str != NULL) {
+- ret|=str_token_proc(tmp_str);
+- tmp_str=(char *)strtok(NULL,",");
++ str_copy = tac_strdup(str);
++ tmp_str = (char *) strtok(str_copy,",|");
++
++ while ( tmp_str != NULL) {
++ ret |= str_token_proc(tmp_str);
++ tmp_str = (char *) strtok(NULL,",");
+ }
+-return (ret);
++ free(str_copy);
++
++ return (ret);
+ }
+
+-int
++static int str_token_proc TAC_ARGS((char *str));
++
++static int
+ str_token_proc(str)
+ char *str;
+ {
+-int inv=0,ret;
++ int inv = 0, ret;
+
+-/* Pass space characters */
+-while (isspace(*str)) str++;
++ /* Pass space characters */
++ while (isspace((int) *str))
++ str++;
+
+-if (*str=='!') {
+- inv=1;str++;
+-}
++ if (*str=='!') {
++ inv=1;
++ str++;
++ }
+
+-ret=process(str);
++ ret=process(str);
+
+-if (problem) {
++ if (problem) {
+ if ( debug & DEBUG_AUTHEN_FLAG )
+ report(LOG_DEBUG,"Timestamp format incorrect");
+ problem=0;
+ return(0);
+-}
++ }
+
+-if (inv)
++ if (inv)
+ ret=!ret;
+-return(ret);
++
++ return(ret);
+ }
+
+
+-int
++static void str_up TAC_ARGS((char *str));
++
++static void
++str_up(str)
++char *str;
++{
++ while (*str) {
++ if (islower((int) *str))
++ *str = toupper((int) *str);
++ str++;
++ }
++}
++
++
++static int process TAC_ARGS((char *str));
++
++static int
+ process(str)
+ char *str;
+ {
+-int count=0,ret=0,i,j,localtm;
+-char *head,*buf,*gec;
+-long sec;
+-struct tm *tms;
++ int count = 0, ret = 0, i, j, localtm;
++ char *head, *buf, *gec;
++ time_t sec;
++ struct tm *tms;
+
+-/* Pass space characters */
+-while (isspace(*str)) str++;
++ /* Pass space characters */
++ while (isspace((int) *str))
++ str++;
+
+-head=str;
++ head=str;
+
+-/* Count alphanumeric char */
+-while (isalpha(*str)) {
++ /* Count alphanumeric char */
++ while (isalpha((int) *str)) {
+ count++;
+ str++;
+-}
++ }
+
+-if ( count==0 || count%2 ) {
++ if ( count==0 || count%2 ) {
+ problem++;
+ return 0;
+-}
++ }
+
+-buf=(char *)malloc(count+1);
+-strncpy(buf,head,count);
+-gec=buf;
+-str_up(buf);
++ buf = (char *) tac_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];
+- }
++ for (i=1; i<=(count/2); i++) {
++ for (j=0; j<NUM; j++)
++ if(!strncmp(gec,week_days[j],2))
++ ret ^= week_day_val[j];
++ gec += 2;
+ }
+- gec+=2;
+-}
+
+-/* We finished to use buffer so free it */
+-free(buf);
++ /* 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);
++ sec = time(NULL);
++ 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)
++ if (ret>0)
+ return (1);
+-else
+- return(0);
++ else
++ return (0);
+ }
+
+-str_up(str)
+-char *str;
+-{
+- while(*str) {
+- if(islower(*str)) *str=toupper(*str);
+- str++;
+- }
+-}
+
+-int
+-time_calc(str,lct)
++static int time_calc TAC_ARGS((char *str, int lct));
++
++static int
++time_calc(str, lct)
+ char *str;
+ int lct;
+ {
+-char *t1,*t2,*head;
+-int say1,say2,count=0;
++ char *t1, *t2, *head;
++ int say1, say2, count=0;
+
+-head=str;
++ head = str;
+
+- while (isdigit(*head) || *head=='-') {
++ while (isdigit((int) *head) || *head=='-') {
+ count++;
+ head++;
+ }
+
+-if (*str=='\0' || count!= TPL ) {
++ if ( *str=='\0' || count!= TPL ) {
+ problem++;
+ return (0);
+-}
++ }
+
+- t1=(char *) malloc(count);
+- strncpy(t1,str,count); /*Put str value to t1*/
++ t1 = (char *) tac_malloc(count);
++ strncpy(t1, str, count); /* Put str value to t1 */
+
+- t2=(char *) strstr(t1,"-"); /* Find next time part */
++ t2 = (char *) strstr(t1,"-"); /* Find next time part */
+
+-if (t2==NULL) {
++ if (t2==NULL) {
+ free(t1);
+ problem++;
+ return(0);
+-}
++ }
+
+-*t2='\0';t2++;
++ *t2++ = '\0';
+
+-if ( strlen(t1)<4 || strlen(t2)<4 ) {
++ 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);
++ }
++ say1 = antoi(t1,2)*60 + antoi(t1+2,2);
++ say2 = antoi(t2,2)*60 + antoi(t2+2,2);
+
+-free(t1);
++ free(t1);
+
+-if (say1<=say2) {
+- if( (lct>=say1) && (lct<=say2) ) return(1);
+-}
+-else {
+- if( (lct>=say1) || (lct<=say2) ) return(1);
++ if (say1 <= say2) {
++ if( (lct>=say1) && (lct<=say2) )
++ return(1);
++ } else {
++ if( (lct>=say1) || (lct<=say2) )
++ return(1);
++ }
++ return(0);
+ }
+-return(0);
+
+-}
++static int antoi TAC_ARGS((char *str, int n));
+
+-int
+-antoi(str,n)
+-char *str;int n;
++static int
++antoi(str, n)
++char *str;
++int n;
+ {
+-char *buf;
+-int ret;
++ char *buf;
++ int ret;
+
+- buf=(char *) malloc(n);
+- strncpy(buf,str,n);
+- ret=atoi(buf);
++ buf = (char *) tac_malloc(n);
++ strncpy(buf, str, n);
++ ret = atoi(buf);
+ free(buf);
+
+-return(ret);
++ return(ret);
+ }
+diff --git a/time_limit.h b/time_limit.h
+index e8bb6fb..eac2096 100644
+--- a/time_limit.h
++++ b/time_limit.h
+@@ -1,13 +1,14 @@
+-#include<stdlib.h>
+-#include<ctype.h>
+-#include<stdio.h>
+-#include<time.h>
+-#include<string.h>
++#ifndef TIME_LIMIT_H
++#define TIME_LIMIT_H 1
++
++#include "tac_plus.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();
++extern int time_limit_process TAC_ARGS((const char *str));
++
++
++#endif /* TIME_LIMIT_H */
+diff --git a/users_guide b/users_guide
+index 2626115..c1a8cfd 100644
+--- a/users_guide
++++ b/users_guide
+@@ -337,6 +337,83 @@ 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.
+
++MEMBERSHIP IN MULTIPLE GROUPS
++-----------------------------
++
++Besides the descibed single-membership of user to some group, you may also find
++useful if user (or host) belongs to multiple groups at once. You can naturally
++specify multiple "member = group_X" commands for such user:
++
++user = fred {
++ # fred is a member of both groups: admins_company_A, admins_company_B
++ member = admins_company_A
++ member = admins_company_B
++}
++
++group = admins_company_A {
++ # group admins_company_A is not a member of any group
++ member = admins_company_A_privilege_X
++ member = admins_company_A_privilege_Y
++}
++group = admins_company_A_privilege_X {
++}
++group = admins_company_A_privilege_Y {
++}
++
++group = admins_company_B {
++ # group admins_company_B is not a member of any group
++}
++
++Here it is important to respect the ordering of "member" commands: Any
++searching for attributes/values is done by Depth-First Search - so Daemon would
++first try to look all members of admins_company_A and THEN (after it would
++failed to find any) it would start to searching through admins_company_B. The
++searching through the proposed example would be done in the following order:
++
++ fred
++ admins_company_A
++ admins_company_A_privilege_X
++ admins_company_A_privilege_Y
++ admins_company_B
++
++Sometimes you would want to only list some members (users, hosts or groups) but
++you don't want to specify any attributes for them. You would be able to do it:
++
++group = city_X_NASes {
++}
++host first.NAS.X.city {
++ member = city_X_NASes
++}
++host second.NAS.X.city {
++ member = city_X_NASes
++}
++
++But you will probably find more comfortable to use "enlist" keyword. It has
++the same functionality but it goes from the other way:
++
++ member: current entity is connected as CHILD to the specified PARENT entity
++ enlist: specified entity is connected as CHILD to the current one as PARENT
++
++The example would be re-written using "enlist" keyword as:
++
++group = city_X_NASes {
++ enlist = host first.NAS.X.city
++ enlist = host second.NAS.X.city
++}
++
++As you can see, "enlist" doesn't require the existence of the given entity as
++it would loose its primary purpose. If the entity doesn't exist it will be
++automatically created (as empty one) - this doesn't apply to "member"! Any
++argument to "member" MUST already exist. "enlist" is provided to save you from
++writing a lot of empty definition lines like:
++
++host = first.NAS.X.city {
++}
++
++All forward references are not a problem, you can still make membership or
++enlistment on the top of the file with the entity which will be defined on the
++end of the configuration file.
++
+ CONFIGURING USER AUTHENTICATION
+ -------------------------------
+
+@@ -1013,6 +1090,229 @@ group = admin {
+ }
+
+
++CONFIGURATION RESPECTING NAS HOST OF THE USER
++---------------------------------------------
++
++Sometimes you would want to modify the configuration file according to the
++source NAS where the user is being authenticated/authorized. For example if you
++are big ISP you want to permit administrator of company X to be able to monitor
++status of the links on NAS in company X but, of course, she should be able to
++monitor any other links on any other NAS of the same ISP. As all the NASes are
++authorized from the same Daemon, it seems as a problem. (You can workaround it
++by using custom authorization program - see "USING PROGRAMS TO DO
++AUTHORIZATION" section below - but it is not nice solution.)
++
++For this purposes there exists another entity 'host':
++
++ user wilma
++ group admin
++ host 198.133.219.25
++ host nas.cisco.com
++
++As you can see you may use either IP address or DNS name. Anyway, we strongly
++recommend to always use only IP addresses - DNS subsystem may fail or it may be
++forged by the enemy.
++
++You have two methods of utilizing the differences between NASes:
++
++1) Current user is always automatically enlisted (=given membership to) to its
++ current NAS host. This looks weird as only groups can have members but this
++ is the only exception to this rule, current NAS host can really have a
++ member:
++
++ ( user "current_user" | user DEFAULT )
++ |
++ +-- host "current_NAS_IP_address"
++ | |
++ | +- group DEFAULT
++ |
++ +-- host "current_NAS_hostname"
++ | |
++ | +-- group DEFAULT
++ |
++ +-- group DEFAULT
++
++ Each link only exists in the case it there exist both its peers, of course.
++ user/group DEFAULT is written here only for the completeness of the chart.
++ DEFAULT is discussed elsewhere in this documentation.
++
++ According to the shown ordering, attributes/values in the host of current
++ NAS identified by its IP address has _higher_ precence over the attributes
++ in the current NAS identified by its (reverse-resolved) hostname.
++
++ According to this auto-connections, you can for example permit some command
++ to ALL the users on such NAS:
++
++user = fred {
++ login = cleartext LLLL
++}
++host = machine.A.company {
++ cmd = write {
++ permit terminal
++ }
++}
++
++ In this configuration file ALL the valid users can do "write terminal" when
++ logged in on NAS "machine.A.company".
++
++2) Sometimes you need to do the authorization specific only to some users on
++ some NASes. For example to permit "write terminal" ONLY to user fred
++ connected to NAS "machine.A.company". That means that you want to forbid it
++ to user fred on any other NAS and yuo also want to forbid it to all the
++ other users on NAS "machine.A.company". (Line "authorization = recursive" is
++ required but read the following section "FULL RECURSIVITY" to know all its
++ consequences.)
++
++authorization = recursive
++user = fred {
++ login = cleartext LLLL
++}
++host = machine.A.company {
++ when = user fred {
++ cmd = write {
++ permit terminal
++ }
++ }
++}
++
++ This file has the same effect as:
++
++authorization = recursive
++user = fred {
++ login = cleartext LLLL
++ when = host machine.A.company {
++ cmd = write {
++ permit terminal
++ }
++ }
++}
++
++ You can see the (nested) command "when" can limit the scope of existence of
++ its contents. Definition of "host machine.A.company" with empty block (no
++ attributes) isn't needed as "when" line will automatically create hosts not
++ defined elsewhere, in the same style as "enlist" keyword creates them. Any
++ "user"s or "group"s referenced by "when" MUST be defined, such entities are
++ never created automatically!
++
++ Unfortunately you cannot use "when" to limit any items, just a few of them
++ are possible:
++
++member
++enlist
++cmd
++cmd arguments (to limit specific "permit"/"deny" lines)
++service (or incl. "protocol" specification)
++service AV pairs (to limit specific "attr=value" lines)
++when (enabling pure nesting of "when" blocks)
++
++ Full flexibility to limit any contents may be done in future (needs complete
++ cfgfile.c rewrite) but currently it is not supported. Fortunately you can
++ get the same behaviour by appropriate usage of "member" keyword - all the
++ attributes to be conditioned are put into separate group and you limit only
++ the "member" keyword to such group.
++
++ "when" command has the form "when = CONDITION { BLOCK }", CONDITION can be:
++
++user USR i.e. that current user is "USR")
++host HOSTNAME i.e. that current user is on NAS "HOSTNAME")
++group GRP i.e. current user belongs to the group "GRP",
++ All possible "when" conditions to reach such belonging
++ have to be successfuly met to make this condition successful.
++ Realize that "member" can be limited by "when" keyword.
++CONDITION and CONDITION and CONDITION ...
++CONDITION or CONDITION or CONDITION ...
++not CONDITION
++( CONDITION )
++
++ You can see that you CANNOT use for example "user A and user B or user C",
++ such condition would have ambiguous precedence of operators, you must
++ explicitly write: ( user A and user B ) or user C
++ or: user A and ( user B or user C )
++
++ Both parentheses have to be written as separate words, NOT "(user A)"
++ but "( user A )" instead.
++ (Proper solution would also need the complete cfgfile.c rewrite.)
++
++ "not" operator has the highest precendence, so: not user A or user B
++ has the same meaning as: ( not user A ) or user B
++
++ Sometimes the "when" condition is so-called unresolvable. Example:
++
++group = GRP { }
++user = USR {
++ when = group GRP {
++ member = GRP
++ }
++}
++
++ It is looping, when we would be the member of GRP, we would be really the
++ member of GRP but otherwise sould wouldn't be the member of GRP. Weird?
++ Yes, such case is considered as configuration bug, it is reported as:
++
++ Unable to resolve expression from line 243, some looping occured
++
++ Generally such unresolvable conditional expression is considered as UNKNOWN
++ and the "when" keyword is then evaluated as "false" (=skip it's block).
++ This MAY produce unwanted privilege access (if you were conditionally
++ forbidding some privileges) so always watch out all the error messages in
++ logs! (You should generally do default deny and specifically only "permit"
++ all the privileges so the unintentional unresolvable expressions shouldn't
++ hurt you in real.)
++
++FULL RECURSIVITY
++----------------
++
++We have written in the previous examples line:
++
++authorization = recursive
++
++This changes some behaviour of Daemon:
++
++1) Looping of memberships: By default any looped memberships are reported as
++ error with message: recursively defined groups: ...
++
++ After "authorization = recursive" any looping is silently accepted as it
++ would be sometimes hard to prevent it in some complex configurations.
++ Searching is done in normal Depth-First Search, but traversion is
++ backtracked one step when the entity was already visited. Simply it will
++ work 'magically', just safely ignore the fact.
++
++2) "default service = default" is supported only with:
++ authorization = recursive
++ This isn't any real change, just some formality. In fact each entity block
++ has "default service = default" in effect as default (in the case you don't
++ write any "default service =" assign).
++
++ "authorization = recursive" is enforced here due to the change of Daemon
++ behaviour: In non-recursive (old) case the Daemon assumes "deny .*" as the
++ last line of "cmd" block. This makes proper NAS-host based permissions
++ impossible. In "authorization = recursive" mode it requires appropriate
++ "permit" or "deny" line to apply - otherwise the Depth-First Search through
++ the entities will continue to find another applicable "cmd" block (toplevel
++ "default authorization" may get into effect as the last resort).
++
++ "default service" keyword takes then another meaning: In non-recursive mode
++ it will mean "if the command wasn't found". In fully-recursive mode it will
++ mean "if any cmd block didn't decide".
++
++ Please keep in mind that "service =" (with its possible "protocol =" mate)
++ has always the same behaviour, nothing is changed with "authorization =
++ recursive" enabled. Its functionality is already a bit complex (with AV
++ pairs get erased, which added, which copied, which are optional from NAS,
++ which optional from Daemon, whether to do default permit or deny etc.).
++ Fortunately we didn't found that there would be needed some extended
++ 'recursiveness' functionality of "service" for application of NAS-host based
++ authorization.
++
++3) "host" entities are not recursive at all (=its attributes aren't looked up
++ in its parent groups) without "authorization = recursive". This isn't any
++ much change as in the previous versions of Daemon "host" entity was either
++ completely unsupported or it was supported only with the only one attribute:
++
++ "key": Attribute defines different protocol key (instead of the specified
++ toplevel one) for the specified host. Its use is recommended to
++ improve overall network security (by using unique key for each NAS).
++
+ USING PROGRAMS TO DO AUTHORIZATION
+ ----------------------------------
+
+diff --git a/utils.c b/utils.c
+index b597e70..43b50f3 100644
+--- a/utils.c
++++ b/utils.c
+@@ -17,19 +17,36 @@
+ FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+-#include "tac_plus.h"
+
+-#ifdef STDLIB_MALLOC
++#include "tac_plus.h"
+
+-#include <stdlib.h>
++#include <stdlib.h> /* malloc() can be found in <stdlib.h> OR <malloc.h> */
++#ifdef HAVE_MALLOC_H
++#include <malloc.h>
++#endif
++#include <string.h>
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h> /* for "struct flock" */
++#endif
++#include <errno.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_SYSLOG_H
++#include <syslog.h>
++#endif
++#ifdef HAVE_SYS_SYSLOG_H
++#include <sys/syslog.h>
++#endif
+
+-#else /* !STDLIB_MALLOC */
++#include "utils.h"
++#include "report.h"
++#include "main.h"
+
+-#include <malloc.h>
+
+-#endif /* STDLIB_MALLOC */
++void *tac_malloc TAC_ARGS((int size));
+
+-char *
++void *
+ tac_malloc(size)
+ int size;
+ {
+@@ -49,9 +66,11 @@ int size;
+ return (p);
+ }
+
+-char *
++void *tac_realloc TAC_ARGS((void *ptr, int size));
++
++void *
+ tac_realloc(ptr, size)
+-char *ptr;
++void *ptr;
+ int size;
+ {
+ char *p;
+@@ -70,6 +89,9 @@ int size;
+ return (p);
+ }
+
++void tac_exit TAC_ARGS((int status)) G_GNUC_NORETURN;
++
++void
+ tac_exit(status)
+ int status;
+ {
+@@ -78,9 +100,11 @@ int status;
+ exit(status);
+ }
+
++char *tac_strdup TAC_ARGS((const char *p));
++
+ char *
+ tac_strdup(p)
+-char *p;
++const char *p;
+ {
+ char *n = strdup(p);
+
+@@ -91,6 +115,8 @@ char *p;
+ return (n);
+ }
+
++char *tac_make_string TAC_ARGS((u_char *p, int len));
++
+ char *
+ tac_make_string(p, len)
+ u_char *p;
+@@ -116,9 +142,13 @@ int len;
+ /* return a pointer to the end of substring in string, or NULL. Substring
+ must begin at start of string.
+ */
+-char *
++
++const char *tac_find_substring TAC_ARGS((const char *substring, const char *string));
++
++const char *
+ tac_find_substring(substring, string)
+-char *substring, *string;
++const char *substring;
++const char *string;
+ {
+ int len;
+
+@@ -190,6 +220,8 @@ bcmp(s1,s2,n)
+ are at the mercy of SUN's lockd, which is probably a bad idea
+ */
+
++int tac_lockfd TAC_ARGS((char *filename, int lockfd));
++
+ int
+ tac_lockfd (filename, lockfd)
+ char *filename;
+@@ -273,7 +305,8 @@ int lockfd;
+ are at the mercy of SUN's lockd, which is probably a bad idea
+ */
+
+-int
++#if 0 /* unused */
++static int
+ tac_unlockfd (filename,lockfd)
+ char *filename;
+ int lockfd;
+@@ -303,3 +336,184 @@ int lockfd;
+ }
+ return(0);
+ }
++#endif /* unused */
++
++/* Management of bidirectional lists.
++*/
++
++#ifdef TAC_LIST_PARANOIA
++
++static void tac_list_check_magic TAC_ARGS((const struct tac_list *list));
++
++static void
++tac_list_check_magic(list)
++const struct tac_list *list;
++{
++ if (list->_magic != TAC_LIST_MAGIC)
++ report(LOG_ERR, "MAGIC fail for tac_list");
++}
++
++static void tac_list_node_check_magic TAC_ARGS((const struct tac_list_node *node));
++
++static void
++tac_list_node_check_magic(node)
++const struct tac_list_node *node;
++{
++ if (node->_magic != TAC_LIST_NODE_MAGIC)
++ report(LOG_ERR, "MAGIC fail for tac_list_node");
++}
++#else /* TAC_LIST_PARANOIA */
++
++#define tac_list_check_magic(list)
++#define tac_list_node_check_magic(node)
++
++#endif /* TAC_LIST_PARANOIA */
++
++
++void tac_list_init TAC_ARGS((struct tac_list *list));
++
++void
++tac_list_init(list)
++struct tac_list *list;
++{
++#ifdef TAC_LIST_PARANOIA
++ list->_magic = TAC_LIST_MAGIC;
++#endif
++ list->_head = NULL;
++ list->_tail = NULL;
++
++ tac_list_check_magic(list);
++}
++
++void tac_list_node_init TAC_ARGS((struct tac_list_node *node));
++
++void
++tac_list_node_init(node)
++struct tac_list_node *node;
++{
++#ifdef TAC_LIST_PARANOIA
++ node->_magic = TAC_LIST_NODE_MAGIC;
++#endif
++ node->_list = NULL;
++
++ tac_list_node_check_magic(node);
++}
++
++struct tac_list *tac_list_node_get_list TAC_ARGS((struct tac_list_node *node));
++
++struct tac_list *
++tac_list_node_get_list(node)
++struct tac_list_node *node;
++{
++ tac_list_node_check_magic(node);
++
++ return (node->_list);
++}
++
++struct tac_list_node *tac_list_first_node TAC_ARGS((struct tac_list *list));
++
++struct tac_list_node *
++tac_list_first_node(list)
++struct tac_list *list;
++{
++ tac_list_check_magic(list);
++
++ return (list->_head);
++}
++
++#if 0 /* unused */
++struct tac_list_node *tac_list_last_node TAC_ARGS((struct tac_list *list));
++
++struct tac_list_node *
++tac_list_last_node(list)
++struct tac_list *list;
++{
++ tac_list_check_magic(list);
++
++ return (list->_tail);
++}
++#endif /* unused */
++
++void tac_list_addhead TAC_ARGS((struct tac_list *list, struct tac_list_node *node));
++
++void
++tac_list_addhead(list, node)
++struct tac_list *list;
++struct tac_list_node *node;
++{
++ tac_list_check_magic(list);
++ tac_list_node_check_magic(node);
++
++ if ((node->_next = list->_head))
++ node->_next->_prev = node;
++ list->_head = node;
++ node->_prev = NULL;
++ if (!list->_tail)
++ list->_tail = node;
++ node->_list = list;
++}
++
++void tac_list_addtail TAC_ARGS((struct tac_list *list, struct tac_list_node *node));
++
++void
++tac_list_addtail(list, node)
++struct tac_list *list;
++struct tac_list_node *node;
++{
++ tac_list_check_magic(list);
++ tac_list_node_check_magic(node);
++
++ if ((node->_prev = list->_tail))
++ node->_prev->_next = node;
++ list->_tail = node;
++ node->_next = NULL;
++ if (!list->_head)
++ list->_head = node;
++ node->_list = list;
++}
++
++struct tac_list_node *tac_list_node_next TAC_ARGS((struct tac_list_node *node));
++
++struct tac_list_node *
++tac_list_node_next(node)
++struct tac_list_node *node;
++{
++ tac_list_node_check_magic(node);
++
++ return (node->_next);
++}
++
++#if 0 /* unused */
++struct tac_list_node *tac_list_node_prev TAC_ARGS((struct tac_list_node *node));
++
++struct tac_list_node *
++tac_list_node_prev(node)
++struct tac_list_node *node;
++{
++ tac_list_node_check_magic(node);
++
++ return (node->_prev);
++}
++#endif /* unused */
++
++void tac_list_node_remove TAC_ARGS((struct tac_list_node *node));
++
++void
++tac_list_node_remove(node)
++struct tac_list_node *node;
++{
++ tac_list_node_check_magic(node);
++ tac_list_check_magic(node->_list);
++
++ if (node->_next)
++ node->_next->_prev = node->_prev;
++ else
++ node->_list->_tail = node->_prev;
++
++ if (node->_prev)
++ node->_prev->_next = node->_next;
++ else
++ node->_list->_head = node->_next;
++
++ node->_list = NULL;
++}
+diff --git a/utils.h b/utils.h
+new file mode 100644
+index 0000000..f8c39e8
+--- /dev/null
++++ b/utils.h
+@@ -0,0 +1,59 @@
++#ifndef UTILS_H
++#define UTILS_H 1
++
++#include "tac_plus.h"
++
++#include <sys/types.h> /* for u_* */
++
++
++/* Configurable:
++ */
++#define TAC_LIST_PARANOIA 1
++
++
++/* Bidirectional lists */
++
++#ifdef TAC_LIST_PARANOIA
++#define TAC_LIST_MAGIC (0xBEF78147U)
++#define TAC_LIST_NODE_MAGIC (0x297DA735U)
++#endif
++
++struct tac_list {
++#ifdef TAC_LIST_PARANOIA
++ unsigned _magic;
++#endif
++ struct tac_list_node *_head, *_tail;
++};
++
++struct tac_list_node {
++#ifdef TAC_LIST_PARANOIA
++ unsigned _magic;
++#endif
++ struct tac_list_node *_next, *_prev;
++ struct tac_list *_list;
++};
++
++
++extern void *tac_malloc TAC_ARGS((int size));
++extern void *tac_realloc TAC_ARGS((void *ptr, int size));
++extern void tac_exit TAC_ARGS((int status)) G_GNUC_NORETURN;
++extern char *tac_strdup TAC_ARGS((const char *p));
++extern char *tac_make_string TAC_ARGS((u_char *p, int len));
++extern const char *tac_find_substring TAC_ARGS((const char *substring, const char *string));
++extern int tac_lockfd TAC_ARGS((char *filename, int lockfd));
++
++/* Bidirectional lists: */
++
++extern void tac_list_init TAC_ARGS((struct tac_list *list));
++extern void tac_list_node_init TAC_ARGS((struct tac_list_node *node));
++extern struct tac_list *tac_list_node_get_list TAC_ARGS((struct tac_list_node *node));
++struct tac_list_node *tac_list_first_node TAC_ARGS((struct tac_list *list));
++struct tac_list_node *tac_list_last_node TAC_ARGS((struct tac_list *list));
++extern void tac_list_addhead TAC_ARGS((struct tac_list *list, struct tac_list_node *node));
++extern void tac_list_addtail TAC_ARGS((struct tac_list *list, struct tac_list_node *node));
++extern struct tac_list_node *tac_list_node_next TAC_ARGS((struct tac_list_node *node));
++extern struct tac_list_node *tac_list_node_prev TAC_ARGS((struct tac_list_node *node));
++extern void tac_list_node_remove TAC_ARGS((struct tac_list_node *node));
++
++
++#endif /* UTILS_H */