@@ -0,0 +1,1104 @@
+diff -urw ../tacacs+-F4.0.4.15/CHANGES ./CHANGES
+--- ../tacacs+-F4.0.4.15/CHANGES 2007-12-13 20:18:38.000000000 +0100
++++ ./CHANGES 2008-04-19 13:27:50.697162145 +0200
+@@ -356,4 +356,15 @@
+ - Fix syslog facility selection - from Timo Vanoni & Josef Voggesser
+ - Add -G/foreground option
+ - Deal with missing socklen_t
+-p
++
++F4.0.4.15-k1
++ Set of changes by Gabor Kiss borrowed from patch
++ http://bakacsin.ki.iif.hu/~kissg/pd/tac_plus/tacacs+-F4.0.4.14-k5.diff.
++ - Allow multiple group membership
++ - Fix a config tree traversal bug
++ - Add new keyword 'return' to ACL-s. If NAS address matches
++ such a line check of current ACL is terminated and search for
++ another ACL is continued in higher level groups.
++ - Add new keyword 'include'. Configuration files can be nested
++ in any depth. Syntax: is: include = /absolute/path/file
++ - Fix stderr/stdin confusion with -P option.
+diff -urw ../tacacs+-F4.0.4.15/config.c ./config.c
+--- ../tacacs+-F4.0.4.15/config.c 2007-12-13 20:18:38.000000000 +0100
++++ ./config.c 2008-04-19 13:01:43.763373472 +0200
+@@ -108,8 +108,8 @@
+ static int sym_ch; /* current parse character */
+ static int sym_code; /* parser output */
+ static int sym_line = 1; /* current line number */
+-static FILE *cf = NULL; /* config file pointer */
+-static int sym_error = 0; /* a parsing error occurred */
++static FILE *cf = NULL; /* current cfg file pointer */
++static NODE *pending = NULL; /* chain of pending cfg files */
+ static int no_user_dflt = 0; /* default if user doesn't exist */
+ static char *authen_default = NULL; /* top level authentication default */
+ static char *nopasswd_str = "nopassword";
+@@ -158,7 +158,7 @@
+ # endif
+ #endif
+ char *global; /* password to use if none set */
+- char *member; /* group we are a member of */
++ NODE *member; /* groups we are a member of */
+ char *expires; /* expiration date */
+ char *arap; /* our arap secret */
+ char *pap; /* our pap secret */
+@@ -222,7 +222,7 @@
+ void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
+ static VALUE cfg_get_hvalue(char *, int);
+-static VALUE cfg_get_value(char *, int, int, int);
++static VALUE cfg_get_value(char *, int, int, int, ITER_CB *);
+ static int circularity_check(void);
+ static void free_aclstruct(ACL *);
+ static void free_attrs(NODE *);
+@@ -247,6 +247,15 @@
+ static int parse_user(void);
+ static void rch(void);
+ static void sym_get(void);
++static FILE * open_config(char *);
++static int restore_config(void);
++static void free_pending(void);
++
++typedef NODE ITERATOR;
++static ITERATOR *new_iterator(char *, int);
++static void free_iterator(ITERATOR *);
++static NODE *next_iteration(ITERATOR *);
++static int depth(ITERATOR *);
+
+ #ifdef __STDC__
+ #include <stdarg.h> /* ANSI C, variable length args */
+@@ -671,7 +680,7 @@
+ {
+ ACL *n;
+ ACL *acl = (ACL *) tac_malloc(sizeof(ACL));
+- int isdeny = S_permit;
++ int perm;
+
+ bzero(acl, sizeof(ACL));
+
+@@ -696,13 +705,13 @@
+ return(0);
+
+ case S_deny:
+- isdeny = S_deny;
+ case S_permit:
++ case S_return:
++ perm = sym_code;
+ sym_get();
+ parse(S_separator);
+- insert_acl_entry(acl, isdeny);
++ insert_acl_entry(acl, perm);
+ parse(S_string);
+- isdeny = S_permit;
+ continue;
+
+ case S_closebra:
+@@ -823,6 +832,17 @@
+ parse_logging();
+ continue;
+
++ case S_include:
++ /* Process an include file */
++ sym_get();
++ parse(S_separator);
++ if (open_config(sym_buf)==NULL) {
++ parse_error("Cannot include file %s", sym_buf);
++ return 1;
++ }
++ sym_get();
++ continue;
++
+ default:
+ parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
+ return(1);
+@@ -985,6 +1005,7 @@
+ int save_sym;
+ char **fieldp;
+ char buf[MAX_INPUT_LINE_LEN];
++ NODE *newnode;
+
+ fieldp = NULL;
+ bzero(user, sizeof(USER));
+@@ -1189,7 +1210,23 @@
+ continue;
+
+ case S_member:
+- ASSIGN(user->member);
++ sym_get();
++ parse(S_separator);
++ newnode = (NODE *)tac_malloc(sizeof(NODE));
++ newnode->type = N_group;
++ newnode->value = NULL;
++ newnode->value1 = tac_strdup(sym_buf);
++ newnode->next=NULL;
++ newnode->line = sym_line;
++ if (user->member) {
++ /* append chain */
++ NODE *p;
++ for (p=user->member; p->next; p=p->next) /* EMPTY */;
++ p->next = newnode;
++ }
++ else {
++ user->member = newnode;
++ }
+ sym_get();
+ continue;
+
+@@ -1573,14 +1610,12 @@
+ static void
+ rch(void)
+ {
+- if (sym_error) {
+- sym_ch = EOF;
+- return;
++ while ((sym_ch = getc(cf)) == EOF) {
++ if (restore_config()) break;
+ }
+- sym_ch = getc(cf);
+
+ if (parse_only && sym_ch != EOF)
+- fprintf(stderr, "%c", sym_ch);
++ fputc(sym_ch, stdout);
+ }
+
+ /* For a user or group, find the value of a field. Does not recurse. */
+@@ -1735,63 +1770,53 @@
+ static int
+ circularity_check(void)
+ {
+- USER *user, *entry, *group;
++ USER *entry;
+ 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;
++ ITERATOR *iterator;
++ NODE *node;
+
+ if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "circularity_check: user=%s", user->name);
++ report(LOG_DEBUG, "circularity_check: user=%s", (*p)->name);
+
+ /* Initialise all groups "seen" flags to zero */
+ for (q = groups; *q; q++) {
+- group = *q;
+- group->flags &= ~FLAG_SEEN;
++ (*q)->flags &= ~FLAG_SEEN;
+ }
+
+- entry = user;
+-
+- while (entry) {
+- /* check groups we are a member of */
+- char *groupname = entry->member;
+-
+- if (debug & DEBUG_PARSE_FLAG)
+- report(LOG_DEBUG, "\tmember of group %s",
+- groupname ? groupname : "<none>");
|