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://" + 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 - - 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 + * We don't use 'typedef' to not to yet require 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 /struct passwd and /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 +#include /* 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 /* 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 +#include /* 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 /* 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 +#include /* 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 /* 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 +# +# $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 +# +# $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 </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 + +#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 +#include #include -#include "regexp.h" +#include + +#ifndef WITH_INCLUDED_REGEX +#ifdef HAVE_REGEX_H +#include +#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)); + /* := * - := | + := | := | - accounting file = + accounting file = | default authorization = permit | - key = + key = | + authorization = ( first | recursive ) := default authentication = file #if defined(DB) - | db ) + | db #endif - := permit | deny + := permit | deny := := := user = { - [ default service = [ permit | deny ] ] + [ ] * * } + := host = { + [ ] + * + * + } + + := group = { + [ ] + * + * + } + + := default service = ( permit | deny | default ) + := file | skey | cleartext | @@ -80,23 +129,66 @@ global = cleartext | msg = before authorization = | - after authorization = + after authorization = | + + + := key = | + + + := enlist = | + key = | + + + := member = | + enlist = | + | + + + := { + * + } := | := cmd = { - * + * } - := + := | + + + := { + * + } - := service = ( exec | arap | slip | ppp protocol = { + := service = ( exec | arap | slip | ppp protocol = ) { +# first matching is the FINAL one, no further graph scanning occurs! [ default attribute = permit ] - * + * + } + + := [ optional ] = | + + + := { + * } - := [ optional ] = + := when = +# to avoid ambiguous precence by forbid of "or" & "and" without parentheses: + := | + not | + '(' ')' | + '(' ')' + + := | + or + + := | + and + + := ( user | host | group ) */ 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 /* ANSI C, variable length args */ static void parse_error(char *fmt,...) -#else + +#else /* __STDC__ */ + #include /* 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 = is obsolete. Use login = des \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); } -/* := */ +/* := */ + +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 : ""); + return (VSFR_CONTINUE); +} +static int circularity_check TAC_ARGS((void)); - /* if not a member of any groups, go on to next user */ - if (!groupname) - break; +static int +circularity_check() +{ + ENTITY *entity; + ENTITY **users_base = (ENTITY **) hash_get_entries(usertable); + ENTITY **users; - group = (USER *) hash_lookup(grouptable, groupname); - if (!group) { - report(LOG_ERR, "%s=%s, group %s does not exist", - (entry->flags & FLAG_ISUSER) ? "user" : "group", - entry->name, groupname); - free(users); - free(groups); - return (1); - } - if (group->flags & FLAG_SEEN) { - report(LOG_ERR, "recursively defined groups"); + /* users */ + for (users = users_base; *users; users++) { + entity = *users; - /* print all seen "groups" */ - for (q = groups; *q; q++) { - group = *q; - if (group->flags & FLAG_SEEN) - report(LOG_ERR, "%s", group->name); - } - free(users); - free(groups); - return (1); - } - group->flags |= FLAG_SEEN; /* mark group as seen */ - entry = group; - } + if (debug & DEBUG_PARSE_FLAG) + report(LOG_DEBUG, "circularity_check: user=%s", entity->name); + + circularity_check_failed = 0; + value_scan_forward_seen_hook = circularity_check_fail; + value_scan_entity(entity, TAC_PLUS_RECURSE, + (value_scan_func_t) circularity_check_func, NULL /* func_data-unused */); + value_scan_forward_seen_hook = NULL; + if (circularity_check_failed) + break; } - free(users); - free(groups); - return (0); + free(users_base); + return (circularity_check_failed); } @@ -1525,147 +2152,85 @@ circularity_check() Returns void * because it can return a string or a node pointer (should really return a union pointer). */ -static VALUE -cfg_get_value(name, isuser, attr, recurse) -char *name; -int isuser, attr, recurse; -{ - USER *user, *group; - VALUE value; - value.pval = NULL; +static VALUE cfg_get_value_VALUE; /* private */ - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_value: name=%s isuser=%d attr=%s rec=%d", - name, isuser, codestring(attr), recurse); - - /* find the user/group entry */ - - user = (USER *) hash_lookup(isuser ? usertable : grouptable, name); - - if (!user) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name); - return (value); - } +static enum value_scan_func_result cfg_get_value_func TAC_ARGS((ENTITY *entity, int *attrp)); +static enum value_scan_func_result +cfg_get_value_func(entity,attrp /* func_data */) +ENTITY *entity; +int *attrp; +{ /* found the entry. Lookup value from attr=value */ - value = get_value(user, attr); + cfg_get_value_VALUE = get_value(entity, *attrp); + if (cfg_get_value_VALUE.pval) + return (VSFR_FOUND); - if (value.pval || !recurse) { - return (value); - } - /* no value. Check containing group */ - if (user->member) - group = (USER *) hash_lookup(grouptable, user->member); - else - group = NULL; + return (VSFR_CONTINUE); +} - while (group) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_value: recurse group = %s", - group->name); +static VALUE cfg_get_value TAC_ARGS((int type, const char *name, int attr, int recurse)); - value = get_value(group, attr); +static VALUE +cfg_get_value(type, name, attr, recurse) +int type; +const char *name; +int attr, recurse; +{ + if (debug & DEBUG_CONFIG_FLAG) + report(LOG_DEBUG, "cfg_get_value: type=%s name=%s attr=%s recurse=%d", + entity_type_to_string(type), name, codestring(attr), recurse); - if (value.pval) { - return (value); - } - /* still nothing. Check containing group and so on */ + cfg_get_value_VALUE.pval = NULL; + value_scan(type, name, recurse, + (value_scan_func_t) cfg_get_value_func, &attr /* func_data */); + return (cfg_get_value_VALUE); +} - if (group->member) - group = (USER *) hash_lookup(grouptable, group->member); - else - group = NULL; - } - /* no value for this user or her containing groups */ - value.pval = NULL; - return (value); -} +/* Wrappers for cfg_get_value: + */ +int cfg_get_intvalue TAC_ARGS((int type, const char *name, int attr, int recurse)); -/* Wrappers for cfg_get_value */ int -cfg_get_intvalue(name, isuser, attr, recurse) -char *name; -int isuser, attr, recurse; +cfg_get_intvalue(type, name, attr, recurse) +int type; +const char *name; +int attr, recurse; { - int val = cfg_get_value(name, isuser, attr, recurse).intval; + int val = cfg_get_value(type, name, attr, recurse).intval; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val); return(val); } -char * -cfg_get_pvalue(name, isuser, attr, recurse) -char *name; -int isuser, attr, recurse; -{ - char *p = cfg_get_value(name, isuser, attr, recurse).pval; - - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_pvalue: returns %s", - p ? p : "NULL"); - return(p); -} - -/* For getting host values */ -static VALUE -cfg_get_hvalue(name, attr) -char *name; -int attr; -{ - HOST *host; - VALUE value; - - value.pval = NULL; - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_hvalue: name=%s attr=%s ", - name, codestring(attr)); - - /* find the host entry in hash table */ +const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse)); - host = (HOST *) hash_lookup( hosttable, name); - - if (!host) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_hvalue: no host named %s", name); - return (value); - } - - /* found the entry. Lookup value from attr=value */ - value = get_hvalue(host, attr); - - if (value.pval) { - return (value); - } - /* No any value for this host */ - value.pval = NULL; - return (value); -} - -/* Wrappers for cfg_get_hvalue */ -char * -cfg_get_phvalue(name, attr) -char *name; -int attr; +const char * +cfg_get_pvalue(type, name, attr, recurse) +int type; +const char *name; +int attr, recurse; { - char *p = cfg_get_hvalue(name, attr).pval; + char *p = cfg_get_value(type, name, attr, recurse).pval; if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_phvalue: returns %s", + report(LOG_DEBUG, "cfg_get_pvalue: returns %s", p ? p : "NULL"); return(p); } -/* - Read the config file and do some basic sanity checking on - it. Return 1 if we find any errors. */ +/* Read the config file and do some basic sanity checking on + * it. Return 1 if we find any errors. + */ +int cfg_read_config TAC_ARGS((const char *cfile)); +int cfg_read_config(cfile) -char *cfile; +const char *cfile; { sym_line = 1; @@ -1679,7 +2244,17 @@ char *cfile; return (1); } - if (circularity_check()) { + if (0 + || enlist_entity_connect() + || expr_sink_commit() + /* circularity is allowed in the new fully-recursive algorithm */ + || (!algorithm_recursive && circularity_check()) + ) { + fclose(cf); + return (1); + } + if (!WHEN_EXPR_ROOT_EMPTY() || when_expr_dungeon) { + report(LOG_ERR, "Some 'when' expression found still pushed on stack"); fclose(cf); return (1); } @@ -1688,346 +2263,443 @@ char *cfile; return (0); } -/* return 1 if user exists, 0 otherwise */ +/* return 1 if user exists, 0 otherwise + */ +int cfg_user_exists TAC_ARGS((const char *username)); + int cfg_user_exists(username) -char *username; +const char *username; { - USER *user = (USER *) hash_lookup(usertable, username); - - return (user != NULL); + return (NULL != hash_lookup(usertable, username)); } /* return expiry string of user. If none, try groups she is a member - on, and so on, recursively if recurse is non-zero */ -char * -cfg_get_expires(username, recurse) -char *username; + * on, and so on, recursively if recurse is non-zero + */ +const char *cfg_get_expires TAC_ARGS((const char *username, int recurse)); +const char * +cfg_get_expires(username, recurse) +const char *username; +int recurse; { - return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse)); + return (cfg_get_pvalue(S_user, username, S_expires, recurse)); } /* return time string of user. If none, try groups she is a member - on, and so on, recursively if recurse is non-zero */ -char * + * on, and so on, recursively if recurse is non-zero + */ +const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse)); + +const char * cfg_get_timestamp(username, recurse) -char *username; +const char *username; +int recurse; { - return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse)); + return (cfg_get_pvalue(S_user, username, S_time, recurse)); } - /* return password string of user. If none, try groups she is a member - on, and so on, recursively if recurse is non-zero */ -char * -cfg_get_login_secret(user, recurse) -char *user; + * on, and so on, recursively if recurse is non-zero + */ +const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse)); +const char * +cfg_get_login_secret(user, recurse) +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse)); + return (cfg_get_pvalue(S_user, user, S_login, recurse)); } /* return value of the nopasswd field. If none, try groups she is a member - on, and so on, recursively if recurse is non-zero */ + * on, and so on, recursively if recurse is non-zero + */ +int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse)); + int cfg_get_user_nopasswd(user, recurse) - char *user; +const char *user; +int recurse; { - return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse)); + return (cfg_get_intvalue(S_user, user, S_nopasswd, recurse)); } /* return user's secret. If none, try groups she is a member - on, and so on, recursively if recurse is non-zero */ -char * -cfg_get_arap_secret(user, recurse) -char *user; + * on, and so on, recursively if recurse is non-zero + */ +const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse)); +const char * +cfg_get_arap_secret(user, recurse) +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse)); + return (cfg_get_pvalue(S_user, user, S_arap, recurse)); } -char * -cfg_get_chap_secret(user, recurse) -char *user; +const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse)); +const char * +cfg_get_chap_secret(user, recurse) +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse)); + return (cfg_get_pvalue(S_user, user, S_chap, recurse)); } #ifdef MSCHAP -char * -cfg_get_mschap_secret(user, recurse) -char *user; +const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse)); + +const char * +cfg_get_mschap_secret(user, recurse) +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse)); + return (cfg_get_pvalue(S_user, user, S_mschap, recurse)); } + #endif /* MSCHAP */ -char * +const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse)); + +const char * cfg_get_pap_secret(user, recurse) -char *user; +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse)); + return (cfg_get_pvalue(S_user, user, S_pap, recurse)); } -char * +const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse)); + +const char * cfg_get_opap_secret(user, recurse) -char *user; +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse)); + return (cfg_get_pvalue(S_user, user, S_opap, recurse)); } /* return the global password for the user (or the group, etc.) */ -char * -cfg_get_global_secret(user, recurse) -char *user; +const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse)); +const char * +cfg_get_global_secret(user, recurse) +const char *user; +int recurse; { - return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse)); + return (cfg_get_pvalue(S_user, user, S_global, recurse)); } #ifdef USE_PAM + /* Return a pointer to a node representing a PAM Service name */ -char * -cfg_get_pam_service(user,recurse) -char *user; +const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse)); + +const char * +cfg_get_pam_service(user, recurse) +const char *user; +int recurse; { - char *cfg_passwd; - char *p; + const char *cfg_passwd; + const char *p; -cfg_passwd = cfg_get_pap_secret(user, recurse); + cfg_passwd = cfg_get_pap_secret(user, recurse); -if (!cfg_passwd) { + if (!cfg_passwd) cfg_passwd = cfg_get_global_secret(user, recurse); -} -if (!cfg_passwd && !cfg_user_exists(user)) { + if (!cfg_passwd && !cfg_user_exists(user)) { cfg_passwd = cfg_get_authen_default(); switch (cfg_get_authen_default_method()) { + case (S_pam): if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd); return(cfg_passwd); break; + default: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I havent find any PAM Service!!"); return(NULL);/* Haven't any PAM Service!! */ } -} + } -p=tac_find_substring("pam ", cfg_passwd); + p = tac_find_substring("pam ", cfg_passwd); -if(p) { /* We find PAM services */ + if(p) { /* We find PAM services */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I get PAM sevice:%s",p); -return (p); -} + return (p); + } -if (debug & DEBUG_AUTHOR_FLAG) + if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "No any PAM Sevice"); -return(NULL); + return(NULL); } #endif /* For PAM */ - /* Return a pointer to a node representing a given service authorization, taking care of recursion issues correctly. Protocol - is only read if the type is N_svc_ppp. svcname is only read if type + is only read if the svctype is N_svc_ppp. svcname is only read if type is N_svc. */ -NODE * -cfg_get_svc_node(username, type, protocol, svcname, recurse) -char *username; -int type; -char *protocol, *svcname; -int recurse; -{ - USER *user, *group; - NODE *svc; - - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, - "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d", - username, - cfg_nodestring(type), - protocol ? protocol : "", - svcname ? svcname : "", - recurse); - - /* find the user/group entry */ - user = (USER *) hash_lookup(usertable, username); +struct cfg_get_svc_node_param { + int svctype; + const char *protocol, *svcname; + NODE *node; + int retval; +}; - if (!user) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username); - return (NULL); - } +static enum value_scan_func_result cfg_get_svc_node_func TAC_ARGS((ENTITY *entity, struct cfg_get_svc_node_param *param)); - /* found the user entry. Find svc node */ - for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) { +static enum value_scan_func_result +cfg_get_svc_node_func(entity, param /* func_data */) +ENTITY *entity; +struct cfg_get_svc_node_param *param; +{ + NODE *svc; + enum eval_result svc_default; - if (svc->type != type) + for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) { + if (svc->type != param->svctype) continue; - - if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) { + if (param->svctype == N_svc_ppp && param->protocol && !STREQ(svc->value1, param->protocol)) continue; - } - - if (type == N_svc && !STREQ(svc->value1, svcname)) { + if (param->svctype == N_svc && param->svcname && !STREQ(svc->value1, param->svcname )) + continue; + if (expr_eval(svc->when) != ER_TRUE) /* expensive */ continue; - } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: found %s proto=%s svcname=%s", - cfg_nodestring(type), - protocol ? protocol : "", - svcname ? svcname : ""); + cfg_nodestring(param->svctype), + param->protocol ? param->protocol : "", + param->svcname ? param->svcname : ""); - return(svc); + param->node = svc; + param->retval = 1; + return (VSFR_FOUND); } - if (!recurse) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_svc_node: returns NULL"); - return (NULL); - } + /* look at 'default service' settings */ + svc_default = entity_svc_default(entity); + switch (svc_default) { - /* no matching node. Check containing group */ - if (user->member) - group = (USER *) hash_lookup(grouptable, user->member); - else - group = NULL; + case ER_TRUE: + case ER_FALSE: + if (debug & DEBUG_AUTHOR_FLAG) + report(LOG_DEBUG, + "cfg_get_svc_node: svc=%s protocol=%s svcname=%s forced %s by default service", + cfg_nodestring(param->svctype), + param->protocol ? param->protocol : "", + param->svcname ? param->svcname : "", + (svc_default == ER_TRUE ? "permit" : "deny")); - while (group) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s", - group->name); + param->retval = (svc_default == ER_TRUE); + return (VSFR_FOUND); - for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) { + default: /* shouldn't happen */ + case ER_UNKNOWN: + return (VSFR_CONTINUE); + } + /* NOTREACHED */ +} - if (svc->type != type) - continue; +int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep)); - if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) { - continue; - } +int +cfg_get_svc_node(username, svctype, protocol, svcname, recurse, nodep) +const char *username; +int svctype; +const char *protocol; +const char *svcname; +int recurse; +NODE **nodep; +{ + struct cfg_get_svc_node_param param; + enum value_scan_func_result vsfr; - if (type == N_svc && !STREQ(svc->value1, svcname)) { - continue; - } + param.svctype = svctype; + param.protocol = protocol; + param.svcname = svcname; + param.node = NULL; + param.retval = 0; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, - "cfg_get_svc_node: found %s proto=%s svcname=%s", - cfg_nodestring(type), + "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d", + username, + cfg_nodestring(svctype), protocol ? protocol : "", - svcname ? svcname : ""); - - return(svc); - } + svcname ? svcname : "", + recurse); - /* still nothing. Check containing group and so on */ + vsfr = value_scan(S_user, username, recurse, + (value_scan_func_t) cfg_get_svc_node_func, ¶m /* func_data */); + if (nodep) + *nodep = param.node; - if (group->member) - group = (USER *) hash_lookup(grouptable, group->member); - else - group = NULL; - } + if (vsfr == VSFR_FOUND) + return (param.retval); - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_svc_node: returns NULL"); - - /* no matching svc node for this user or her containing groups */ - return (NULL); + /* The service does not exist. Do the default */ + return (cfg_no_user_permitted() ? 1 : 0); } -/* Return a pointer to the node representing a set of command regexp +/* Return a pointer to the node representing a set of command tac_regexp matches for a user and command, handling recursion issues correctly */ -NODE * -cfg_get_cmd_node(name, cmdname, recurse) -char *name, *cmdname; -int recurse; +struct cfg_authorize_cmd_param { + const char *cmd; + const char *args; + enum eval_result result; +}; + +static enum value_scan_func_result cfg_authorize_cmd_func TAC_ARGS((ENTITY *entity, struct cfg_authorize_cmd_param *param)); + +static enum value_scan_func_result +cfg_authorize_cmd_func(entity, param /* func_data */) +ENTITY *entity; +struct cfg_authorize_cmd_param *param; { - USER *user, *group; NODE *svc; - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: name=%s cmdname=%s rec=%d", - name, cmdname, recurse); + for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) { + NODE *node; - /* find the user/group entry */ - user = (USER *) hash_lookup(usertable, name); + if (svc->type != N_svc_cmd) + continue; + if (!STREQ(svc->value, param->cmd)) + continue; + if (expr_eval(svc->when) != ER_TRUE) /* expensive */ + continue; - if (!user) { if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name); - return (NULL); + report(LOG_DEBUG, "cfg_authorize_cmd: found cmd %s %s node", + param->cmd, cfg_nodestring(svc->type)); + + /* we have 'cmd ' point, now traverse through its 'permit'/'deny' pairs: */ + + for (node = svc->value1; node; node = node->next) { + int match; + + if (expr_eval(node->when) != ER_TRUE) /* expensive */ + continue; + +#ifdef WITH_INCLUDED_REGEX + + match = tac_regexec((tac_regexp *) node->value1, param->args); + +#else /* WITH_INCLUDED_REGEX */ + + match = !regexec((const regex_t *) node->value1, param->args /* string */, + 0 /* nmatch */, NULL /* pmatch */, 0 /* eflags */); + +#endif /* WITH_INCLUDED_REGEX */ + + if (debug & DEBUG_AUTHOR_FLAG) { + report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch", + node->line, param->cmd, + node->type == N_permit ? "permit" : "deny", + (const char *) node->value, param->args, (match ? "" : "no ")); } - /* found the user entry. Find svc node */ - svc = (NODE *) get_value(user, S_svc).pval; - while (svc) { - if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s %s node", - cmdname, cfg_nodestring(svc->type)); - return (svc); + if (!match) + continue; + + switch (node->type) { + case N_permit: + if (debug & DEBUG_AUTHOR_FLAG) { + report(LOG_DEBUG, "%s %s permitted by line %d", + param->cmd, param->args, node->line); } - svc = svc->next; + param->result = ER_TRUE; + return (VSFR_FOUND); + break; + case N_deny: + if (debug & DEBUG_AUTHOR_FLAG) { + report(LOG_DEBUG, "%s %s denied by line %d", + param->cmd, param->args, node->line); + } + param->result = ER_FALSE; + return (VSFR_FOUND); + break; + default: + report(LOG_ERR, "INTERNAL: illegal configuration node: %s: %s %s", + session.peer, param->cmd, param->args); + param->result = ER_UNKNOWN; /* error */ + return (VSFR_FOUND); + } + } + if (!algorithm_recursive) { /* compatibility mode: */ + if (debug & DEBUG_AUTHOR_FLAG) + report(LOG_DEBUG, "cmd %s exists, but no args match, denied (as no 'authorization = recursive' found)", + param->cmd); + param->result = ER_FALSE; /* emulate last "deny .*" */ + return (VSFR_FOUND); } - - if (!recurse) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL"); - return (NULL); } - /* no matching node. Check containing group */ - if (user->member) - group = (USER *) hash_lookup(grouptable, user->member); - else - group = NULL; - while (group) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: recurse group = %s", - group->name); + /* look at 'default service' settings */ + param->result = entity_svc_default(entity); + switch (param->result) { - svc = get_value(group, S_svc).pval; + case ER_TRUE: + if (debug & DEBUG_AUTHOR_FLAG) + report(LOG_DEBUG, "cmd %s does not exist, permitted by default", param->cmd); + return (VSFR_FOUND); - while (svc) { - if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s node %s", - cmdname, cfg_nodestring(svc->type)); - return (svc); - } - svc = svc->next; - } + case ER_FALSE: - /* still nothing. Check containing group and so on */ + if (debug & DEBUG_AUTHOR_FLAG) + report(LOG_DEBUG, "cmd %s does not exist, denied by default", param->cmd); + return (VSFR_FOUND); - if (group->member) - group = (USER *) hash_lookup(grouptable, group->member); - else - group = NULL; + default: /* shouldn't happen */ + case ER_UNKNOWN: + return (VSFR_CONTINUE); } + /* NOTREACHED */ +} + +enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args)); + +enum eval_result +cfg_authorize_cmd(username, cmd, args) +const char *username; +const char *cmd; +const char *args; +{ + struct cfg_authorize_cmd_param param; + + param.cmd = cmd; + param.args = args; + param.result = ER_UNKNOWN; /* error */ if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL"); + report(LOG_DEBUG, "cfg_authorize_cmd: name=%s cmdname=%s args=%s", + username, cmd, args); - /* no matching cmd node for this user or her containing groups */ - return (NULL); + value_scan(S_user, username, TAC_PLUS_RECURSE, + (value_scan_func_t) cfg_authorize_cmd_func, ¶m /* func_data */); + + if (param.result != ER_UNKNOWN) + return (param.result); + + /* The command does not exist. Do the default */ + return (cfg_no_user_permitted() ? ER_TRUE : ER_FALSE); } /* Return an array of character strings representing configured AV @@ -2039,6 +2711,8 @@ int recurse; * Lastly, indicate what default permission was configured by setting * denyp */ +char **cfg_get_svc_attrs TAC_ARGS((NODE *svcnode, int *denyp)); + char ** cfg_get_svc_attrs(svcnode, denyp) NODE *svcnode; @@ -2063,9 +2737,14 @@ int *denyp; i = 0; for (node = svcnode->value; node; node = node->next) { - char *arg = tac_strdup(node->value); - char *p = index(arg, '='); + char *arg; + char *p; + if (expr_eval(node->when) != ER_TRUE) /* expensive */ + continue; /* ignore this node */ + + arg = tac_strdup(node->value); + p = index(arg, '='); if (p && node->type == N_optarg) *p = '*'; args[i++] = arg; @@ -2075,23 +2754,28 @@ int *denyp; } -int -cfg_user_svc_default_is_permit(user) -char *user; +static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity)); +static enum eval_result +entity_svc_default(entity) +ENTITY *entity; { - int permit = cfg_get_intvalue(user, TAC_IS_USER, S_svc_dflt, - TAC_PLUS_RECURSE); - - switch (permit) { - default: /* default is deny */ - case S_deny: - return (0); + switch (entity->svc_dflt) { case S_permit: - return (1); + return (ER_TRUE); + case S_deny: + return (ER_FALSE); + case S_default: + case 0: /* not specified */ + return (ER_UNKNOWN); + default: + report(LOG_ERR, "INTERNAL: invalid entity svc_dflt (%d)", entity->svc_dflt); + return (ER_UNKNOWN); } } +int cfg_no_user_permitted TAC_ARGS((void)); + int cfg_no_user_permitted() { @@ -2101,12 +2785,16 @@ cfg_no_user_permitted() } -char * +const char *cfg_get_authen_default TAC_ARGS((void)); + +const char * cfg_get_authen_default() { return (authen_default); } +int cfg_get_authen_default_method TAC_ARGS((void)); + /* For describe authentication method(pam,file,db..etc) */ int cfg_get_authen_default_method() @@ -2115,92 +2803,123 @@ cfg_get_authen_default_method() } -/* Return 1 if this user has any ppp services configured. Used for - authorizing ppp/lcp requests */ -int -cfg_ppp_is_configured(username, recurse) - char *username; - int recurse; +/* Host entity management: + */ + +const char *cfg_get_host_key TAC_ARGS((const char *host)); + +/* For getting host key */ +const char * +cfg_get_host_key(host) +const char *host; { - USER *user, *group; - NODE *svc; + return (cfg_get_pvalue(S_host, host, S_key, algorithm_recursive /* recurse */)); +} - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d", - username, recurse); +static ENTITY *force_belong_entity TAC_ARGS((int type, const char *name)); + +static ENTITY * +force_belong_entity(type, name) +int type; +const char *name; +{ + ENTITY *entity = entity_lookup(type, name); - /* find the user/group entry */ - user = (USER *) hash_lookup(usertable, username); + if (entity) + eval_force_belong_entity(entity); - if (!user) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s", - username); - return (0); - } + return (entity); +} - /* found the user entry. Find svc node */ - for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) { +/* assumed existing initialized "session.peer*" */ - if (svc->type != N_svc_ppp) - continue; +static ENTITY *request_peer_addr; +static ENTITY *request_peer; +static ENTITY *request_DEFAULT_group; - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node", - svc->value1); +static void enlist_request_peer TAC_ARGS((const char *hostname, ENTITY **entityp)); - return(1); - } +static void +enlist_request_peer(hostname, entityp) +const char *hostname; +ENTITY **entityp; +{ + *entityp = NULL; + if (!hostname) + return; - if (!recurse) { - if (debug & DEBUG_CONFIG_FLAG) - report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0"); - return (0); - } + *entityp = force_belong_entity(S_host, hostname); + if (*entityp && request_DEFAULT_group) + virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, *entityp /* child */); +} - /* no matching node. Check containing group */ - if (user->member) - group = (USER *) hash_lookup(grouptable, user->member); - else - group = NULL; +/* Try to build the following scenery: + * + * host [=ER_TRUE] + * | + * +-- group + * + * host [=ER_TRUE] + * | + * +-- group + */ - 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 username> | user ) [=ER_TRUE] + * | + * +-- host [=ER_TRUE] + * | | + * | +- group + * | + * +-- host [=ER_TRUE] + * | | + * | +-- group + * | + * +-- group + */ - /* 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 /* 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 +#include +#if STDC_HEADERS +#include +#include +#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 +#endif +#ifdef HAVE_REGEX_H +#include +#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 ], +[ 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 ], +[ 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 ], +[ 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 + #include "tac_plus.h" + +#ifdef DB + +#include +#include +#include + +#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 -#include "mysql.h" +#include +#include + +#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 -#include "libpq-fe.h" +#include +#include +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#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 +#include + +#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 /* 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 /* 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 +#include + +#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 +#include /* 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 /* 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 +#include +#include +#include + #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 +#endif +#include +#include +#include +#include + + +#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 #include +#include #include #include #include -#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif +#ifdef HAVE_SYS_SYSLOG_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#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 \n"); fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p ]\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 /* 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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#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 + #include "md4.h" /* #include "master.h" #include */ + 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 +#include /* for ntohl() */ +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#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 /* 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; + /* */ + /* */ + /* */ + /* */ +}; + +#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 + + /* */ + /* */ +}; + +#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; + + /* */ + /* */ +}; + +#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 */ + + /* */ + /* */ + /* */ + /* */ + /* */ +}; + +#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; + + /* */ + /* */ + /* */ + /* */ +}; + +#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 */ + /* */ + /* */ + /* */ + /* 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 +#include +#include +#ifdef HAVE_UNISTD_H #include -#include "signal.h" +#endif +#include +#ifdef HAVE_SYSLOG_H +#include +#endif +#ifdef HAVE_SYS_SYSLOG_H +#include +#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 #include #include +#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 +#endif +#include +#include +#include +#include +#include +#include #ifdef SHADOW_PASSWORDS +#ifdef HAVE_SHADOW_H #include #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 -#ifdef AIX +#include #include -#else #include +#include +#ifdef HAVE_FCNTL_H +#include #endif - -#ifdef __STDC__ -#include /* ANSI C, variable length args */ -#else -#include /* has 'vararg' definitions */ +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include #endif +#ifdef HAVE_SYS_SYSLOG_H +#include +#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 /* ANSI C, variable length args */ void -report(int priority, char *fmt,...) -#else +report(int priority, const char *fmt,...) + +#else /* __STDC__ */ + +#include /* 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 +#include /* for u_* */ +#ifdef HAVE_SYSLOG_H +#include /* for LOG_* level values */ +#endif +#ifdef HAVE_SYS_SYSLOG_H +#include /* 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 + +#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 + +#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 - 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 * 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 #include #include #include -#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; imsg_style) { - for(i=0;imsg_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 */ +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 -#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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef SYSLOG_IN_SYS -#include -#else -#include -#endif - -#include - -#include - #ifdef SYSV +#ifdef HAVE_FCNTL_H #include +#endif #define index strchr -#else /* ! SYSV */ -#include #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 +#ifdef HAVE_STRINGS_H +#include +#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()). +/* 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 #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; - /* */ - /* */ - /* */ - /* */ -}; - -#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 - - /* */ - /* */ -}; - -#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; - - /* */ - /* */ -}; - -#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 */ - - /* */ - /* */ - /* */ - /* */ - /* */ -}; - -#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; - - /* */ - /* */ - /* */ - /* */ -}; - -#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 */ - /* */ - /* */ - /* */ - /* 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 +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 +- following changes supported by GTS (www.gts.com), cooperation by: + Pavel Ruzicka + Michael Macek +- 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 +- 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 +- 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 +- I am add PAM patch from Max Liccardo +- 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 +- 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 +- I moved to RedHat 6.0 =) +- changes of the package internals! + +* Mon Oct 18 1999 D'mon +- massive remake to suit RedHat 5.2 standard +- patch for RedHat 5.2 + +* Wed Aug 4 1999 Erhan Bilgili +- fixes for the RPM_OPT_FLAGS +- change the buildroot to /var/tmp/tacacsd + +* Wed Aug 4 1999 Devrim SERAL +- 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 -#include "regexp.h" -#include "regmagic.h" +#include +#include /* malloc() can be found in OR */ +#ifdef HAVE_MALLOC_H +#include +#endif + +#include "tac_regexp.h" +#include "tac_regmagic.h" +#include "report.h" /* for regerror() */ + /* * The "internal use only" fields in regexp.h are present to pass info from @@ -149,7 +163,7 @@ #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif -#define FAIL(m) { regerror(m); return(NULL); } +#define FAIL(m) { tac_regerror(m); return(NULL); } #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') #define META "^$.[()|?+*\\" @@ -164,7 +178,7 @@ /* * Global work variables for regcomp(). */ -static char *regparse; /* Input-scan pointer. */ +static const char *regparse; /* Input-scan pointer. */ static int regnpar; /* () count. */ static char regdummy; static char *regcode; /* Code-emit pointer; ®dummy = don't. */ @@ -173,23 +187,24 @@ static long regsize; /* Code size. */ /* * Forward declarations for regcomp()'s friends. */ -#ifndef STATIC -#define STATIC static -#endif -STATIC char *reg(); -STATIC char *regbranch(); -STATIC char *regpiece(); -STATIC char *regatom(); -STATIC char *regnode(); -STATIC char *regnext(); -STATIC void regc(); -STATIC void reginsert(); -STATIC void regtail(); -STATIC void regoptail(); -#ifdef STRCSPN -STATIC int strcspn(); +static char *reg TAC_ARGS((int paren, int *flagp)); +static char *regbranch TAC_ARGS((int *flagp)); +static char *regpiece TAC_ARGS((int *flagp)); +static char *regatom TAC_ARGS((int *flagp)); +static char *regnode TAC_ARGS((int op)); +static void regc TAC_ARGS((int b)); +static void reginsert TAC_ARGS((int op, char *opnd)); +static void regtail TAC_ARGS((char *p, char *val)); +static void regoptail TAC_ARGS((char *p, char *val)); +static int regtry TAC_ARGS((tac_regexp *prog, const char *string)); +static int regmatch TAC_ARGS((char *prog)); +static int regrepeat TAC_ARGS((char *p)); +static char *regnext TAC_ARGS((register char *p)); +#ifndef HAVE_STRCSPN +static int strcspn TAC_ARGS((char *s1, char *s2)); #endif + /* - regcomp - compile a regular expression into internal code * @@ -205,16 +220,18 @@ STATIC int strcspn(); * Beware that the optimization-preparation code in here knows about some * of the structure of the compiled regexp. */ -regexp * -regcomp(exp) -char *exp; + +tac_regexp *tac_regcomp TAC_ARGS((const char *exp)); + +tac_regexp * +tac_regcomp(exp) +const char *exp; { - register regexp *r; + register tac_regexp *r; register char *scan; register char *longest; register int len; int flags; - extern char *malloc(); if (exp == NULL) FAIL("NULL argument"); @@ -233,7 +250,7 @@ char *exp; FAIL("regexp too big"); /* Allocate space. */ - r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); + r = (tac_regexp *)malloc(sizeof(tac_regexp) + (unsigned)regsize); if (r == NULL) FAIL("out of space"); @@ -272,7 +289,7 @@ char *exp; longest = NULL; len = 0; for (; scan != NULL; scan = regnext(scan)) - if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= (unsigned)len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); } @@ -293,6 +310,9 @@ char *exp; * is a trifle forced, but the need to tie the tails of the branches to what * follows makes it hard to avoid. */ + +static char *reg TAC_ARGS((int paren, int *flagp)); + static char * reg(paren, flagp) int paren; /* Parenthesized? */ @@ -301,7 +321,7 @@ int *flagp; register char *ret; register char *br; register char *ender; - register int parno; + register int parno = 0 /* GCC paranoia */; int flags; *flagp = HASWIDTH; /* Tentatively. */ @@ -365,6 +385,9 @@ int *flagp; * * Implements the concatenation operator. */ + +static char *regbranch TAC_ARGS((int *flagp)); + static char * regbranch(flagp) int *flagp; @@ -404,6 +427,9 @@ int *flagp; * It might seem that this node could be dispensed with entirely, but the * endmarker role is not redundant. */ + +static char *regpiece TAC_ARGS((int *flagp)); + static char * regpiece(flagp) int *flagp; @@ -468,6 +494,9 @@ int *flagp; * faster to run. Backslashed characters are exceptions, each becoming a * separate node; the code is simpler that way and it's not worth fixing. */ + +static char *regatom TAC_ARGS((int *flagp)); + static char * regatom(flagp) int *flagp; @@ -577,9 +606,12 @@ int *flagp; /* - regnode - emit a node */ + +static char *regnode TAC_ARGS((int op)); + static char * /* Location. */ regnode(op) -char op; +int op; /* promoted "char" type */ { register char *ret; register char *ptr; @@ -602,9 +634,12 @@ char op; /* - regc - emit (if appropriate) a byte of code */ + +static void regc TAC_ARGS((int b)); + static void regc(b) -char b; +int b; /* promoted "char" type */ { if (regcode != ®dummy) *regcode++ = b; @@ -617,9 +652,12 @@ char b; * * Means relocating the operand. */ + +static void reginsert TAC_ARGS((int op, char *opnd)); + static void reginsert(op, opnd) -char op; +int op; /* promoted "char" type */ char *opnd; { register char *src; @@ -646,6 +684,9 @@ char *opnd; /* - regtail - set the next-pointer at the end of a node chain */ + +static void regtail TAC_ARGS((char *p, char *val)); + static void regtail(p, val) char *p; @@ -678,6 +719,9 @@ char *val; /* - regoptail - regtail on operand of first argument; nop if operandless */ + +static void regoptail TAC_ARGS((char *p, char *val)); + static void regoptail(p, val) char *p; @@ -696,44 +740,40 @@ char *val; /* * Global work variables for regexec(). */ -static char *reginput; /* String-input pointer. */ -static char *regbol; /* Beginning of input, for ^ check. */ -static char **regstartp; /* Pointer to startp array. */ -static char **regendp; /* Ditto for endp. */ +static const char *reginput; /* String-input pointer. */ +static const char *regbol; /* Beginning of input, for ^ check. */ +static const char **regstartp; /* Pointer to startp array. */ +static const char **regendp; /* Ditto for endp. */ -/* - * Forwards. - */ -STATIC int regtry(); -STATIC int regmatch(); -STATIC int regrepeat(); #ifdef DEBUG int regnarrate = 0; -void regdump(); -STATIC char *regprop(); +static char *regprop TAC_ARGS((char *op)); +static void regdump TAC_ARGS((tac_regexp *r)); #endif /* - regexec - match a regexp against a string */ + +int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string)); + int -regexec(prog, string) -register regexp *prog; -register char *string; +tac_regexec(prog, string) +register tac_regexp *prog; +register const char *string; { - register char *s; - extern char *strchr(); + register const char *s; /* Be paranoid... */ if (prog == NULL || string == NULL) { - regerror("NULL parameter"); + tac_regerror("NULL parameter"); return(0); } /* Check validity of program. */ if (UCHARAT(prog->program) != MAGIC) { - regerror("corrupted program"); + tac_regerror("corrupted program"); return(0); } @@ -779,14 +819,17 @@ register char *string; /* - regtry - try match at specific point */ + +static int regtry TAC_ARGS((tac_regexp *prog, const char *string)); + static int /* 0 failure, 1 success */ regtry(prog, string) -regexp *prog; -char *string; +tac_regexp *prog; +const char *string; { register int i; - register char **sp; - register char **ep; + register const char **sp; + register const char **ep; reginput = string; regstartp = prog->startp; @@ -816,13 +859,15 @@ char *string; * need to know whether the rest of the match failed) by a loop instead of * by recursion. */ + +static int regmatch TAC_ARGS((char *prog)); + static int /* 0 failure, 1 success */ regmatch(prog) char *prog; { register char *scan; /* Current node. */ char *next; /* Next node. */ - extern char *strchr(); scan = prog; #ifdef DEBUG @@ -888,7 +933,7 @@ char *prog; case OPEN+8: case OPEN+9: { register int no; - register char *save; + register const char *save; no = OP(scan) - OPEN; save = reginput; @@ -916,7 +961,7 @@ char *prog; case CLOSE+8: case CLOSE+9: { register int no; - register char *save; + register const char *save; no = OP(scan) - CLOSE; save = reginput; @@ -935,7 +980,7 @@ char *prog; } break; case BRANCH: { - register char *save; + register const char *save; if (OP(next) != BRANCH) /* No choice. */ next = OPERAND(scan); /* Avoid recursion. */ @@ -956,7 +1001,7 @@ char *prog; case PLUS: { register char nextch; register int no; - register char *save; + register const char *save; register int min; /* @@ -985,7 +1030,7 @@ char *prog; return(1); /* Success! */ break; default: - regerror("memory corruption"); + tac_regerror("memory corruption"); return(0); break; } @@ -997,21 +1042,23 @@ char *prog; * We get here only if there's trouble -- normally "case END" is * the terminating point. */ - regerror("corrupted pointers"); + tac_regerror("corrupted pointers"); return(0); } /* - regrepeat - repeatedly match something simple, report how many */ + +static int regrepeat TAC_ARGS((char *p)); + static int regrepeat(p) char *p; { register int count = 0; - register char *scan; + register const char *scan; register char *opnd; - extern char *strchr(); scan = reginput; opnd = OPERAND(p); @@ -1039,7 +1086,7 @@ char *p; } break; default: /* Oh dear. Called inappropriately. */ - regerror("internal foulup"); + tac_regerror("internal foulup"); count = 0; /* Best compromise. */ break; } @@ -1051,6 +1098,9 @@ char *p; /* - regnext - dig the "next" pointer out of a node */ + +static char *regnext TAC_ARGS((register char *p)); + static char * regnext(p) register char *p; @@ -1072,19 +1122,19 @@ register char *p; #ifdef DEBUG -STATIC char *regprop(); - /* - regdump - dump a regexp onto stdout in vaguely comprehensible form */ -void + +static void regdump TAC_ARGS((tac_regexp *r)); + +static void regdump(r) -regexp *r; +tac_regexp *r; { register char *s; register char op = EXACTLY; /* Arbitrary non-END op. */ register char *next; - extern char *strchr(); s = r->program + 1; @@ -1121,6 +1171,9 @@ regexp *r; /* - regprop - printable representation of opcode */ + +static char *regprop TAC_ARGS((char *op)); + static char * regprop(op) char *op; @@ -1192,7 +1245,7 @@ char *op; p = "PLUS"; break; default: - regerror("corrupted opcode"); + tac_regerror("corrupted opcode"); break; } if (p != NULL) @@ -1207,12 +1260,14 @@ char *op; * about it; at least one public-domain implementation of those (highly * useful) string routines has been published on Usenet. */ -#ifdef STRCSPN +#ifndef HAVE_STRCSPN /* * strcspn - find length of initial segment of s1 consisting entirely * of characters not from s2 */ +static int strcspn TAC_ARGS((char *s1, char *s2)); + static int strcspn(s1, s2) char *s1; @@ -1231,4 +1286,10 @@ char *s2; } return(count); } -#endif +#endif /* HAVE_STRCSPN */ + +#else /* WITH_INCLUDED_REGEX */ + +TAC_SOURCEFILE_EMPTY + +#endif /* WITH_INCLUDED_REGEX */ diff --git a/tac_regexp.h b/tac_regexp.h index b23d97e..7a1a884 100644 --- a/tac_regexp.h +++ b/tac_regexp.h @@ -1,3 +1,10 @@ +#ifndef TAC_REGEXP_H +#define TAC_REGEXP_H 1 + +#include "tac_plus.h" + +#ifdef WITH_INCLUDED_REGEX + /* Copyright (c) 1995-1998 by Cisco systems, Inc. @@ -17,6 +24,7 @@ FITNESS FOR A PARTICULAR PURPOSE. */ + /* * Definitions etc. for regexp(3) routines. * @@ -24,17 +32,21 @@ * not the System V one. */ #define NSUBEXP 10 -typedef struct regexp { - char *startp[NSUBEXP]; - char *endp[NSUBEXP]; +typedef struct tac_regexp { + const char *startp[NSUBEXP]; + const char *endp[NSUBEXP]; char regstart; /* Internal use only. */ char reganch; /* Internal use only. */ char *regmust; /* Internal use only. */ int regmlen; /* Internal use only. */ char program[1]; /* Unwarranted chumminess with compiler. */ -} regexp; +} tac_regexp; + + +extern tac_regexp *tac_regcomp TAC_ARGS((const char *exp)); +extern int tac_regexec TAC_ARGS((register tac_regexp *prog, register const char *string)); + + +#endif /* WITH_INCLUDED_REGEX */ -extern regexp *regcomp(); -extern int regexec(); -extern void regsub(); -extern void regerror(); +#endif /* TAC_REGEXP_H */ diff --git a/tac_regmagic.h b/tac_regmagic.h index 0b18b0a..ff321df 100644 --- a/tac_regmagic.h +++ b/tac_regmagic.h @@ -1,3 +1,8 @@ +#ifndef REGMAGIC_H +#define REGMAGIC_H 1 + +#include "tac_plus.h" + /* Copyright (c) 1995-1998 by Cisco systems, Inc. @@ -17,8 +22,12 @@ FITNESS FOR A PARTICULAR PURPOSE. */ + /* * The first byte of the regexp internal "program" is actually this magic * number; the start node begins in the second byte. */ #define MAGIC 0234 + + +#endif /* REGMAGIC_H */ diff --git a/tcpwrap.c b/tcpwrap.c index ef3d3ad..744a1df 100644 --- a/tcpwrap.c +++ b/tcpwrap.c @@ -4,21 +4,41 @@ Writen by Devrim SERAL. This file protected by GNU Copyright agreement. */ + + +#include "tac_plus.h" + #ifdef TCPWRAPPER + #include -#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 +#include +#include +#include + +#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;jtm_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 -#include -#include -#include -#include +#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 +#include /* malloc() can be found in OR */ +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include /* for "struct flock" */ +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif +#ifdef HAVE_SYS_SYSLOG_H +#include +#endif -#else /* !STDLIB_MALLOC */ +#include "utils.h" +#include "report.h" +#include "main.h" -#include -#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 /* 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 */