tacacs-F4.0.4.28/config.c (1,981 lines of code) (raw):

/* * $Id: config.c,v 1.48 2009-04-10 16:19:04 heas Exp $ * * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved * 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 <regex.h> #ifndef REG_OK # ifdef REG_NOERROR # define REG_OK REG_NOERROR # else # define REG_OK 0 # endif #endif /* <config> := <decl>* <decl> := <top_level_decl> | <acl_decl> | <user_decl> | <group_decl> | <host_decl> <top_level_decl> := <authen_default> | accounting file = <filename> | accounting syslog | key = <string> | logging = <syslog_fac> | maxprocs = <maxprocs> | maxprocsperclt = <maxprocsperclt> <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]) <logauthor> <host_decl> := host = <string> { key = <string> prompt = <string> enable = aceclnt|cleartext|des| file <filename/string>| nopassword|skey } <user_decl> := user = <string> { [ default service = <permission> ] <user_attr>* <svc>* } <password_spec> := nopassword | #ifdef ACECLNT aceclnt| #endif cleartext <password> | des <password> | file <filename> | #ifdef HAVE_PAM PAM | #endif skey <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 HAVE_PAM pap = PAM | #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> <proto> := XXX define this <svc_auth> := service = ( arap | connection | exec | ppp protocol = <proto> | shell | slip | system | tty-daemon | <client defined> ) { [ 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 char *authen_default = NULL; /* top level authentication default */ static char *nopasswd_str = "nopassword"; static long int maxprocs = TAC_MAX_PROCS; /* max procs to fork */ static long int maxprocsperclt = TAC_MAX_PROCS_PER_CLIENT; /* max per client */ static long int readtimeout = TAC_PLUS_READ_TIMEOUT; /* read timeout */ static long int writetimeout = TAC_PLUS_WRITE_TIMEOUT; /* write timeout */ static long int accepttimeout = TAC_PLUS_ACCEPT_TIMEOUT; /* accept timeout */ static int logauthor = 0; /* log authorization requests */ /* * 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. */ 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 */ 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; typedef USER GROUP; #ifdef ACLS typedef struct filter { int isdeny; char *string; regex_t *string_reg; struct filter *next; } FILTER; typedef struct acl { char *name; /* acl name */ void *hash; /* hash table next pointer */ int line; /* line number defined on */ NODE *nodes; /* list of entrys */ } ACL; #endif /* * Only the first 2 fields (name and hash) are used by the hash table * routines to hash structures into a table. */ typedef union hash { struct user u; #ifdef ACLS struct acl a; #endif struct host h; } 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) { char buf[256]; NODE *next = acl->nodes; NODE *entry = (NODE *)tac_malloc(sizeof(NODE)); int ecode; memset(entry, 0, sizeof(NODE)); entry->type = isdeny; entry->value = tac_strdup(sym_buf); entry->line = sym_line; /* compile the regex */ entry->value1 = tac_malloc(sizeof(regex_t)); ecode = regcomp((regex_t *)entry->value1, (char *)entry->value, (REG_EXTENDED | REG_NOSUB)); if (ecode) { regerror(ecode, (regex_t *)entry->value1, buf, 256); report(LOG_ERR, "in regex %s on line %d", sym_buf, sym_line); report(LOG_ERR, "regex compile failed: %s", buf); 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() { 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 authentication on line %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_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_maxprocs: parse(S_maxprocs); parse(S_separator); errno = 0; maxprocs = strtol(tac_strdup(sym_buf), NULL, 10); if ((errno) || (maxprocs < 0)) { parse_error("maxprocs must a valid positive integer"); return 1; } sym_get(); continue; case S_maxprocsperclt: parse(S_maxprocsperclt); parse(S_separator); errno = 0; maxprocsperclt = strtol(tac_strdup(sym_buf), NULL, 10); if ((errno) || (maxprocsperclt < 0)) { parse_error("maxprocsperclt must a valid positive integer"); return 1; } sym_get(); continue; case S_readtimeout: parse(S_readtimeout); parse(S_separator); errno = 0; readtimeout = strtol(tac_strdup(sym_buf), NULL, 10); if ((errno) || (readtimeout < 0)) { parse_error("readtimeout must a valid positive integer"); return 1; } sym_get(); continue; case S_writetimeout: parse(S_writetimeout); parse(S_separator); errno = 0; writetimeout = strtol(tac_strdup(sym_buf), NULL, 10); if ((errno) || (writetimeout < 0)) { parse_error("writetimeout must a valid positive integer"); return 1; } sym_get(); continue; case S_accepttimeout: parse(S_accepttimeout); parse(S_separator); errno = 0; accepttimeout = strtol(tac_strdup(sym_buf), NULL, 10); if ((errno) || (accepttimeout < 0)) { parse_error("accepttimeout must a valid positive integer"); return 1; } sym_get(); continue; case S_logauthor: parse(S_logauthor); logauthor = 1; 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 ACECLNT case S_aceclnt: 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 ACECLNT "'aceclnt', " #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) { #ifdef HAVE_PAM case S_pam: user->pap = tac_strdup(sym_buf); break; #endif case S_file: 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', " #ifdef HAVE_PAM "'PAM', " #endif "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 #ifdef ACECLNT case S_aceclnt: user->enable = tac_strdup(sym_buf); break; #endif default: parse_error("expecting 'file', 'cleartext', 'nopassword', " #ifdef SKEY "'skey', " #endif #ifdef ACECLNT "'aceclnt', " #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) { case S_string: result->type = N_svc; /* XXX 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); /* XXX Should perhaps check that this is a known PPP protocol name */ result->value1 = tac_strdup(sym_buf); break; default: parse_error("expecting service type but found %s on line %d", sym_buf, sym_line); return(NULL); } 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) { char buf[256]; NODE *result; int ecode; 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 = tac_malloc(sizeof(regex_t)); ecode = regcomp((regex_t *)result->value1, (char *)result->value, (REG_EXTENDED | REG_NOSUB)); if (ecode) { regerror(ecode, (regex_t *)result->value1, buf, 256); report(LOG_ERR, "in regex %s on line %d", sym_buf, sym_line); report(LOG_ERR, "regex compile failed: %s", buf); 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; /* XXX 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); if (isuser && cfg_user_exists(DEFAULT_USERNAME)) { user = (USER *)hash_lookup(usertable, DEFAULT_USERNAME); report(LOG_DEBUG, "cfg_get_value: using DEFAULT"); } else 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((regex_t *)next->value1, ip, 0, NULL, 0) == REG_OK) { 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_get_maxprocs(void) { return(maxprocs); } int cfg_get_maxprocsperclt(void) { return(maxprocsperclt); } int cfg_get_readtimeout(void) { return(readtimeout); } int cfg_get_writetimeout(void) { return(writetimeout); } int cfg_get_accepttimeout(void) { return(accepttimeout); } int cfg_get_logauthor(void) { return(logauthor); } 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); }