/* 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 "regexp.h" /* := * := | := | accounting file = default authorization = permit | key = := default authentication = file #if defined(DB) | db ) #endif := permit | deny := := := user = { [ default service = [ permit | deny ] ] * * } := 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 = := | := cmd = { * } := := service = ( exec | arap | slip | ppp protocol = { [ default attribute = permit ] * } := [ optional ] = */ static char sym_buf[MAX_INPUT_LINE_LEN]; /* parse buffer */ static int sym_pos=0; /* current place in sym_buf */ static int sym_ch; /* current parse character */ static int sym_code; /* parser output */ static int sym_line = 1; /* current line number for parsing */ static FILE *cf = NULL; /* config file pointer */ static int sym_error = 0; /* a parsing error has occurred */ static int no_user_dflt = 0; /* default if user doesn't exist */ static char *authen_default = NULL; /* top level authentication default */ static int authen_default_method = 0; /*For method check */ static char *nopasswd_str = "nopassword"; /* A host definition structure. Currently unused, but when we start configuring host-specific information e.g. per-host keys, this is where it should be kept. The first 2 fields (name and hash) are used by the hash table routines to hash this structure into a table. Do not (re)move them */ struct host { char *name; /* host name */ void *hash; /* hash table next pointer */ int line; /* line number defined on */ char *key; /* host spesific key */ char *type; /* host type */ }; /* A user or group definition The first 2 fields (name and hash) are used by the hash table routines to hash this structure into a table. Move them at your peril */ struct user { char *name; /* username */ void *hash; /* hash table next pointer */ int line; /* line number defined on */ long flags; /* flags field */ #define FLAG_ISUSER 1 /* this structure represents a user */ #define FLAG_ISGROUP 2 /* this structure represents a group */ #define FLAG_SEEN 4 /* for circular definition detection */ char *full_name; /* users full name */ char *login; /* Login password */ int nopasswd; /* user requires no password */ char *global; /* password to use if none set */ char *member; /* group we are a member of */ char *expires; /* expiration date */ char *arap; /* our arap secret */ char *pap; /* our pap secret */ char *opap; /* our outbound pap secret */ char *chap; /* our chap secret */ #ifdef MSCHAP char *mschap; /* our mschap secret */ #endif /* MSCHAP */ char *msg; /* a message for this user */ char *before_author; /* command to run before authorization */ char *after_author; /* command to run after authorization */ int svc_dflt; /* default authorization behaviour for svc or * cmd */ NODE *svcs; /* pointer to svc nodes */ #ifdef MAXSESS int maxsess; /* Max sessions/user */ #endif /* MAXSESS */ char *time; /* Timestamp */ }; typedef struct host HOST; typedef struct user USER; /* Only the first 2 fields (name and hash) are used by the hash table routines to hashh structures into a table. */ union hash { struct user u; struct host h; }; typedef union hash HASH; void *grouptable[HASH_TAB_SIZE];/* Table of group declarations */ void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */ void *hosttable[HASH_TAB_SIZE]; /* Table of host declarations */ static void sym_get(); #ifdef __STDC__ #include /* ANSI C, variable length args */ static void parse_error(char *fmt,...) #else #include /* has 'vararg' definitions */ /* VARARGS2 */ static void parse_error(fmt, va_alist) char *fmt; va_dcl /* no terminating semi-colon */ #endif { char msg[256]; /* temporary string */ va_list ap; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vsprintf(msg, fmt, ap); va_end(ap); report(LOG_ERR, "%s", msg); fprintf(stderr, "Error: %s\n", msg); tac_exit(1); } char * cfg_nodestring(type) int type; { switch (type) { default: return ("unknown node type"); case N_arg: return ("N_arg"); case N_optarg: return ("N_optarg"); case N_svc: return ("N_svc"); case N_svc_exec: return ("N_svc_exec"); case N_svc_slip: return ("N_svc_slip"); case N_svc_ppp: return ("N_svc_ppp"); case N_svc_arap: return ("N_svc_arap"); case N_svc_cmd: return ("N_svc_cmd"); case N_permit: return ("N_permit"); case N_deny: return ("N_deny"); } } static void free_attrs(node) NODE *node; { NODE *next; while (node) { switch (node->type) { case N_optarg: case N_arg: if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free_cmd_match %s %s", cfg_nodestring(node->type), node->value); break; default: report(LOG_ERR, "Illegal node type %s for free_attrs", cfg_nodestring(node->type)); return; } free(node->value); next = node->next; free(node); node = next; } } static void free_cmd_matches(node) NODE *node; { NODE *next; while (node) { if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free_cmd_match %s %s", cfg_nodestring(node->type), node->value); free(node->value); /* text */ free(node->value1); /* regexp compiled text */ next = node->next; free(node); node = next; } } static void free_svcs(node) NODE *node; { NODE *next; while (node) { switch (node->type) { case N_svc_cmd: if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s %s", cfg_nodestring(node->type), node->value); free(node->value); /* cmd name */ free_cmd_matches(node->value1); next = node->next; free(node); node = next; continue; case N_svc: case N_svc_ppp: free(node->value1); /* FALL-THROUGH */ case N_svc_exec: case N_svc_arap: case N_svc_slip: if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s", cfg_nodestring(node->type)); free_attrs(node->value); next = node->next; free(node); node = next; continue; default: report(LOG_ERR, "Illegal node type %d for free_svcs", node->type); return; } } } static void free_userstruct(user) USER *user; { if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s %s", (user->flags & FLAG_ISUSER) ? "user" : "group", user->name); if (user->name) free(user->name); if (user->full_name) free(user->full_name); if (user->login) free(user->login); if (user->member) free(user->member); if (user->expires) free(user->expires); if (user->time) free(user->time); if (user->arap) free(user->arap); if (user->chap) free(user->chap); #ifdef MSCHAP if (user->mschap) free(user->mschap); #endif /* MSCHAP */ if (user->pap) free(user->pap); if (user->opap) free(user->opap); if (user->global) free(user->global); if (user->msg) free(user->msg); if (user->before_author) free(user->before_author); if (user->after_author) free(user->after_author); free_svcs(user->svcs); } static void free_hoststruct(host) HOST *host; { if (debug & DEBUG_CLEAN_FLAG) report(LOG_DEBUG, "free %s", host->name); if (host->name) free(host->name); if (host->key) free(host->key); if (host->type) free(host->type); } /* * Exported routines */ /* Free all allocated structures preparatory to re-reading the config file */ void cfg_clean_config() { int i; USER *entry, *next; HOST *host_entry,*hn; if (authen_default) { free(authen_default); authen_default = NULL; } if (authen_default_method) { authen_default_method = 0; } if (session.key) { free(session.key); session.key = NULL; } if (session.acctfile) { free(session.acctfile); session.acctfile = NULL; } if (session.db_acct) { free(session.db_acct); session.db_acct = NULL; } /* clean the hosttable */ for (i = 0; i < HASH_TAB_SIZE; i++) { host_entry = (HOST *) hosttable[i]; while (host_entry) { hn = host_entry->hash; free_hoststruct(host_entry); free(host_entry); host_entry = hn; } hosttable[i] = NULL; } /* the grouptable */ for (i = 0; i < HASH_TAB_SIZE; i++) { entry = (USER *) grouptable[i]; while (entry) { next = entry->hash; free_userstruct(entry); free(entry); entry = next; } grouptable[i] = NULL; } /* the usertable */ for (i = 0; i < HASH_TAB_SIZE; i++) { entry = (USER *) usertable[i]; while (entry) { next = entry->hash; free_userstruct(entry); free(entry); entry = next; } usertable[i] = NULL; } } static int parse_permission() { int symbol = sym_code; if (sym_code != S_permit && sym_code != S_deny) { parse_error("expecting permit or deny but found '%s' on line %d", sym_buf, sym_line); return (0); } sym_get(); return (symbol); } static int parse(symbol) int symbol; { if (sym_code != symbol) { parse_error("expecting '%s' but found '%s' on line %d", (symbol == S_string ? "string" : codestring(symbol)), sym_buf, sym_line); return (1); } sym_get(); return (0); } static int parse_opt_svc_default() { if (sym_code != S_default) { return (0); } parse(S_default); parse(S_svc); parse(S_separator); if (sym_code == S_permit) { parse(S_permit); return (S_permit); } parse(S_deny); return (S_deny); } static int parse_opt_attr_default() { if (sym_code != S_default) return (S_deny); parse(S_default); parse(S_attr); parse(S_separator); parse(S_permit); return (S_permit); } static int parse_user(); static int parse_host(); static void rch(); /* Parse lines in the config file, creating data structures Return 1 on error, otherwise 0 */ static int parse_decls() { no_user_dflt = 0; /* default if user doesn't exist */ sym_code = 0; rch(); bzero(grouptable, sizeof(grouptable)); bzero(usertable, sizeof(usertable)); bzero(hosttable, sizeof(hosttable)); sym_get(); /* Top level of parser */ while (1) { switch (sym_code) { case S_eof: return (0); case S_accounting: sym_get(); parse(S_file); parse(S_separator); if (session.acctfile) free(session.acctfile); session.acctfile = tac_strdup(sym_buf); sym_get(); continue; #ifdef DB case S_db_accounting: sym_get(); parse(S_separator); if (session.db_acct) free(session.db_acct); session.db_acct = tac_strdup(sym_buf); sym_get(); continue; #endif case S_default: sym_get(); switch (sym_code) { default: parse_error( "Expecting default authorization/authentication on lines %d", sym_line); return (1); case S_authentication: if (authen_default) { parse_error( "Multiply defined authentication default on line %d", sym_line); return (1); } parse(S_authentication); parse(S_separator); switch(sym_code) { case S_file: #ifdef DB case S_db: #endif #ifdef USE_LDAP case S_ldap; #endif #ifdef USE_PAM case S_pam: #endif authen_default_method = sym_code; break; default: parse_error("expecting default_method keyword after 'default authentication = ' on line %d",sym_line); return (1); } sym_get(); authen_default = tac_strdup(sym_buf); sym_get(); continue; case S_authorization: parse(S_authorization); parse(S_separator); parse(S_permit); no_user_dflt = S_permit; report(LOG_INFO, "default authorization = permit is now deprecated. Please use user = DEFAULT instead"); continue; } case S_key: /* Process a key declaration. */ sym_get(); parse(S_separator); if (session.key) { parse_error("multiply defined key on lines %d and %d", session.keyline, sym_line); return (1); } session.key = tac_strdup(sym_buf); session.keyline = sym_line; sym_get(); continue; case S_host: parse_host(); continue; case S_user: case S_group: parse_user(); continue; /* case S_host: parse_host(); continue; */ default: parse_error("Unrecognised token %s on line %d", sym_buf, sym_line); return (1); } } } static NODE *parse_svcs(); /* Assign a value to a field. Issue an error message and return 1 if it's already been assigned. This is a macro because I was sick of repeating the same code fragment over and over */ #define ASSIGN(field) \ sym_get(); parse(S_separator); if (field) { \ parse_error("Duplicate value for %s %s and %s on line %d", \ codestring(sym_code), field, sym_buf, sym_line); \ tac_exit(1); \ } \ field = tac_strdup(sym_buf); static int parse_host() { HOST *h; HOST *host = (HOST *) tac_malloc(sizeof(HOST)); int save_sym; char buf[MAX_INPUT_LINE_LEN]; bzero(host, sizeof(HOST)); sym_get(); parse(S_separator); host->name = tac_strdup(sym_buf); host->line = sym_line; h = hash_add_entry(hosttable, (void *) host); if (h) { parse_error("multiply defined %s on lines %d and %d", host->name, h->line, sym_line); return (1); } sym_get(); parse(S_openbra); while (1) { switch (sym_code) { case S_eof: return (0); case S_key: ASSIGN(host->key); sym_get(); continue; case S_type: ASSIGN(host->type); sym_get(); continue; case S_closebra: parse(S_closebra); return (0); default: parse_error("Unrecognised keyword %s for host %s on line %d", sym_buf, host->name,sym_line); return (0); } } /* while */ } /* finish parse_host */ static int parse_user() { USER *n; int isuser; USER *user = (USER *) tac_malloc(sizeof(USER)); int save_sym; char **fieldp; char buf[MAX_INPUT_LINE_LEN]; bzero(user, sizeof(USER)); isuser = (sym_code == S_user); sym_get(); parse(S_separator); user->name = tac_strdup(sym_buf); user->line = sym_line; if (isuser) { user->flags |= FLAG_ISUSER; n = hash_add_entry(usertable, (void *) user); } else { user->flags |= FLAG_ISGROUP; n = hash_add_entry(grouptable, (void *) user); } if (n) { parse_error("multiply defined %s %s on lines %d and %d", isuser ? "user" : "group", user->name, n->line, sym_line); return (1); } sym_get(); parse(S_openbra); /* Is the default deny for svcs or cmds to be overridden? */ user->svc_dflt = parse_opt_svc_default(); while (1) { switch (sym_code) { case S_eof: return (0); case S_time: ASSIGN(user->time); sym_get(); continue; case S_before: sym_get(); parse(S_authorization); if (user->before_author) free(user->before_author); user->before_author = tac_strdup(sym_buf); sym_get(); continue; case S_after: sym_get(); parse(S_authorization); if (user->after_author) free(user->after_author); user->after_author = tac_strdup(sym_buf); sym_get(); continue; case S_svc: case S_cmd: if (user->svcs) { /* * Already parsed some services/commands. Thanks to Gabor Kiss * who found this bug. */ NODE *p; for (p=user->svcs; p->next; p=p->next) /* NULL STMT */; p->next = parse_svcs(); } else { user->svcs = parse_svcs(); } continue; case S_login: if (user->login) { parse_error("Duplicate value for %s %s and %s on line %d", codestring(sym_code), user->login, sym_buf, sym_line); tac_exit(1); } sym_get(); parse(S_separator); switch(sym_code) { case S_skey: user->login = tac_strdup(sym_buf); break; case S_nopasswd: /* set to dummy string, so that we detect a duplicate * password definition attempt */ user->login = tac_strdup(nopasswd_str); user->nopasswd = 1; break; case S_file: case S_cleartext: case S_des: #ifdef USE_PAM case S_pam: #endif /* USE_PAM */ #ifdef DB case S_db: #endif /* USE DB */ sprintf(buf, "%s ", sym_buf); sym_get(); strcat(buf, sym_buf); user->login = tac_strdup(buf); break; default: #ifdef USE_PAM parse_error( "expecting 'file', 'cleartext', 'pam'.'nopassword', 'skey', or 'des' keyword after 'login =' on line %d", sym_line); #else parse_error( "expecting 'file', 'cleartext', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d", sym_line); #endif /* USE_PAM */ } sym_get(); continue; case S_pap: if (user->pap) { parse_error("Duplicate value for %s %s and %s on line %d", codestring(sym_code), user->pap, sym_buf, sym_line); tac_exit(1); } sym_get(); parse(S_separator); switch(sym_code) { case S_cleartext: case S_des: #ifdef USE_PAM case S_pam: #endif /*USE_PAM */ sprintf(buf, "%s ", sym_buf); sym_get(); strcat(buf, sym_buf); user->pap = tac_strdup(buf); break; sprintf(buf, "%s ", sym_buf); user->pap = tac_strdup(buf); break; default: #ifdef USE_PAM parse_error( "expecting 'cleartext', 'pam', or 'des' keyword after 'pap =' on line %d", sym_line); #else parse_error( "expecting 'cleartext', or 'des' keyword after 'pap =' on line %d", sym_line); #endif /*USE_PAM */ } sym_get(); continue; case S_name: ASSIGN(user->full_name); sym_get(); continue; case S_member: ASSIGN(user->member); sym_get(); continue; case S_expires: ASSIGN(user->expires); sym_get(); continue; case S_message: ASSIGN(user->msg); sym_get(); continue; case S_arap: case S_chap: #ifdef MSCHAP case S_mschap: #endif /* MSCHAP */ case S_opap: case S_global: save_sym = sym_code; sym_get(); parse(S_separator); sprintf(buf, "%s ", sym_buf); parse(S_cleartext); strcat(buf, sym_buf); if (save_sym == S_arap) fieldp = &user->arap; if (save_sym == S_chap) fieldp = &user->chap; #ifdef MSCHAP if (save_sym == S_mschap) fieldp = &user->mschap; #endif /* MSCHAP */ if (save_sym == S_pap) fieldp = &user->pap; if (save_sym == S_opap) fieldp = &user->opap; if (save_sym == S_global) fieldp = &user->global; if (*fieldp) { parse_error("Duplicate value for %s %s and %s on line %d", codestring(save_sym), *fieldp, sym_buf, sym_line); tac_exit(1); } *fieldp = tac_strdup(buf); sym_get(); continue; case S_closebra: parse(S_closebra); return (0); #ifdef MAXSESS case S_maxsess: sym_get(); parse(S_separator); if (sscanf(sym_buf, "%d", &user->maxsess) != 1) { parse_error("expecting integer, found '%s' on line %d", sym_buf, sym_line); } sym_get(); continue; #endif /* MAXSESS */ default: if (STREQ(sym_buf, "password")) { fprintf(stderr, "\npassword = is obsolete. Use login = des \n"); } parse_error("Unrecognised keyword %s for user on line %d", sym_buf, sym_line); return (0); } } } static NODE *parse_attrs(); static NODE *parse_cmd_matches(); static NODE * parse_svcs() { NODE *result; switch (sym_code) { default: return (NULL); case S_svc: case S_cmd: break; } result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; /* cmd declaration */ if (sym_code == S_cmd) { parse(S_cmd); parse(S_separator); result->value = tac_strdup(sym_buf); sym_get(); parse(S_openbra); result->value1 = parse_cmd_matches(); result->type = N_svc_cmd; parse(S_closebra); result->next = parse_svcs(); return (result); } /* svc declaration */ parse(S_svc); parse(S_separator); switch (sym_code) { default: parse_error("expecting service type but found %s on line %d", sym_buf, sym_line); return (NULL); case S_string: result->type = N_svc; /* should perhaps check that this is an allowable service name */ result->value1 = tac_strdup(sym_buf); break; case S_exec: result->type = N_svc_exec; break; case S_arap: result->type = N_svc_arap; break; case S_slip: result->type = N_svc_slip; break; case S_ppp: result->type = N_svc_ppp; parse(S_ppp); parse(S_protocol); parse(S_separator); /* Should perhaps check that this is a known PPP protocol name */ result->value1 = tac_strdup(sym_buf); break; } sym_get(); parse(S_openbra); result->dflt = parse_opt_attr_default(); result->value = parse_attrs(); parse(S_closebra); result->next = parse_svcs(); return (result); } /* := */ static NODE * parse_cmd_matches() { NODE *result; if (sym_code != S_permit && sym_code != S_deny) { return (NULL); } result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; result->type = (parse_permission() == S_permit) ? N_permit : N_deny; result->value = tac_strdup(sym_buf); result->value1 = (void *) regcomp(result->value); if (!result->value1) { report(LOG_ERR, "in regular expression %s on line %d", sym_buf, sym_line); tac_exit(1); } sym_get(); result->next = parse_cmd_matches(); return (result); } static NODE * parse_attrs() { NODE *result; char buf[MAX_INPUT_LINE_LEN]; int optional = 0; if (sym_code == S_closebra) { return (NULL); } result = (NODE *) tac_malloc(sizeof(NODE)); bzero(result, sizeof(NODE)); result->line = sym_line; if (sym_code == S_optional) { optional++; sym_get(); } result->type = optional ? N_optarg : N_arg; strcpy(buf, sym_buf); parse(S_string); strcat(buf, sym_buf); parse(S_separator); strcat(buf, sym_buf); parse(S_string); result->value = tac_strdup(buf); result->next = parse_attrs(); return (result); } static void getsym(); static void sym_get() { getsym(); if (debug & DEBUG_PARSE_FLAG) { report(LOG_DEBUG, "line=%d sym=%s code=%d buf='%s'", sym_line, codestring(sym_code), sym_code, sym_buf); } } static char * sym_buf_add(c) char c; { if (sym_pos >= MAX_INPUT_LINE_LEN) { sym_buf[MAX_INPUT_LINE_LEN-1] = '\0'; if (debug & DEBUG_PARSE_FLAG) { report(LOG_DEBUG, "line too long: line=%d sym=%s code=%d buf='%s'", sym_line, codestring(sym_code), sym_code, sym_buf); } return(NULL); } sym_buf[sym_pos++] = c; return(sym_buf); } static void getsym() { next: switch (sym_ch) { case EOF: sym_code = S_eof; return; case '\n': sym_line++; rch(); goto next; case '\t': case ' ': while (sym_ch == ' ' || sym_ch == '\t') rch(); goto next; case '=': strcpy(sym_buf, "="); sym_code = S_separator; rch(); return; case '{': strcpy(sym_buf, "{"); sym_code = S_openbra; rch(); return; case '}': strcpy(sym_buf, "}"); sym_code = S_closebra; rch(); return; case '#': while ((sym_ch != '\n') && (sym_ch != EOF)) rch(); goto next; case '"': rch(); sym_pos = 0; while (1) { if (sym_ch == '"') { break; } /* backslash-double-quote is supported inside strings */ /* also allow \n */ if (sym_ch == '\\') { rch(); switch (sym_ch) { case 'n': /* preserve the slash for \n */ if (!sym_buf_add('\\')) { sym_code = S_unknown; rch(); return; } /* fall through */ case '"': if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); continue; default: sym_code = S_unknown; rch(); return; } } if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); } rch(); if (!sym_buf_add('\0')) { sym_code = S_unknown; rch(); return; } sym_code = S_string; return; default: sym_pos = 0; while (sym_ch != '\t' && sym_ch != ' ' && sym_ch != '=' && sym_ch != '\n') { if (!sym_buf_add(sym_ch)) { sym_code = S_unknown; rch(); return; } rch(); } if (!sym_buf_add('\0')) { sym_code = S_unknown; rch(); return; } sym_code = keycode(sym_buf); if (sym_code == S_unknown) sym_code = S_string; return; } } static void rch() { if (sym_error) { sym_ch = EOF; return; } sym_ch = getc(cf); if (parse_only && sym_ch != EOF) fprintf(stderr, "%c", sym_ch); } /* For a user or group, find the value of a field. Does not recurse. */ VALUE get_value(user, field) USER *user; int field; { VALUE v; v.intval = 0; if (!user) { parse_error("get_value: illegal user"); return (v); } switch (field) { case S_name: v.pval = user->name; break; case S_login: v.pval = user->login; break; case S_global: v.pval = user->global; break; case S_member: v.pval = user->member; break; case S_expires: v.pval = user->expires; break; case S_arap: v.pval = user->arap; break; case S_chap: v.pval = user->chap; break; #ifdef MSCHAP case S_mschap: v.pval = user->mschap; break; #endif /* MSCHAP */ case S_pap: v.pval = user->pap; break; case S_opap: v.pval = user->opap; break; case S_message: v.pval = user->msg; break; case S_svc: v.pval = user->svcs; break; case S_before: v.pval = user->before_author; break; case S_after: v.pval = user->after_author; break; case S_svc_dflt: v.intval = user->svc_dflt; break; #ifdef MAXSESS case S_maxsess: v.intval = user->maxsess; break; #endif case S_nopasswd: v.intval = user->nopasswd; break; case S_time: v.pval = user->time; break; default: report(LOG_ERR, "get_value: unknown field %d", field); break; } return (v); } /* For host , find value of field. Doesn't recursive */ VALUE get_hvalue(host, field) HOST *host; int field; { VALUE v; v.intval = 0; if(!host) { parse_error("get_hvalue: illegal host"); return (v); } switch (field) { case S_name: v.pval = host->name; break; case S_key: v.pval = host->key; break; default: report(LOG_ERR, "get_value: unknown field %d", field); break; } return (v); } /* For each user, check she doesn't circularly reference a group. Return 1 if it does */ static int circularity_check() { USER *user, *entry, *group; USER **users = (USER **) hash_get_entries(usertable); USER **groups = (USER **) hash_get_entries(grouptable); USER **p, **q; /* users */ for (p = users; *p; p++) { user = *p; if (debug & DEBUG_PARSE_FLAG) report(LOG_DEBUG, "circularity_check: user=%s", user->name); /* Initialise all groups "seen" flags to zero */ for (q = groups; *q; q++) { group = *q; group->flags &= ~FLAG_SEEN; } entry = user; while (entry) { /* check groups we are a member of */ char *groupname = entry->member; if (debug & DEBUG_PARSE_FLAG) report(LOG_DEBUG, "\tmember of group %s", groupname ? groupname : ""); /* if not a member of any groups, go on to next user */ if (!groupname) break; group = (USER *) hash_lookup(grouptable, groupname); if (!group) { report(LOG_ERR, "%s=%s, group %s does not exist", (entry->flags & FLAG_ISUSER) ? "user" : "group", entry->name, groupname); free(users); free(groups); return (1); } if (group->flags & FLAG_SEEN) { report(LOG_ERR, "recursively defined groups"); /* print all seen "groups" */ for (q = groups; *q; q++) { group = *q; if (group->flags & FLAG_SEEN) report(LOG_ERR, "%s", group->name); } free(users); free(groups); return (1); } group->flags |= FLAG_SEEN; /* mark group as seen */ entry = group; } } free(users); free(groups); return (0); } /* Return a value for a group or user (isuser says if this name is a group or a user name). If no value exists, and recurse is true, also check groups we are a member of, recursively. Returns void * because it can return a string or a node pointer (should really return a union pointer). */ static VALUE cfg_get_value(name, isuser, attr, recurse) char *name; int isuser, attr, recurse; { USER *user, *group; VALUE value; value.pval = NULL; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_value: name=%s isuser=%d attr=%s rec=%d", name, isuser, codestring(attr), recurse); /* find the user/group entry */ user = (USER *) hash_lookup(isuser ? usertable : grouptable, name); if (!user) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name); return (value); } /* found the entry. Lookup value from attr=value */ value = get_value(user, attr); if (value.pval || !recurse) { return (value); } /* no value. Check containing group */ if (user->member) group = (USER *) hash_lookup(grouptable, user->member); else group = NULL; while (group) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_value: recurse group = %s", group->name); value = get_value(group, attr); if (value.pval) { return (value); } /* still nothing. Check containing group and so on */ if (group->member) group = (USER *) hash_lookup(grouptable, group->member); else group = NULL; } /* no value for this user or her containing groups */ value.pval = NULL; return (value); } /* Wrappers for cfg_get_value */ int cfg_get_intvalue(name, isuser, attr, recurse) char *name; int isuser, attr, recurse; { int val = cfg_get_value(name, isuser, attr, recurse).intval; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_intvalue: returns %d", val); return(val); } char * cfg_get_pvalue(name, isuser, attr, recurse) char *name; int isuser, attr, recurse; { char *p = cfg_get_value(name, isuser, attr, recurse).pval; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_pvalue: returns %s", p ? p : "NULL"); return(p); } /* For getting host values */ static VALUE cfg_get_hvalue(name, attr) char *name; int attr; { HOST *host; VALUE value; value.pval = NULL; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_hvalue: name=%s attr=%s ", name, codestring(attr)); /* find the host entry in hash table */ host = (HOST *) hash_lookup( hosttable, name); if (!host) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_hvalue: no host named %s", name); return (value); } /* found the entry. Lookup value from attr=value */ value = get_hvalue(host, attr); if (value.pval) { return (value); } /* No any value for this host */ value.pval = NULL; return (value); } /* Wrappers for cfg_get_hvalue */ char * cfg_get_phvalue(name, attr) char *name; int attr; { char *p = cfg_get_hvalue(name, attr).pval; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_phvalue: returns %s", p ? p : "NULL"); return(p); } /* Read the config file and do some basic sanity checking on it. Return 1 if we find any errors. */ cfg_read_config(cfile) char *cfile; { sym_line = 1; if ((cf = fopen(cfile, "r")) == NULL) { report(LOG_ERR, "read_config: fopen() error for file %s %s, exiting", cfile, sys_errlist[errno]); return (1); } if (parse_decls() || sym_error) { fclose(cf); return (1); } if (circularity_check()) { fclose(cf); return (1); } fclose(cf); return (0); } /* return 1 if user exists, 0 otherwise */ int cfg_user_exists(username) char *username; { USER *user = (USER *) hash_lookup(usertable, username); return (user != NULL); } /* return expiry string of user. If none, try groups she is a member on, and so on, recursively if recurse is non-zero */ char * cfg_get_expires(username, recurse) char *username; { return (cfg_get_pvalue(username, TAC_IS_USER, S_expires, recurse)); } /* return time string of user. If none, try groups she is a member on, and so on, recursively if recurse is non-zero */ char * cfg_get_timestamp(username, recurse) char *username; { return (cfg_get_pvalue(username, TAC_IS_USER, S_time, recurse)); } /* return password string of user. If none, try groups she is a member on, and so on, recursively if recurse is non-zero */ char * cfg_get_login_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_login, recurse)); } /* return value of the nopasswd field. If none, try groups she is a member on, and so on, recursively if recurse is non-zero */ int cfg_get_user_nopasswd(user, recurse) char *user; { return (cfg_get_intvalue(user, TAC_IS_USER, S_nopasswd, recurse)); } /* return user's secret. If none, try groups she is a member on, and so on, recursively if recurse is non-zero */ char * cfg_get_arap_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_arap, recurse)); } char * cfg_get_chap_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_chap, recurse)); } #ifdef MSCHAP char * cfg_get_mschap_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_mschap, recurse)); } #endif /* MSCHAP */ char * cfg_get_pap_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_pap, recurse)); } char * cfg_get_opap_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_opap, recurse)); } /* return the global password for the user (or the group, etc.) */ char * cfg_get_global_secret(user, recurse) char *user; { return (cfg_get_pvalue(user, TAC_IS_USER, S_global, recurse)); } #ifdef USE_PAM /* Return a pointer to a node representing a PAM Service name */ char * cfg_get_pam_service(user,recurse) char *user; { char *cfg_passwd; char *p; cfg_passwd = cfg_get_pap_secret(user, recurse); if (!cfg_passwd) { cfg_passwd = cfg_get_global_secret(user, recurse); } if (!cfg_passwd && !cfg_user_exists(user)) { cfg_passwd = cfg_get_authen_default(); switch (cfg_get_authen_default_method()) { case (S_pam): if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Get Default PAM Service :%s",cfg_passwd); return(cfg_passwd); break; default: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I havent find any PAM Service!!"); return(NULL);/* Haven't any PAM Service!! */ } } p=tac_find_substring("pam ", cfg_passwd); if(p) { /* We find PAM services */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "I get PAM sevice:%s",p); return (p); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "No any PAM Sevice"); return(NULL); } #endif /* For PAM */ /* Return a pointer to a node representing a given service authorization, taking care of recursion issues correctly. Protocol is only read if the type is N_svc_ppp. svcname is only read if type is N_svc. */ NODE * cfg_get_svc_node(username, type, protocol, svcname, recurse) char *username; int type; char *protocol, *svcname; int recurse; { USER *user, *group; NODE *svc; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: username=%s %s proto=%s svcname=%s rec=%d", username, cfg_nodestring(type), protocol ? protocol : "", svcname ? svcname : "", recurse); /* find the user/group entry */ user = (USER *) hash_lookup(usertable, username); if (!user) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: no user named %s", username); return (NULL); } /* found the user entry. Find svc node */ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) { if (svc->type != type) continue; if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) { continue; } if (type == N_svc && !STREQ(svc->value1, svcname)) { continue; } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: found %s proto=%s svcname=%s", cfg_nodestring(type), protocol ? protocol : "", svcname ? svcname : ""); return(svc); } if (!recurse) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: returns NULL"); return (NULL); } /* no matching node. Check containing group */ if (user->member) group = (USER *) hash_lookup(grouptable, user->member); else group = NULL; while (group) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: recurse group = %s", group->name); for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) { if (svc->type != type) continue; if (type == N_svc_ppp && !STREQ(svc->value1, protocol)) { continue; } if (type == N_svc && !STREQ(svc->value1, svcname)) { continue; } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: found %s proto=%s svcname=%s", cfg_nodestring(type), protocol ? protocol : "", svcname ? svcname : ""); return(svc); } /* still nothing. Check containing group and so on */ if (group->member) group = (USER *) hash_lookup(grouptable, group->member); else group = NULL; } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_svc_node: returns NULL"); /* no matching svc node for this user or her containing groups */ return (NULL); } /* Return a pointer to the node representing a set of command regexp matches for a user and command, handling recursion issues correctly */ NODE * cfg_get_cmd_node(name, cmdname, recurse) char *name, *cmdname; int recurse; { USER *user, *group; NODE *svc; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: name=%s cmdname=%s rec=%d", name, cmdname, recurse); /* find the user/group entry */ user = (USER *) hash_lookup(usertable, name); if (!user) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: no user named %s", name); return (NULL); } /* found the user entry. Find svc node */ svc = (NODE *) get_value(user, S_svc).pval; while (svc) { if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s %s node", cmdname, cfg_nodestring(svc->type)); return (svc); } svc = svc->next; } if (!recurse) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL"); return (NULL); } /* no matching node. Check containing group */ if (user->member) group = (USER *) hash_lookup(grouptable, user->member); else group = NULL; while (group) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: recurse group = %s", group->name); svc = get_value(group, S_svc).pval; while (svc) { if (svc->type == N_svc_cmd && STREQ(svc->value, cmdname)) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: found cmd %s node %s", cmdname, cfg_nodestring(svc->type)); return (svc); } svc = svc->next; } /* still nothing. Check containing group and so on */ if (group->member) group = (USER *) hash_lookup(grouptable, group->member); else group = NULL; } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_get_cmd_node: returns NULL"); /* no matching cmd node for this user or her containing groups */ return (NULL); } /* Return an array of character strings representing configured AV * pairs, given a username and a service node. * * In the AV strings returned, manipulate the separator character to * indicate which args are optional and which are mandatory. * * Lastly, indicate what default permission was configured by setting * denyp */ char ** cfg_get_svc_attrs(svcnode, denyp) NODE *svcnode; int *denyp; { int i; NODE *node; char **args; *denyp = 1; if (!svcnode) return (NULL); *denyp = (svcnode->dflt == S_deny); i = 0; for (node = svcnode->value; node; node = node->next) i++; args = (char **) tac_malloc(sizeof(char *) * (i + 1)); i = 0; for (node = svcnode->value; node; node = node->next) { char *arg = tac_strdup(node->value); char *p = index(arg, '='); if (p && node->type == N_optarg) *p = '*'; args[i++] = arg; } args[i] = NULL; return (args); } int cfg_user_svc_default_is_permit(user) char *user; { int permit = cfg_get_intvalue(user, TAC_IS_USER, S_svc_dflt, TAC_PLUS_RECURSE); switch (permit) { default: /* default is deny */ case S_deny: return (0); case S_permit: return (1); } } int cfg_no_user_permitted() { if (no_user_dflt == S_permit) return (1); return (0); } char * cfg_get_authen_default() { return (authen_default); } /* For describe authentication method(pam,file,db..etc) */ int cfg_get_authen_default_method() { return (authen_default_method); } /* Return 1 if this user has any ppp services configured. Used for authorizing ppp/lcp requests */ int cfg_ppp_is_configured(username, recurse) char *username; int recurse; { USER *user, *group; NODE *svc; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: username=%s rec=%d", username, recurse); /* find the user/group entry */ user = (USER *) hash_lookup(usertable, username); if (!user) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: no user named %s", username); return (0); } /* found the user entry. Find svc node */ for(svc = (NODE *) get_value(user, S_svc).pval; svc; svc = svc->next) { if (svc->type != N_svc_ppp) continue; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node", svc->value1); return(1); } if (!recurse) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0"); return (0); } /* no matching node. Check containing group */ if (user->member) group = (USER *) hash_lookup(grouptable, user->member); else group = NULL; while (group) { if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: recurse group = %s", group->name); for(svc = (NODE *) get_value(group, S_svc).pval; svc; svc = svc->next) { if (svc->type != N_svc_ppp) continue; if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: found svc ppp %s node", svc->value1); return(1); } /* still nothing. Check containing group and so on */ if (group->member) group = (USER *) hash_lookup(grouptable, group->member); else group = NULL; } if (debug & DEBUG_CONFIG_FLAG) report(LOG_DEBUG, "cfg_ppp_is_configured: returns 0"); /* no PPP svc nodes for this user or her containing groups */ return (0); } /* For getting host key */ char * cfg_get_host_key(host) char *host; { return (cfg_get_phvalue(host, S_key)); }