tac_plus: Cleanup the patch.
authorJan Kratochvil <jan@jankratochvil.net>
Sun, 5 Sep 2021 16:13:13 +0000 (18:13 +0200)
committerJan Kratochvil <jan@jankratochvil.net>
Sun, 5 Sep 2021 16:13:13 +0000 (18:13 +0200)
project/tac_plus/Index.pm
project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff [new file with mode: 0644]
project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz [deleted file]

index 5d0391b..ca1de5f 100644 (file)
@@ -32,7 +32,7 @@ our @ListItem=(
                "platform"=>"unixuser",
                "priority"=>270,
                "icon"=>"cisco-icon.jpeg",
-               "download-GTS rel.4 diff for TACACS+ v4.0.3"=>"tac_plus-F4.0.3.alpha.8.gts4.diff.gz",
+               "download-GTS rel.4 diff for TACACS+ v4.0.3"=>"tac_plus-F4.0.3.alpha.8.gts4.diff",
                "download-TACACS+ v4.0.3, Devrim Seral rel.8"=>"http://www.gazi.edu.tr/tacacs/index.php?page=download",
                "cvs"=>"tac_plus",
                "link-Freshmeat"=>sub {
diff --git a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff b/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff
new file mode 100644 (file)
index 0000000..19303bc
--- /dev/null
@@ -0,0 +1,15697 @@
+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, &param /* 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, &param /* 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; &regdummy = 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 != &regdummy)
+               *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 */
diff --git a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz b/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz
deleted file mode 100644 (file)
index a6b722c..0000000
Binary files a/project/tac_plus/tac_plus-F4.0.3.alpha.8.gts4.diff.gz and /dev/null differ