--- /dev/null
+/*
+ Copyright (c) 1995-1998 by Cisco systems, Inc.
+
+ Permission to use, copy, modify, and distribute this software for
+ any purpose and without fee is hereby granted, provided that this
+ copyright and permission notice appear on all copies of the
+ software and supporting documentation, the name of Cisco Systems,
+ Inc. not be used in advertising or publicity pertaining to
+ distribution of the program without specific prior permission, and
+ notice be given in supporting documentation that modification,
+ copying and distribution is by permission of Cisco Systems, Inc.
+
+ Cisco Systems, Inc. makes no representations about the suitability
+ of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
+ IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#include "tac_plus.h"
+#include <stdio.h>
+#include <errno.h>
+#include "regexp.h"
+
+/*
+ <config> := <decl>*
+
+ <decl> := <top_level_decl> | <user_decl>
+
+ <top_level_decl> := <authen_default> |
+ accounting file = <string>
+ default authorization = permit |
+ key = <string>
+
+ <authen_default> := default authentication = file <filename>
+#if defined(DB)
+ | db <string> )
+#endif
+
+<permission> := permit | deny
+
+ <filename> := <string>
+
+ <password> := <string>
+
+ <user_decl> := user = <string> {
+ [ default service = [ permit | deny ] ]
+ <user_attr>*
+ <svc>*
+ }
+
+ <password_spec> := file <filename> |
+ skey |
+ cleartext <password> |
+ des <password> |
+
+#ifdef USE_PAM
+ pam <pam_service> |
+#endif
+#if defined(DB)
+ db <string>
+#endif
+ nopassword
+
+ <user_attr> := name = <string> |
+ login = <password_spec> |
+ member = <string> |
+ expires = <string> |
+ arap = cleartext <string> |
+ chap = cleartext <string> |
+#ifdef MSCHAP
+ ms-chap = cleartext <string> |
+#endif
+ pap = cleartext <string> |
+ pap = des <string> |
+#ifdef USE_PAM
+ pap = pam <pam_service> |
+#endif
+ opap = cleartext <string> |
+ global = cleartext <string> |
+ msg = <string>
+ before authorization = <string> |
+ after authorization = <string>
+
+ <svc> := <svc_auth> | <cmd_auth>
+
+ <cmd_auth> := cmd = <string> {
+ <cmd-match>*
+ }
+
+ <cmd-match> := <permission> <string>
+
+ <svc_auth> := service = ( exec | arap | slip | ppp protocol = <string> {
+ [ default attribute = permit ]
+ <attr_value_pair>*
+ }
+
+ <attr_value_pair> := [ optional ] <string> = <string>
+
+*/
+
+static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */
+static int sym_pos=0; /* current place in sym_buf */
+static int sym_ch; /* current parse character */
+static int sym_code; /* parser output */
+static int sym_line = 1; /* current line number for parsing */
+static FILE *cf = NULL; /* config file pointer */
+static int sym_error = 0; /* a parsing error has occurred */
+static int no_user_dflt = 0; /* default if user doesn't exist */
+static char *authen_default = NULL; /* top level authentication default */
+static int authen_default_method = 0; /*For method check */
+static char *nopasswd_str = "nopassword";
+
+/* A host definition structure. Currently unused, but when we start
+ configuring host-specific information e.g. per-host keys, this is
+ where it should be kept.
+
+ The first 2 fields (name and hash) are used by the hash table
+ routines to hash this structure into a table. Do not (re)move them */
+
+struct host {
+ char *name; /* host name */
+ void *hash; /* hash table next pointer */
+ int line; /* line number defined on */
+ char *key; /* host spesific key */
+ char *type; /* host type */
+};
+
+/* A user or group definition
+
+ The first 2 fields (name and hash) are used by the hash table
+ routines to hash this structure into a table. Move them at your
+ peril */
+
+struct user {
+ char *name; /* username */
+ void *hash; /* hash table next pointer */
+ int line; /* line number defined on */
+ long flags; /* flags field */
+
+#define FLAG_ISUSER 1 /* this structure represents a user */
+#define FLAG_ISGROUP 2 /* this structure represents a group */
+#define FLAG_SEEN 4 /* for circular definition detection */
+
+ char *full_name; /* users full name */
+ char *login; /* Login password */
+ int nopasswd; /* user requires no password */
+ char *global; /* password to use if none set */
+ char *member; /* group we are a member of */
+ char *expires; /* expiration date */
+ char *arap; /* our arap secret */
+ char *pap; /* our pap secret */
+ char *opap; /* our outbound pap secret */
+ char *chap; /* our chap secret */
+#ifdef MSCHAP
+ char *mschap; /* our mschap secret */
+#endif /* MSCHAP */
+ char *msg; /* a message for this user */
+ char *before_author; /* command to run before authorization */
+ char *after_author; /* command to run after authorization */
+ int svc_dflt; /* default authorization behaviour for svc or
+ * cmd */
+ NODE *svcs; /* pointer to svc nodes */
+#ifdef MAXSESS
+ int maxsess; /* Max sessions/user */
+#endif /* MAXSESS */
+ char *time; /* Timestamp */
+};
+
+typedef struct host HOST;
+typedef struct user USER;
+
+/* Only the first 2 fields (name and hash) are used by the hash table
+ routines to hashh structures into a table.
+*/
+
+union hash {
+ struct user u;
+ struct host h;
+};
+
+typedef union hash HASH;
+
+void *grouptable[HASH_TAB_SIZE];/* Table of group declarations */
+void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
+void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */
+
+
+static void
+ sym_get();
+
+
+#ifdef __STDC__
+#include <stdarg.h> /* ANSI C, variable length args */
+static void
+parse_error(char *fmt,...)
+#else
+#include <varargs.h> /* has 'vararg' definitions */
+/* VARARGS2 */
+static void
+parse_error(fmt, va_alist)
+char *fmt;
+
+va_dcl /* no terminating semi-colon */
+#endif
+{
+ char msg[256]; /* temporary string */
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+
+ report(LOG_ERR, "%s", msg);
+ fprintf(stderr, "Error: %s\n", msg);
+ tac_exit(1);
+}
+
+char *
+cfg_nodestring(type)
+ int type;
+{
+ switch (type) {
+ default:
+ return ("unknown node type");
+ case N_arg:
+ return ("N_arg");
+ case N_optarg:
+ return ("N_optarg");
+ case N_svc:
+ return ("N_svc");
+ case N_svc_exec:
+ return ("N_svc_exec");
+ case N_svc_slip:
+ return ("N_svc_slip");
+ case N_svc_ppp:
+ return ("N_svc_ppp");
+ case N_svc_arap:
+ return ("N_svc_arap");
+ case N_svc_cmd:
+ return ("N_svc_cmd");
+ case N_permit:
+ return ("N_permit");
+ case N_deny:
+ return ("N_deny");
+ }
+}
+
+static void
+free_attrs(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+ switch (node->type) {
+ case N_optarg:
+ case N_arg:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+ node->value);
+ break;
+ default:
+ report(LOG_ERR, "Illegal node type %s for free_attrs",
+ cfg_nodestring(node->type));
+ return;
+ }
+
+ free(node->value);
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void
+free_cmd_matches(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free_cmd_match %s %s",
+ cfg_nodestring(node->type),
+ node->value);
+
+ free(node->value); /* text */
+ free(node->value1); /* regexp compiled text */
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void
+free_svcs(node)
+NODE *node;
+{
+ NODE *next;
+
+ while (node) {
+
+ switch (node->type) {
+ case N_svc_cmd:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+ cfg_nodestring(node->type), node->value);
+ free(node->value); /* cmd name */
+ free_cmd_matches(node->value1);
+ next = node->next;
+ free(node);
+ node = next;
+ continue;
+
+ case N_svc:
+ case N_svc_ppp:
+ free(node->value1);
+ /* FALL-THROUGH */
+ case N_svc_exec:
+ case N_svc_arap:
+ case N_svc_slip:
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s", cfg_nodestring(node->type));
+ free_attrs(node->value);
+ next = node->next;
+ free(node);
+ node = next;
+ continue;
+
+ default:
+ report(LOG_ERR, "Illegal node type %d for free_svcs", node->type);
+ return;
+ }
+ }
+}
+
+static void
+free_userstruct(user)
+USER *user;
+{
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s %s",
+ (user->flags & FLAG_ISUSER) ? "user" : "group",
+ user->name);
+
+ if (user->name)
+ free(user->name);
+ if (user->full_name)
+ free(user->full_name);
+ if (user->login)
+ free(user->login);
+ if (user->member)
+ free(user->member);
+ if (user->expires)
+ free(user->expires);
+ if (user->time)
+ free(user->time);
+ if (user->arap)
+ free(user->arap);
+ if (user->chap)
+ free(user->chap);
+#ifdef MSCHAP
+ if (user->mschap)
+ free(user->mschap);
+#endif /* MSCHAP */
+ if (user->pap)
+ free(user->pap);
+ if (user->opap)
+ free(user->opap);
+ if (user->global)
+ free(user->global);
+ if (user->msg)
+ free(user->msg);
+ if (user->before_author)
+ free(user->before_author);
+ if (user->after_author)
+ free(user->after_author);
+ free_svcs(user->svcs);
+}
+
+static void
+free_hoststruct(host)
+HOST *host;
+{
+ if (debug & DEBUG_CLEAN_FLAG)
+ report(LOG_DEBUG, "free %s",
+ host->name);
+
+ if (host->name)
+ free(host->name);
+
+ if (host->key)
+ free(host->key);
+
+ if (host->type)
+ free(host->type);
+}
+
+/*
+ * Exported routines
+ */
+
+/* Free all allocated structures preparatory to re-reading the config file */
+void
+cfg_clean_config()
+{
+ int i;
+ USER *entry, *next;
+ HOST *host_entry,*hn;
+
+ if (authen_default) {
+ free(authen_default);
+ authen_default = NULL;
+ }
+
+ if (authen_default_method) {
+ authen_default_method = 0;
+ }
+
+ if (session.key) {
+ free(session.key);
+ session.key = NULL;
+ }
+
+ if (session.acctfile) {
+ free(session.acctfile);
+ session.acctfile = NULL;
+ }
+
+ if (session.db_acct) {
+ free(session.db_acct);
+ session.db_acct = NULL;
+ }
+
+ /* clean the hosttable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ host_entry = (HOST *) hosttable[i];
+ while (host_entry) {
+ hn = host_entry->hash;
+ free_hoststruct(host_entry);
+ free(host_entry);
+ host_entry = hn;
+ }
+ hosttable[i] = NULL;
+ }
+
+ /* the grouptable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = (USER *) grouptable[i];
+ while (entry) {
+ next = entry->hash;
+ free_userstruct(entry);
+ free(entry);
+ entry = next;
+ }
+ grouptable[i] = NULL;
+ }
+
+ /* the usertable */
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ entry = (USER *) usertable[i];
+ while (entry) {
+ next = entry->hash;
+ free_userstruct(entry);
+ free(entry);
+ entry = next;
+ }
+ usertable[i] = NULL;
+ }
+}
+
+static int
+parse_permission()
+{
+ int symbol = sym_code;
+
+ if (sym_code != S_permit && sym_code != S_deny) {
+ parse_error("expecting permit or deny but found '%s' on line %d",
+ sym_buf, sym_line);
+ return (0);
+ }
+ sym_get();
+
+ return (symbol);
+}
+
+static int
+parse(symbol)
+int symbol;
+
+{
+ if (sym_code != symbol) {
+ parse_error("expecting '%s' but found '%s' on line %d",
+ (symbol == S_string ? "string" : codestring(symbol)),
+ sym_buf, sym_line);
+ return (1);
+ }
+ sym_get();
+ return (0);
+}
+
+static int
+parse_opt_svc_default()
+{
+ if (sym_code != S_default) {
+ return (0);
+ }
+
+ parse(S_default);
+ parse(S_svc);
+ parse(S_separator);
+ if (sym_code == S_permit) {
+ parse(S_permit);
+ return (S_permit);
+ }
+ parse(S_deny);
+ return (S_deny);
+}
+
+static int
+parse_opt_attr_default()
+{
+ if (sym_code != S_default)
+ return (S_deny);
+
+ parse(S_default);
+ parse(S_attr);
+ parse(S_separator);
+ parse(S_permit);
+ return (S_permit);
+}
+
+static int parse_user();
+static int parse_host();
+
+static void
+ rch();
+
+/*
+ Parse lines in the config file, creating data structures
+ Return 1 on error, otherwise 0 */
+
+static int
+parse_decls()
+{
+ no_user_dflt = 0; /* default if user doesn't exist */
+
+ sym_code = 0;
+ rch();
+
+ bzero(grouptable, sizeof(grouptable));
+ bzero(usertable, sizeof(usertable));
+ bzero(hosttable, sizeof(hosttable));
+
+ sym_get();
+
+ /* Top level of parser */
+ while (1) {
+
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+
+ case S_accounting:
+ sym_get();
+ parse(S_file);
+ parse(S_separator);
+ if (session.acctfile)
+ free(session.acctfile);
+ session.acctfile = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+#ifdef DB
+ case S_db_accounting:
+ sym_get();
+ parse(S_separator);
+ if (session.db_acct)
+ free(session.db_acct);
+ session.db_acct = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+#endif
+
+ case S_default:
+ sym_get();
+ switch (sym_code) {
+ default:
+ parse_error(
+ "Expecting default authorization/authentication on lines %d",
+ sym_line);
+ return (1);
+
+ case S_authentication:
+ if (authen_default) {
+ parse_error(
+ "Multiply defined authentication default on line %d",
+ sym_line);
+ return (1);
+ }
+ parse(S_authentication);
+ parse(S_separator);
+
+ switch(sym_code) {
+
+ case S_file:
+#ifdef DB
+ case S_db:
+#endif
+#ifdef USE_LDAP
+ case S_ldap;
+#endif
+#ifdef USE_PAM
+ case S_pam:
+#endif
+ authen_default_method = sym_code;
+ break;
+
+ default:
+ parse_error("expecting default_method keyword after 'default authentication = ' on line %d",sym_line);
+ return (1);
+ }
+ sym_get();
+
+ authen_default = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_authorization:
+ parse(S_authorization);
+ parse(S_separator);
+ parse(S_permit);
+ no_user_dflt = S_permit;
+ report(LOG_INFO,
+ "default authorization = permit is now deprecated. Please use user = DEFAULT instead");
+ continue;
+ }
+
+ case S_key:
+ /* Process a key declaration. */
+ sym_get();
+ parse(S_separator);
+ if (session.key) {
+ parse_error("multiply defined key on lines %d and %d",
+ session.keyline, sym_line);
+ return (1);
+ }
+ session.key = tac_strdup(sym_buf);
+ session.keyline = sym_line;
+ sym_get();
+ continue;
+
+ case S_host:
+ parse_host();
+ continue;
+
+ case S_user:
+ case S_group:
+ parse_user();
+ continue;
+
+ /* case S_host: parse_host(); continue; */
+
+ default:
+ parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
+ return (1);
+ }
+ }
+}
+
+static NODE *parse_svcs();
+
+/* Assign a value to a field. Issue an error message and return 1 if
+ it's already been assigned. This is a macro because I was sick of
+ repeating the same code fragment over and over */
+
+#define ASSIGN(field) \
+sym_get(); parse(S_separator); if (field) { \
+ parse_error("Duplicate value for %s %s and %s on line %d", \
+ codestring(sym_code), field, sym_buf, sym_line); \
+ tac_exit(1); \
+ } \
+ field = tac_strdup(sym_buf);
+
+static int
+parse_host()
+{
+ HOST *h;
+ HOST *host = (HOST *) tac_malloc(sizeof(HOST));
+ int save_sym;
+ char buf[MAX_INPUT_LINE_LEN];
+
+ bzero(host, sizeof(HOST));
+
+ sym_get();
+ parse(S_separator);
+ host->name = tac_strdup(sym_buf);
+ host->line = sym_line;
+
+ h = hash_add_entry(hosttable, (void *) host);
+
+ if (h) {
+ parse_error("multiply defined %s on lines %d and %d",
+ host->name, h->line, sym_line);
+ return (1);
+ }
+
+ sym_get();
+ parse(S_openbra);
+
+ while (1) {
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+ case S_key:
+ ASSIGN(host->key);
+ sym_get();
+ continue;
+ case S_type:
+ ASSIGN(host->type);
+ sym_get();
+ continue;
+
+ case S_closebra:
+ parse(S_closebra);
+ return (0);
+
+ default:
+ parse_error("Unrecognised keyword %s for host %s on line %d",
+ sym_buf, host->name,sym_line);
+
+ return (0);
+ }
+ } /* while */
+} /* finish parse_host */
+
+
+static int
+parse_user()
+{
+ USER *n;
+ int isuser;
+ USER *user = (USER *) tac_malloc(sizeof(USER));
+ int save_sym;
+ char **fieldp;
+ char buf[MAX_INPUT_LINE_LEN];
+
+ bzero(user, sizeof(USER));
+
+ isuser = (sym_code == S_user);
+
+ sym_get();
+ parse(S_separator);
+ user->name = tac_strdup(sym_buf);
+ user->line = sym_line;
+
+ if (isuser) {
+ user->flags |= FLAG_ISUSER;
+ n = hash_add_entry(usertable, (void *) user);
+ } else {
+ user->flags |= FLAG_ISGROUP;
+ n = hash_add_entry(grouptable, (void *) user);
+ }
+
+ if (n) {
+ parse_error("multiply defined %s %s on lines %d and %d",
+ isuser ? "user" : "group",
+ user->name, n->line, sym_line);
+ return (1);
+ }
+ sym_get();
+ parse(S_openbra);
+
+ /* Is the default deny for svcs or cmds to be overridden? */
+ user->svc_dflt = parse_opt_svc_default();
+
+ while (1) {
+ switch (sym_code) {
+ case S_eof:
+ return (0);
+
+ case S_time:
+ ASSIGN(user->time);
+ sym_get();
+ continue;
+
+ case S_before:
+ sym_get();
+ parse(S_authorization);
+ if (user->before_author)
+ free(user->before_author);
+ user->before_author = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_after:
+ sym_get();
+ parse(S_authorization);
+ if (user->after_author)
+ free(user->after_author);
+ user->after_author = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+
+ case S_svc:
+ case S_cmd:
+
+ if (user->svcs) {
+ /*
+ * Already parsed some services/commands. Thanks to Gabor Kiss
+ * who found this bug.
+ */
+ NODE *p;
+ for (p=user->svcs; p->next; p=p->next)
+ /* NULL STMT */;
+ p->next = parse_svcs();
+ } else {
+ user->svcs = parse_svcs();
+ }
+ continue;
+
+ case S_login:
+ if (user->login) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(sym_code), user->login,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+ parse(S_separator);
+ switch(sym_code) {
+
+ case S_skey:
+ user->login = tac_strdup(sym_buf);
+ break;
+
+ case S_nopasswd:
+ /* set to dummy string, so that we detect a duplicate
+ * password definition attempt
+ */
+ user->login = tac_strdup(nopasswd_str);
+ user->nopasswd = 1;
+ break;
+
+ case S_file:
+ case S_cleartext:
+ case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /* USE_PAM */
+#ifdef DB
+ case S_db:
+#endif /* USE DB */
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+ user->login = tac_strdup(buf);
+ break;
+
+ default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'file', 'cleartext', 'pam'.'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
+ sym_line);
+#else
+ parse_error(
+ "expecting 'file', 'cleartext', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
+ sym_line);
+#endif /* USE_PAM */
+ }
+ sym_get();
+ continue;
+
+ case S_pap:
+ if (user->pap) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(sym_code), user->pap,
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+ parse(S_separator);
+ switch(sym_code) {
+
+ case S_cleartext:
+ case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /*USE_PAM */
+ sprintf(buf, "%s ", sym_buf);
+ sym_get();
+ strcat(buf, sym_buf);
+ user->pap = tac_strdup(buf);
+ break;
+
+ sprintf(buf, "%s ", sym_buf);
+ user->pap = tac_strdup(buf);
+ break;
+
+ default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'cleartext', 'pam', or 'des' keyword after 'pap =' on line %d",
+ sym_line);
+#else
+ parse_error(
+ "expecting 'cleartext', or 'des' keyword after 'pap =' on line %d",
+ sym_line);
+#endif /*USE_PAM */
+ }
+ sym_get();
+ continue;
+
+ case S_name:
+ ASSIGN(user->full_name);
+ sym_get();
+ continue;
+
+ case S_member:
+ ASSIGN(user->member);
+ sym_get();
+ continue;
+
+
+ case S_expires:
+ ASSIGN(user->expires);
+ sym_get();
+ continue;
+
+ case S_message:
+ ASSIGN(user->msg);
+ sym_get();
+ continue;
+
+ case S_arap:
+ case S_chap:
+#ifdef MSCHAP
+ case S_mschap:
+#endif /* MSCHAP */
+ case S_opap:
+ case S_global:
+ save_sym = sym_code;
+ sym_get();
+ parse(S_separator);
+ sprintf(buf, "%s ", sym_buf);
+ parse(S_cleartext);
+ strcat(buf, sym_buf);
+
+ if (save_sym == S_arap)
+ fieldp = &user->arap;
+ if (save_sym == S_chap)
+ fieldp = &user->chap;
+#ifdef MSCHAP
+ if (save_sym == S_mschap)
+ fieldp = &user->mschap;
+#endif /* MSCHAP */
+ if (save_sym == S_pap)
+ fieldp = &user->pap;
+ if (save_sym == S_opap)
+ fieldp = &user->opap;
+ if (save_sym == S_global)
+ fieldp = &user->global;
+
+ if (*fieldp) {
+ parse_error("Duplicate value for %s %s and %s on line %d",
+ codestring(save_sym), *fieldp, sym_buf, sym_line);
+ tac_exit(1);
+ }
+ *fieldp = tac_strdup(buf);
+ sym_get();
+ continue;
+
+ case S_closebra:
+ parse(S_closebra);
+ return (0);
+
+#ifdef MAXSESS
+ case S_maxsess:
+ sym_get();
+ parse(S_separator);
+ if (sscanf(sym_buf, "%d", &user->maxsess) != 1) {
+ parse_error("expecting integer, found '%s' on line %d",
+ sym_buf, sym_line);
+ }
+ sym_get();
+ continue;
+#endif /* MAXSESS */
+
+ default:
+ if (STREQ(sym_buf, "password")) {
+ fprintf(stderr,
+ "\npassword = <string> is obsolete. Use login = des <string>\n");
+ }
+ parse_error("Unrecognised keyword %s for user on line %d",
+ sym_buf, sym_line);
+
+ return (0);
+ }
+ }
+}
+
+static NODE *parse_attrs();
+static NODE *parse_cmd_matches();
+
+static NODE *
+parse_svcs()
+{
+ NODE *result;
+
+ switch (sym_code) {
+ default:
+ return (NULL);
+ case S_svc:
+ case S_cmd:
+ break;
+ }
+
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ /* cmd declaration */
+ if (sym_code == S_cmd) {
+ parse(S_cmd);
+ parse(S_separator);
+ result->value = tac_strdup(sym_buf);
+
+ sym_get();
+ parse(S_openbra);
+
+ result->value1 = parse_cmd_matches();
+ result->type = N_svc_cmd;
+
+ parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+ }
+
+ /* svc declaration */
+ parse(S_svc);
+ parse(S_separator);
+ switch (sym_code) {
+ default:
+ parse_error("expecting service type but found %s on line %d",
+ sym_buf, sym_line);
+ return (NULL);
+
+ case S_string:
+ result->type = N_svc;
+ /* should perhaps check that this is an allowable service name */
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ case S_exec:
+ result->type = N_svc_exec;
+ break;
+ case S_arap:
+ result->type = N_svc_arap;
+ break;
+ case S_slip:
+ result->type = N_svc_slip;
+ break;
+ case S_ppp:
+ result->type = N_svc_ppp;
+ parse(S_ppp);
+ parse(S_protocol);
+ parse(S_separator);
+ /* Should perhaps check that this is a known PPP protocol name */
+ result->value1 = tac_strdup(sym_buf);
+ break;
+ }
+ sym_get();
+ parse(S_openbra);
+ result->dflt = parse_opt_attr_default();
+ result->value = parse_attrs();
+ parse(S_closebra);
+ result->next = parse_svcs();
+ return (result);
+}
+
+/* <cmd-match> := <permission> <string> */
+
+static NODE *
+parse_cmd_matches()
+{
+ NODE *result;
+
+ if (sym_code != S_permit && sym_code != S_deny) {
+ return (NULL);
+ }
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ result->type = (parse_permission() == S_permit) ? N_permit : N_deny;
+ result->value = tac_strdup(sym_buf);
+
+ result->value1 = (void *) regcomp(result->value);
+ if (!result->value1) {
+ report(LOG_ERR, "in regular expression %s on line %d",
+ sym_buf, sym_line);
+ tac_exit(1);
+ }
+ sym_get();
+
+ result->next = parse_cmd_matches();
+
+ return (result);
+}
+
+static NODE *
+parse_attrs()
+{
+ NODE *result;
+ char buf[MAX_INPUT_LINE_LEN];
+ int optional = 0;
+
+ if (sym_code == S_closebra) {
+ return (NULL);
+ }
+ result = (NODE *) tac_malloc(sizeof(NODE));
+
+ bzero(result, sizeof(NODE));
+ result->line = sym_line;
+
+ if (sym_code == S_optional) {
+ optional++;
+ sym_get();
+ }
+ result->type = optional ? N_optarg : N_arg;
+
+ strcpy(buf, sym_buf);
+ parse(S_string);
+ strcat(buf, sym_buf);
+ parse(S_separator);
+ strcat(buf, sym_buf);
+ parse(S_string);
+
+ result->value = tac_strdup(buf);
+ result->next = parse_attrs();
+ return (result);
+}
+
+
+static void
+ getsym();
+
+static void
+sym_get()
+{
+ getsym();
+
+ if (debug & DEBUG_PARSE_FLAG) {
+ report(LOG_DEBUG, "line=%d sym=%s code=%d buf='%s'",
+ sym_line, codestring(sym_code), sym_code, sym_buf);
+ }
+}
+
+static char *
+sym_buf_add(c)
+char c;
+{
+ if (sym_pos >= MAX_INPUT_LINE_LEN) {
+ sym_buf[MAX_INPUT_LINE_LEN-1] = '\0';
+ if (debug & DEBUG_PARSE_FLAG) {
+ report(LOG_DEBUG, "line too long: line=%d sym=%s code=%d buf='%s'",
+ sym_line, codestring(sym_code), sym_code, sym_buf);
+ }
+ return(NULL);
+ }
+
+ sym_buf[sym_pos++] = c;
+ return(sym_buf);
+}
+
+static void
+getsym()
+{
+
+next:
+ switch (sym_ch) {
+
+ case EOF:
+ sym_code = S_eof;
+ return;
+
+ case '\n':
+ sym_line++;
+ rch();
+ goto next;
+
+ case '\t':
+ case ' ':
+ while (sym_ch == ' ' || sym_ch == '\t')
+ rch();
+ goto next;
+
+ case '=':
+ strcpy(sym_buf, "=");
+ sym_code = S_separator;
+ rch();
+ return;
+
+ case '{':
+ strcpy(sym_buf, "{");
+ sym_code = S_openbra;
+ rch();
+ return;
+
+ case '}':
+ strcpy(sym_buf, "}");
+ sym_code = S_closebra;
+ rch();
+ return;
+
+ case '#':
+ while ((sym_ch != '\n') && (sym_ch != EOF))
+ rch();
+ goto next;
+
+ case '"':
+ rch();
+ sym_pos = 0;
+ while (1) {
+
+ if (sym_ch == '"') {
+ break;
+ }
+
+ /* backslash-double-quote is supported inside strings */
+ /* also allow \n */
+ if (sym_ch == '\\') {
+ rch();
+ switch (sym_ch) {
+ case 'n':
+ /* preserve the slash for \n */
+ if (!sym_buf_add('\\')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+
+ /* fall through */
+ case '"':
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ continue;
+ default:
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ }
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ }
+ rch();
+
+ if (!sym_buf_add('\0')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ sym_code = S_string;
+ return;
+
+ default:
+ sym_pos = 0;
+ while (sym_ch != '\t' && sym_ch != ' ' && sym_ch != '='
+ && sym_ch != '\n') {
+
+ if (!sym_buf_add(sym_ch)) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ rch();
+ }
+
+ if (!sym_buf_add('\0')) {
+ sym_code = S_unknown;
+ rch();
+ return;
+ }
+ sym_code = keycode(sym_buf);
+ if (sym_code == S_unknown)
+ sym_code = S_string;
+ return;
+ }
+}
+
+static void
+rch()
+{
+ if (sym_error) {
+ sym_ch = EOF;
+ return;
+ }
+ sym_ch = getc(cf);
+
+ if (parse_only && sym_ch != EOF)
+ fprintf(stderr, "%c", sym_ch);
+}
+
+
+/* For a user or group, find the value of a field. Does not recurse. */
+VALUE
+get_value(user, field)
+USER *user;
+int field;
+{
+ VALUE v;
+
+ v.intval = 0;
+
+ if (!user) {
+ parse_error("get_value: illegal user");
+ return (v);
+ }
+ switch (field) {
+
+ case S_name:
+ v.pval = user->name;
+ break;
+
+ case S_login:
+ v.pval = user->login;
+ break;
+
+ case S_global:
+ v.pval = user->global;
+ break;
+
+ case S_member:
+ v.pval = user->member;
+ break;
+
+ case S_expires:
+ v.pval = user->expires;
+ break;
+
+ case S_arap:
+ v.pval = user->arap;
+ break;
+
+ case S_chap:
+ v.pval = user->chap;
+ break;
+
+#ifdef MSCHAP
+ case S_mschap:
+ v.pval = user->mschap;
+ break;
+#endif /* MSCHAP */
+
+ case S_pap:
+ v.pval = user->pap;
+ break;
+
+ case S_opap:
+ v.pval = user->opap;
+ break;
+
+ case S_message:
+ v.pval = user->msg;
+ break;
+
+ case S_svc:
+ v.pval = user->svcs;
+ break;
+
+ case S_before:
+ v.pval = user->before_author;
+ break;
+
+ case S_after:
+ v.pval = user->after_author;
+ break;
+
+ case S_svc_dflt:
+ v.intval = user->svc_dflt;
+ break;
+
+#ifdef MAXSESS
+ case S_maxsess:
+ v.intval = user->maxsess;
+ break;
+#endif
+
+ case S_nopasswd:
+ v.intval = user->nopasswd;
+ break;
+
+ case S_time:
+ v.pval = user->time;
+ break;
+
+ default:
+ report(LOG_ERR, "get_value: unknown field %d", field);
+ break;
+ }
+ return (v);
+}
+
+/* For host , find value of field. Doesn't recursive */
+VALUE
+get_hvalue(host, field)
+HOST *host;
+int field;
+{
+ VALUE v;
+ v.intval = 0;
+ if(!host) {
+ parse_error("get_hvalue: illegal host");
+ return (v);
+ }
+ switch (field) {
+ case S_name:
+ v.pval = host->name;
+ break;
+
+ case S_key:
+ v.pval = host->key;
+ break;
+
+ default:
+ report(LOG_ERR, "get_value: unknown field %d", field);
+ break;
+ }
+ return (v);
+}
+
+
+/* For each user, check she doesn't circularly reference a
+ group. Return 1 if it does */
+static int
+circularity_check()
+{
+ USER *user, *entry, *group;
+ USER **users = (USER **) hash_get_entries(usertable);
+ USER **groups = (USER **) hash_get_entries(grouptable);
+ USER **p, **q;
+
+ /* users */
+ for (p = users; *p; p++) {
+ user = *p;
+
+ if (debug & DEBUG_PARSE_FLAG)
+ report(LOG_DEBUG, "circularity_check: user=%s", user->name);
+
+ /* Initialise all groups "seen" flags to zero */
+ for (q = groups; *q; q++) {
+ group = *q;
+ group->flags &= ~FLAG_SEEN;
+ }
+
+ entry = user;
+
+ while (entry) {
+ /* check groups we are a member of */
+ char *groupname = entry->member;
+
+ if (debug & DEBUG_PARSE_FLAG)
+ report(LOG_DEBUG, "\tmember of group %s",
+ groupname ? groupname : "<none>");
+
+
+ /* if not a member of any groups, go on to next user */
+ if (!groupname)
+ break;
+
+ group = (USER *) hash_lookup(grouptable, groupname);
+ if (!group) {
+ report(LOG_ERR, "%s=%s, group %s does not exist",
+ (entry->flags & FLAG_ISUSER) ? "user" : "group",
+ entry->name, groupname);
+ free(users);
+ free(groups);
+ return (1);
+ }
+ if (group->flags & FLAG_SEEN) {
+ report(LOG_ERR, "recursively defined groups");
+
+ /* print all seen "groups" */
+ for (q = groups; *q; q++) {
+ group = *q;
+ if (group->flags & FLAG_SEEN)
+ report(LOG_ERR, "%s", group->name);
+ }
+ free(users);
+ free(groups);
+ return (1);
+ }
+ group->flags |= FLAG_SEEN; /* mark group as seen */
+ entry = group;
+ }
+ }
+ free(users);
+ free(groups);
+ return (0);
+}
+
+
+/* Return a value for a group or user (isuser says if
+ this name is a group or a user name).
+
+ If no value exists, and recurse is true, also check groups we are a
+ member of, recursively.
+
+ Returns void * because it can return a string or a node pointer
+ (should really return a union pointer).
+*/
+static VALUE
+cfg_get_value(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ USER *user, *group;
+ VALUE value;
+
+ value.pval = NULL;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: name=%s isuser=%d attr=%s rec=%d",
+ name, isuser, codestring(attr), recurse);
+
+ /* find the user/group entry */
+
+ user = (USER *) hash_lookup(isuser ? usertable : grouptable, name);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name);
+ return (value);
+ }
+
+ /* found the entry. Lookup value from attr=value */
+ value = get_value(user, attr);
+
+ if (value.pval || !recurse) {
+ return (value);
+ }
+ /* no value. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: recurse group = %s",
+ group->name);
+
+ value = get_value(group, attr);
+
+ if (value.pval) {
+ return (value);
+ }
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ /* no value for this user or her containing groups */
+ value.pval = NULL;
+ return (value);
+}
+
+
+/* Wrappers for cfg_get_value */
+int
+cfg_get_intvalue(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ int val = cfg_get_value(name, isuser, attr, recurse).intval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val);
+ return(val);
+}
+
+char *
+cfg_get_pvalue(name, isuser, attr, recurse)
+char *name;
+int isuser, attr, recurse;
+{
+ char *p = cfg_get_value(name, isuser, attr, recurse).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_pvalue: returns %s",
+ p ? p : "NULL");
+ return(p);
+}
+
+/* For getting host values */
+static VALUE
+cfg_get_hvalue(name, attr)
+char *name;
+int attr;
+{
+ HOST *host;
+ VALUE value;
+
+ value.pval = NULL;
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_hvalue: name=%s attr=%s ",
+ name, codestring(attr));
+
+ /* find the host entry in hash table */
+
+ host = (HOST *) hash_lookup( hosttable, name);
+
+ if (!host) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_hvalue: no host named %s", name);
+ return (value);
+ }
+
+ /* found the entry. Lookup value from attr=value */
+ value = get_hvalue(host, attr);
+
+ if (value.pval) {
+ return (value);
+ }
+ /* No any value for this host */
+ value.pval = NULL;
+ return (value);
+}
+
+/* Wrappers for cfg_get_hvalue */
+char *
+cfg_get_phvalue(name, attr)
+char *name;
+int attr;
+{
+ char *p = cfg_get_hvalue(name, attr).pval;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_phvalue: returns %s",
+ p ? p : "NULL");
+ return(p);
+}
+
+/*
+ Read the config file and do some basic sanity checking on
+ it. Return 1 if we find any errors. */
+
+cfg_read_config(cfile)
+char *cfile;
+{
+ sym_line = 1;
+
+ if ((cf = fopen(cfile, "r")) == NULL) {
+ report(LOG_ERR, "read_config: fopen() error for file %s %s, exiting",
+ cfile, sys_errlist[errno]);
+ return (1);
+ }
+ if (parse_decls() || sym_error) {
+ fclose(cf);
+ return (1);
+ }
+
+ if (circularity_check()) {
+ fclose(cf);
+ return (1);
+ }
+
+ fclose(cf);
+ return (0);
+}
+
+/* return 1 if user exists, 0 otherwise */
+int
+cfg_user_exists(username)
+char *username;
+{
+ USER *user = (USER *) hash_lookup(usertable, username);
+
+ return (user != NULL);
+}
+
+/* return expiry string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_expires(username, recurse)
+char *username;
+
+{
+ return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse));
+}
+
+/* return time string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_timestamp(username, recurse)
+char *username;
+{
+ return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse));
+}
+
+
+/* return password string of user. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_login_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse));
+}
+
+/* return value of the nopasswd field. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+int
+cfg_get_user_nopasswd(user, recurse)
+ char *user;
+{
+ return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse));
+}
+
+/* return user's secret. If none, try groups she is a member
+ on, and so on, recursively if recurse is non-zero */
+char *
+cfg_get_arap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse));
+}
+
+char *
+cfg_get_chap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse));
+}
+
+#ifdef MSCHAP
+char *
+cfg_get_mschap_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse));
+}
+#endif /* MSCHAP */
+
+char *
+cfg_get_pap_secret(user, recurse)
+char *user;
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse));
+}
+
+char *
+cfg_get_opap_secret(user, recurse)
+char *user;
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse));
+}
+
+/* return the global password for the user (or the group, etc.) */
+
+char *
+cfg_get_global_secret(user, recurse)
+char *user;
+
+{
+ return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse));
+}
+
+#ifdef USE_PAM
+/* Return a pointer to a node representing a PAM Service name */
+char *
+cfg_get_pam_service(user,recurse)
+char *user;
+
+{
+ char *cfg_passwd;
+ char *p;
+
+cfg_passwd = cfg_get_pap_secret(user, recurse);
+
+if (!cfg_passwd) {
+ cfg_passwd = cfg_get_global_secret(user, recurse);
+}
+
+if (!cfg_passwd && !cfg_user_exists(user)) {
+ cfg_passwd = cfg_get_authen_default();
+ switch (cfg_get_authen_default_method()) {
+ case (S_pam):
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd);
+ return(cfg_passwd);
+ break;
+ default:
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I havent find any PAM Service!!");
+ return(NULL);/* Haven't any PAM Service!! */
+ }
+}
+
+p=tac_find_substring("pam ", cfg_passwd);
+
+if(p) { /* We find PAM services */
+ if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "I get PAM sevice:%s",p);
+return (p);
+}
+
+if (debug & DEBUG_AUTHOR_FLAG)
+ report(LOG_DEBUG, "No any PAM Sevice");
+
+return(NULL);
+}
+
+#endif /* For PAM */
+
+
+
+/* Return a pointer to a node representing a given service
+ authorization, taking care of recursion issues correctly. Protocol
+ is only read if the type is N_svc_ppp. svcname is only read if type
+ is N_svc.
+*/
+
+NODE *
+cfg_get_svc_node(username, type, protocol, svcname, recurse)
+char *username;
+int type;
+char *protocol, *svcname;
+int recurse;
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d",
+ username,
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "",
+ recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, username);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username);
+ return (NULL);
+ }
+
+ /* found the user entry. Find svc node */
+ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != type)
+ continue;
+
+ if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+ continue;
+ }
+
+ if (type == N_svc && !STREQ(svc->value1, svcname)) {
+ continue;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: found %s proto=%s svcname=%s",
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "");
+
+ return(svc);
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+ return (NULL);
+ }
+
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s",
+ group->name);
+
+ for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != type)
+ continue;
+
+ if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) {
+ continue;
+ }
+
+ if (type == N_svc && !STREQ(svc->value1, svcname)) {
+ continue;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG,
+ "cfg_get_svc_node: found %s proto=%s svcname=%s",
+ cfg_nodestring(type),
+ protocol ? protocol : "",
+ svcname ? svcname : "");
+
+ return(svc);
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_svc_node: returns NULL");
+
+ /* no matching svc node for this user or her containing groups */
+ return (NULL);
+}
+
+/* Return a pointer to the node representing a set of command regexp
+ matches for a user and command, handling recursion issues correctly */
+NODE *
+cfg_get_cmd_node(name, cmdname, recurse)
+char *name, *cmdname;
+int recurse;
+
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: name=%s cmdname=%s rec=%d",
+ name, cmdname, recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, name);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name);
+ return (NULL);
+ }
+ /* found the user entry. Find svc node */
+ svc = (NODE *) get_value(user, S_svc).pval;
+
+ while (svc) {
+ if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s %s node",
+ cmdname, cfg_nodestring(svc->type));
+ return (svc);
+ }
+ svc = svc->next;
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
+ return (NULL);
+ }
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: recurse group = %s",
+ group->name);
+
+ svc = get_value(group, S_svc).pval;
+
+ while (svc) {
+ if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s node %s",
+ cmdname, cfg_nodestring(svc->type));
+ return (svc);
+ }
+ svc = svc->next;
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL");
+
+ /* no matching cmd node for this user or her containing groups */
+ return (NULL);
+}
+
+/* Return an array of character strings representing configured AV
+ * pairs, given a username and a service node.
+ *
+ * In the AV strings returned, manipulate the separator character to
+ * indicate which args are optional and which are mandatory.
+ *
+ * Lastly, indicate what default permission was configured by setting
+ * denyp */
+
+char **
+cfg_get_svc_attrs(svcnode, denyp)
+NODE *svcnode;
+int *denyp;
+{
+ int i;
+ NODE *node;
+ char **args;
+
+ *denyp = 1;
+
+ if (!svcnode)
+ return (NULL);
+
+ *denyp = (svcnode->dflt == S_deny);
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next)
+ i++;
+
+ args = (char **) tac_malloc(sizeof(char *) * (i + 1));
+
+ i = 0;
+ for (node = svcnode->value; node; node = node->next) {
+ char *arg = tac_strdup(node->value);
+ char *p = index(arg, '=');
+
+ if (p && node->type == N_optarg)
+ *p = '*';
+ args[i++] = arg;
+ }
+ args[i] = NULL;
+ return (args);
+}
+
+
+int
+cfg_user_svc_default_is_permit(user)
+char *user;
+
+{
+ int permit = cfg_get_intvalue(user, TAC_IS_USER, S_svc_dflt,
+ TAC_PLUS_RECURSE);
+
+ switch (permit) {
+ default: /* default is deny */
+ case S_deny:
+ return (0);
+ case S_permit:
+ return (1);
+ }
+}
+
+int
+cfg_no_user_permitted()
+{
+ if (no_user_dflt == S_permit)
+ return (1);
+ return (0);
+}
+
+
+char *
+cfg_get_authen_default()
+{
+ return (authen_default);
+}
+
+/* For describe authentication method(pam,file,db..etc) */
+int
+cfg_get_authen_default_method()
+{
+ return (authen_default_method);
+}
+
+
+/* Return 1 if this user has any ppp services configured. Used for
+ authorizing ppp/lcp requests */
+int
+cfg_ppp_is_configured(username, recurse)
+ char *username;
+ int recurse;
+{
+ USER *user, *group;
+ NODE *svc;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d",
+ username, recurse);
+
+ /* find the user/group entry */
+ user = (USER *) hash_lookup(usertable, username);
+
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s",
+ username);
+ return (0);
+ }
+
+ /* found the user entry. Find svc node */
+ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != N_svc_ppp)
+ continue;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+ svc->value1);
+
+ return(1);
+ }
+
+ if (!recurse) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+ return (0);
+ }
+
+ /* no matching node. Check containing group */
+ if (user->member)
+ group = (USER *) hash_lookup(grouptable, user->member);
+ else
+ group = NULL;
+
+ while (group) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: recurse group = %s",
+ group->name);
+
+ for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) {
+
+ if (svc->type != N_svc_ppp)
+ continue;
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node",
+ svc->value1);
+
+ return(1);
+ }
+
+ /* still nothing. Check containing group and so on */
+
+ if (group->member)
+ group = (USER *) hash_lookup(grouptable, group->member);
+ else
+ group = NULL;
+ }
+
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0");
+
+ /* no PPP svc nodes for this user or her containing groups */
+ return (0);
+}
+
+/* For getting host key */
+char *
+cfg_get_host_key(host)
+char *host;
+{
+ return (cfg_get_phvalue(host, S_key));
+}
+
--- /dev/null
+/*
+ * tac_plus.c
+ *
+ * TACACS_PLUS daemon suitable for using on Un*x systems.
+ *
+ * October 1994, Lol Grant
+ *
+ * Copyright (c) 1994-1998 by Cisco systems, Inc.
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that this
+ * copyright and permission notice appear on all copies of the
+ * software and supporting documentation, the name of Cisco Systems,
+ * Inc. not be used in advertising or publicity pertaining to
+ * distribution of the program without specific prior permission, and
+ * notice be given in supporting documentation that modification,
+ * copying and distribution is by permission of Cisco Systems, Inc.
+
+ * Cisco Systems, Inc. makes no representations about the suitability
+ * of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
+ * IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#include "tac_plus.h"
+#include "sys/wait.h"
+#include "signal.h"
+
+static int standalone = 1; /* running standalone (1) or under inetd (0) */
+static int initialised = 0; /* data structures have been allocated */
+int sendauth_only = 0; /* don't respond to sendpass requests */
+int debug = 0; /* debugging flags */
+int port = 0; /* port we're listening on */
+int console = 0; /* write all syslog messages to console */
+int parse_only = 0; /* exit after verbose parsing */
+int single = 0; /* single thread (for debugging) */
+int wtmpfd = 0; /* for wtmp file logging */
+char *wtmpfile = NULL;
+
+struct timeval started_at;
+
+struct session session; /* session data */
+
+static char pidfilebuf[75]; /* holds current name of the pidfile */
+
+void start_session();
+
+#ifndef REAPCHILD
+static
+#ifdef VOIDSIG
+void
+#else
+int
+#endif /* VOIDSIG */
+reapchild()
+{
+#ifdef UNIONWAIT
+ union wait status;
+#else
+ int status;
+#endif
+ int pid;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, 0);
+ if (pid <= 0)
+ return;
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "%d reaped", pid);
+ }
+}
+#endif /* REAPCHILD */
+
+static void
+die(signum)
+int signum;
+{
+ report(LOG_INFO, "Received signal %d, shutting down", signum);
+ unlink(pidfilebuf);
+ tac_exit(0);
+}
+
+static void
+init()
+{
+ if (initialised)
+ cfg_clean_config();
+
+ report(LOG_INFO, "Reading config");
+
+ session.acctfile = tac_strdup("/var/log/acctfile");
+
+ if (!session.cfgfile) {
+ report(LOG_ERR, "no config file specified");
+ tac_exit(1);
+ }
+
+ /* read the config file */
+ if (cfg_read_config(session.cfgfile)) {
+ report(LOG_ERR, "Parsing %s", session.cfgfile);
+ fprintf(stderr,"Config file not found!!\n");
+ tac_exit(1);
+ }
+
+ initialised++;
+
+ report(LOG_INFO, "Version %s Initialized %d", VERSION, initialised);
+
+}
+
+static void
+handler(signum)
+int signum;
+{
+ report(LOG_INFO, "Received signal %d", signum);
+ init();
+#ifdef REARMSIGNAL
+ signal(SIGUSR1, handler);
+ signal(SIGHUP, handler);
+#endif REARMSIGNAL
+}
+
+/*
+ * Return a socket bound to an appropriate port number/address. Exits
+ * the program on failure */
+
+get_socket()
+{
+ int s;
+ struct sockaddr_in sin;
+ struct servent *sp;
+ int on = 1;
+
+ bzero((char *) &sin, sizeof(sin));
+
+ if (port) {
+ sin.sin_port = htons(port);
+ } else {
+ sp = getservbyname("tacacs", "tcp");
+ if (sp)
+ sin.sin_port = sp->s_port;
+ else {
+ report(LOG_ERR, "Cannot find socket port");
+ tac_exit(1);
+ }
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s < 0) {
+ console++;
+ report(LOG_ERR, "get_socket: socket: %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+#ifdef SO_REUSEADDR
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
+ sizeof(on)) < 0)
+ perror("setsockopt - SO_REUSEADDR");
+#endif /* SO_REUSEADDR */
+
+ if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ console++;
+ report(LOG_ERR, "get_socket: bind %d %s",
+ ntohs(sin.sin_port),
+ sys_errlist[errno]);
+ tac_exit(1);
+ }
+ return (s);
+}
+
+static void
+open_logfile()
+{
+#ifdef LOG_LOCAL6
+ openlog("tac_plus", LOG_PID, LOG_LOCAL6);
+#else
+ openlog("tac_plus", LOG_PID);
+#endif
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+}
+
+/*
+ * main
+ *
+ * We will eventually be called from inetd or via the rc scripts directly
+ * Parse arguments and act appropiately.
+ */
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ extern char *optarg;
+ int childpid;
+ int c;
+ int s;
+ FILE *fp;
+ int lookup_peer = 0;
+
+ debug = 0; /* no debugging */
+ standalone = 1; /* standalone */
+ single = 0; /* single threaded */
+
+ /* initialise global session data */
+ bzero(&session, sizeof(session));
+ session.peer = tac_strdup("unknown");
+
+ open_logfile();
+
+#ifdef TAC_PLUS_PORT
+ port = TAC_PLUS_PORT;
+#endif
+
+ if (argc <= 1) {
+ fprintf(stderr, "Usage: tac_plus -C <configuration file>\n");
+ fprintf(stderr, "\t[ -t ] [ -P ] [ -g ] [ -p <port> ]\n");
+ fprintf(stderr, "\t[ -d <debug level> ] [ -i ] [ -v ] [ -s ]\n");
+ fprintf(stderr, "\t[ -l logfile ]");
+#ifdef MAXSESS
+ fprintf(stderr, " [ -w whologfile ]");
+#endif
+ fprintf(stderr, "\n");
+ tac_exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "td:C:ip:PgvsLl:w:u:")) != EOF)
+ switch (c) {
+ case 'L': /* lookup peer names via DNS */
+ lookup_peer++;
+ break;
+ case 's': /* don't respond to sendpass */
+ sendauth_only++;
+ break;
+ case 'v': /* print version and exit */
+ version();
+ tac_exit(1);
+ case 't':
+ console++; /* log to console too */
+ break;
+ case 'P': /* Parse config file only */
+ parse_only++;
+ break;
+ case 'g': /* single threaded */
+ single++;
+ break;
+ case 'p': /* port */
+ port = atoi(optarg);
+ break;
+ case 'd': /* debug */
+ debug = atoi(optarg);
+ break;
+ case 'C': /* config file name */
+ session.cfgfile = tac_strdup(optarg);
+ break;
+ case 'i': /* stand-alone */
+ standalone = 0;
+ break;
+ case 'l': /* logfile */
+ logfile = tac_strdup(optarg);
+ break;
+#ifdef MAXSESS
+ case 'w': /* wholog file */
+ wholog = tac_strdup(optarg);
+ break;
+#endif
+ case 'u':
+ wtmpfile = tac_strdup(optarg);
+ break;
+
+ default:
+ fprintf(stderr, "%s: bad switch %c\n", argv[0], c);
+ tac_exit(1);
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "Warning, not running as uid 0\n");
+ fprintf(stderr, "Tac_plus is usually run as root\n");
+ }
+
+ parser_init();
+
+ init();
+
+ signal(SIGUSR1, handler);
+ signal(SIGHUP, handler);
+ signal(SIGTERM, die);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (parse_only)
+ tac_exit(0);
+
+ if (debug)
+ report(LOG_DEBUG, "tac_plus server %s starting", VERSION);
+
+ if (!standalone) {
+ /* running under inetd */
+ struct sockaddr_in name;
+ int name_len;
+ int on = 1;
+
+ name_len = sizeof(name);
+
+ session.sock = 0;
+ if (getpeername(session.sock, (struct sockaddr *) &name, &name_len)) {
+ report(LOG_ERR, "getpeername failure %s", sys_errlist[errno]);
+ } else {
+ struct hostent *hp;
+ hp = gethostbyaddr((char *) &name.sin_addr.s_addr,
+ sizeof(name.sin_addr.s_addr), AF_INET);
+ if (session.peer) {
+ free(session.peer);
+ }
+ session.peer = tac_strdup(hp ? hp->h_name :
+ (char *) inet_ntoa(name.sin_addr));
+ }
+#ifdef FIONBIO
+ if (ioctl(session.sock, FIONBIO, &on) < 0) {
+ report(LOG_ERR, "ioctl(FIONBIO) %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+#endif
+ start_session();
+ tac_exit(0);
+ }
+
+ if (!single) {
+ /* Running standalone. Background ourselves, let go of controlling tty */
+
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+ signal(SIGHUP, SIG_IGN);
+
+ if ((childpid = fork()) < 0)
+ report(LOG_ERR, "Can't fork first child");
+ else if (childpid > 0)
+ exit(0); /* parent */
+
+ if (debug)
+ report(LOG_DEBUG, "Backgrounded");
+
+#ifndef REAPCHILD
+
+#ifdef LINUX
+ if (setpgrp() == -1)
+#else /* LINUX */
+ if (setpgrp(0, getpid()) == -1)
+#endif /* LINUX */
+ report(LOG_ERR, "Can't change process group");
+
+ c = open("/dev/tty", O_RDWR);
+ if (c >= 0) {
+ ioctl(c, TIOCNOTTY, (char *) 0);
+ (void) close(c);
+ }
+ signal(SIGCHLD, reapchild);
+
+#else /* REAPCHILD */
+
+ if (setpgrp() == 1)
+ report(LOG_ERR, "Can't change process group");
+
+ signal(SIGHUP, SIG_IGN);
+
+ if ((childpid = fork()) < 0)
+ report(LOG_ERR, "Can't fork second child");
+ else if (childpid > 0)
+ exit(0);
+
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "Forked grandchild");
+
+ signal(SIGCHLD, SIG_IGN);
+
+#endif /* REAPCHILD */
+
+ closelog(); /* some systems require this */
+
+ for (c = 0; c < getdtablesize(); c++)
+ (void) close(c);
+
+ /* make sure we can still log to syslog now we've closed everything */
+ open_logfile();
+
+ } /* ! single threaded */
+
+ ostream = NULL;
+ /* chdir("/"); */
+ umask(0);
+ errno = 0;
+
+ s = get_socket();
+
+#ifndef SOMAXCONN
+#ifdef LINUX
+#define SOMAXCONN 128
+#else
+#define SOMAXCONN 5
+#endif /* LINUX */
+#endif /* SOMAXCONN */
+
+ if (listen(s, SOMAXCONN) < 0) {
+ console++;
+ report(LOG_ERR, "listen: %s", sys_errlist[errno]);
+ tac_exit(1);
+ }
+
+ if (port == TAC_PLUS_PORT) {
+ strcpy(pidfilebuf, TACPLUS_PIDFILE);
+ } else {
+ sprintf(pidfilebuf, "%s.%d", TACPLUS_PIDFILE, port);
+ }
+
+ /* write process id to pidfile */
+ if ((fp = fopen(pidfilebuf, "w")) != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ } else
+ report(LOG_ERR, "Cannot write pid to %s %s",
+ pidfilebuf, sys_errlist[errno]);
+
+#ifdef TACPLUS_GROUPID
+ if (setgid(TACPLUS_GROUPID))
+ report(LOG_ERR, "Cannot set group id to %d %s",
+ TACPLUS_GROUPID, sys_errlist[errno]);
+#endif
+
+#ifdef TACPLUS_USERID
+ if (setuid(TACPLUS_USERID))
+ report(LOG_ERR, "Cannot set user id to %d %s",
+ TACPLUS_USERID, sys_errlist[errno]);
+#endif
+
+#ifdef MAXSESS
+ maxsess_loginit();
+#endif /* MAXSESS */
+
+ report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d",
+ getuid(), geteuid(), getgid(), getegid(), s);
+
+ for (;;) {
+ int pid;
+ struct sockaddr_in from;
+ int from_len;
+ int newsockfd;
+ struct hostent *hp = NULL;
+
+ bzero((char *) &from, sizeof(from));
+ from_len = sizeof(from);
+
+ newsockfd = accept(s, (struct sockaddr *) &from, &from_len);
+
+ if (newsockfd < 0) {
+ if (errno == EINTR)
+ continue;
+
+ report(LOG_ERR, "accept: %s", sys_errlist[errno]);
+ continue;
+ }
+
+ if (lookup_peer) {
+ hp = gethostbyaddr((char *) &from.sin_addr.s_addr,
+ sizeof(from.sin_addr.s_addr), AF_INET);
+ }
+
+ if (session.peer) {
+ free(session.peer);
+ }
+ session.peer = tac_strdup(hp ? hp->h_name :
+ (char *) inet_ntoa(from.sin_addr));
+
+ if (debug & DEBUG_PACKET_FLAG)
+ report(LOG_DEBUG, "session request from %s sock=%d",
+ session.peer, newsockfd);
+
+ if (!single) {
+ pid = fork();
+
+ if (pid < 0) {
+ report(LOG_ERR, "fork error");
+ tac_exit(1);
+ }
+ } else {
+ pid = 0;
+ }
+
+ if (pid == 0) {
+ /* child */
+ if (!single)
+ close(s);
+ session.sock = newsockfd;
+ start_session();
+ shutdown(session.sock, 2);
+ close(session.sock);
+ if (!single)
+ tac_exit(0);
+ } else {
+ if (debug & DEBUG_FORK_FLAG)
+ report(LOG_DEBUG, "forked %d", pid);
+ /* parent */
+ close(newsockfd);
+ }
+ }
+}
+
+#ifdef GETDTABLESIZE
+int
+getdtablesize()
+{
+ return(_NFILE);
+}
+#endif /* GETDTABLESIZE */
+
+/* Make sure version number is kosher. Return 0 if it is */
+int
+bad_version_check(pak)
+u_char *pak;
+{
+ HDR *hdr = (HDR *) pak;
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ /*
+ * Let authen routines take care of more sophisticated version
+ * checking as its now a bit involved.
+ */
+ return(0);
+
+ case TAC_PLUS_AUTHOR:
+ case TAC_PLUS_ACCT:
+ if (hdr->version != TAC_PLUS_VER_0) {
+ send_error_reply(hdr->type, "Illegal packet version");
+ return(1);
+ }
+ return(0);
+
+ default:
+ return(1);
+ }
+}
+
+/*
+ * Determine the packet type, read the rest of the packet data,
+ * decrypt it and call the appropriate service routine.
+ *
+ */
+
+void
+start_session()
+{
+ u_char *pak, *read_packet();
+ HDR *hdr;
+ void authen();
+
+ session.seq_no = 0;
+ session.aborted = 0;
+ session.version = 0;
+
+ pak = read_packet();
+ if (!pak) {
+ return;
+ }
+
+ if (debug & DEBUG_PACKET_FLAG) {
+ report(LOG_DEBUG, "validation request from %s", session.peer);
+ dump_nas_pak(pak);
+ }
+ hdr = (HDR *) pak;
+
+ session.session_id = ntohl(hdr->session_id);
+
+ /* Do some version checking */
+ if (bad_version_check(pak)) {
+ free(pak);
+ return;
+ }
+
+ switch (hdr->type) {
+ case TAC_PLUS_AUTHEN:
+ authen(pak);
+ free(pak);
+ return;
+
+ case TAC_PLUS_AUTHOR:
+ author(pak);
+ free(pak);
+ return;
+
+ case TAC_PLUS_ACCT:
+ accounting(pak);
+ return;
+
+ default:
+ /* Note: can't send error reply if type is unknown */
+ report(LOG_ERR, "Illegal type %d in received packet", hdr->type);
+ free(pak);
+ return;
+ }
+}
+
+version()
+{
+ fprintf(stdout, "tac_plus version %s\n", VERSION);
+#ifdef AIX
+ fprintf(stdout,"AIX\n");
+#endif
+#ifdef ARAP_DES
+ fprintf(stdout,"ARAP_DES\n");
+#endif
+#ifdef BSDI
+ fprintf(stdout,"BSDI\n");
+#endif
+#ifdef CONST_SYSERRLIST
+ fprintf(stdout,"CONST_SYSERRLIST\n");
+#endif
+#ifdef DEBUG
+ fprintf(stdout,"DEBUG\n");
+#endif
+#ifdef DES_DEBUG
+ fprintf(stdout,"DES_DEBUG\n");
+#endif
+#ifdef FIONBIO
+ fprintf(stdout,"FIONBIO\n");
+#endif
+#ifdef FREEBSD
+ fprintf(stdout,"FREEBSD\n");
+#endif
+#ifdef GETDTABLESIZE
+ fprintf(stdout,"GETDTABLESIZE\n");
+#endif
+#ifdef HPUX
+ fprintf(stdout,"HPUX\n");
+#endif
+#ifdef LINUX
+ fprintf(stdout,"LINUX\n");
+#endif
+#ifdef LITTLE_ENDIAN
+ fprintf(stdout,"LITTLE_ENDIAN\n");
+#endif
+#ifdef LOG_LOCAL6
+ fprintf(stdout,"LOG_LOCAL6\n");
+#endif
+#ifdef MAXSESS
+ fprintf(stdout,"MAXSESS\n");
+#endif
+#ifdef MIPS
+ fprintf(stdout,"MIPS\n");
+#endif
+#ifdef NEED_BZERO
+ fprintf(stdout,"NEED_BZERO\n");
+#endif
+#ifdef NETBSD
+ fprintf(stdout,"NETBSD\n");
+#endif
+#ifdef NO_PWAGE
+ fprintf(stdout,"NO_PWAGE\n");
+#endif
+#ifdef REAPCHILD
+ fprintf(stdout,"REAPCHILD\n");
+#endif
+#ifdef REARMSIGNAL
+ fprintf(stdout,"REARMSIGNAL\n");
+#endif
+#ifdef SHADOW_PASSWORDS
+ fprintf(stdout,"SHADOW_PASSWORDS\n");
+#endif
+#ifdef SIGTSTP
+ fprintf(stdout,"SIGTSTP\n");
+#endif
+#ifdef SIGTTIN
+ fprintf(stdout,"SIGTTIN\n");
+#endif
+#ifdef SIGTTOU
+ fprintf(stdout,"SIGTTOU\n");
+#endif
+#ifdef SKEY
+ fprintf(stdout,"SKEY\n");
+#endif
+#ifdef SOLARIS
+ fprintf(stdout,"SOLARIS\n");
+#endif
+#ifdef SO_REUSEADDR
+ fprintf(stdout,"SO_REUSEADDR\n");
+#endif
+#ifdef STDLIB_MALLOC
+ fprintf(stdout,"STDLIB_MALLOC\n");
+#endif
+#ifdef STRCSPN
+ fprintf(stdout,"STRCSPN\n");
+#endif
+#ifdef SYSLOG_IN_SYS
+ fprintf(stdout,"SYSLOG_IN_SYS\n");
+#endif
+#ifdef SYSV
+ fprintf(stdout,"SYSV\n");
+#endif
+#ifdef TACPLUS_GROUPID
+ fprintf(stdout,"TACPLUS_GROUPID\n");
+#endif
+#ifdef TAC_PLUS_PORT
+ fprintf(stdout,"TAC_PLUS_PORT\n");
+#endif
+#ifdef TACPLUS_USERID
+ fprintf(stdout,"TACPLUS_USERID\n");
+#endif
+#ifdef TRACE
+ fprintf(stdout,"TRACE\n");
+#endif
+#ifdef UNIONWAIT
+ fprintf(stdout,"UNIONWAIT\n");
+#endif
+#ifdef VOIDSIG
+ fprintf(stdout,"VOIDSIG\n");
+#endif
+#ifdef _BSD1
+ fprintf(stdout,"_BSD1\n");
+#endif
+#ifdef _BSD_INCLUDES
+ fprintf(stdout,"_BSD_INCLUDES\n");
+#endif
+#ifdef __STDC__
+ fprintf(stdout,"__STDC__\n");
+#endif
+}