Logo Search packages:      
Sourcecode: tacacs+ version File versions  Download package

config.c

/*
 * $Id: config.c,v 1.48 2009-04-10 16:19:04 heas Exp $
 *
 * 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 "regexp.h"

/*
   <config>       :=    <decl>*

   <decl>         :=    <top_level_decl> |
                        <acl_decl> |
                        <user_decl> |
                        <group_decl> |
                        <host_decl>

   <top_level_decl>     :=    <authen_default> |
                        accounting file = <filename> |
                        accounting syslog |
                        default authorization = <permission> |
                        key = <string> |
                        logging = <syslog_fac>

   <authen_default>     :=    default authentication = file <filename>

   <permission>         :=    permit | deny

   <filename>           :=    <string>

   <password>           :=    <string>

   <syslog_fac>         :=    (auth|cron|daemon|ftp|kern|lpr|mail|news|
                         syslog|user|uucp|local[0-7])

   <host_decl>          :=    host = <string> {
                              key = <string>
                              prompt = <string>
                              enable = (file|skey|cleartext|des|
                                      nopassword) <filename/string>
                        }

   <user_decl>          :=    user = <string> {
                              [ default service = <permission> ]
                              <user_attr>*
                              <svc>*
                        }

   <password_spec>      :=    file <filename> |
                        skey |
                        cleartext <password> |
                        des <password> |
                        PAM |
                        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> |
                        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 */
static FILE *cf = NULL;                   /* config file pointer */
static int sym_error = 0;                 /* a parsing error occurred */
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";

/*
 * A host definition structure.
 * host-specific information e.g. per-host keys
 *
 * 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.
 */
00125 typedef struct host {
    char *name;               /* host name */
    void *hash;               /* hash table next pointer */
    int line;                 /* line number defined on */
    char *key;                /* host specific key */
    /*char *type;*/           /* host type - XXX: no idea what should be
                         * here */
    char *prompt;       /* host login/username prompt string */
    char *enable;       /* host enable password */
    int noenablepwd;          /* host requires no enable password */
} HOST;

/*
 * 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
 */
00142 typedef 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 */
#ifdef ACLS
    char *acl;                /* hosts (NASs) to allow / deny ACL */
# ifdef UENABLE
    char *enable;       /* user enable pwd */
    int noenablepwd;          /* user requires no enable password */
    char *enableacl;          /* hosts (NASs) to allow/deny enabling */
# endif
#endif
    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 */
} USER;

#ifdef ACLS
struct filter {
    int isdeny;
    char *string;
    regexp *string_reg;
    struct filter *next;
};
typedef struct filter FILTER;

struct acl {
    char *name;               /* acl name */
    void *hash;               /* hash table next pointer */
    int line;                 /* line number defined on */
    NODE *nodes;        /* list of entrys */
};

typedef struct acl ACL;
#endif

/*
 * Only the first 2 fields (name and hash) are used by the hash table
 * routines to hash structures into a table.
 */
00206 union hash {
    struct user u;
#ifdef ACLS
    struct acl a;
#endif
    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 */
#ifdef ACLS
void *acltable[HASH_TAB_SIZE];      /* Table of ACL declarations */
#endif
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 int  circularity_check(void);
static void free_aclstruct(ACL *);
static void free_attrs(NODE *);
static void free_cmd_matches(NODE *);
static void free_hoststruct(HOST *);
static void free_svcs(NODE *);
static void free_userstruct(USER *);
static void getsym(void);
static VALUE      get_value(USER *, int);
#ifdef ACLS
static int  insert_acl_entry(ACL *, int);
static int  parse_acl(void);
#endif
static NODE *parse_attrs(void);
static NODE *parse_cmd_matches(void);
static int  parse_logging(void);
static int  parse_host(void);
static int  parse_opt_attr_default(void);
static int  parse_opt_svc_default(void);
static int  parse_permission(void);
static NODE *parse_svcs(void);
static int  parse_user(void);
static void rch(void);
static void sym_get(void);

#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(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 *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;
    }
}

#ifdef ACLS
static void
free_aclstruct(ACL *acl)
{
    NODE *last, *next = acl->nodes;

    last = next;

    if (debug & DEBUG_CLEAN_FLAG)
      report(LOG_DEBUG, "free_aclstruct %s", acl->name);

    while (next) {
      if (debug & DEBUG_CLEAN_FLAG)
          report(LOG_DEBUG, "free_aclstruct %s %s", acl->name, next->value);
      if (next->value)
          free(next->value);
      if (next->value1)
          free(next->value1);
      next = next->next;
      free(last);
      last = next;
    }

    if (acl->name)
      free(acl->name);
}
#endif

static void
free_cmd_matches(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_hoststruct(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); */
    if (host->prompt)
      free(host->prompt);
    if (host->enable)
      free(host->enable);

    return;
}

static void
free_svcs(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)
{
    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);
#ifdef ACLS
    if (user->acl)
      free(user->acl);
# ifdef UENABLE
    if (user->enable)
      free(user->enable);
    if (user->enableacl)
      free(user->enableacl);
# endif
#endif
    if (user->expires)
      free(user->expires);
    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);
}

/*
 * Exported routines
 */

/* Free all allocated structures preparatory to re-reading the config file */
void
cfg_clean_config(void)
{
    int i;
    USER *entry, *next;
    HOST *host_entry, *hn;
#ifdef ACLS
    ACL *aentry, *anext;
#endif

    if (authen_default) {
      free(authen_default);
      authen_default = NULL;
    }

    if (session.key) {
      free(session.key);
      session.key = NULL;
    }

    if (session.acctfile) {
      free(session.acctfile);
      session.acctfile = NULL;
    }
    session.flags = 0;

#ifdef ACLS
    /* clean the acltable */
    for (i = 0; i < HASH_TAB_SIZE; i++) {
      aentry = (ACL *) acltable[i];
      while (aentry) {
          anext = aentry->hash;
          free_aclstruct(aentry);
          free(aentry);
          aentry = anext;
      }
      acltable[i] = NULL;
    }
#endif

    /* 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;
    }

    /* 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 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(void)
{
    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(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(void)
{
    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(void)
{
    if (sym_code != S_default)
      return(S_deny);

    parse(S_default);
    parse(S_attr);
    parse(S_separator);
    parse(S_permit);
    return(S_permit);
}

#ifdef ACLS
/* insert an acl entry into the named acl node list */
static int
insert_acl_entry(ACL *acl, int isdeny)
{
    NODE *next = acl->nodes;
    NODE *entry = (NODE *) tac_malloc(sizeof(NODE));

    memset(entry, 0, sizeof(NODE));

    entry->type = isdeny;
    entry->value = tac_strdup(sym_buf);
    entry->line = sym_line;

    /* compile the regex */
    entry->value1 = (void *) regcomp((char *) entry->value);
    if (!entry->value1) {
      report(LOG_ERR, "in regex %s on line %d", sym_buf, sym_line);
      tac_exit(1);
    }

    if (acl->nodes == NULL) {
      acl->nodes = entry;
      return(0);
    }

    while (next->next != NULL) {
      next = next->next;
    }
    next->next = entry;

    return(0);
}

/* parse the acl = NAME { allow = regex  deny = regex } */
static int
parse_acl(void)
{
    ACL *n;
    ACL *acl = (ACL *) tac_malloc(sizeof(ACL));
    int isdeny = S_permit;

    memset(acl, 0, sizeof(ACL));

    sym_get();
    parse(S_separator);
    acl->name = tac_strdup(sym_buf);
    acl->line = sym_line;

    n = hash_add_entry(acltable, (void *) acl);

    if (n) {
      parse_error("multiply defined acl %s on lines %d and %d", acl->name,
                n->line, sym_line);
      return(1);
    }
    sym_get();
    parse(S_openbra);

    while (1) {
      switch (sym_code) {
      case S_eof:
          return(0);

      case S_deny:
          isdeny = S_deny;
      case S_permit:
          sym_get();
          parse(S_separator);
          insert_acl_entry(acl, isdeny);
          parse(S_string);
          isdeny = S_permit;
          continue;

      case S_closebra:
          parse(S_closebra);
          return(0);

      default:
          parse_error("Unrecognised keyword %s for acl on line %d", sym_buf,
                  sym_line);

          return(0);
      }
    }
}
#endif

/*
 * 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();

#ifdef ACLS
    memset(acltable, 0, sizeof(acltable));
#endif
    memset(grouptable, 0, sizeof(grouptable));
    memset(usertable, 0, sizeof(usertable));
    memset(hosttable, 0, sizeof(hosttable));

    sym_get();

    /* Top level of parser */
    while (1) {

      switch (sym_code) {
      case S_eof:
          return(0);

      case S_accounting:
          sym_get();

          switch(sym_code) {
            case S_file:
                parse(S_file);
                parse(S_separator);
                if (session.acctfile != NULL)
                  free(session.acctfile);
                session.acctfile = tac_strdup(sym_buf);
                break;

            case S_syslog:
                session.flags |= SESS_FLAG_ACCTSYSL;
                break;
          }

          sym_get();
          continue;

      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);
            parse(S_file);
            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_NOTICE, "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;

#ifdef ACLS
      case S_acl:
          parse_acl();
          continue;
#endif

      case S_logging:
          parse_logging();
          continue;

      default:
          parse_error("Unrecognised token %s on line %d", sym_buf, sym_line);
          return(1);
      }
    }
}

/*
 * Assign a value to a field.  Issue an error message and return 1 if
 * it has 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(void)
{
    HOST *h;
    HOST *host = (HOST *)tac_malloc(sizeof(HOST));
    char buf[MAX_INPUT_LINE_LEN];

    memset(host, 0, 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_prompt:
          ASSIGN(host->prompt);
          sym_get();
          continue;

      case S_enable:
          if (host->enable) {
            parse_error("Duplicate value for %s %s and %s on line %d",
                      codestring(sym_code), host->enable, sym_buf,
                      sym_line);
            tac_exit(1);
          }
          sym_get();
          parse(S_separator);
          switch(sym_code) {

          case S_cleartext:
          case S_des:
            sprintf(buf, "%s ", sym_buf);
            sym_get();
            /* XXX: naughty, this should check the size */
            strcat(buf, sym_buf);
            host->enable = tac_strdup(buf);
            break;

          case S_nopasswd:
            /* set to dummy string, so that we detect a duplicate
             * password definition attempt
             */
            host->enable = tac_strdup(nopasswd_str);
            host->noenablepwd = 1;
            break;

          default:
            parse_error("expecting 'cleartext' or 'des' keyword after "
                      "'enable =' on line %d", sym_line);
          }
          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);
      }
    }
}

/*
 * Parse logging statement.
 * Return 1 on error, otherwise 0
 */
static int
parse_logging(void)
{
    struct _facs  { char *name;
                    int level;
                  } facs[] = {      {"auth", LOG_AUTH},
                              {"cron", LOG_CRON},
                              {"daemon", LOG_DAEMON},
#ifdef LOG_FTP
                              {"ftp", LOG_FTP},
#endif
                              {"kern", LOG_KERN},
                              {"local0", LOG_LOCAL0},
                              {"local1", LOG_LOCAL1},
                              {"local2", LOG_LOCAL2},
                              {"local3", LOG_LOCAL3},
                              {"local4", LOG_LOCAL4},
                              {"local5", LOG_LOCAL5},
                              {"local6", LOG_LOCAL6},
                              {"local7", LOG_LOCAL7},
                              {"lpr", LOG_LPR},
                              {"mail", LOG_MAIL},
                              {"news", LOG_NEWS},
                              {"syslog", LOG_SYSLOG},
                              {"user", LOG_USER},
                              {"uucp", LOG_UUCP},
                              {NULL, 0}
                           };
    int     fac;

    sym_get();
    parse(S_separator);

    for (fac = 0; sym_code == S_string && facs[fac].name != NULL; fac++) {
      if (strcmp(sym_buf, facs[fac].name) == 0)
          break;
    }
    if (facs[fac].name == NULL)
      parse_error("expecting syslog facility on line %d, got %s", sym_line,
                sym_buf);

    facility = facs[fac].level;
    closelog();
    open_logfile();

    sym_get();
    return(0);
}

static int
parse_user(void)
{
    USER *n;
    int isuser;
    USER *user = (USER *) tac_malloc(sizeof(USER));
    int save_sym;
    char **fieldp;
    char buf[MAX_INPUT_LINE_LEN];

    fieldp = NULL;
    memset(user, 0, 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_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) {

#ifdef SKEY
          case S_skey:
            user->login = tac_strdup(sym_buf);
            break;
#endif

#ifdef HAVE_PAM
          case S_pam:
            user->login = tac_strdup(sym_buf);
            break;
#endif

          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:
            sprintf(buf, "%s ", sym_buf);
            sym_get();
            strcat(buf, sym_buf);
            user->login = tac_strdup(buf);
            break;

          default:
            parse_error("expecting 'file', 'cleartext', 'nopassword', "
#ifdef SKEY
                      "'skey', "
#endif
#ifdef HAVE_PAM
                      "'PAM', "
#endif
                      "or 'des' keyword after 'login =' on line %d",
                      sym_line);
          }
          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:
            sprintf(buf, "%s ", sym_buf);
            sym_get();
            strcat(buf, sym_buf);
            user->pap = tac_strdup(buf);
            break;

          default:
            parse_error("expecting 'cleartext', or 'des' keyword after "
                      "'pap =' on line %d", sym_line);
          }
          sym_get();
          continue;

#ifdef ACLS
      case S_acl:
          ASSIGN(user->acl);
          sym_get();
          continue;

# ifdef UENABLE
      case S_enable:
          sym_get();
          parse(S_separator);

          switch(sym_code) {
            case S_file:
            case S_cleartext:
            case S_des:
                sprintf(buf, "%s ", sym_buf);
                sym_get();
                strcat(buf, sym_buf);
                user->enable = tac_strdup(buf);
                break;

            case S_nopasswd:
                /* set to dummy string, so that we detect a duplicate
                 * password definition attempt
                 */
                user->enable = tac_strdup(nopasswd_str);
                user->noenablepwd = 1;
                break;

#ifdef SKEY
            case S_skey:
                user->enable = tac_strdup(sym_buf);
                break;
#endif

            default:
                parse_error("expecting 'file', 'cleartext', 'nopassword', "
#ifdef SKEY
                        "'skey', "
#endif
                        "or 'des' keyword after 'enable =' on line %d",
                        sym_line);
          }
          sym_get();
          continue;

      case S_enableacl:
          ASSIGN(user->enableacl);
          sym_get();
          continue;
# endif
#endif

      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_svcs(void)
{
    NODE *result;

    switch (sym_code) {
    default:
      return(NULL);
    case S_svc:
    case S_cmd:
      break;
    }

    result = (NODE *) tac_malloc(sizeof(NODE));

    memset(result, 0, 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(void)
{
    NODE *result;

    if (sym_code != S_permit && sym_code != S_deny) {
      return(NULL);
    }
    result = (NODE *) tac_malloc(sizeof(NODE));

    memset(result, 0, 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(void)
{
    NODE *result;
    char buf[MAX_INPUT_LINE_LEN];
    int optional = 0;

    if (sym_code == S_closebra) {
      return(NULL);
    }
    result = (NODE *) tac_malloc(sizeof(NODE));

    memset(result, 0, sizeof(NODE));
    result->line = sym_line;

    if (sym_code == S_optional) {
      optional++;
      sym_get();
    }
    result->type = optional ? N_optarg : N_arg;

#ifdef ACLS
    /*
     * "acl" is an acceptable AV for service=exec and may as well be permitted
     * for any other service.  I did not know this when I defined "acl" for
     * connection ACLs.  So, hack it to be a string here.  If the parser were
     * half-way decent, acl just wouldnt be a keyword here.
     */
    if (sym_code == S_acl)
      sym_code = S_string;
#endif
    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
sym_get(void)
{
    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(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(void)
{

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 '"':
            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(void)
{
    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. */
static VALUE
get_value(USER *user, int field)
{
    VALUE v;

    memset(&v, 0, sizeof(VALUE));

    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 */

#ifdef ACLS
    case S_acl:
      v.pval = user->acl;
      break;
# ifdef UENABLE
    case S_enable:
      v.pval = user->enable;
      break;
    case S_noenablepwd:
      v.intval = user->noenablepwd;
      break;
    case S_enableacl:
      v.pval = user->enableacl;
      break;
# endif
#endif

    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;

    default:
      report(LOG_ERR, "get_value: unknown field %d", field);
      break;
    }
    return(v);
}

/* For host, find value of field.  Is not recursive */
VALUE
get_hvalue(HOST *host, int field)
{
    VALUE v;

    memset(&v, 0, sizeof(VALUE));
    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;

      /*case S_type:
          v.pval = host->type;
          break;*/

      case S_prompt:
          v.pval = host->prompt;
          break;

      case S_enable:
          v.pval = host->enable;
          break;

#ifdef UENABLE
      case S_noenablepwd:
          v.intval = host->noenablepwd;
          break;
#endif

      default:
          report(LOG_ERR, "get_value: unknown field %d", field);
          break;
    }
    return(v);
}

/*
 * For each user, check the user does not circularly reference a group.
 * Return 1 if it does.
 */
static int
circularity_check(void)
{
    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 the user is 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(char *name, int isuser, int attr, int recurse)
{
    USER *user, *group;
    VALUE value;

    memset(&value, 0, sizeof(VALUE));

    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 */
    memset(&value, 0, sizeof(VALUE));
    return(value);
}

/* For getting host values */
static VALUE
cfg_get_hvalue(char *name, int attr)
{
    HOST *host;
    VALUE value;

    memset(&value, 0, sizeof(VALUE));

    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 */
    memset(&value, 0, sizeof(VALUE));
    return(value);
}

/* Wrappers for cfg_get_value */
int
cfg_get_intvalue(char *name, int isuser, int attr, int recurse)
{
    int val;

    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);
}

/* Wrappers for cfg_get_hvalue */
char *
cfg_get_phvalue(char *name, int attr)
{
    char *p;

    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);
}

char *
cfg_get_pvalue(char *name, int isuser, int attr, int recurse)
{
    char *p;

    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);
}

/*
 * Read the config file and do some basic sanity checking on it.  Return 1
 * if we find any errors.
 */
int
cfg_read_config(char *cfile)
{
    sym_line = 1;

    if ((cf = fopen(cfile, "r")) == NULL) {
      report(LOG_ERR, "read_config: fopen() error for file %s %s, exiting",
             cfile, strerror(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(char *username)
{
    USER *user;

    user = (USER *) hash_lookup(usertable, username);

    return(user != NULL);
}

/*
 * return expiry string of user. If none, try groups she is a member
 * of, and so on, recursively if recurse is non-zero
 */
char *
cfg_get_expires(char *username, int recurse)
{
    return(cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse));
}

#ifdef ACLS
/*
 * check the acl against the provided ip.  return S_permit (succeed) if the
 * ip matches a permit, else S_deny (fail) if it matches a deny or does not
 * match any of the entries.
 */
int
cfg_acl_check(char *aclname, char *ip)
{
    NODE *next;
    ACL *acl;

    acl = (ACL *) hash_lookup(acltable, aclname);

    if (debug & DEBUG_AUTHEN_FLAG)
      report(LOG_DEBUG, "cfg_acl_check(%s, %s)", aclname, ip);

    if (acl == NULL) {
      report(LOG_ERR, "non-existent acl reference %s", aclname);
      return(S_deny);
    }

    next = acl->nodes;
    while (next) {
      if (regexec(next->value1, ip)) {
          if (debug & DEBUG_AUTHEN_FLAG)
            report(LOG_DEBUG, "ip %s matched %s regex %s of acl filter %s",
                  ip, next->type == S_deny ? "deny" : "permit",
                  next->value, aclname);
          return(next->type);
      }
      next = next->next;
    }

    /* default is fail (implicit deny) - ie: fell off the end */
    if (debug & DEBUG_AUTHEN_FLAG)
      report(LOG_DEBUG, "ip %s did not match in acl filter %s", ip, aclname);
    return(S_deny);
}
#endif

#if defined(UENABLE) || defined(SKEY)
/*
 * return enable password string of user.  If none, try groups user is a
 * member of, and so on, recursively if recurse is non-zero.
 */
char *
cfg_get_enable_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_enable, recurse));
}
#endif

/* For getting host key */
char *
cfg_get_host_key(char *host)
{
    return(cfg_get_phvalue(host, S_key));
}

/* For getting host prompt */
char *
cfg_get_host_prompt(char *host)
{
    return(cfg_get_phvalue(host, S_prompt));
}

/* For getting host enable */
char *
cfg_get_host_enable(char *host)
{
    return(cfg_get_phvalue(host, S_enable));
}

#ifdef UENABLE
/*
 * return value of the noenablepwd field for the host.
 */
int
cfg_get_host_noenablepwd(char *host)
{
    return(cfg_get_hvalue(host, S_noenablepwd).intval);
}
#endif

/*
 * return password string of user.  If none, try groups the user is a member
 * of, and so on, recursively if recurse is non-zero.
 */
char *
cfg_get_login_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse));
}

#ifdef UENABLE 
/*
 * return value of the noenablepwd field.  If none, try groups the user is a
 * member of, and so on, recursively if recurse is non-zero.
 */
int
cfg_get_user_noenablepwd(char *user, int recurse)
{
    return(cfg_get_intvalue(user, TAC_IS_USER, S_noenablepwd, recurse));
}
#endif

/*
 * return value of the nopasswd field.  If none, try groups the user is a
 * member of, and so on, recursively if recurse is non-zero.
 */
int
cfg_get_user_nopasswd(char *user, int recurse)
{
    return(cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse));
}

/*
 * return the secret of the user.  If none, try groups the user is a member of,
 * and so on, recursively if recurse is non-zero.
 */
char *
cfg_get_arap_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse));
}

char *
cfg_get_chap_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse));
}

#ifdef MSCHAP
char *
cfg_get_mschap_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse));
}
#endif /* MSCHAP */

char *
cfg_get_pap_secret(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse));
}

char *
cfg_get_opap_secret(char *user, int recurse)
{
    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(char *user, int recurse)
{
    return(cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse));
}

/*
 * 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(char *username, int type, char *protocol, char *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(char *name, char *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(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 = strchr(arg, '=');

      if (p && node->type == N_optarg)
          *p = '*';
      args[i++] = arg;
    }
    args[i] = NULL;
    return(args);
}

int
cfg_user_svc_default_is_permit(char *user)
{
    int permit;

    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(void)
{
    if (no_user_dflt == S_permit)
      return(1);
    return(0);
}

char *
cfg_get_authen_default(void)
{
    return(authen_default);
}

/*
 * Return 1 if this user has any ppp services configured. Used for
 * authorizing ppp/lcp requests
 */
int
cfg_ppp_is_configured(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);
}

Generated by  Doxygen 1.6.0   Back to index