/* 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 #include #include #include #ifndef WITH_INCLUDED_REGEX #ifdef HAVE_REGEX_H #include #endif #endif #include "cfgfile.h" #include "report.h" #include "utils.h" #include "hash.h" #include "parse.h" #include "main.h" #include "do_author.h" /* for "struct identity" */ #ifdef WITH_INCLUDED_REGEX #include "tac_regexp.h" #endif static void sym_get TAC_ARGS((void)); static void when_expr_root_init TAC_ARGS((void)); static void rch TAC_ARGS((void)); static int parse_entity TAC_ARGS((int entity_type)); static NODE *parse_svcs TAC_ARGS((void)); static int parse_conditional_block TAC_ARGS((ENTITY *entity)); static NODE *parse_cmd_matches TAC_ARGS((void)); static NODE *parse_attrs TAC_ARGS((void)); static void getsym TAC_ARGS((void)); static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity)); /* := * := | := | accounting file = | default authorization = permit | key = | authorization = ( first | recursive ) := default authentication = file #if defined(DB) | db #endif := permit | deny := := := user = { [ ] * * } := host = { [ ] * * } := group = { [ ] * * } := default service = ( permit | deny | default ) := file | skey | cleartext | des | #ifdef USE_PAM pam | #endif #if defined(DB) db #endif nopassword := name = | login = | member = | expires = | arap = cleartext | chap = cleartext | #ifdef MSCHAP ms-chap = cleartext | #endif pap = cleartext | pap = des | #ifdef USE_PAM pap = pam | #endif opap = cleartext | global = cleartext | msg = before authorization = | after authorization = | := key = | := enlist = | key = | := member = | enlist = | | := { * } := | := cmd = { * } := | := { * } := service = ( exec | arap | slip | ppp protocol = ) { # first matching is the FINAL one, no further graph scanning occurs! [ default attribute = permit ] * } := [ optional ] = | := { * } := when = # to avoid ambiguous precence by forbid of "or" & "and" without parentheses: := | not | '(' ')' | '(' ')' := | or := | and := ( user | host | group ) */ static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */ static int sym_pos=0; /* current place in sym_buf */ static int sym_ch; /* current parse character */ static int sym_code; /* parser output */ static int sym_line = 1; /* current line number for parsing */ static FILE *cf = NULL; /* config file pointer */ static int sym_error = 0; /* a parsing error has occurred */ static int no_user_dflt = 0; /* default if user doesn't exist */ /* ='default authorization': 0/S_permit */ static int algorithm_recursive = 0; /* use newer authorization alogrithm? */ /* 1 if 'authorization = recursive' */ static char *authen_default = NULL; /* top level authentication default */ /* ='default authentication' */ static int authen_default_method = 0; /* For method check */ /* ='default authentication' symbol */ static char *nopasswd_str = "nopassword"; /* Only the first 2 fields (name and hash) are used by the hash table routines to hashh structures into a table. */ static void *grouptable[HASH_TAB_SIZE]; /* Table of group declarations */ static void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */ static void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */ struct enlist_entity_item { struct enlist_entity_item *next; int parent_type; char *parent; int child_type; char * child; /* will be created when not found (for "enlist") */ struct expr *when; int line; }; static struct enlist_entity_item * enlist_entity_list; static struct enlist_entity_item **enlist_entity_list_tailp = &enlist_entity_list; static void parse_error TAC_ARGS((char *fmt,...)) G_GNUC_PRINTF(1, 2); #ifdef __STDC__ #include /* ANSI C, variable length args */ static void parse_error(char *fmt,...) #else /* __STDC__ */ #include /* has 'vararg' definitions */ /* VARARGS2 */ static void parse_error(fmt, va_alist) char *fmt; va_dcl /* no terminating semi-colon */ #endif /* __STDC__ */ { 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); } const char *cfg_nodestring TAC_ARGS((int type)); const char * cfg_nodestring(type) int type; { switch (type) { default: return ("unknown node type"); case N_arg: return ("N_arg"); case N_optarg: return ("N_optarg"); case N_svc: return ("N_svc"); case N_svc_exec: return ("N_svc_exec"); case N_svc_slip: return ("N_svc_slip"); case N_svc_ppp: return ("N_svc_ppp"); case N_svc_arap: return ("N_svc_arap"); case N_svc_cmd: return ("N_svc_cmd"); case N_permit: return ("N_permit"); case N_deny: return ("N_deny"); } } const char *entity_type_to_string TAC_ARGS((int entity_type)); const char * entity_type_to_string(entity_type) int entity_type; { switch (entity_type) { case S_user: return ("user"); case S_host: return ("host"); case S_group: return ("group"); } return (NULL); } static void **entity_type_to_hashtable TAC_ARGS((int entity_type)); static void ** entity_type_to_hashtable(entity_type) int entity_type; { switch (entity_type) { case S_user: return (usertable); case S_host: return (hosttable); case S_group: return (grouptable); } return (NULL); } void scan_invalidate_entities TAC_ARGS((enum invalidate_scan what)); void scan_invalidate_entities(what) enum invalidate_scan what; { scan_invalidate_entities_hashtable( usertable, what); scan_invalidate_entities_hashtable( hosttable, what); scan_invalidate_entities_hashtable(grouptable, what); } static void free_attrs TAC_ARGS((NODE *node)); static void free_attrs(node) NODE *node; { NODE *next; while (node) { unlink_expr(node->when); free_expr(node->when); node->when = NULL; switch (node->type) { case N_optarg: case N_arg: if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free_cmd_match %s %s", cfg_nodestring(node->type), (const char *) node->value); break; default: report(LOG_ERR, "Illegal node type %s for free_attrs", cfg_nodestring(node->type)); return; } free(node->value); next = node->next; free(node); node = next; } } static void free_cmd_matches TAC_ARGS((NODE *node)); static void free_cmd_matches(node) NODE *node; { NODE *next; while (node) { if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free_cmd_match %s %s", cfg_nodestring(node->type), (const char *) node->value); unlink_expr(node->when); free_expr(node->when); node->when = NULL; free(node->value); /* text */ #ifdef WITH_INCLUDED_REGEX free(node->value1); /* tac_regexp compiled text */ #else /* WITH_INCLUDED_REGEX */ regfree((regex_t *) node->value1); /* POSIX regex compiled text */ #endif /* WITH_INCLUDED_REGEX */ next = node->next; free(node); node = next; } } static void free_svcs TAC_ARGS((NODE *node)); static void free_svcs(node) NODE *node; { NODE *next; while (node) { unlink_expr(node->when); free_expr(node->when); node->when = NULL; switch (node->type) { case N_svc_cmd: if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s %s", cfg_nodestring(node->type), (const char *) 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_enlist_entity_item TAC_ARGS((struct enlist_entity_item *item)); static void free_enlist_entity_item(item) struct enlist_entity_item *item; { free(item->parent); free(item->child); free_expr(item->when); } static void free_entity TAC_ARGS((ENTITY *entity)); static void free_entity(entity) ENTITY *entity; { if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s %s", entity_type_to_string(entity->type), entity->name); /* function MUST be called while the whole entity is still VALID! */ scan_free_entity(entity); if (entity->name) free(entity->name); if (entity->full_name) free(entity->full_name); if (entity->login) free(entity->login); if (entity->expires) free(entity->expires); if (entity->time) free(entity->time); if (entity->arap) free(entity->arap); if (entity->chap) free(entity->chap); #ifdef MSCHAP if (entity->mschap) free(entity->mschap); #endif /* MSCHAP */ if (entity->pap) free(entity->pap); if (entity->opap) free(entity->opap); if (entity->global) free(entity->global); if (entity->msg) free(entity->msg); if (entity->before_author) free(entity->before_author); if (entity->after_author) free(entity->after_author); if (entity->key) free(entity->key); free_svcs(entity->svcs); } static void free_hashtable TAC_ARGS((void **hashtable)); static void free_hashtable(hashtable) void **hashtable; { int i; ENTITY *entity,**entityp; for (i = 0; i < HASH_TAB_SIZE; i++) { entityp = (ENTITY **) (hashtable+i); while ((entity = *entityp)) { *entityp = entity->hash; free_entity(entity); free(entity); } } } /* * Exported routines */ void cfg_clean_config TAC_ARGS((void)); /* Free all allocated structures preparatory to re-reading the config file */ void cfg_clean_config() { struct enlist_entity_item *enlist_entity_item; if (authen_default) { free(authen_default); authen_default = NULL; } if (algorithm_recursive) { algorithm_recursive = 0; } if (authen_default_method) { authen_default_method = 0; } if (session.key) { free(session.key); session.key = NULL; } if (session.acctfile) { free(session.acctfile); session.acctfile = NULL; } if (session.db_acct) { free(session.db_acct); session.db_acct = NULL; } free_hashtable( usertable); free_hashtable( hosttable); free_hashtable(grouptable); while (enlist_entity_list) { enlist_entity_item = enlist_entity_list; enlist_entity_list = enlist_entity_item->next; free_enlist_entity_item(enlist_entity_item); free(enlist_entity_item); } enlist_entity_list_tailp = &enlist_entity_list; } static int parse_permission TAC_ARGS((void)); static int parse_permission() { 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 TAC_ARGS((int symbol)); static int parse(symbol) int symbol; { if (sym_code != symbol) { parse_error("expecting '%s' but found '%s' on line %d", (symbol == S_string ? "string" : codestring(symbol)), sym_buf, sym_line); return (1); } sym_get(); return (0); } static int parse_opt_svc_default TAC_ARGS((void)); static int parse_opt_svc_default() { int retval; if (sym_code != S_default) { return (0); } parse(S_default); parse(S_svc); parse(S_separator); switch (sym_code) { default: parse_error("expecting 'permit', 'deny' or 'default' but found '%s' on line %d", sym_buf, sym_line); return (1); case S_default: if (!algorithm_recursive) { parse_error("'default service = %s' supported only if set top level 'authorization = recursive', on line %d", sym_buf, sym_line); return (1); } /* FALLTHRU */ case S_permit: case S_deny: retval = sym_code; sym_get(); return (retval); } } static int parse_opt_attr_default TAC_ARGS((void)); static int parse_opt_attr_default() { if (sym_code != S_default) return (S_deny); parse(S_default); parse(S_attr); parse(S_separator); parse(S_permit); return (S_permit); } /* Parse lines in the config file, creating data structures Return 1 on error, otherwise 0 */ static int parse_decls TAC_ARGS((void)); static int parse_decls() { no_user_dflt = 0; /* default if user doesn't exist */ algorithm_recursive = 0; /* use backward compatible alg. by default */ when_expr_root_init(); enlist_entity_list_tailp = &enlist_entity_list; sym_code = 0; rch(); bzero(grouptable, sizeof(grouptable)); bzero(usertable, sizeof(usertable)); bzero(hosttable, sizeof(hosttable)); sym_get(); /* Top level of parser */ while (1) { switch (sym_code) { case S_eof: return (0); case S_accounting: sym_get(); parse(S_file); parse(S_separator); if (session.acctfile) free(session.acctfile); session.acctfile = tac_strdup(sym_buf); sym_get(); continue; #ifdef DB case S_db_accounting: sym_get(); parse(S_separator); if (session.db_acct) free(session.db_acct); session.db_acct = tac_strdup(sym_buf); sym_get(); continue; #endif case S_default: sym_get(); switch (sym_code) { default: parse_error( "Expecting default authorization/authentication on lines %d", sym_line); return (1); case S_authentication: if (authen_default) { parse_error( "Multiply defined authentication default on line %d", sym_line); return (1); } parse(S_authentication); parse(S_separator); switch(sym_code) { case S_file: #ifdef DB case S_db: #endif #ifdef USE_LDAP case S_ldap: #endif #ifdef USE_PAM case S_pam: #endif authen_default_method = sym_code; break; default: parse_error("expecting default_method keyword after 'default authentication = ' on line %d",sym_line); return (1); } sym_get(); authen_default = tac_strdup(sym_buf); sym_get(); continue; case S_authorization: parse(S_authorization); parse(S_separator); parse(S_permit); no_user_dflt = S_permit; report(LOG_INFO, "default authorization = permit is now deprecated. Please use user = DEFAULT instead"); continue; } case S_authorization: sym_get(); parse(S_separator); switch (sym_code) { default: parse_error("expecting 'first' or 'recursive' but found '%s' on line %d", sym_buf, sym_line); return (1); case S_first: parse(S_first); algorithm_recursive = 0; continue; case S_recursive: parse(S_recursive); algorithm_recursive = 1; continue; } case S_key: /* Process a key declaration. */ sym_get(); 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_user: case S_host: case S_group: parse_entity(sym_code); 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's already been assigned. This is a macro because I was sick of repeating the same code fragment over and over */ #define ASSIGN(field) \ sym_get(); parse(S_separator); if (field) { \ parse_error("Duplicate value for %s %s and %s on line %d", \ codestring(sym_code), field, sym_buf, sym_line); \ tac_exit(1); \ } \ field = tac_strdup(sym_buf); static struct expr *when_expr_root; #define WHEN_EXPR_ROOT_SANE() \ (when_expr_root && !when_expr_root->next && when_expr_root->type==S_and) #define WHEN_EXPR_ROOT_EMPTY() \ (WHEN_EXPR_ROOT_SANE() && !when_expr_root->u.and_or.child_first) static struct expr *parse_expr_node TAC_ARGS((int single_item)); static struct expr * parse_expr_node(single_item) int single_item; { struct expr *expr_root = NULL; struct expr **succ_exprp = &expr_root; struct expr *expr; while (1) { switch (sym_code) { case S_not: expr = (struct expr *) tac_malloc(sizeof(struct expr)); expr->line = sym_line; *succ_exprp = expr; expr->next = NULL; succ_exprp = &expr->next; expr->type = S_not; sym_get(); expr->u.not.child = parse_expr_node(1 /* single_item */); if (!expr->u.not.child) { free_expr(expr_root); return (NULL); } break; case S_user: case S_host: case S_group: expr = (struct expr *) tac_malloc(sizeof(struct expr)); expr->line = sym_line; *succ_exprp = expr; expr->next = NULL; succ_exprp = &expr->next; expr->type = sym_code; sym_get(); expr->u.entity.name = tac_strdup(sym_buf); sym_get(); expr->u.entity.entity = NULL; /* not known yet */ break; case S_openparen: sym_get(); expr = parse_expr_node(0 /* single_item */); *succ_exprp = expr; parse(S_closeparen); if (expr->next) { report(LOG_ERR, "Illegal filled next field of parsed parenthesed expr"); free_expr(expr_root); return (NULL); } succ_exprp = &expr->next; break; default: parse_error("expecting 'not', 'user', 'host', 'group' or '(' but found '%s' on line %d", sym_buf, sym_line); free_expr(expr_root); return (NULL); } if (single_item) /* used by 'not' operator with high precedence */ return (expr_root); switch (sym_code) { case S_and: case S_or: if (expr_root->type == (sym_code==S_and ? S_or : S_and)) { parse_error("ambiguous use of 'and' together with 'or', parentheses required on line %d", sym_line); free_expr(expr_root); return (NULL); } if (expr_root->type != sym_code) { expr = (struct expr *) tac_malloc(sizeof(struct expr)); expr->line = sym_line; expr->next = NULL; expr->type = sym_code; expr->u.and_or.child_first = expr_root; expr_root = expr; } sym_get(); continue; } return(expr_root); } } static struct expr *parse_when_decl TAC_ARGS((void)); static struct expr * parse_when_decl() { parse(S_when); if (!algorithm_recursive) { parse_error("'when' conditionals supported only if set top level 'authorization = recursive', on line %d", sym_line); return (NULL); } parse(S_separator); return (parse_expr_node(0 /* single_item */)); } static void when_expr_root_init TAC_ARGS((void)); static void when_expr_root_init() { free_expr(when_expr_root); when_expr_root = new_expr(S_and); } static int push_parsed_when_decl TAC_ARGS((void)); static int push_parsed_when_decl() { struct expr *parsed_expr; parsed_expr = parse_when_decl(); if (!parsed_expr) return (1); if (!WHEN_EXPR_ROOT_SANE()) { report(LOG_ERR, "INTERNAL: when_expr_root not valid during push_parsed_when_decl()!"); free_expr(parsed_expr); return (1); } if (parsed_expr->next) { report(LOG_ERR, "INTERNAL: Illegal filled next field of parsed expr"); free_expr(parsed_expr); return (1); } parsed_expr->next = when_expr_root->u.and_or.child_first; when_expr_root->u.and_or.child_first = parsed_expr; when_expr_root->line = parsed_expr->line; return (0); } static int pop_when_decl TAC_ARGS((void)); static int pop_when_decl() { struct expr *first_expr; if (!WHEN_EXPR_ROOT_SANE()) { report(LOG_ERR, "INTERNAL: when_expr_root not valid during pop_when_decl()!"); return (1); } first_expr = when_expr_root->u.and_or.child_first; if (!first_expr) { report(LOG_ERR, "No expr in stack and pop_when_decl() called"); return (1); } when_expr_root->u.and_or.child_first = first_expr->next; first_expr->next = NULL; free_expr(first_expr); return (0); } static struct expr *copy_current_when_decl TAC_ARGS((void)); static struct expr * copy_current_when_decl() { return (dupl_expr(when_expr_root)); } static struct expr *when_expr_dungeon; static void starve_when_decl TAC_ARGS((void)); static void starve_when_decl() { if (!WHEN_EXPR_ROOT_SANE()) { report(LOG_WARNING, "INTERNAL: when_expr_root not sane during starve_when_decl!"); } when_expr_root->next = when_expr_dungeon; when_expr_dungeon = when_expr_root; when_expr_root = NULL; when_expr_root_init(); } static int feed_when_decl TAC_ARGS((void)); static int feed_when_decl() { if (!when_expr_dungeon) { report(LOG_ERR, "INTERNAL: No expr in dungeon and feed_when_decl() called"); return (1); } if (!WHEN_EXPR_ROOT_EMPTY()) { report(LOG_WARNING, "INTERNAL: Some 'when' expression found still pushed in dungeon during feed_when_decl()!"); } free_expr(when_expr_root); when_expr_root = when_expr_dungeon; when_expr_dungeon = when_expr_dungeon->next; when_expr_root->next = NULL; return (0); } ENTITY *entity_lookup TAC_ARGS((int type, const char *name)); ENTITY * entity_lookup(type, name) int type; const char *name; { return (hash_lookup(entity_type_to_hashtable(type), name)); } static int enlist_entity_connect TAC_ARGS((void)); static int enlist_entity_connect() { struct enlist_entity_item *item; ENTITY *parent_entity, *child_entity; while ((item=enlist_entity_list)) { parent_entity = entity_lookup(item->parent_type, item->parent); if (!parent_entity) { parse_error("Entity %s %s not defined, referenced as parent on line %d", entity_type_to_string(item->parent_type), item->parent, item->line); return (1); } child_entity = entity_lookup(item-> child_type, item-> child); if (!child_entity) { child_entity = new_entity(item->child_type, item->child, item->line); if (!child_entity) return (1); /* 'hash_add_entry()' conflict */ item->child = NULL; /* don't free string ref'ed from 'child_entity'! */ } if (!enlist_entity_direct(parent_entity, child_entity, item->when)) return (1); /* entities not found */ enlist_entity_list = item->next; item->when = NULL; free_enlist_entity_item(item); free(item); } enlist_entity_list_tailp = &enlist_entity_list; return (0); } static void enlist_entity TAC_ARGS((int parent_type, const char *parent, int child_type, const char *child)); static void enlist_entity(parent_type, parent, child_type, child) int parent_type; const char *parent; int child_type; const char *child; { struct enlist_entity_item *item = (struct enlist_entity_item *) tac_malloc(sizeof(struct enlist_entity_item)); item->next = NULL; *enlist_entity_list_tailp = item; enlist_entity_list_tailp = &item->next; item->parent_type = parent_type; item->parent = tac_strdup(parent); item-> child_type = child_type; item->child = tac_strdup(child); item->when = copy_current_when_decl(); item->line = sym_line; } static int parse_entity_spec TAC_ARGS((void)); /* returns 0 for error, otherwise S_user, S_host or S_group; sym_buf filled */ static int parse_entity_spec() { int retval; if (sym_code != S_user && sym_code != S_host && sym_code != S_group ) { parse_error("Expecting 'user', 'host' or ' group' as entity specification, found %s on line %d", sym_buf, sym_line); return (0); } retval = sym_code; sym_get(); return (retval); } static int parse_conditional_block_item TAC_ARGS((ENTITY *entity)); static int parse_conditional_block_item(entity) ENTITY *entity; { switch (sym_code) { case S_eof: return (1); /* case S_closebra: not needed, handled by our caller parse_conditional_block() */ default: parse_error("Unrecognised keyword %s for entity on line %d", sym_buf, sym_line); return (1); case S_member: sym_get(); parse(S_separator); enlist_entity(S_group, sym_buf, entity->type, entity->name); sym_get(); break; case S_enlist: { int parsed_entity_type; if (entity->type != S_group) { parse_error("'enlist' keyword allowed only in 'group' section on line %d", sym_line); return (1); } sym_get(); parse(S_separator); parsed_entity_type = parse_entity_spec(); if (!parsed_entity_type) return (1); enlist_entity(entity->type, entity->name, parsed_entity_type, sym_buf); sym_get(); break; } case S_svc: case S_cmd: if (entity->svcs) { /* * Already parsed some services/commands. Thanks to Gabor Kiss * who found this bug. */ NODE *p; for (p=entity->svcs; p->next; p=p->next) /* NULL STMT */; p->next = parse_svcs(); } else { entity->svcs = parse_svcs(); } break; case S_when: if (parse_conditional_block(entity)) return (1); break; } return (0); } static int parse_conditional_block TAC_ARGS((ENTITY *entity)); static int parse_conditional_block(entity) ENTITY *entity; { int retval = -1 /* GCC paranoia */; if (push_parsed_when_decl()) return (1); parse(S_openbra); while (1) { if (sym_code == S_closebra) { sym_get(); retval = 0; /* success */ break; } if (parse_conditional_block_item(entity)) { retval = 1; /* failure */ break; } } if (pop_when_decl()) return (1); return (retval); } /* passed 'name' WILL be directly stored to returned ENTITY, don't touch it! */ ENTITY *new_entity TAC_ARGS((int type, char *name, int line)); ENTITY * new_entity(type, name, line) int type; char *name; int line; { ENTITY *entity = (ENTITY *) tac_malloc(sizeof(ENTITY)); ENTITY *hash_conflict; bzero(entity, sizeof(ENTITY)); tac_list_init(&entity->to_parent_membership_list); tac_list_init(&entity->to_child_membership_list ); entity->to_child_membership_num = 0; scan_init_entity(entity); entity->type = type; entity->name = name; entity->line = line; hash_conflict = hash_add_entry(entity_type_to_hashtable(type), (void *) entity); if (hash_conflict) { parse_error("multiply defined %s %s on lines %d and %d", entity_type_to_string(type), entity->name, hash_conflict->line, sym_line); free (entity); return (NULL); } return (entity); } static int parse_entity TAC_ARGS((int entity_type)); static int parse_entity(entity_type) int entity_type; { ENTITY *entity; int save_sym; char **fieldp = NULL /* GCC paranoia */; char buf[MAX_INPUT_LINE_LEN]; sym_get(); parse(S_separator); entity = new_entity(entity_type, tac_strdup(sym_buf) /* name */, sym_line /* line */); if (!entity) return (1); /* 'hash_add_entry()' conflict, 'tac_strdup(sym_buf)' leaked! */ sym_get(); parse(S_openbra); /* Is the default deny for svcs or cmds to be overridden? */ entity->svc_dflt = parse_opt_svc_default(); while (1) { if (entity_type != S_user) switch (sym_code) { case S_key: ASSIGN(entity->key); sym_get(); continue; } switch (sym_code) { case S_eof: return (0); case S_time: ASSIGN(entity->time); sym_get(); continue; case S_before: sym_get(); parse(S_authorization); if (entity->before_author) free(entity->before_author); entity->before_author = tac_strdup(sym_buf); sym_get(); continue; case S_after: sym_get(); parse(S_authorization); if (entity->after_author) free(entity->after_author); entity->after_author = tac_strdup(sym_buf); sym_get(); continue; case S_login: if (entity->login) { parse_error("Duplicate value for %s %s and %s on line %d", codestring(sym_code), entity->login, sym_buf, sym_line); tac_exit(1); } sym_get(); parse(S_separator); switch(sym_code) { case S_skey: entity->login = tac_strdup(sym_buf); break; case S_nopasswd: /* set to dummy string, so that we detect a duplicate * password definition attempt */ entity->login = tac_strdup(nopasswd_str); entity->nopasswd = 1; break; case S_file: case S_cleartext: case S_des: #ifdef USE_PAM case S_pam: #endif /* USE_PAM */ #ifdef DB case S_db: #endif /* USE DB */ sprintf(buf, "%s ", sym_buf); sym_get(); strcat(buf, sym_buf); entity->login = tac_strdup(buf); break; default: #ifdef USE_PAM parse_error( "expecting 'file', 'cleartext', 'pam'.'nopassword', 'skey', or 'des' keyword after 'login =' on line %d", sym_line); #else parse_error( "expecting 'file', 'cleartext', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d", sym_line); #endif /* USE_PAM */ } sym_get(); continue; case S_pap: if (entity->pap) { parse_error("Duplicate value for %s %s and %s on line %d", codestring(sym_code), entity->pap, sym_buf, sym_line); tac_exit(1); } sym_get(); parse(S_separator); switch(sym_code) { case S_cleartext: case S_des: #ifdef USE_PAM case S_pam: #endif /*USE_PAM */ sprintf(buf, "%s ", sym_buf); sym_get(); strcat(buf, sym_buf); entity->pap = tac_strdup(buf); break; sprintf(buf, "%s ", sym_buf); entity->pap = tac_strdup(buf); break; default: #ifdef USE_PAM parse_error( "expecting 'cleartext', 'pam', or 'des' keyword after 'pap =' on line %d", sym_line); #else parse_error( "expecting 'cleartext', or 'des' keyword after 'pap =' on line %d", sym_line); #endif /*USE_PAM */ } sym_get(); continue; case S_name: ASSIGN(entity->full_name); sym_get(); continue; case S_expires: ASSIGN(entity->expires); sym_get(); continue; case S_message: ASSIGN(entity->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); switch (save_sym) { case S_arap: fieldp = &entity->arap; break; case S_chap: fieldp = &entity->chap; break; #ifdef MSCHAP case S_mschap: fieldp = &entity->mschap; break; #endif /* MSCHAP */ case S_pap: fieldp = &entity->pap; break; case S_opap: fieldp = &entity->opap; break; case S_global: fieldp = &entity->global; break; default: report(LOG_ERR, "INTERNAL: fieldp not recognized (on line %d)", sym_line); continue; } if (*fieldp) { parse_error("Duplicate value for %s %s and %s on line %d", 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", &entity->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 = is obsolete. Use login = des \n"); } if (parse_conditional_block_item(entity)) return (0); /* error message already printed */ } } } static NODE *parse_svcs TAC_ARGS((void)); static NODE * parse_svcs() { NODE *result; switch (sym_code) { default: return (NULL); case S_svc: case S_cmd: break; } result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; /* cmd declaration */ if (sym_code == S_cmd) { parse(S_cmd); parse(S_separator); result->value = tac_strdup(sym_buf); sym_get(); parse(S_openbra); starve_when_decl(); result->value1 = parse_cmd_matches(); parse(S_closebra); if (feed_when_decl()) tac_exit(1); /* no error return possibility */ result->type = N_svc_cmd; result->when = copy_current_when_decl(); expr_sink_register(result->when); 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); starve_when_decl(); result->dflt = parse_opt_attr_default(); result->value = parse_attrs(); parse(S_closebra); feed_when_decl(); result->when = copy_current_when_decl(); expr_sink_register(result->when); result->next = parse_svcs(); return (result); } /* := */ static NODE *parse_cmd_matches TAC_ARGS((void)); static NODE * parse_cmd_matches() { NODE *retval = NULL, **succp = &retval; NODE *result; for (;;) { switch (sym_code) { default: return (retval); case S_when: if (push_parsed_when_decl()) tac_exit(1); /* no error return possibility */ parse(S_openbra); result = parse_cmd_matches(); parse(S_closebra); if (pop_when_decl()) tac_exit(1); /* no error return possibility */ break; case S_permit: case S_deny: result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; result->type = (parse_permission() == S_permit) ? N_permit : N_deny; result->value = tac_strdup(sym_buf); #ifdef WITH_INCLUDED_REGEX result->value1 = (void *) tac_regcomp(result->value); #else /* WITH_INCLUDED_REGEX */ result->value1 = tac_malloc(sizeof(regex_t)); if (regcomp(result->value1, result->value /* regex */, REG_NOSUB /* cflags */)) { free(result->value1); result->value1 = NULL; } #endif /* WITH_INCLUDED_REGEX */ if (!result->value1) { report(LOG_ERR, "in regular expression %s on line %d", sym_buf, sym_line); tac_exit(1); } sym_get(); result->when = copy_current_when_decl(); expr_sink_register(result->when); result->next = NULL; } *succp = result; while (result->next) result = result->next; /* skip parsed chain from parse_cmd_matches() */ succp = &result->next; } /* NOTREACHED */ } static NODE *parse_attrs TAC_ARGS((void)); static NODE * parse_attrs() { NODE *retval = NULL, **succp = &retval; NODE *result; char buf[MAX_INPUT_LINE_LEN]; int optional; for (;;) { optional = 0; switch (sym_code) { case S_closebra: return (retval); case S_when: if (push_parsed_when_decl()) tac_exit(1); /* no error return possibility */ parse(S_openbra); result = parse_attrs(); parse(S_closebra); if (pop_when_decl()) tac_exit(1); /* no error return possibility */ break; case S_optional: optional = 1; sym_get(); /* FALLTHRU */ default: result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; result->type = optional ? N_optarg : N_arg; strcpy(buf, sym_buf); parse(S_string); strcat(buf, sym_buf); parse(S_separator); strcat(buf, sym_buf); parse(S_string); result->value = tac_strdup(buf); result->when = copy_current_when_decl(); expr_sink_register(result->when); result->next = NULL; } *succp = result; while (result->next) result = result->next; /* skip parsed chain from parse_attrs() */ succp = &result->next; } /* NOTREACHED */ } static void sym_get TAC_ARGS((void)); static void sym_get() { getsym(); if (debug & DEBUG_PARSE_FLAG) { report(LOG_DEBUG, "line=%d sym=%s code=%d buf='%s'", sym_line, codestring(sym_code), sym_code, sym_buf); } } static char *sym_buf_add TAC_ARGS((int c)); static char * sym_buf_add(c) int c; /* promoted "char" type */ { 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 TAC_ARGS((void)); static void getsym() { next: switch (sym_ch) { case EOF: sym_code = S_eof; return; case '\n': sym_line++; rch(); goto next; case '\t': case ' ': while (sym_ch == ' ' || sym_ch == '\t') rch(); goto next; case '=': strcpy(sym_buf, "="); sym_code = S_separator; rch(); return; case '{': strcpy(sym_buf, "{"); sym_code = S_openbra; rch(); return; case '}': strcpy(sym_buf, "}"); sym_code = S_closebra; rch(); return; case '(': strcpy(sym_buf, "("); sym_code = S_openparen; rch(); return; case ')': strcpy(sym_buf, ")"); sym_code = S_closeparen; rch(); return; case '#': while ((sym_ch != '\n') && (sym_ch != EOF)) rch(); goto next; case '"': rch(); sym_pos = 0; while (1) { if (sym_ch == '"') { break; } /* backslash-double-quote is supported inside strings */ /* also allow \n */ if (sym_ch == '\\') { rch(); switch (sym_ch) { case 'n': /* preserve the slash for \n */ if (!sym_buf_add('\\')) { sym_code = S_unknown; rch(); return; } /* fall through */ case '"': if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); continue; default: sym_code = S_unknown; rch(); return; } } if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); } rch(); if (!sym_buf_add('\0')) { sym_code = S_unknown; rch(); return; } sym_code = S_string; return; default: sym_pos = 0; while (sym_ch != '\t' && sym_ch != ' ' && sym_ch != '=' && sym_ch != '\n') { if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); } if (!sym_buf_add('\0')) { sym_code = S_unknown; rch(); return; } sym_code = keycode(sym_buf); if (sym_code == S_unknown) sym_code = S_string; return; } } static void rch TAC_ARGS((void)); static void rch() { if (sym_error) { sym_ch = EOF; return; } sym_ch = getc(cf); if (parse_only && sym_ch != EOF) fprintf(stderr, "%c", sym_ch); } static VALUE get_value TAC_ARGS((ENTITY *entity, int field)); /* Find the value of a field. Does not recurse. */ static VALUE get_value(entity, field) ENTITY *entity; int field; { VALUE v; v.pval = NULL; /* do both just for sure... */ v.intval = 0; if (!entity) { parse_error("get_value: illegal entity"); return (v); } switch (field) { case S_name: v.pval = entity->name; break; case S_login: v.pval = entity->login; break; case S_global: v.pval = entity->global; break; case S_expires: v.pval = entity->expires; break; case S_arap: v.pval = entity->arap; break; case S_chap: v.pval = entity->chap; break; #ifdef MSCHAP case S_mschap: v.pval = entity->mschap; break; #endif /* MSCHAP */ case S_pap: v.pval = entity->pap; break; case S_opap: v.pval = entity->opap; break; case S_message: v.pval = entity->msg; break; case S_svc: v.pval = entity->svcs; break; case S_before: v.pval = entity->before_author; break; case S_after: v.pval = entity->after_author; break; case S_svc_dflt: v.intval = entity->svc_dflt; break; #ifdef MAXSESS case S_maxsess: v.intval = entity->maxsess; break; #endif case S_nopasswd: v.intval = entity->nopasswd; break; case S_time: v.pval = entity->time; break; case S_key: if (entity->type == S_user) { report(LOG_ERR, "get_value: S_key field not supported in %s %s", entity_type_to_string(entity->type), entity->name); v.pval = NULL; return(v); } v.pval = entity->key; break; default: report(LOG_ERR, "get_value: unknown field %d", field); break; } return (v); } /* Internal graph scanning routines */ static enum value_scan_func_result value_scan TAC_ARGS((int type, const char *name, int recurse, value_scan_func_t func, void *func_data)); static enum value_scan_func_result value_scan(type, name, recurse, func, func_data) int type; const char *name; int recurse; value_scan_func_t func; void *func_data; { ENTITY *entity; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "value_scan: find %s %s, recurse=%d", entity_type_to_string(type), name, recurse); entity = entity_lookup(type, name); if (!entity) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "value_scan: no %s named %s", entity_type_to_string(type), name); return (VSFR_CONTINUE); } return (value_scan_entity(entity, recurse, func, func_data)); } /* For each user, check she doesn't circularly reference a group. Return 1 if it does */ static int circularity_check_failed; static void circularity_check_fail TAC_ARGS((struct membership *membership)); static void circularity_check_fail(membership) struct membership *membership; { ENTITY *entity; circularity_check_failed = 1; report(LOG_ERR, "recursively defined groups:"); while (membership) { entity = MEMBERSHIP_TO_CHILD_ENTITY(membership); report(LOG_ERR, "%s %s", entity_type_to_string(entity->type), entity->name); membership = value_scan_backward(entity); } } static enum value_scan_func_result circularity_check_func TAC_ARGS((ENTITY *entity, void *func_data)); static enum value_scan_func_result circularity_check_func(entity, func_data /* unused */) ENTITY *entity; void *func_data; { /* only useful to speedup case of failure */ if (circularity_check_failed) return (VSFR_FOUND); return (VSFR_CONTINUE); } static int circularity_check TAC_ARGS((void)); static int circularity_check() { ENTITY *entity; ENTITY **users_base = (ENTITY **) hash_get_entries(usertable); ENTITY **users; /* users */ for (users = users_base; *users; users++) { entity = *users; if (debug & DEBUG_PARSE_FLAG) report(LOG_DEBUG, "circularity_check: user=%s", entity->name); circularity_check_failed = 0; value_scan_forward_seen_hook = circularity_check_fail; value_scan_entity(entity, TAC_PLUS_RECURSE, (value_scan_func_t) circularity_check_func, NULL /* func_data-unused */); value_scan_forward_seen_hook = NULL; if (circularity_check_failed) break; } free(users_base); return (circularity_check_failed); } /* Return a value for a group or user (isuser says if this name is a group or a user name). If no value exists, and recurse is true, also check groups we are a member of, recursively. Returns void * because it can return a string or a node pointer (should really return a union pointer). */ static VALUE cfg_get_value_VALUE; /* private */ static enum value_scan_func_result cfg_get_value_func TAC_ARGS((ENTITY *entity, int *attrp)); static enum value_scan_func_result cfg_get_value_func(entity,attrp /* func_data */) ENTITY *entity; int *attrp; { /* found the entry. Lookup value from attr=value */ cfg_get_value_VALUE = get_value(entity, *attrp); if (cfg_get_value_VALUE.pval) return (VSFR_FOUND); return (VSFR_CONTINUE); } static VALUE cfg_get_value TAC_ARGS((int type, const char *name, int attr, int recurse)); static VALUE cfg_get_value(type, name, attr, recurse) int type; const char *name; int attr, recurse; { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_value: type=%s name=%s attr=%s recurse=%d", entity_type_to_string(type), name, codestring(attr), recurse); cfg_get_value_VALUE.pval = NULL; value_scan(type, name, recurse, (value_scan_func_t) cfg_get_value_func, &attr /* func_data */); return (cfg_get_value_VALUE); } /* Wrappers for cfg_get_value: */ int cfg_get_intvalue TAC_ARGS((int type, const char *name, int attr, int recurse)); int cfg_get_intvalue(type, name, attr, recurse) int type; const char *name; int attr, recurse; { int val = cfg_get_value(type, name, attr, recurse).intval; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val); return(val); } const char *cfg_get_pvalue TAC_ARGS((int type, const char *name, int attr, int recurse)); const char * cfg_get_pvalue(type, name, attr, recurse) int type; const char *name; int attr, recurse; { char *p = cfg_get_value(type, name, 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 TAC_ARGS((const char *cfile)); int cfg_read_config(cfile) const char *cfile; { sym_line = 1; if ((cf = fopen(cfile, "r")) == NULL) { report(LOG_ERR, "read_config: fopen() error for file %s %s, exiting", cfile, sys_errlist[errno]); return (1); } if (parse_decls() || sym_error) { fclose(cf); return (1); } if (0 || enlist_entity_connect() || expr_sink_commit() /* circularity is allowed in the new fully-recursive algorithm */ || (!algorithm_recursive && circularity_check()) ) { fclose(cf); return (1); } if (!WHEN_EXPR_ROOT_EMPTY() || when_expr_dungeon) { report(LOG_ERR, "Some 'when' expression found still pushed on stack"); fclose(cf); return (1); } fclose(cf); return (0); } /* return 1 if user exists, 0 otherwise */ int cfg_user_exists TAC_ARGS((const char *username)); int cfg_user_exists(username) const char *username; { return (NULL != hash_lookup(usertable, username)); } /* return expiry string of user. If none, try groups she is a member * on, and so on, recursively if recurse is non-zero */ const char *cfg_get_expires TAC_ARGS((const char *username, int recurse)); const char * cfg_get_expires(username, recurse) const char *username; int recurse; { return (cfg_get_pvalue(S_user, username, S_expires, recurse)); } /* return time string of user. If none, try groups she is a member * on, and so on, recursively if recurse is non-zero */ const char *cfg_get_timestamp TAC_ARGS((const char *username, int recurse)); const char * cfg_get_timestamp(username, recurse) const char *username; int recurse; { return (cfg_get_pvalue(S_user, username, S_time, recurse)); } /* return password string of user. If none, try groups she is a member * on, and so on, recursively if recurse is non-zero */ const char *cfg_get_login_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_login_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_login, recurse)); } /* return value of the nopasswd field. If none, try groups she is a member * on, and so on, recursively if recurse is non-zero */ int cfg_get_user_nopasswd TAC_ARGS((const char *user, int recurse)); int cfg_get_user_nopasswd(user, recurse) const char *user; int recurse; { return (cfg_get_intvalue(S_user, user, S_nopasswd, recurse)); } /* return user's secret. If none, try groups she is a member * on, and so on, recursively if recurse is non-zero */ const char *cfg_get_arap_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_arap_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_arap, recurse)); } const char *cfg_get_chap_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_chap_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_chap, recurse)); } #ifdef MSCHAP const char *cfg_get_mschap_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_mschap_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_mschap, recurse)); } #endif /* MSCHAP */ const char *cfg_get_pap_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_pap_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_pap, recurse)); } const char *cfg_get_opap_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_opap_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_opap, recurse)); } /* return the global password for the user (or the group, etc.) */ const char *cfg_get_global_secret TAC_ARGS((const char *user, int recurse)); const char * cfg_get_global_secret(user, recurse) const char *user; int recurse; { return (cfg_get_pvalue(S_user, user, S_global, recurse)); } #ifdef USE_PAM /* Return a pointer to a node representing a PAM Service name */ const char *cfg_get_pam_service TAC_ARGS((const char *user, int recurse)); const char * cfg_get_pam_service(user, recurse) const char *user; int recurse; { const char *cfg_passwd; const char *p; cfg_passwd = cfg_get_pap_secret(user, recurse); if (!cfg_passwd) cfg_passwd = cfg_get_global_secret(user, recurse); if (!cfg_passwd && !cfg_user_exists(user)) { cfg_passwd = cfg_get_authen_default(); switch (cfg_get_authen_default_method()) { case (S_pam): if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd); return(cfg_passwd); break; default: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I havent find any PAM Service!!"); return(NULL);/* Haven't any PAM Service!! */ } } p = tac_find_substring("pam ", cfg_passwd); if(p) { /* We find PAM services */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I get PAM sevice:%s",p); return (p); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "No any PAM Sevice"); return(NULL); } #endif /* For PAM */ /* Return a pointer to a node representing a given service authorization, taking care of recursion issues correctly. Protocol is only read if the svctype is N_svc_ppp. svcname is only read if type is N_svc. */ struct cfg_get_svc_node_param { int svctype; const char *protocol, *svcname; NODE *node; int retval; }; static enum value_scan_func_result cfg_get_svc_node_func TAC_ARGS((ENTITY *entity, struct cfg_get_svc_node_param *param)); static enum value_scan_func_result cfg_get_svc_node_func(entity, param /* func_data */) ENTITY *entity; struct cfg_get_svc_node_param *param; { NODE *svc; enum eval_result svc_default; for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) { if (svc->type != param->svctype) continue; if (param->svctype == N_svc_ppp && param->protocol && !STREQ(svc->value1, param->protocol)) continue; if (param->svctype == N_svc && param->svcname && !STREQ(svc->value1, param->svcname )) continue; if (expr_eval(svc->when) != ER_TRUE) /* expensive */ continue; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: found %s proto=%s svcname=%s", cfg_nodestring(param->svctype), param->protocol ? param->protocol : "", param->svcname ? param->svcname : ""); param->node = svc; param->retval = 1; return (VSFR_FOUND); } /* look at 'default service' settings */ svc_default = entity_svc_default(entity); switch (svc_default) { case ER_TRUE: case ER_FALSE: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: svc=%s protocol=%s svcname=%s forced %s by default service", cfg_nodestring(param->svctype), param->protocol ? param->protocol : "", param->svcname ? param->svcname : "", (svc_default == ER_TRUE ? "permit" : "deny")); param->retval = (svc_default == ER_TRUE); return (VSFR_FOUND); default: /* shouldn't happen */ case ER_UNKNOWN: return (VSFR_CONTINUE); } /* NOTREACHED */ } int cfg_get_svc_node TAC_ARGS((const char *username, int svctype, const char *protocol, const char *svcname, int recurse, NODE **nodep)); int cfg_get_svc_node(username, svctype, protocol, svcname, recurse, nodep) const char *username; int svctype; const char *protocol; const char *svcname; int recurse; NODE **nodep; { struct cfg_get_svc_node_param param; enum value_scan_func_result vsfr; param.svctype = svctype; param.protocol = protocol; param.svcname = svcname; param.node = NULL; param.retval = 0; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d", username, cfg_nodestring(svctype), protocol ? protocol : "", svcname ? svcname : "", recurse); vsfr = value_scan(S_user, username, recurse, (value_scan_func_t) cfg_get_svc_node_func, ¶m /* func_data */); if (nodep) *nodep = param.node; if (vsfr == VSFR_FOUND) return (param.retval); /* The service does not exist. Do the default */ return (cfg_no_user_permitted() ? 1 : 0); } /* Return a pointer to the node representing a set of command tac_regexp matches for a user and command, handling recursion issues correctly */ struct cfg_authorize_cmd_param { const char *cmd; const char *args; enum eval_result result; }; static enum value_scan_func_result cfg_authorize_cmd_func TAC_ARGS((ENTITY *entity, struct cfg_authorize_cmd_param *param)); static enum value_scan_func_result cfg_authorize_cmd_func(entity, param /* func_data */) ENTITY *entity; struct cfg_authorize_cmd_param *param; { NODE *svc; for (svc = (NODE *) get_value(entity, S_svc).pval; svc; svc = svc->next) { NODE *node; if (svc->type != N_svc_cmd) continue; if (!STREQ(svc->value, param->cmd)) continue; if (expr_eval(svc->when) != ER_TRUE) /* expensive */ continue; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_authorize_cmd: found cmd %s %s node", param->cmd, cfg_nodestring(svc->type)); /* we have 'cmd ' point, now traverse through its 'permit'/'deny' pairs: */ for (node = svc->value1; node; node = node->next) { int match; if (expr_eval(node->when) != ER_TRUE) /* expensive */ continue; #ifdef WITH_INCLUDED_REGEX match = tac_regexec((tac_regexp *) node->value1, param->args); #else /* WITH_INCLUDED_REGEX */ match = !regexec((const regex_t *) node->value1, param->args /* string */, 0 /* nmatch */, NULL /* pmatch */, 0 /* eflags */); #endif /* WITH_INCLUDED_REGEX */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %smatch", node->line, param->cmd, node->type == N_permit ? "permit" : "deny", (const char *) node->value, param->args, (match ? "" : "no ")); } if (!match) continue; switch (node->type) { case N_permit: if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%s %s permitted by line %d", param->cmd, param->args, node->line); } param->result = ER_TRUE; return (VSFR_FOUND); break; case N_deny: if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%s %s denied by line %d", param->cmd, param->args, node->line); } param->result = ER_FALSE; return (VSFR_FOUND); break; default: report(LOG_ERR, "INTERNAL: illegal configuration node: %s: %s %s", session.peer, param->cmd, param->args); param->result = ER_UNKNOWN; /* error */ return (VSFR_FOUND); } } if (!algorithm_recursive) { /* compatibility mode: */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s exists, but no args match, denied (as no 'authorization = recursive' found)", param->cmd); param->result = ER_FALSE; /* emulate last "deny .*" */ return (VSFR_FOUND); } } /* look at 'default service' settings */ param->result = entity_svc_default(entity); switch (param->result) { case ER_TRUE: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s does not exist, permitted by default", param->cmd); return (VSFR_FOUND); case ER_FALSE: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s does not exist, denied by default", param->cmd); return (VSFR_FOUND); default: /* shouldn't happen */ case ER_UNKNOWN: return (VSFR_CONTINUE); } /* NOTREACHED */ } enum eval_result cfg_authorize_cmd TAC_ARGS((const char *username, const char *cmd, const char *args)); enum eval_result cfg_authorize_cmd(username, cmd, args) const char *username; const char *cmd; const char *args; { struct cfg_authorize_cmd_param param; param.cmd = cmd; param.args = args; param.result = ER_UNKNOWN; /* error */ if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_authorize_cmd: name=%s cmdname=%s args=%s", username, cmd, args); value_scan(S_user, username, TAC_PLUS_RECURSE, (value_scan_func_t) cfg_authorize_cmd_func, ¶m /* func_data */); if (param.result != ER_UNKNOWN) return (param.result); /* The command does not exist. Do the default */ return (cfg_no_user_permitted() ? ER_TRUE : ER_FALSE); } /* Return an array of character strings representing configured AV * 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 TAC_ARGS((NODE *svcnode, int *denyp)); char ** cfg_get_svc_attrs(svcnode, denyp) NODE *svcnode; int *denyp; { int i; NODE *node; char **args; *denyp = 1; if (!svcnode) return (NULL); *denyp = (svcnode->dflt == S_deny); i = 0; for (node = svcnode->value; node; node = node->next) i++; args = (char **) tac_malloc(sizeof(char *) * (i + 1)); i = 0; for (node = svcnode->value; node; node = node->next) { char *arg; char *p; if (expr_eval(node->when) != ER_TRUE) /* expensive */ continue; /* ignore this node */ arg = tac_strdup(node->value); p = index(arg, '='); if (p && node->type == N_optarg) *p = '*'; args[i++] = arg; } args[i] = NULL; return (args); } static enum eval_result entity_svc_default TAC_ARGS((ENTITY *entity)); static enum eval_result entity_svc_default(entity) ENTITY *entity; { switch (entity->svc_dflt) { case S_permit: return (ER_TRUE); case S_deny: return (ER_FALSE); case S_default: case 0: /* not specified */ return (ER_UNKNOWN); default: report(LOG_ERR, "INTERNAL: invalid entity svc_dflt (%d)", entity->svc_dflt); return (ER_UNKNOWN); } } int cfg_no_user_permitted TAC_ARGS((void)); int cfg_no_user_permitted() { if (no_user_dflt == S_permit) return (1); return (0); } const char *cfg_get_authen_default TAC_ARGS((void)); const char * cfg_get_authen_default() { return (authen_default); } int cfg_get_authen_default_method TAC_ARGS((void)); /* For describe authentication method(pam,file,db..etc) */ int cfg_get_authen_default_method() { return (authen_default_method); } /* Host entity management: */ const char *cfg_get_host_key TAC_ARGS((const char *host)); /* For getting host key */ const char * cfg_get_host_key(host) const char *host; { return (cfg_get_pvalue(S_host, host, S_key, algorithm_recursive /* recurse */)); } static ENTITY *force_belong_entity TAC_ARGS((int type, const char *name)); static ENTITY * force_belong_entity(type, name) int type; const char *name; { ENTITY *entity = entity_lookup(type, name); if (entity) eval_force_belong_entity(entity); return (entity); } /* assumed existing initialized "session.peer*" */ static ENTITY *request_peer_addr; static ENTITY *request_peer; static ENTITY *request_DEFAULT_group; static void enlist_request_peer TAC_ARGS((const char *hostname, ENTITY **entityp)); static void enlist_request_peer(hostname, entityp) const char *hostname; ENTITY **entityp; { *entityp = NULL; if (!hostname) return; *entityp = force_belong_entity(S_host, hostname); if (*entityp && request_DEFAULT_group) virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, *entityp /* child */); } /* Try to build the following scenery: * * host [=ER_TRUE] * | * +-- group * * host [=ER_TRUE] * | * +-- group */ void cfg_request_scan_begin TAC_ARGS((void)); void cfg_request_scan_begin() { request_scan_begin(); request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME); if (session.peer_addr != session.peer) enlist_request_peer(session.peer_addr, &request_peer_addr); enlist_request_peer(session.peer, &request_peer); } /* Try to build the following scenery: * * ( user username> | user ) [=ER_TRUE] * | * +-- host [=ER_TRUE] * | | * | +- group * | * +-- host [=ER_TRUE] * | | * | +-- group * | * +-- group */ void cfg_request_identity TAC_ARGS((const struct identity *identity)); void cfg_request_identity(identity) const struct identity *identity; { ENTITY *user_entity,*request_DEFAULT_group; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_request_identity: username=%s, NAS_name=%s, NAS_port=%s, NAC_address=%s, priv_lvl=%d", identity->username, identity->NAS_name, identity->NAS_port, identity->NAC_address, identity->priv_lvl); user_entity = force_belong_entity(S_user, identity->username); request_DEFAULT_group = entity_lookup(S_group, DEFAULT_GROUPNAME); if (!user_entity) user_entity = force_belong_entity(S_user, DEFAULT_USERNAME); request_scan_user_known = 1; if (!user_entity) return; if (request_peer_addr) virtual_enlist_entity_direct(request_peer_addr /* parent */, user_entity /* child */); if (request_peer ) virtual_enlist_entity_direct(request_peer /* parent */, user_entity /* child */); if (request_DEFAULT_group) virtual_enlist_entity_direct(request_DEFAULT_group /* parent */, user_entity /* child */); }