/*- * Copyright (c) 1999-2004 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: ipa_conf.c,v 1.3.2.8 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "parser.h" #include "pathnames.h" #include "ipa_ac.h" #include "ipa_db.h" #include "ipa_time.h" #include "ipa_cmd.h" #include "ipa_conf.h" #include "ipa_ctl.h" #include "ipa_log.h" #include "ipa_main.h" #include "ipa_rules.h" #include "ipa_autorules.h" char *ipa_conf_file = IPA_CONF_FILE; /* -f */ int mimic_real_config = 0; /* 1, if -tt. */ #ifdef CTL_CHECK_CREDS static regex_t reg_acl; static regex_t reg_ctl_acl_class; #endif /* * IPA_CONF_SECT_* with 1-7 values are defined in the ipa_mod.h file. * These are local values, modules should not use them. Even if we have * some WITHOUT_xxx macros let's #define all SECT_xxx here, else we have * to #ifdef too many lines in conf_sect_tbl[] and conf_param_tbl[], * which is really not necessary. */ #define SECT_SUBLIMIT 8 /* Next sections can contain "exec" parameters. */ #define SECT_RESTART 9 #define SECT_REACH 10 #define SECT_EXPIRE 11 #define SECT_STARTUP 12 #define SECT_SHUTDOWN 13 #define SECT_IF_REACHED 14 #define SECT_IF_NOT_REACHED 15 #define SECT_IF_ALL_REACHED 16 #define SECT_IF_ANY_REACHED 17 #define SECT_IF_ANY_NOT_REACHED 18 #define SECT_IF_ALL_NOT_REACHED 19 #define SECT_BELOW_THRESHOLD 20 #define SECT_EQUAL_THRESHOLD 21 #define SECT_ABOVE_THRESHOLD 22 #ifdef WITH_RULES static struct rule *currule; /* Current rule. */ #endif static struct rulepat *currulepat; /* Current rulepat. */ static u_int rulepatno; #ifdef WITH_AUTORULES static struct autorule *curautorule; /* Current autorule. */ #endif #ifdef WITH_LIMITS static struct limit *curlimit; /* Current limit. */ static u_int limitno; /* Order number of the current limit. */ static regex_t reg_texp; /* Regular expression for +X like arguments. */ static struct limits_list *limits_list; #endif #ifdef WITH_SUBLIMITS static struct sublimit *cursublimit; /* Current sublimit. */ #endif #ifdef WITH_THRESHOLDS static struct threshold *curthreshold; /* Current threshold. */ static u_int thresholdno; /* Order number of current threshold. */ static struct thresholds_list *thresholds_list; static regex_t reg_thr_balance;/* Regular expression for "threshold_balance". */ #endif static struct cmd_list *curcmdl; /* Current cmd list. */ /* Internal "null" accounting system and database. */ static struct ac_list ac_list_null = STAILQ_HEAD_INITIALIZER(ac_list_null); static struct db_list db_list_null = STAILQ_HEAD_INITIALIZER(db_list_null); /* Empty time interval. */ static struct tint_list tint_list_empty = STAILQ_HEAD_INITIALIZER(tint_list_empty); static int posix_re_pattern; /* posix_re_pattern parameter. */ static int only_abs_paths; /* only_abs_paths parameter. */ struct debug_param { const char *param_name; int *param_value; }; /* debug_* parameters in IPA_CONF_SECT_ROOT. */ static struct debug_param common_debug_param[] = { #ifdef WITH_AUTORULES { "debug_autorule", &debug_autorule }, #endif { "debug_ctl", &debug_ctl, }, { "debug_time", &debug_time }, { "debug_worktime", &debug_worktime }, { "debug_ac_null", &debug_ac_null }, { "debug_db_null", &debug_db_null }, { NULL, NULL } }; /* debug_* parameters in IPA_CONF_SECT_GLOBAL. */ static struct debug_param global_debug_param[] = { #ifdef WITH_LIMITS { "debug_limit", &global_debug_limit }, { "debug_limit_init", &global_debug_limit_init }, #endif #ifdef WITH_THRESHOLDS { "debug_threshold", &global_debug_threshold }, { "debug_threshold_init", &global_debug_threshold_init }, #endif { "debug_exec", &global_debug_exec }, { NULL, NULL } }; struct time_param { const char *param_name; u_int *param_value; }; /* *_time parameters in IPA_CONF_SECT_ROOT. */ static struct time_param common_time_param[] = { { "wakeup_time", &wakeup_time }, { "freeze_time", &freeze_time }, { "sleep_after_dump", &sleep_after_dump }, { "sensitive_time", &sensitive_time }, { NULL, NULL } }; static int use_log = 1; /* 0, if -t or -x switch was used. */ static PARSING_MODE parsing_mode; /* xxx_PARSING. */ static u_int section; /* Current section ID. */ static u_int section_first; /* autorule rulepat rule */ static u_int section_top; /* autorule rulepat rule limit sublimit threshold */ static int global_section_set; /* 1, if has global{}. */ static regex_t reg_worktime; static regex_t reg_list; void conferrx(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void confvlogmsgx_prefix(int, const char *, const char *, va_list) ATTR_FORMAT(printf, 3, 0); /* Macro for validating if empty TAILQ_HEAD is in consistent state. */ #define TAILQ_EMPTY_HEAD_VALID(x) \ ((x)->tqh_first == NULL && (x)->tqh_last == &(x)->tqh_first) /* * Exported support functions for modules. */ static const ipa_suppfunc suppfunc = { print_string, /* print_string */ print_bytes, /* print_bytes */ print_time, /* print_time */ print_value, /* print_value */ print_boolean, /* print_boolean */ print_space, /* print_space */ set_indent, /* set_indent */ print_param_name, /* print_param_name */ print_args, /* print_param_args */ print_param_end, /* print_param_end */ print_sect_name, /* print_sect_name */ print_args, /* print_sect_args */ print_sect_begin, /* print_sect_begin */ print_sect_end, /* print_sect_end */ open_log, /* open_log */ close_log, /* close_log */ mod_logmsg, /* logmsg */ mod_logconferr, /* logconferr */ parser_local_sym_add, /* local_sym_add */ parser_local_sym_del, /* local_sym_del */ parser_global_sym_add, /* global_sym_add */ parser_global_sym_del /* global_sym_del */ }; /* * Log message prepending it with prefix. * If use_log == 1 then log is used, else printf(3) is used. * Don't use mem_* functions. */ static void confvlogmsgx_prefix(int priority, const char *prefix, const char *format, va_list ap) { if (use_log) { int rv; char *ptr; char buf[LOG_BUF_SIZE]; /* Try to use buffer in stack. */ if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0) { logmsg(IPA_LOG_ERR, "confvlogmsgx_prefix: vsnprintf failed"); goto log_unformated; } if (rv >= sizeof buf) { if ( (ptr = malloc(++rv)) == NULL) { logmsgx(IPA_LOG_ERR, "confvlogmsgx_prefix: malloc failed"); goto log_unformated; } if (vsnprintf(ptr, rv, format, ap) < 0) { logmsg(IPA_LOG_ERR, "confvlogmsgx_prefix: vsnprintf failed again"); free(ptr); goto log_unformated; } logmsgx(priority, "%s: %s", prefix, ptr); free(ptr); } else logmsgx(priority, "%s: %s", prefix, buf); return; log_unformated: logmsgx(priority, "%s: unformated message: %s", prefix, format); } else { fflush(stdout); fprintf(stderr, "%s: ", prefix); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); } } /* * The wrapper for parser_vlogmsgx. */ static void parser_vlogmsgx_wrapper(const char *format, va_list ap) { confvlogmsgx_prefix(IPA_LOG_ERR, "parsing error", format, ap); } /* * The wrapper for mvlogmsgx during configuration., don't use * mem_* functions. */ static void mvlogmsgx_conf_wrapper(const char *format, va_list ap) { confvlogmsgx_prefix(IPA_LOG_ERR, "config error", format, ap); } /* * A wrapper for logging about errors. */ void conferrx(const char *format, ...) { va_list ap; va_start(ap, format); confvlogmsgx_prefix(IPA_LOG_ERR, "config error", format, ap); va_end(ap); } /* * The same as above one, but with priority argument and va_list. */ static void vconferrx_priority(int priority, const char *format, va_list ap) { confvlogmsgx_prefix(priority, "config error", format, ap); } /* * Try to find tevent with the given value of event_step, * if such tevent does not exist, then allocate new one. * Return NULL if new tevent can't be allocated. */ static struct tevent * find_tevent(u_int event_step) { struct tevent *tevent; SLIST_FOREACH(tevent, &tevents_list, link) if (tevent->event_step == event_step) return tevent; if ( (tevent = mzone_alloc(tevent_mzone)) != NULL) { tevent->event_step = event_step; SLIST_INSERT_HEAD(&tevents_list, tevent, link); } else conferrx("find_tevent: mzone_alloc failed"); return tevent; } /* * Register a configuration event in ac_mod module. */ static int ac_mod_conf_event(const struct ac_mod *ac_mod, u_int event, u_int no, const void *arg) { if (ac_mod->ipa_ac_mod->conf_event(event, no, arg) < 0) { logconferrx("module %s: conf_event(IPA_CONF_EVENT_%s) failed", ac_mod->mod_file, conf_event_msg[event]); return -1; } return 0; } /* * Register a configuration event in db_mod module. */ static int db_mod_conf_event(const struct db_mod *db_mod, u_int event, u_int no, const void *arg) { if (db_mod->ipa_db_mod->conf_event(event, no, arg) < 0) { logconferrx("module %s: conf_event(IPA_CONF_EVENT_%s) failed", db_mod->mod_file, conf_event_msg[event]); return -1; } return 0; } /* * Register a configuration event in each module. */ static int mod_conf_event(u_int event, u_int no, const void *arg) { const struct ac_mod *ac_mod; const struct db_mod *db_mod; SLIST_FOREACH(ac_mod, &ac_mod_list, link) if (ac_mod_conf_event(ac_mod, event, no, arg) < 0) { logconferrx("mod_conf_event: ac_mod_conf_event failed"); return -1; } SLIST_FOREACH(db_mod, &db_mod_list, link) if (db_mod_conf_event(db_mod, event, no, arg) < 0) { logconferrx("mod_conf_event: db_mod_conf_event failed"); return -1; } return 0; } /* * Inherit settings from rulepat from modules for rule. */ int mod_conf_inherit(const struct rulepat *rulepat, const struct rule *rule) { const struct ac_mod *ac_mod; const struct db_mod *db_mod; SLIST_FOREACH(ac_mod, &ac_mod_list, link) { if (ac_mod->ipa_ac_mod->conf_inherit != NULL) if (ac_mod->ipa_ac_mod->conf_inherit(rulepat->rulepatno, rule->ruleno, rule->rule_name) < 0) { xlogmsgx(IPA_LOG_ERR, "module %s: conf_inherit(%s, %s) failed", ac_mod->mod_file, parser_buf_to_string(rulepat->pat), rule->rule_name); return -1; } } SLIST_FOREACH(db_mod, &db_mod_list, link) { if (db_mod->ipa_db_mod->conf_inherit != NULL) if (db_mod->ipa_db_mod->conf_inherit(rulepat->rulepatno, rule->ruleno, rule->rule_name) < 0) { xlogmsgx(IPA_LOG_ERR, "module %s: conf_inherit(%s, %s) failed", db_mod->mod_file, parser_buf_to_string(rulepat->pat), rule->rule_name); return -1; } } return 0; } /* * Parse the "global" section. */ /* ARGSUSED */ static int parse_global(void *arg ATTR_UNUSED) { if (global_section_set) { logconferrx("this section is duplicated"); return -1; } global_section_set = 1; return mod_conf_event(IPA_CONF_EVENT_GLOBAL_BEGIN, 0, (void *)NULL); } #ifdef WITH_RULES /* * Parse the "rule" section. */ static int parse_rule(void *arg) { const char *rule_name = *(char **)arg; struct rule *rule; if (rule_by_name(rule_name) != NULL) { logconferrx("this section is duplicated"); return -1; } if ( (rule = mzone_alloc(rule_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } if ( (rule->rule_name = mem_strdup(rule_name, m_anon)) == NULL) { logconferrx("mem_strdup failed"); return -1; } rule->rule_info = NULL; rule->ruleno = nstatrules++; rule->cnt_neg = UINT64_C(0); rule->update_tevent = rule->append_tevent = NULL; rule->worktime = NULL; rule->is_active = UNKNOWN_ACTIVE_FLAG; rule->ac_list = NULL; rule->db_list = NULL; rule->ac_gather_add_pat = rule->ac_gather_sub_pat = NULL; SLIST_INIT(&rule->ac_gather_rev); rule->debug_exec = -1; init_cmds_in_rule(rule); #ifdef WITH_LIMITS rule->debug_limit = rule->debug_limit_init = -1; limits_list = &rule->limits; STAILQ_INIT(limits_list); limitno = 0; #endif #ifdef WITH_THRESHOLDS rule->debug_threshold = rule->debug_threshold_init = -1; thresholds_list = &rule->thresholds; STAILQ_INIT(thresholds_list); thresholdno = 0; #endif #ifdef CTL_CHECK_CREDS rule->ctl_rule_acl = NULL; #endif rule->free_mask = RULE_FREE_LIMITS|RULE_FREE_THRESHOLDS; section_first = section_top = IPA_CONF_SECT_RULE; if (parser_local_sym_add("rule", rule->rule_name, 0) < 0) return -1; TAILQ_INSERT_TAIL(&rules_list, rule, list); add_rule_to_hash(rule); if (mod_conf_event(IPA_CONF_EVENT_RULE_BEGIN, rule->ruleno, rule->rule_name) < 0) return -1; currule = rule; return 0; } #endif /* WITH_RULES */ /* * Parse the "rulepat" section. */ static int parse_rulepat(void *arg) { char *pat = *(char **)arg; struct rulepat *rulepat; STAILQ_FOREACH(rulepat, &rulepats_list, link) if (strcmp(rulepat->pat, pat) == 0) { logconferrx("this section is duplicated"); return -1; } if ( (rulepat = mzone_alloc(rulepat_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(&rulepats_list, rulepat, link); if ( (re_errcode = regcomp(&rulepat->reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf); return -1; } rulepat->pat = pat; rulepat->rulepatno = rulepatno++; rulepat->check_next_rulepat = -1; rulepat->update_tevent = rulepat->append_tevent = NULL; rulepat->worktime = NULL; rulepat->ac_list = NULL; rulepat->db_list = NULL; rulepat->debug_exec = -1; init_cmds_in_rulepat(rulepat); #ifdef WITH_LIMITS rulepat->debug_limit = rulepat->debug_limit_init = -1; limits_list = &rulepat->limits; STAILQ_INIT(limits_list); limitno = 0; #endif #ifdef WITH_THRESHOLDS rulepat->debug_threshold = rulepat->debug_threshold_init = -1; thresholds_list = &rulepat->thresholds; STAILQ_INIT(thresholds_list); thresholdno = 0; #endif #ifdef CTL_CHECK_CREDS rulepat->ctl_rule_acl = NULL; #endif section_first = section_top = IPA_CONF_SECT_RULEPAT; if (mod_conf_event(IPA_CONF_EVENT_RULEPAT_BEGIN, rulepat->rulepatno, rulepat->pat) < 0) return -1; currulepat = rulepat; return 0; } /* * Parse the "check_next_rulepat" parameter. */ static int parse_check_next_rulepat(void *arg) { currulepat->check_next_rulepat = *(int *)arg; return 0; } /* * Parse an argument of some "debug_*" parameter. */ static int check_debug_level(uint32_t level, u_int maxlevel) { if (level > maxlevel) { logconferrx("too big debug level, max level is %u", maxlevel); return -1; } return (int)level; } /* * Parse the "debug_time" parameter. */ static int parse_debug_time(void *arg) { return debug_time = check_debug_level(*(uint32_t *)arg, 2); } /* * Parse the "debug_ac_null" parameter. */ static int parse_debug_ac_null(void *arg) { return debug_ac_null = check_debug_level(*(uint32_t *)arg, 1); } /* * Parse the "debug_db_null" parameter. */ static int parse_debug_db_null(void *arg) { return debug_db_null = check_debug_level(*(uint32_t *)arg, 1); } /* * Parse the "debug_worktime" parameter. */ static int parse_debug_worktime(void *arg) { return debug_worktime = check_debug_level(*(uint32_t *)arg, 1); } /* * Parse the "debug_exec" parameter. */ static int parse_debug_exec(void *arg) { int level; if ( (level = check_debug_level(*(uint32_t *)arg, 2)) < 0) return -1; switch (section) { case IPA_CONF_SECT_GLOBAL: global_debug_exec = level; break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->debug_exec = level; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->debug_exec = level; break; #endif default: /* IPA_CONF_SECT_RULEPAT */ currulepat->debug_exec = level; } return 0; } /* * Parse the "update_time" parameter. */ static int parse_update_time(void *arg) { uint64_t timeval = *(uint64_t *)arg; struct tevent *tevent; if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) { logconferrx("argument should be greater than zero seconds and less than or equal to 1 day"); return -1; } if ( (tevent = find_tevent((u_int)timeval)) == NULL) { logconferrx("find_tevent failed"); return -1; } switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->update_tevent = tevent; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->update_tevent = tevent; break; #endif case IPA_CONF_SECT_RULEPAT: currulepat->update_tevent = tevent; break; default: /* IPA_CONF_SECT_GLOBAL */ global_update_tevent = tevent; } return 0; } /* * Parse the "freeze_time" parameter. */ static int parse_freeze_time(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0) || timeval >= 5 * SECONDS_IN_MINUTE) { logconferrx("argument should be greater than zero seconds and less than 5 minutes"); return -1; } freeze_time = (u_int)timeval; return 0; } /* * Parse the "append_time" parameter. */ static int parse_append_time(void *arg) { uint64_t timeval = *(uint64_t *)arg; struct tevent *tevent; if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) { logconferrx("argument should be greater than zero seconds and less than or equal to 1 day"); return -1; } if ( (tevent = find_tevent((u_int)timeval)) == NULL) { logconferrx("find_tevent failed"); return -1; } switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->append_tevent = tevent; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->append_tevent = tevent; break; #endif case IPA_CONF_SECT_RULEPAT: currulepat->append_tevent = tevent; break; default: /* IPA_CONF_SECT_GLOBAL */ global_append_tevent = tevent; } return 0; } /* * Parse the "wakeup_time" parameter. */ static int parse_wakeup_time(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0) || timeval > SECONDS_IN_DAY) { logconferrx("argument should be greater than zero seconds and less than or equal to 1 day"); return -1; } wakeup_time = (u_int)timeval; return 0; } #if defined(WITH_THRESHOLDS) || defined(WITH_SUBLIMITS) /* * get_arg_value() + get_arg_per_cent(). */ static int get_value_or_per_cent(uint64_t *val_p, u_int *cnt_type_p) { u_int cnt_type; uint64_t val; if (regexec(®_bytes, parser_args, 0, (regmatch_t *)NULL, 0) == 0) { if (get_arg_bytes(&val) < 0) return -1; cnt_type = IPA_CONF_TYPE_BYTES; } else if (regexec(®_time, parser_args, 0, (regmatch_t *)NULL, 0) == 0) { if (get_arg_time(&val) < 0) return -1; cnt_type = IPA_CONF_TYPE_TIME; } else { char *ptr; ptr = parser_args + parser_args_len - 1; if (*ptr == '%') { *ptr = '\0'; cnt_type = IPA_CONF_TYPE_PER_CENT; } else cnt_type = IPA_CONF_TYPE_UINT64; if (get_arg_uint64(&val) < 0) return -1; if (cnt_type == IPA_CONF_TYPE_PER_CENT && val > 100) { logconferrx("per cent value should <= 100%%"); return -1; } } *val_p = val; *cnt_type_p = cnt_type; return 0; } #endif /* WITH_THRESHOLDS || WITH_SUBLIMITS */ #ifdef WITH_LIMITS /* * Parse the "debug_limit" parameter. */ static int parse_debug_limit(void *arg) { int level; if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0) return -1; switch (section) { case IPA_CONF_SECT_GLOBAL: global_debug_limit = level; break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->debug_limit = level; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->debug_limit = level; break; #endif default: /* IPA_CONF_SECT_RULEPAT */ currulepat->debug_limit = level; } return 0; } /* * Parse the "debug_limit_init" parameter. */ static int parse_debug_limit_init(void *arg) { int level; if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0) return -1; switch (section) { case IPA_CONF_SECT_GLOBAL: global_debug_limit_init = level; break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->debug_limit_init = level; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->debug_limit_init = level; break; #endif default: /* IPA_CONF_SECT_RULEPAT */ currulepat->debug_limit_init = level; } return 0; } /* * Parse the "load_limit" parameter. */ static int parse_load_limit(void *arg) { if (section == IPA_CONF_SECT_LIMIT) curlimit->load_limit = *(int *)arg; else /* IPA_CONF_SECT_GLOBAL */ global_load_limit = *(int *)arg; return 0; } /* * Parse the "limit" section. */ static int parse_limit_sect(void *arg) { const char *limit_name = *(char **)arg; struct limit *limit; STAILQ_FOREACH(limit, limits_list, link) if (strcmp(limit->limit_name, limit_name) == 0) { logconferrx("this section is duplicated"); return -1; } if ( (limit = mzone_alloc(limit_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(limits_list, limit, link); if ( (limit->limit_name = mem_strdup(limit_name, m_anon)) == NULL) { logconferrx("mem_strdup failed"); return -1; } limit->limit_info = NULL; limit->limitno = limitno++; limit->lim = limit->cnt_neg = UINT64_C(0); limit->load_limit = -1; limit->restart.restart.upto = limit->expire.expire.upto = TEXP_UPTO_NOTSET; init_cmds_in_limit(limit); limit->worktime = NULL; limit->is_active = ACTIVE_FLAG; limit->db_list = NULL; #ifdef WITH_SUBLIMITS STAILQ_INIT(&limit->sublimits); #endif limit->wpid.type = WPID_TYPE_LIMIT; limit->wpid.pid = 0; limit->wpid.u.limit = limit; #ifdef WITH_RULES limit->rule = currule; #endif if (section_top == IPA_CONF_SECT_RULE) ++nstatlimits; section_top = IPA_CONF_SECT_LIMIT; if (parser_local_sym_add("limit", limit->limit_name, 0) < 0) return -1; if (mod_conf_event(IPA_CONF_EVENT_LIMIT_BEGIN, limit->limitno, limit->limit_name) < 0) return -1; curlimit = limit; return 0; } /* * Parse the "limit" parameter. */ static int parse_limit_param(void *arg) { uint64_t *ptr = (uint64_t *)arg; struct limit *limit; limit = curlimit; if (limit->lim != UINT64_C(0)) { logconferrx("cannot redefine this parameter"); return -1; } if ( (limit->lim = *(ptr + 1)) == UINT64_C(0)) { logconferrx("argument should be greater than zero"); return -1; } limit->cnt_type = (u_int)*ptr; return 0; } #ifdef WITH_SUBLIMITS /* * Parse the "sublimit" section. */ static int parse_sublimit(void *arg) { char *sublimit_name; u_int cnt_type; uint64_t lim; struct sublimit *sublimit; if (curlimit->lim == UINT64_C(0)) { logconferrx("\"limit\" parameter in previous section should be specified before"); return -1; } if ( (sublimit_name = mem_strdup(*(char **)arg, m_anon)) == NULL) { logconferrx("mem_strdup failed"); return -1; } if (get_value_or_per_cent(&lim, &cnt_type) < 0) return -1; if (lim == UINT64_C(0)) { logconferrx("argument should be greater than zero"); return -1; } if (cnt_type != IPA_CONF_TYPE_PER_CENT && cnt_type != curlimit->cnt_type) { logconferrx("different arguments types for sublimit and limit"); return -1; } STAILQ_FOREACH(sublimit, &curlimit->sublimits, link) if (cnt_type == IPA_CONF_TYPE_PER_CENT) { if ((u_int)lim == sublimit->lim_per_cent) { logconferrx("this section is duplicated"); return -1; } } else { if (lim == sublimit->lim) { logconferrx("this section is duplicated"); return -1; } } if ( (sublimit = mzone_alloc(sublimit_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(&curlimit->sublimits, sublimit, link); sublimit->sublimit_name = sublimit_name; if (cnt_type == IPA_CONF_TYPE_PER_CENT) sublimit->lim_per_cent = (u_int)lim; else { sublimit->lim = lim; sublimit->lim_per_cent = 0; } sublimit->cnt_type = cnt_type; init_cmds_in_sublimit(sublimit); sublimit->wpid.type = WPID_TYPE_SUBLIMIT; sublimit->wpid.pid = 0; sublimit->wpid.u.sublimit = sublimit; sublimit->limit = curlimit; if (section_top == IPA_CONF_SECT_RULE) ++nstatsublimits; section_top = SECT_SUBLIMIT; if (parser_local_sym_add("sublimit", sublimit->sublimit_name, 0) < 0) return -1; cursublimit = sublimit; return 0; } #endif /* WITH_SUBLIMITS */ /* * Parse "expired time" value. */ static int parse_texp(struct texp *texp, const void *arg) { const char *ptr = (const char *)arg; char *endptr; int level = 0, error = 0, overflow = 0; uint32_t value; texp->seconds = 0; texp->upto = TEXP_UPTO_SIMPLE; for (;;) { if (*ptr == ' ') ++ptr; if (*ptr == '+') { texp->upto = *++ptr; texp->side = texp->seconds > 0; if (*++ptr == '\0') break; /* EOL */ continue; } if (strto_uint32(&value, ptr, &endptr) < 0) return -1; ptr = endptr; switch (*ptr) { case 'W': if (level > 1) error = 1; else { if (value > UINT32_MAX / SECONDS_IN_WEEK) overflow = 1; else { level = 2; value *= SECONDS_IN_WEEK; } } break; case 'D': if (level > 2) error = 1; else { if (value > UINT32_MAX / SECONDS_IN_DAY) overflow = 1; else { level = 3; value *= SECONDS_IN_DAY; } } break; case 'h': if (level > 3) error = 1; else { if (value > UINT32_MAX / SECONDS_IN_HOUR) overflow = 1; else { level = 4; value *= SECONDS_IN_HOUR; } } break; case 'm': if (level > 4) error = 1; else { if (value > UINT32_MAX / SECONDS_IN_MINUTE) overflow = 1; else { level = 5; value *= SECONDS_IN_MINUTE; } } break; default: /* 's' */ if (level > 5) error = 1; else level = 6; } if (error) { logconferrx("wrong format of an argument"); return -1; } if (overflow || texp->seconds > UINT_MAX - value) { logconferrx("too big value for u_int"); return -1; } texp->seconds += (u_int)value; if (*++ptr == '\0') break; /* EOL */ } return 0; } /* * Parse the "restart" parameter. */ static int parse_restart(void *arg) { struct texp *texp = &curlimit->restart.restart; if (parse_texp(texp, *(char **)arg) < 0) return -1; if (texp->seconds == 0 && texp->upto == TEXP_UPTO_SIMPLE) { logconferrx("argument should be greater than zero"); return -1; } return 0; } /* * Parse the "expire" parameter. */ static int parse_expire(void *arg) { return parse_texp(&curlimit->expire.expire, *(char **)arg); } /* * Inherit settings for a limit from the "global" section. */ static void inherit_limit_from_global(struct limit *limit) { if (limit->load_limit < 0) limit->load_limit = global_load_limit; } #endif /* WITH_LIMITS */ /* * Parse the "exec" parameter. */ static int parse_exec(void *arg) { char *start, *ptr, *str = *(char **)arg; int allow_subst; struct cmd *cmd; switch (parser_nargs) { case 1: case 2: break; default: logconferrx("one or two arguments are expected"); return -1; } /* Allocate new structure for command. */ if ( (cmd = mzone_alloc(cmd_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(&curcmdl->list, cmd, link); /* user "..." or "..." */ if (parser_nargs == 2) { if (*str == '\"') { logconferrx("wrong format of an argument"); return -1; } ptr = str; str = strchr(str, ' '); *str++ = '\0'; if ( (cmd->user = mem_strdup(ptr, m_cmd)) == NULL) { logconferrx("mem_strdup failed"); return -1; } cmd->free_mask = CMD_FREE_STR|CMD_FREE_USER; } else { cmd->user = NULL; cmd->free_mask = CMD_FREE_STR; } /* Check for "..." */ if (!parser_buf_is_string(str)) { logconferrx("wrong format of an argument"); return -1; } /* Copy "..." */ if ( (cmd->str = parser_string_strdup(str, m_cmd)) == NULL) return -1; /* Validate "..." */ str = cmd->str; if (*str == '\0') { logconferrx("command should be a non-empty string"); return -1; } if (*str != '/' && only_abs_paths != 0) { logconferrx("command should be given with absolute path"); return -1; } switch (section_first) { #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: #endif case IPA_CONF_SECT_RULEPAT: allow_subst = 1; break; default: allow_subst = 0; } cmd->subst_per_cent = cmd->subst_rule = 0; /* Check if a string has correct substitutions and count them. */ for (ptr = str, start = NULL, cmd->str_size = 1; *ptr != '\0'; cmd->str_size++, ++ptr) if (allow_subst && *ptr == '%') { if (start == NULL) start = ptr + 1; else { if (ptr != start) { /* %xxx% */ *ptr = '\0'; if (strcmp(start, "rule") != 0) { *ptr = '%'; logconferrx("unknown substitute \"%s\" in command", start); return -1; } *ptr = '%'; cmd->subst_rule++; } else { /* %% */ cmd->subst_per_cent++; } start = NULL; } } if (start != NULL) { logconferrx("unclosed substitute in command"); return -1; } return 0; } /* * Parse the "sync_exec" parameter. */ static int parse_sync_exec(void *arg) { curcmdl->sync_exec = *(int *)arg; return 0; } #if defined(WITH_RULES) || defined(WITH_ANY_LIMITS) /* * Parse the "info" parameter. */ static int parse_info(void *arg) { char *ptr, **pptr; for (ptr = *(char **)arg; *ptr != '\0'; ++ptr) switch (*ptr) { case '\t': case '\n': logconferrx("'\\t' and '\\n' characters are not allowed here"); return -1; } switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: pptr = &currule->rule_info; break; #endif #ifdef WITH_LIMITS case IPA_CONF_SECT_LIMIT: pptr = &curlimit->limit_info; break; #endif #ifdef WITH_THRESHOLDS default: /* IPA_CONF_SECT_THRESHOLD */ pptr = &curthreshold->threshold_info; break; #endif } if (*pptr != NULL) { logconferrx("cannot redefine this parameter"); return -1; } *pptr = *(char **)arg; return 0; } #endif /* WITH_RULES || WITH_ANY_LIMITS */ static const struct worktime * parse_generic_worktime(void *arg) { char *ptr = *(char **)arg; u_int wday, h1, m1, h2, m2, h2_prev, m2_prev; struct worktime *wt; struct tint *tint1, *tint2; struct tint_set *set1, *set2; struct tint_list *list; if ( (wt = mzone_alloc(worktime_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return NULL; } for (wday = 0; wday < DAYS_IN_WEEK; ++wday) wt->tint_list[wday] = &tint_list_empty; for (; *ptr != '\0';) { switch (*ptr) { case 'S': wday = 0; break; case 'M': wday = 1; break; case 'T': wday = 2; break; case 'W': wday = 3; break; case 'H': wday = 4; break; case 'F': wday = 5; break; default: /* 'A' */ wday = 6; } if (wt->tint_list[wday] != &tint_list_empty) { logconferrx("each day (currently %s) should be specified only one time", wdays[wday]); return NULL; } if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return NULL; } list = &set1->list; STAILQ_INIT(list); h2_prev = m2_prev = 0; ptr += 2; /* Skip day name and one white space. */ for (;;) { if (*ptr == '*') { h1 = m1 = m2 = 0; h2 = HOURS_IN_DAY; } else { if (isdigit(*ptr) == 0) /* Next day in worktime. */ break; errno = 0; if (sscanf(ptr, "%u:%u-%u:%u", &h1, &m1, &h2, &m2) != 4) { logconferr("sscanf(%s) failed", ptr); return NULL; } if ((h1 > 23 || h2 > 23 || m1 > 59 || m2 > 59) && !(h2 == HOURS_IN_DAY && m2 == 0)) { logconferrx("wrong value of hours or minutes in time interval for %s", wdays[wday]); return NULL; } if (h1 == h2 && m1 == m2) { logconferrx("zero seconds time time intervals are not allowed"); return NULL; } if ((h1 * MINUTES_IN_HOUR + m1) > (h2 * MINUTES_IN_HOUR + m2)) { logconferrx("wrong time interval for %s: %02u:%02u-%02u:%02u", wdays[wday], h1, m1, h2, m2); return NULL; } if (h2_prev * MINUTES_IN_HOUR + m2_prev > h1 * MINUTES_IN_HOUR + m1) { logconferrx("nonsuccessive time interval for %s", wdays[wday]); return NULL; } } if ( (tint1 = mzone_alloc(tint_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return NULL; } tint1->sec1 = h1 * SECONDS_IN_HOUR + m1 * SECONDS_IN_MINUTE; tint1->sec2 = h2 * SECONDS_IN_HOUR + m2 * SECONDS_IN_MINUTE; STAILQ_INSERT_TAIL(&set1->list, tint1, link); for (; *ptr != ' '; ++ptr) if (*ptr == '\0') goto done; ++ptr; h2_prev = h2; m2_prev = m2; } done: /* * If there is already the same tint_list, * then free memory used by just allocated one. */ STAILQ_FOREACH(set2, &tint_sets, link) { tint1 = STAILQ_FIRST(&set1->list); tint2 = STAILQ_FIRST(&set2->list); while (tint1 != NULL && tint2 != NULL) { if (tint1->sec1 != tint2->sec1 || tint1->sec2 != tint2->sec2) break; tint1 = STAILQ_NEXT(tint1, link); tint2 = STAILQ_NEXT(tint2, link); } if (tint1 == NULL && tint2 == NULL) { /* Duplicate tint_list was found. */ free_tint_set(set1); set1 = set2; break; } } if (set2 == NULL) /* New tint_list --> add it to tint_sets. */ STAILQ_INSERT_TAIL(&tint_sets, set1, link); wt->tint_list[wday] = &set1->list; } return find_worktime(wt); } /* * Parse the "worktime" parameter. */ static int parse_worktime(void *arg) { const struct worktime **wtpp; switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: wtpp = &currule->worktime; break; #endif #ifdef WITH_LIMITS case IPA_CONF_SECT_LIMIT: wtpp = &curlimit->worktime; break; #endif #ifdef WITH_THRESHOLDS case IPA_CONF_SECT_THRESHOLD: wtpp = &curthreshold->worktime; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: wtpp = &curautorule->worktime; break; #endif case IPA_CONF_SECT_RULEPAT: wtpp = &currulepat->worktime; break; default: /* IPA_CONF_SECT_GLOBAL */ wtpp = &global_worktime; } if (*wtpp != NULL) { logconferrx("cannot redefine this parameter"); return -1; } return ( (*wtpp = parse_generic_worktime(arg)) == NULL) ? -1 : 0; } #ifdef WITH_AUTORULES /* * Parse the "worktime_rule" parameter. */ static int parse_worktime_rule(void *arg) { if (curautorule->worktime_rule != NULL) { logconferrx("cannot redefine this parameter"); return -1; } return ( (curautorule->worktime_rule = parse_generic_worktime(arg)) == NULL) ? -1 : 0; } #endif /* * Check security of configuration file: absolute path, regular file, * is owned by the user, who run ipa, writable only for the user. */ static int check_conf_file(const char *fname, int ignore_non_regular) { struct stat statbuf; if (lstat(fname, &statbuf) < 0) { conferrx("lstat(%s): %s", fname, strerror(errno)); return -1; } if (!S_ISREG(statbuf.st_mode)) { if (ignore_non_regular) return 0; conferrx("configuration file \"%s\" should be a regular file", fname); return -1; } if (statbuf.st_uid != myuid) { conferrx("configuration file \"%s\" should be owned by the user, who run this program", fname); return -1; } if (statbuf.st_mode & (S_IWGRP|S_IWOTH)) { conferrx("configuration file \"%s\" should not have write permissions for group or other users", fname); return -1; } return 1; } /* * Check file path: non empty string and should start with '/'. */ static int check_file_path(const char *fname, const char *what) { if (*fname == '\0') { logconferrx("argument should be a non-empty string"); return -1; } if (*fname != '/') { logconferrx("%s should be given with absolute path", what); return -1; } return 0; } /* * Parse the "include" parameter. */ static int parse_include(void *arg) { char *fname = *(char **)arg; struct parser_pbuf *pbuf; /* Save offset of current configuration file and close it. */ if ( (parser_curpbuf->foff = ftell(parser_curpbuf->fp)) < 0) { logconferr("ftell(%s)", parser_curpbuf->fname); return -1; } if (fclose(parser_curpbuf->fp) != 0) { logconferr("fclose(%s)", parser_curpbuf->fname); return -1; } /* Validate an argument. */ if (check_file_path(fname, "file") < 0) return -1; /* Check security of configuration file. */ if (check_conf_file(fname, 0) < 0) return -1; /* Open included configuration file and put it to stack. */ if ( (pbuf = parser_new_pbuf(0)) == NULL) return -1; if ( (pbuf->fp = fopen(fname, "r")) == NULL) { logconferr("fopen(%s, \"r\")", fname); return -1; } pbuf->fname = fname; pbuf->foff = 0; return parser_push_pbuf(pbuf); } /* * Parse the "include_files" parameter. */ static int parse_include_files(void *arg) { char *dir = *(char **)arg, *pat, *fname; int include_something; DIR *dirp; regex_t re; struct stat statbuf; struct dirent *dp; struct parser_pbuf *pbuf, *old_curpbuf; /* Validate an argument. */ if (check_file_path(dir, "directory") < 0) return -1; pat = strrchr(dir, '/'); *pat++ = '\0'; if (posix_re_pattern > 0) if ( (re_errcode = regcomp(&re, pat, REG_EXTENDED|REG_NOSUB)) != 0) { regerror(re_errcode, (regex_t *)NULL, re_errbuf, sizeof re_errbuf); logconferrx("cannot compile regular expression: regcomp(\"%s\"): %s", pat, re_errbuf); return -1; } /* Check security of the given directory. */ if (lstat(dir, &statbuf) < 0) { logconferr("lstat(%s)", dir); return -1; } if (!S_ISDIR(statbuf.st_mode)) { logconferrx("given pathname is not a directory"); return -1; } if (statbuf.st_uid != myuid) { logconferrx("directory should be owned by the user, who run this program"); return -1; } if (statbuf.st_mode & (S_IWGRP|S_IWOTH)) { logconferrx("directory should not have write permissions for group and other users"); return -1; } if ( (dirp = opendir(dir)) == NULL) { logconferr("opendir(%s)", dir); return -1; } include_something = 0; old_curpbuf = parser_curpbuf; for (;;) { errno = 0; if ( (dp = readdir(dirp)) == NULL) { if (errno != 0) { logconferr("readdir(%s)", dir); return -1; } break; } if (*dp->d_name == '.') /* Optimize and don't use strcmp(3) here for "." and "..". */ switch (*(dp->d_name + 1)) { case '\0': continue; case '.': if (*(dp->d_name + 2) == '\0') continue; } if (posix_re_pattern != 1) { if (fnmatch(pat, dp->d_name, FNM_PERIOD) == FNM_NOMATCH) continue; } else { if (regexec(&re, dp->d_name, 0, (regmatch_t *)NULL, 0) != 0) continue; } if (mem_asprintf(m_parser, &fname, "%s/%s", dir, dp->d_name) < 0) { logconferrx("mem_asprintf failed"); return -1; } switch (check_conf_file(fname, 1)) { case -1: return -1; case 0: mem_free(fname, m_parser); continue; } if ( (pbuf = parser_new_pbuf(0)) == NULL) return -1; pbuf->fname = fname; pbuf->foff = 0; if (parser_push_pbuf(pbuf) < 0) return -1; include_something = 1; } if (posix_re_pattern > 0) regfree(&re); if (closedir(dirp) < 0) { logconferr("closedir(%s)", dir); return -1; } mem_free(dir, m_parser); if (include_something) { if ( (old_curpbuf->foff = ftell(old_curpbuf->fp)) < 0) { logconferr("ftell(%s)", old_curpbuf->fname); return -1; } if (fclose(old_curpbuf->fp) != 0) { logconferr("fclose(%s)", old_curpbuf->fname); return -1; } if ( (parser_curpbuf->fp = fopen(parser_curpbuf->fname, "r")) == NULL) { logconferr("fopen(%s, \"r\")", parser_curpbuf->fname); return -1; } } return 0; } /* * Parse the "keep_rules_order" parameter. */ static int parse_keep_rules_order(void *arg) { keep_rules_order = *(int *)arg; return 0; } /* * Parse the "only_abs_paths" parameter. */ static int parse_only_abs_paths(void *arg) { only_abs_paths = *(int *)arg; return 0; } /* * Get module's name from the file name "[/path/]foobar[-x.y.z][.so]". * Return "foobar" part from the given path name. */ static char * get_mod_name(char *file_name) { char *ptr, *ptr2, *name; if ( (ptr = strrchr(file_name, '/')) != NULL) ++ptr; /* foobar[-x.y.z].[so] */ else ptr = file_name; if ( (name = mem_strdup(ptr, m_anon)) == NULL) { logconferrx("get_mod_name: mem_strdup failed"); return NULL; } if ( (ptr2 = strchr(name, '.')) != NULL) *ptr2 = '\0'; /* foobar[-x] */ if ( (ptr = strrchr(name, '-')) != NULL) { for (ptr2 = ptr; *ptr2 != '\0'; ++ptr2) if (isdigit(*ptr2) == 0) break; if (*ptr2 == '\0') *ptr = '\0'; /* foobar */ } return name; } /* * Parse the "ac_mod" parameter. */ static int parse_ac_mod(void *arg) { char *mod_name, *sym; struct ac_mod *ac_mod, *ac_mod2; struct ipa_ac_mod *ipa_ac_mod; const struct db_mod *db_mod; if ( (ac_mod = mem_malloc(sizeof *ac_mod, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } ac_mod->mod_file = *(char **)arg; ac_mod->mod_ref_count = 0; if ( (ac_mod->mod_handle = dl_open(ac_mod->mod_file)) == NULL) { logconferrx("dl_open(%s): %s", ac_mod->mod_file, dl_error()); return -1; } if ( (mod_name = get_mod_name(ac_mod->mod_file)) == NULL) return -1; if (mem_asprintf(m_anon, &sym, "%s_ac_mod", mod_name) < 0) { logconferrx("mem_asprintf failed"); return -1; } if ( (ac_mod->ipa_ac_mod = (struct ipa_ac_mod *)dl_lookup_sym(ac_mod->mod_handle, sym)) == NULL) { logconferrx("given module is not an IPA accounting module or unknown symbol naming scheme is used"); return -1; } mem_free(sym, m_anon); mem_free(mod_name, m_anon); ipa_ac_mod = ac_mod->ipa_ac_mod; /* Check ipa_ac_mod API version. */ if (ipa_ac_mod->api_ver != IPA_AC_MOD_API_VERSION) { logconferrx("module %s uses ipa_ac_mod API version %u, my ipa_ac_mod API version is %u", ac_mod->mod_file, ipa_ac_mod->api_ver, IPA_AC_MOD_API_VERSION); return -1; } /* Check if module is thread-safe or vice versa. */ #ifdef WITH_PTHREAD if (!(ipa_ac_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) { logconferrx("module %s must be thread-safe", ac_mod->mod_file); return -1; } #else if (ipa_ac_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) { logconferrx("module %s must not be thread-safe", ac_mod->mod_file); return -1; } #endif /* WITH_PTHREAD */ if (strcmp(ipa_ac_mod->ac_name, "null") == 0) { logconferrx("module's accounting system name is \"null\", this is a name of the builtin accounting system"); return -1; } if ( (ac_mod2 = ac_mod_by_name(ipa_ac_mod->ac_name)) != NULL) { conferrx("duplicated accounting system name \"%s\" in %s and %s modules", ipa_ac_mod->ac_name, ac_mod->mod_file, ac_mod2->mod_file); return -1; } if ( (ac_mod2 = ac_mod_by_prefix(ipa_ac_mod->conf_prefix)) != NULL) { conferrx("duplicated configuration prefix \"%s\" in %s and %s modules", ipa_ac_mod->conf_prefix, ac_mod->mod_file, ac_mod2->mod_file); return -1; } if ( (db_mod = db_mod_by_prefix(ipa_ac_mod->conf_prefix)) != NULL) { conferrx("duplicated configuration prefix \"%s\" in %s and %s modules", ipa_ac_mod->conf_prefix, ac_mod->mod_file, db_mod->mod_file); return -1; } ipa_ac_mod->suppfunc = &suppfunc; ipa_ac_mod->memfunc = &memfunc; ipa_ac_mod->ac_create_rule = create_rule; ipa_ac_mod->ac_delete_rule = delete_rule; if (init_conf_tbls(ac_mod->mod_file, 1, ipa_ac_mod->conf_sect_tbl, &ac_mod->conf_sect_hash, ipa_ac_mod->conf_param_tbl, &ac_mod->conf_param_hash) < 0) return -1; if (ipa_ac_mod->conf_init() < 0) { conferrx("module %s: conf_init failed", ac_mod->mod_file); return -1; } SLIST_INSERT_HEAD(&ac_mod_list, ac_mod, link); ++nac_mods; return 0; } /* * Parse the "db_mod" parameter. */ static int parse_db_mod(void *arg) { char *mod_name, *sym; struct db_mod *db_mod, *db_mod2; struct ipa_db_mod *ipa_db_mod; const struct ac_mod *ac_mod; if ( (db_mod = mem_malloc(sizeof *db_mod, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } db_mod->mod_file = *(char **)arg; if ( (db_mod->mod_handle = dl_open(db_mod->mod_file)) == NULL) { logconferrx("dl_open(%s): %s", db_mod->mod_file, dl_error()); return -1; } if ( (mod_name = get_mod_name(db_mod->mod_file)) == NULL) return -1; if (mem_asprintf(m_anon, &sym, "%s_db_mod", mod_name) < 0) { logconferrx("mem_asprintf failed"); return -1; } if ( (db_mod->ipa_db_mod = (struct ipa_db_mod *)dl_lookup_sym(db_mod->mod_handle, sym)) == NULL) { logconferrx("given module is not an IPA database module or unknown symbol naming scheme is used"); return -1; } mem_free(sym, m_anon); mem_free(mod_name, m_anon); ipa_db_mod = db_mod->ipa_db_mod; /* Check ipa_db_mod API version. */ if (ipa_db_mod->api_ver != IPA_DB_MOD_API_VERSION) { logconferrx("module %s uses ipa_db_mod API version %u, my ipa_db_mod API version is %u", db_mod->mod_file, ipa_db_mod->api_ver, IPA_DB_MOD_API_VERSION); return -1; } /* Check if module is thread-safe or vice versa. */ #ifdef WITH_PTHREAD if (!(ipa_db_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) { logconferrx("module %s must be thread-safe", db_mod->mod_file); return -1; } #else if (ipa_db_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) { logconferrx("module %s must not be thread-safe", db_mod->mod_file); return -1; } #endif /* WITH_PTHREAD */ if (strcmp(ipa_db_mod->db_name, "null") == 0) { logconferrx("module's database name is \"null\", this is a name of the builtin database"); return -1; } if ( (db_mod2 = db_mod_by_name(ipa_db_mod->db_name)) != NULL) { conferrx("duplicated database name \"%s\" in %s and %s modules", ipa_db_mod->db_name, db_mod->mod_file, db_mod2->mod_file); return -1; } if ( (db_mod2 = db_mod_by_prefix(ipa_db_mod->conf_prefix)) != NULL) { conferrx("duplicated configuration prefix \"%s\" in %s and %s modules", ipa_db_mod->conf_prefix, db_mod->mod_file, db_mod2->mod_file); return -1; } if ( (ac_mod = ac_mod_by_prefix(ipa_db_mod->conf_prefix)) != NULL) { conferrx("duplicated configuration prefix \"%s\" in %s and %s modules", ipa_db_mod->conf_prefix, db_mod->mod_file, ac_mod->mod_file); return -1; } ipa_db_mod->suppfunc = &suppfunc; ipa_db_mod->memfunc = &memfunc; if (init_conf_tbls(db_mod->mod_file, 1, ipa_db_mod->conf_sect_tbl, &db_mod->conf_sect_hash, ipa_db_mod->conf_param_tbl, &db_mod->conf_param_hash) < 0) return -1; if (ipa_db_mod->conf_init() < 0) { conferrx("module %s: conf_init failed", db_mod->mod_file); return -1; } SLIST_INSERT_HEAD(&db_mod_list, db_mod, link); ++ndb_mods; return 0; } /* * Parse the "ac_list" parameter. */ static int parse_ac_list(void *arg) { char *ptr; const char *ac_name; struct ac_elem *ac1, *ac2; struct ac_set *set1, *set2; struct ac_list *list; struct ac_mod *ac_mod; const struct ac_list **listp; switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: listp = &currule->ac_list; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: listp = &curautorule->ac_list; break; #endif case IPA_CONF_SECT_RULEPAT: listp = &currulepat->ac_list; break; default: /* IPA_CONF_SECT_GLOBAL */ listp = &global_ac_list; } if (*listp != NULL) { logconferrx("cannot redefine this parameter"); return -1; } set1 = NULL; list = NULL; for (ptr = *(char **)arg; ptr != NULL;) { /* Get the name of the next accounting system. */ ac_name = ptr; if ( (ptr = strchr(ptr, ' ')) != NULL) *ptr++ = '\0'; /* Handle "null" accounting system. */ if (strcmp(ac_name, "null") == 0) { if (list != NULL || ptr != NULL) { logconferrx("builtin accounting system \"null\" cannot be used together with another accounting systems"); return -1; } *listp = &ac_list_null; return 0; } if ( (ac_mod = ac_mod_by_name(ac_name)) == NULL) { logconferrx("cannot find module with \"%s\" accounting system name", ac_name); return -1; } if (set1 != NULL) { /* We already have set for current ac_list parameter. */ STAILQ_FOREACH(ac1, list, link) if (strcmp(ac_name, ac1->ipa_ac_mod->ac_name) == 0) { logconferrx("duplicated accounting system \"%s\"", ac_name); return -1; } } else { /* Create new set for ac_list parameter. */ if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } list = &set1->list; STAILQ_INIT(list); } /* Add new ac element to ac_list. */ if ( (ac1 = mem_malloc(sizeof *ac1, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } ac1->ipa_ac_mod = ac_mod->ipa_ac_mod; ac1->mod_file = ac_mod->mod_file; /* Increment reference counter if needed. */ ac1->mod_ref_count = &ac_mod->mod_ref_count; switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: /* FALLTHROUGH */ #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: #endif ac_mod->mod_ref_count++; break; } STAILQ_INSERT_TAIL(list, ac1, link); } /* * If we already have the same ac_list (with elements exactly * in the same order), then free memory used by just allocated * one (mod_ref_count was updated above). */ SLIST_FOREACH(set2, &ac_sets, link) { ac1 = STAILQ_FIRST(&set1->list); ac2 = STAILQ_FIRST(&set2->list); while (ac1 != NULL && ac2 != NULL) { if (ac1->ipa_ac_mod != ac2->ipa_ac_mod) break; ac1 = STAILQ_NEXT(ac1, link); ac2 = STAILQ_NEXT(ac2, link); } if (ac1 == NULL && ac2 == NULL) { /* Duplicated ac_list was found. */ *listp = &set2->list; free_ac_set(set1); return 0; } } /* New ac_list --> add it to ac_sets. */ *listp = list; SLIST_INSERT_HEAD(&ac_sets, set1, link); return 0; } /* * Parse the "db_list" parameter. */ static int parse_db_list(void *arg) { char *ptr; const char *db_name; struct db_elem *db1, *db2; struct db_set *set1, *set2; struct db_list *list; const struct db_mod *db_mod; const struct db_list **listp; switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: listp = &currule->db_list; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: listp = &curautorule->db_list; break; #endif #ifdef WITH_LIMITS case IPA_CONF_SECT_LIMIT: listp = &curlimit->db_list; break; #endif #ifdef WITH_THRESHOLDS case IPA_CONF_SECT_THRESHOLD: listp = &curthreshold->db_list; break; #endif case IPA_CONF_SECT_RULEPAT: listp = &currulepat->db_list; break; default: /* IPA_CONF_SECT_GLOBAL */ listp = &global_db_list; } if (*listp != NULL) { logconferrx("cannot redefine this parameter"); return -1; } set1 = NULL; list = NULL; for (ptr = *(char **)arg; ptr != NULL;) { /* Get the name of the next database. */ db_name = ptr; if ( (ptr = strchr(ptr, ',')) != NULL) *ptr++ = '\0'; /* Handle "null" database. */ if (strcmp(db_name, "null") == 0) { if (list != NULL && ptr != NULL) { logconferrx("builtin \"null\" database cannot be used together with another databases");; return -1; } *listp = &db_list_null; return 0; } if ( (db_mod = db_mod_by_name(db_name)) == NULL) { logconferrx("cannot find module with \"%s\" database name", db_name); return -1; } if (set1 != NULL) { /* We already have set for current db_list parameter. */ STAILQ_FOREACH(db1, list, link) if (strcmp(db_name, db1->ipa_db_mod->db_name) == 0) { logconferrx("duplicated database \"%s\"", db_name); return -1; } } else { /* Create new set for db_list parameter. */ if ( (set1 = mem_malloc(sizeof *set1, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } list = &set1->list; STAILQ_INIT(list); } /* Add new db element to db_list. */ if ( (db1 = mem_malloc(sizeof *db1, m_anon)) == NULL) { logconferrx("mem_malloc failed"); return -1; } db1->ipa_db_mod = db_mod->ipa_db_mod; db1->mod_file = db_mod->mod_file; STAILQ_INSERT_TAIL(list, db1, link); } /* * If we already have the same db_list (with elements exactly * in the same order), then free memory used by just allocated one. */ SLIST_FOREACH(set2, &db_sets, link) { db1 = STAILQ_FIRST(&set1->list); db2 = STAILQ_FIRST(&set2->list); while (db1 != NULL && db2 != NULL) { if (db1->ipa_db_mod != db2->ipa_db_mod) break; db1 = STAILQ_NEXT(db1, link); db2 = STAILQ_NEXT(db2, link); } if (db1 == NULL && db2 == NULL) { /* Duplicated db_list was found. */ *listp = &set2->list; free_db_set(set1); return 0; } } /* New db_list --> add it to db_sets. */ *listp = list; SLIST_INSERT_HEAD(&db_sets, set1, link); return 0; } #ifdef WITH_RULES /* * Parse the "ac_gather_add" parameter. */ static int parse_ac_gather_add(void *arg) { char *pat; struct rule *rule; rule = currule; if (rule->ac_gather_add_pat != NULL) { logconferrx("cannot redefine this parameter"); return -1; } rule->ac_gather_add_pat = pat = *(char **)arg; if ( (re_errcode = regcomp(&rule->ac_gather_add_reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf); return -1; } if (rule->ac_gather_sub_pat == NULL) SLIST_INSERT_HEAD(&ac_gather_list, rule, ac_gather_link); return 0; } /* * Parse the "ac_gather_sub" parameter. */ static int parse_ac_gather_sub(void *arg) { char *pat; struct rule *rule; rule = currule; if (rule->ac_gather_sub_pat != NULL) { logconferrx("cannot redefine this parameter"); return -1; } rule->ac_gather_sub_pat = pat = *(char **)arg; if ( (re_errcode = regcomp(&rule->ac_gather_sub_reg, pat, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); logconferrx("regcomp(\"%s\"): %s", pat, re_errbuf); return -1; } if (rule->ac_gather_add_pat == NULL) SLIST_INSERT_HEAD(&ac_gather_list, rule, ac_gather_link); return 0; } #endif /* WITH_RULES */ /* * Parse "posix_re_pattern" parameter. */ static int parse_posix_re_pattern(void *arg) { posix_re_pattern = *(int *)arg; return 0; } /* * Parse "shell_path" parameter. */ static int parse_shell_path(void *arg) { shell_path = *(char **)arg; if (*shell_path != '/') { logconferrx("shell path should be absolute"); return -1; } return 0; } /* * Parse "shell_arg1" parameter. */ static int parse_shell_arg1(void *arg) { shell_arg1 = *(char **)arg; return 0; } /* * Parse "ctl_enable" parameter. */ static int parse_ctl_enable(void *arg) { ctl_enable = *(int *)arg; return 0; } /* * Parse "ctl_socket_path" parameter. */ static int parse_ctl_socket_path(void *arg) { const struct sockaddr_un addr; ctl_socket_path = *(char **)arg; if (*ctl_socket_path != '/') { logconferrx("ctl socket path should be absolute"); return -1; } if (strlen(ctl_socket_path) + 1 > sizeof addr.sun_path) { logconferrx("too long path for ctl socket path"); return -1; } return 0; } /* * Parse "ctl_socket_perm" parameter. */ static int parse_ctl_socket_perm(void *arg) { const char *ptr = *(char **)arg; mode_t val; val = 0; for (; *ptr != '\0'; ++ptr) switch (*ptr) { case 'u': val |= S_IWUSR; break; case 'g': val |= S_IWGRP; break; case 'o': #ifdef CTL_CHECK_CREDS val |= S_IWOTH; break; #else logconferrx("cannot allow to write to socket for other users"); return -1; #endif default: logconferrx("wrong value"); return -1; } ctl_socket_perm = val | S_IRUSR; return 0; } /* * Parse "ctl_timeout" parameter. */ static int parse_ctl_timeout(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0) || timeval >= UINT64_C(30)) { logconferrx("argument should be greater than zero seconds and less than 30 seconds"); return -1; } ctl_timeout = (u_int)timeval; return 0; } /* * Parse "debug_ctl" parameter. */ static int parse_debug_ctl(void *arg) { return debug_ctl = check_debug_level(*(uint32_t *)arg, 1); } #ifdef CTL_CHECK_CREDS /* * Find ctl class with the given name. */ static const struct ctl_acl_class * ctl_class_by_name(const char *class_name) { const struct ctl_acl_class *class; STAILQ_FOREACH(class, &ctl_acl_classes, link) if (strcmp(class->class_name, class_name) == 0) return class; return NULL; } /* * Parse "ctl_acl_class" parameter. */ static int parse_ctl_acl_class(void *arg) { char *ptr, *str = *(char **)arg; struct ctl_acl_elem *elem; struct ctl_acl_class *class; /* [ ...] */ if ( (ptr = strchr(str, ' ')) != NULL) *ptr = '\0'; /* Check if we already have class with the same name. */ if (ctl_class_by_name(str) != NULL) { logconferrx("duplicated ctl class \"%s\"", str); return -1; } /* New class. */ if ( (class = mzone_alloc(ctl_acl_class_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } if ( (class->class_name = mem_strdup(str, m_ctl)) == NULL) { logconferrx("mem_strdup failed"); return -1; } /* Init ACL for new class. */ STAILQ_INIT(&class->list); /* Link new class to all classes list. */ STAILQ_INSERT_TAIL(&ctl_acl_classes, class, link); if (ptr == NULL) /* Empty class. */ return 0; str = ptr + 1; do { if ( (elem = mzone_alloc(ctl_acl_elem_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(&class->list, elem, link); if (*str == '!') { elem->allowed = 0; ++str; } else elem->allowed = 1; ptr = str; if ( (str = strchr(str, ' ')) != NULL) *str++ = '\0'; if (*ptr == '%') { /* Group. */ elem->user = NULL; if ( (elem->group = mem_strdup(++ptr, m_ctl)) == NULL) { logconferrx("mem_strdup failed"); return -1; } } else { /* User. */ elem->group = NULL; if ( (elem->user = mem_strdup(ptr, m_ctl)) == NULL) { logconferrx("mem_strdup failed"); return -1; } } } while (str != NULL); return 0; } static int parse_ctl_xxx_acl(const struct ctl_acl_class **classpp) { if ( (*classpp = ctl_class_by_name(parser_args)) == NULL) { logconferrx("cannot find ctl class \"%s\"", parser_args); return -1; } return 0; } /* * Parse "ctl_dump_acl" parameter. */ /* ARGSUSED */ static int parse_ctl_dump_acl(void *arg ATTR_UNUSED) { return parse_ctl_xxx_acl(&ctl_dump_acl); } /* * Parse "ctl_freeze_acl" parameter. */ /* ARGSUSED */ static int parse_ctl_freeze_acl(void *arg ATTR_UNUSED) { return parse_ctl_xxx_acl(&ctl_freeze_acl); } /* * Parse "ctl_stat_acl" parameter. */ /* ARGSUSED */ static int parse_ctl_stat_acl(void *arg ATTR_UNUSED) { return parse_ctl_xxx_acl(&ctl_stat_acl); } /* * Parse "ctl_rule_acl" parameter. */ /* ARGSUSED */ static int parse_ctl_rule_acl(void *arg ATTR_UNUSED) { const struct ctl_acl_class **classpp; switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: classpp = &currule->ctl_rule_acl; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: classpp = &curautorule->ctl_rule_acl; break; #endif case IPA_CONF_SECT_RULEPAT: classpp = &currulepat->ctl_rule_acl; break; default: /* IPA_CONF_SECT_GLOBAL */ classpp = &global_ctl_rule_acl; } return parse_ctl_xxx_acl(classpp); } #endif /* CTL_CHECK_CREDS */ #ifdef WITH_THRESHOLDS /* * Parse "debug_threshold" parameter. */ static int parse_debug_threshold(void *arg) { int level; if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0) return -1; switch (section) { case IPA_CONF_SECT_GLOBAL: global_debug_threshold = level; break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->debug_threshold = level; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->debug_threshold = level; break; #endif default: /* IPA_CONF_SECT_RULEPAT */ currulepat->debug_threshold = level; } return 0; } /* * Parse "debug_threshold_init" parameter. */ static int parse_debug_threshold_init(void *arg) { int level; if ( (level = check_debug_level(*(uint32_t *)arg, 1)) < 0) return -1; switch (section) { case IPA_CONF_SECT_GLOBAL: global_debug_threshold_init = level; break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: currule->debug_threshold_init = level; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: curautorule->debug_threshold_init = level; break; #endif default: /* IPA_CONF_SECT_RULEPAT */ currulepat->debug_threshold_init = level; } return 0; } /* * Parse "load_threshold" parameter. */ static int parse_load_threshold(void *arg) { if (section == IPA_CONF_SECT_THRESHOLD) curthreshold->load_threshold = *(int *)arg; else /* IPA_CONF_SECT_GLOBAL */ global_load_threshold = *(int *)arg; return 0; } /* * Parse "threshold" section. */ static int parse_threshold_sect(void *arg) { const char *threshold_name = *(char **)arg; struct threshold *threshold; STAILQ_FOREACH(threshold, thresholds_list, link) if (strcmp(threshold->threshold_name, threshold_name) == 0) { logconferrx("this section is duplicated"); return -1; } if ( (threshold = mzone_alloc(threshold_mzone)) == NULL) { logconferrx("mzone_alloc failed"); return -1; } STAILQ_INSERT_TAIL(thresholds_list, threshold, link); if ( (threshold->threshold_name = mem_strdup(threshold_name, m_anon)) == NULL) { logconferrx("mem_strdup failed"); return -1; } threshold->threshold_info = NULL; threshold->thresholdno = thresholdno++; threshold->thr = threshold->thr_dev = threshold->cnt_neg = UINT64_C(0); threshold->thr_dev_per_cent = 0; threshold->cnt_slice = NULL; threshold->cnt_slice_sign = NULL; threshold->threshold_type = -1; threshold->load_threshold = -1; threshold->time_width = 0; threshold->time_slice = NULL; threshold->worktime = NULL; threshold->is_active = ACTIVE_FLAG; threshold->db_list = NULL; threshold->below_lim = threshold->equal_lim = threshold->above_lim = 0; init_cmds_in_threshold(threshold); threshold->wpid.type = WPID_TYPE_THRESHOLD; threshold->wpid.pid = 0; threshold->wpid.u.threshold = threshold; #ifdef WITH_RULES threshold->rule = currule; #endif if (section_top == IPA_CONF_SECT_RULE) ++nstatthresholds; section_top = IPA_CONF_SECT_THRESHOLD; if (parser_local_sym_add("threshold", threshold->threshold_name, 0) < 0) return -1; if (mod_conf_event(IPA_CONF_EVENT_THRESHOLD_BEGIN, threshold->thresholdno, threshold->threshold_name) < 0) return -1; curthreshold = threshold; return 0; } /* * Parse "threshold" parameter. */ static int parse_threshold_param(void *arg) { uint64_t *ptr = (uint64_t *)arg; if ( (curthreshold->thr = *(ptr + 1)) == UINT64_C(0)) { logconferrx("argument should be greater than zero"); return -1; } curthreshold->cnt_type = (u_int)*ptr; return 0; } /* * Parse "threshold_deviation" parameter. */ /* ARGSUSED */ static int parse_threshold_deviation(void *arg ATTR_UNUSED) { u_int cnt_type; struct threshold *threshold; threshold = curthreshold; if (threshold->thr == UINT64_C(0)) { logconferrx("\"threshold\" parameter should be specified before"); return -1; } if (get_value_or_per_cent(&threshold->thr_dev, &cnt_type) < 0) return -1; if (threshold->thr_dev == UINT64_C(0)) { logconferrx("argument should be greater than zero"); return -1; } if (cnt_type == IPA_CONF_TYPE_PER_CENT) threshold->thr_dev_per_cent = (u_int)threshold->thr_dev; else { if (cnt_type != threshold->cnt_type) { logconferrx("different arguments types for \"threshold\" and \"threshold_deviation\" parameters"); return -1; } if (threshold->thr_dev >= threshold->thr) { logconferrx("value of this parameter should be less than value of \"threshold\" parameter"); return -1; } threshold->thr_dev_per_cent = 0; } return 0; } /* * Parse "threshold_time_width" parameter. */ static int parse_threshold_time_width(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0)) { logconferrx("argument should be greater than zero seconds"); return -1; } if (timeval > UINT_MAX) { logconferrx("too big value %"PRIu64" for u_int type", timeval); return -1; } if (section == IPA_CONF_SECT_THRESHOLD) curthreshold->time_width = (u_int)timeval; else /* IPA_CONF_SECT_GLOBAL */ global_threshold_time_width = (u_int)timeval; return 0; } /* * Parse "threshold_time_slice" parameter. */ static int parse_threshold_time_slice(void *arg) { uint64_t timeval = *(uint64_t *)arg; struct tevent *tevent; if (timeval == UINT64_C(0)) { logconferrx("argument should be greater than zero seconds"); return -1; } if (timeval > UINT_MAX) { logconferrx("too big value %"PRIu64" for u_int type", timeval); return -1; } if (SECONDS_IN_DAY % timeval != 0) { logconferrx("number of seconds in one day must be divisible by the value of \"threshold_time_slice\" parameter"); return -1; } if ( (tevent = find_tevent((u_int)timeval)) == NULL) { logconferrx("find_tevent failed"); return -1; } if (section == IPA_CONF_SECT_THRESHOLD) curthreshold->time_slice = tevent; else /* IPA_CONF_SECT_GLOBAL */ global_threshold_time_slice = tevent; return 0; } /* * Parse "threshold_type" parameter. */ static int parse_threshold_type(void *arg) { uint32_t val = *(uint32_t *)arg; if (val > THRESHOLD_JUMP_OVER_ALLBITS) { logconferrx("incorrect value for this parameter"); return -1; } if (section == IPA_CONF_SECT_THRESHOLD) curthreshold->threshold_type = (int)val; else /* IPA_CONF_SECT_GLOBAL */ global_threshold_type = (int)val; return 0; } /* * Parse "threshold_balance" parameter. */ static int parse_threshold_balance(void *arg) { char *ptr1 = *(char **)arg, *ptr2; u_int i, val[3]; for (i = 0, ptr2 = ptr1; i < 3; ptr1 = ptr2, ++i) { if (i != 2) { ptr2 = strchr(ptr2, ':'); *ptr2 = '\0'; ++ptr2; } if (*ptr1 != '-') { if (strto_u_int(&val[i], ptr1, (char **)NULL) < 0) return -1; switch (val[i]) { case 0: case UINT_MAX: logconferrx("values should be greater than zero and less than %u", UINT_MAX); return -1; } } else val[i] = UINT_MAX; } if (section == IPA_CONF_SECT_THRESHOLD) { curthreshold->below_lim = curthreshold->below_cnt = val[0]; curthreshold->equal_lim = curthreshold->equal_cnt = val[1]; curthreshold->above_lim = curthreshold->above_cnt = val[2]; } else { /* IPA_CONF_SECT_GLOBAL */ global_threshold_below_lim = val[0]; global_threshold_equal_lim = val[1]; global_threshold_above_lim = val[2]; } return 0; } /* * Check if threshold's time_width and time_slice can be used together. */ static int validate_threshold_times(u_int time_width, const struct tevent *time_slice) { if (time_width == 0 && time_slice == NULL) return 0; if ((time_width != 0 && time_slice == NULL) || (time_width == 0 && time_slice != NULL)) { logconferrx("\"threshold_time_width\" and \"threshold_time_slice\" parameters should be defined together in one section"); return -1; } if (time_width < time_slice->event_step) { logconferrx("the value of \"threshold_time_width\" parameter must be greater than the value of \"threshold_time_slice\" parameter"); return -1; } if (time_width % time_slice->event_step != 0) { logconferrx("the value of \"threshold_time_width\" parameter must be divisible by the value of \"threshold_time_slice\" parameter"); return -1; } return 0; } /* * Inherit settings for a threshold from the "global" section. */ static int inherit_threshold_from_global(const char *what, const char *sect_name, struct threshold *threshold) { if (threshold->load_threshold < 0) threshold->load_threshold = global_load_threshold; if (threshold->threshold_type < 0) threshold->threshold_type = global_threshold_type; if (threshold->below_lim == 0) { threshold->below_lim = threshold->below_cnt = global_threshold_below_lim; threshold->equal_lim = threshold->equal_cnt = global_threshold_equal_lim; threshold->above_lim = threshold->above_cnt = global_threshold_above_lim; } if (threshold->time_width == 0) { threshold->time_width = global_threshold_time_width; threshold->time_slice = global_threshold_time_slice; if (threshold->time_width == 0) { conferrx("%s %s, threshold %s: \"threshold_time_width\" and \"threshold_time_slice\" parameters are not defined", what, sect_name, threshold->threshold_name); return -1; } } return 0; } #endif /* WITH_THRESHOLDS */ #ifdef WITH_AUTORULES /* * Parse "debug_autorule" parameter. */ static int parse_debug_autorule(void *arg) { return debug_autorule = check_debug_level(*(uint32_t *)arg, 1); } /* * Parse the "autorule" section. */ static int parse_autorule(void *arg) { const char *arule_name = *(char **)arg; struct autorule *autorule; for (autorule = autorules; autorule < autorules + nautorules; ++autorule) if (strcmp(arule_name, autorule->arule_name) == 0) { logconferrx("this section is duplicated"); return -1; } if (marray_alloc(autorules_marray, &nautorules, 1) < 0) { logconferrx("marray_alloc failed"); return -1; } autorule = autorules + nautorules; if ( (autorule->arule_name = mem_strdup(arule_name, m_anon)) == NULL) { logconferrx("mem_strdup failed"); return -1; } autorule->aruleno = nautorules++; autorule->update_tevent = autorule->append_tevent = NULL; autorule->worktime = autorule->worktime_rule = NULL; autorule->is_active = ACTIVE_FLAG; autorule->ac_list = NULL; autorule->db_list = NULL; autorule->debug_exec = -1; init_cmds_in_autorule(autorule); #ifdef WITH_LIMITS autorule->debug_limit = autorule->debug_limit_init = -1; limits_list = &autorule->limits; STAILQ_INIT(limits_list); limitno = 0; #endif #ifdef WITH_THRESHOLDS autorule->debug_threshold = autorule->debug_threshold_init = -1; thresholds_list = &autorule->thresholds; STAILQ_INIT(thresholds_list); thresholdno = 0; #endif #ifdef CTL_CHECK_CREDS autorule->ctl_rule_acl = NULL; #endif section_first = section_top = IPA_CONF_SECT_AUTORULE; if (parser_local_sym_add("autorule", autorule->arule_name, 0) < 0) return -1; if (mod_conf_event(IPA_CONF_EVENT_AUTORULE_BEGIN, autorule->aruleno, autorule->arule_name) < 0) return -1; curautorule = autorule; return 0; } #endif /* WITH_AUTORULES */ /* * Parse "sleep_after_dump" parameter. */ static int parse_sleep_after_dump(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0) || timeval >= 5 * SECONDS_IN_MINUTE) { logconferrx("argument should be greater than zero seconds and less than 5 minutes"); return -1; } sleep_after_dump = (u_int)timeval; return 0; } /* * Parse "sensitive_time" parameter. */ static int parse_sensitive_time(void *arg) { uint64_t timeval = *(uint64_t *)arg; if (timeval == UINT64_C(0)) { logconferrx("argument should be greater than zero"); return -1; } if (timeval >= 5 * SECONDS_IN_MINUTE) { logconferrx("argument should be less than 5 minutes"); return -1; } sensitive_time = (u_int)timeval; return 0; } static int set_curcmdl(void) { int x, *set; #ifdef WITH_LIMITS static void *curcmdsect; #endif x = section == SECT_STARTUP ? RC_STARTUP : RC_SHUTDOWN; set = NULL; switch (section) { case SECT_STARTUP: case SECT_SHUTDOWN: switch (section_top) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: set = &currule->rc[x].set; curcmdl = &currule->rc[x].cmdl; # ifdef WITH_LIMITS curcmdsect = &currule->rc[x]; # endif break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: set = &curautorule->rc[x].set; curcmdl = &curautorule->rc[x].cmdl; # ifdef WITH_LIMITS curcmdsect = &curautorule->rc[x]; # endif break; #endif #ifdef WITH_LIMITS case IPA_CONF_SECT_LIMIT: set = &curlimit->rc[x].set; curcmdl = &curlimit->rc[x].cmdl; curcmdsect = &curlimit->rc[x]; break; #endif #ifdef WITH_SUBLIMITS case SECT_SUBLIMIT: set = &cursublimit->rc[x].set; curcmdl = &cursublimit->rc[x].cmdl; curcmdsect = &cursublimit->rc[x]; break; #endif #ifdef WITH_THRESHOLDS case IPA_CONF_SECT_THRESHOLD: curcmdl = &curthreshold->rc[x]; break; #endif case IPA_CONF_SECT_RULEPAT: set = &currulepat->rc[x].set; curcmdl = &currulepat->rc[x].cmdl; #ifdef WITH_LIMITS curcmdsect = &currulepat->rc[x]; #endif break; default: /* IPA_CONF_SECT_ROOT */ curcmdl = x == RC_STARTUP ? &cmds_startup : &cmds_shutdown; } break; #ifdef WITH_LIMITS case SECT_RESTART: curcmdl = &curlimit->restart.cmdl; break; case SECT_REACH: # ifdef WITH_SUBLIMITS if (section_top == SECT_SUBLIMIT) curcmdl = &cursublimit->reach; else # endif curcmdl = &curlimit->reach; break; case SECT_EXPIRE: curcmdl = &curlimit->expire.cmdl; break; case SECT_IF_REACHED: curcmdl = &((struct cmds_limit *)curcmdsect)->cmdl_if_reached; break; case SECT_IF_NOT_REACHED: curcmdl = &((struct cmds_limit *)curcmdsect)->cmdl_if_not_reached; break; case SECT_IF_ALL_REACHED: curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_all_reached; break; case SECT_IF_ALL_NOT_REACHED: curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_all_not_reached; break; case SECT_IF_ANY_REACHED: curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_any_reached; break; case SECT_IF_ANY_NOT_REACHED: curcmdl = &((struct cmds_rule *)curcmdsect)->cmdl_if_any_not_reached; break; #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS case SECT_BELOW_THRESHOLD: curcmdl = &curthreshold->below_threshold; break; case SECT_ABOVE_THRESHOLD: curcmdl = &curthreshold->above_threshold; break; case SECT_EQUAL_THRESHOLD: curcmdl = &curthreshold->equal_threshold; break; #endif /* WITH_THRESHOLDS */ } if (set != NULL) { if (*set != 0) { logconferrx("this section is duplicated"); return -1; } *set = 1; } if (!STAILQ_EMPTY(&curcmdl->list)) { logconferrx("this section is duplicated"); return -1; } return 0; } static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 }; static const u_int sect_rule[] = { IPA_CONF_SECT_RULE, 0 }; static const u_int sect_rulepat[] = { IPA_CONF_SECT_RULEPAT, 0 }; static const u_int sect_any_rule[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_RULEPAT, 0 }; static const u_int sect_for_exec[] = { SECT_STARTUP, SECT_SHUTDOWN, SECT_RESTART, SECT_REACH, SECT_EXPIRE, SECT_IF_REACHED, SECT_IF_NOT_REACHED, SECT_IF_ANY_REACHED, SECT_IF_ALL_REACHED, SECT_IF_ANY_NOT_REACHED, SECT_IF_ALL_NOT_REACHED, SECT_BELOW_THRESHOLD, SECT_ABOVE_THRESHOLD, SECT_EQUAL_THRESHOLD, 0 }; #ifdef WITH_ANY_LIMITS static const u_int sect_for_any_limit[] = { IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_RULEPAT, 0 }; #endif #ifdef WITH_THRESHOLDS static const u_int sect_threshold[] = { IPA_CONF_SECT_THRESHOLD, 0 }; static const u_int sect_global_threshold[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_THRESHOLD, 0 }; #endif #ifdef WITH_LIMITS static const u_int sect_limit[] = { IPA_CONF_SECT_LIMIT, 0 }; static const u_int sect_global_limit[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_LIMIT, 0 }; static const u_int sect_restart[] = { SECT_RESTART, 0 }; static const u_int sect_expire[] = { SECT_EXPIRE, 0 }; static const u_int sect_startup_shutdown[] = { SECT_STARTUP, SECT_SHUTDOWN, 0 }; static const u_int sect_for_reach[] = { IPA_CONF_SECT_LIMIT, SECT_SUBLIMIT, 0 }; #endif static const u_int sect_for_startup_shutdown[] = { IPA_CONF_SECT_ROOT, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, SECT_SUBLIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 }; #if defined(WITH_RULES) || defined(WITH_ANY_LIMITS) static const u_int sect_for_info[] = { IPA_CONF_SECT_RULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, 0 }; #endif static const u_int sect_for_db_list[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 }; static const u_int sect_for_worktime[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, IPA_CONF_SECT_AUTORULE, IPA_CONF_SECT_LIMIT, IPA_CONF_SECT_THRESHOLD, IPA_CONF_SECT_RULEPAT, 0 }; #ifdef WITH_AUTORULES static const u_int sect_autorule[] = { IPA_CONF_SECT_AUTORULE, 0 }; #endif /* * Sections in ipa.conf(5). */ static ipa_conf_sect conf_sect_tbl[] = { #ifdef WITH_RULES { "rule", IPA_CONF_SECT_RULE, 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_rule }, #endif { "rulepat", IPA_CONF_SECT_RULEPAT, 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_rulepat }, { "global", IPA_CONF_SECT_GLOBAL, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_global }, #ifdef WITH_AUTORULES { "autorule", IPA_CONF_SECT_AUTORULE, 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_autorule }, #endif #ifdef WITH_LIMITS { "limit", IPA_CONF_SECT_LIMIT, 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_limit_sect }, { "restart", SECT_RESTART, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_limit, NULL }, { "reach", SECT_REACH, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_reach, NULL }, { "expire", SECT_EXPIRE, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_limit, NULL }, # ifdef WITH_SUBLIMITS { "sublimit", SECT_SUBLIMIT, -1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_limit, parse_sublimit }, # endif { "if_reached", SECT_IF_REACHED, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, { "if_not_reached", SECT_IF_NOT_REACHED, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, { "if_any_reached", SECT_IF_ANY_REACHED, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, { "if_all_reached", SECT_IF_ALL_REACHED, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, { "if_any_not_reached", SECT_IF_ANY_NOT_REACHED,0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, { "if_all_not_reached", SECT_IF_ALL_NOT_REACHED,0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_startup_shutdown, NULL }, #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS { "threshold", IPA_CONF_SECT_THRESHOLD,1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_threshold_sect }, { "below_threshold", SECT_BELOW_THRESHOLD, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_threshold, NULL }, { "above_threshold", SECT_ABOVE_THRESHOLD, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_threshold, NULL }, { "equal_threshold", SECT_EQUAL_THRESHOLD, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_threshold, NULL }, #endif { "startup", SECT_STARTUP, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_startup_shutdown, NULL }, { "shutdown", SECT_SHUTDOWN, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_startup_shutdown, NULL }, { NULL, 0, 0, NULL, NULL, IPA_CONF_TYPE_MISC, 0, NULL } }; #ifdef WITH_LIMITS #define PAT_TIME_EXP "^(\ (\\+[mhDWM] ?)?([[:digit:]]+[smhDW]( ?[[:digit:]]+[smhDW])*)?|\ ([[:digit:]]+[smhDW]( ?[[:digit:]]+[smhDW])*)?( ?\\+[mhDWM])?)$" #endif #ifdef WITH_THRESHOLDS #define PAT_THR_BALANCE "^(-|[[:digit:]]+):(-|[[:digit:]]+):(-|[[:digit:]]+)$" #endif #define PAT_WORKTIME1 "[SMTWHFA] (\\*|[[:digit:]]{1,2}:[[:digit:]]{1,2}-[[:digit:]]{1,2}:[[:digit:]]{1,2}( [[:digit:]]{1,2}:[[:digit:]]{1,2}-[[:digit:]]{1,2}:[[:digit:]]{1,2})*)" #define PAT_WORKTIME "^"PAT_WORKTIME1"( "PAT_WORKTIME1")*$" #define PAT_LIST "^[^ \"]+( [^ \"]+)*$" #ifdef CTL_CHECK_CREDS # define PAT_ACL "^[[:alnum:]]+$" # define PAT_CTL_ACL_CLASS "^[[:alnum:]]+( !?%?[-_[:alnum:]]+)*$" #endif /* * Parameters in ipa.conf(5). */ static ipa_conf_param conf_param_tbl[] = { { "update_time", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_any_rule, parse_update_time }, { "append_time", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_any_rule, parse_append_time }, { "wakeup_time", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_root, parse_wakeup_time }, { "freeze_time", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_root, parse_freeze_time }, { "sleep_after_dump", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_root, parse_sleep_after_dump }, { "sensitive_time", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_root, parse_sensitive_time }, { "exec", -1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_for_exec, parse_exec }, { "sync_exec", -1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_for_exec, parse_sync_exec }, { "ac_mod", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_ac_mod }, { "db_mod", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_db_mod }, { "ac_list", -1, PAT_LIST, ®_list, IPA_CONF_TYPE_MISC, sect_any_rule, parse_ac_list }, { "db_list", -1, NULL, ®_list, IPA_CONF_TYPE_MISC, sect_for_db_list, parse_db_list }, { "include", 1, NULL, NULL, IPA_CONF_TYPE_STRING, NULL, parse_include }, { "include_files", 1, NULL, NULL, IPA_CONF_TYPE_STRING, NULL, parse_include_files }, #if defined(WITH_RULES) || defined(WITH_ANY_LIMITS) { "info", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_for_info, parse_info }, #endif #ifdef WITH_RULES { "ac_gather_add", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_rule, parse_ac_gather_add }, { "ac_gather_sub", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_rule, parse_ac_gather_sub }, #endif #ifdef WITH_LIMITS { "limit", -1, NULL, NULL, IPA_CONF_TYPE_VALUE, sect_limit, parse_limit_param }, { "restart", -1, PAT_TIME_EXP, ®_texp, IPA_CONF_TYPE_MISC, sect_restart, parse_restart }, { "expire", -1, NULL, ®_texp, IPA_CONF_TYPE_MISC, sect_expire, parse_expire }, { "load_limit", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_global_limit, parse_load_limit }, { "debug_limit", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_any_rule, parse_debug_limit }, { "debug_limit_init", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_any_rule, parse_debug_limit_init }, #endif #ifdef WITH_THRESHOLDS { "threshold", -1, NULL, NULL, IPA_CONF_TYPE_VALUE, sect_threshold, parse_threshold_param }, { "threshold_deviation",-1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_threshold, parse_threshold_deviation }, { "threshold_time_width",-1,NULL, NULL, IPA_CONF_TYPE_TIME, sect_global_threshold, parse_threshold_time_width }, { "threshold_time_slice",-1,NULL, NULL, IPA_CONF_TYPE_TIME, sect_global_threshold, parse_threshold_time_slice }, { "threshold_type", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_global_threshold, parse_threshold_type }, { "threshold_balance", 1, PAT_THR_BALANCE, ®_thr_balance, IPA_CONF_TYPE_MISC, sect_global_threshold, parse_threshold_balance }, { "load_threshold", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_global_threshold, parse_load_threshold }, { "debug_threshold", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_any_rule, parse_debug_threshold }, { "debug_threshold_init",1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_any_rule, parse_debug_threshold_init }, #endif { "worktime", -1, PAT_WORKTIME, ®_worktime, IPA_CONF_TYPE_MISC, sect_for_worktime, parse_worktime }, #ifdef WITH_AUTORULES { "worktime_rule", -1, NULL, ®_worktime, IPA_CONF_TYPE_MISC, sect_autorule, parse_worktime_rule }, #endif { "posix_re_pattern", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_posix_re_pattern }, { "only_abs_paths", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_only_abs_paths }, { "keep_rules_order", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_keep_rules_order }, { "check_next_rulepat", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_rulepat, parse_check_next_rulepat }, { "shell_path", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_shell_path }, { "shell_arg1", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_shell_arg1 }, { "debug_time", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_time }, { "debug_ac_null", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_ac_null }, { "debug_db_null", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_db_null }, { "debug_exec", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_any_rule, parse_debug_exec }, { "debug_worktime", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_worktime }, { "ctl_enable", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_root, parse_ctl_enable }, { "ctl_socket_path", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_root, parse_ctl_socket_path }, { "ctl_socket_perm", 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_ctl_socket_perm }, { "ctl_timeout", -1, NULL, NULL, IPA_CONF_TYPE_TIME, sect_root, parse_ctl_timeout }, { "debug_ctl", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_ctl }, #ifdef CTL_CHECK_CREDS { "ctl_rule_acl", 1, PAT_ACL, ®_acl, IPA_CONF_TYPE_MISC, sect_any_rule, parse_ctl_rule_acl }, { "ctl_dump_acl", 1, NULL, ®_acl, IPA_CONF_TYPE_MISC, sect_root, parse_ctl_dump_acl }, { "ctl_freeze_acl", 1, NULL, ®_acl, IPA_CONF_TYPE_MISC, sect_root, parse_ctl_freeze_acl }, { "ctl_stat_acl", 1, NULL, ®_acl, IPA_CONF_TYPE_MISC, sect_root, parse_ctl_stat_acl }, { "ctl_acl_class", -1, PAT_CTL_ACL_CLASS, ®_ctl_acl_class, IPA_CONF_TYPE_MISC, sect_root, parse_ctl_acl_class }, #endif #ifdef WITH_AUTORULES { "debug_autorule", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_autorule }, #endif { NULL, 0, NULL, NULL, 0, 0, NULL } }; /* * Main function for parsing configuration file(s). */ int configure(PARSING_MODE mode) { int real_config; char *ptr; const u_int *id_ptr; const char *prefix; /* Configuration prefix. */ #ifdef WITH_RULES const struct worktime *rule_wt; #endif struct rulepat *rulepat; #ifdef WITH_RULES struct rule *rule; #endif #ifdef WITH_LIMITS struct limit *limit; struct cmd_list *prevcmdl; #endif #ifdef WITH_THRESHOLDS struct threshold *threshold; #endif #ifdef WITH_AUTORULES struct autorule *autorule; #endif /* Stack of nested configuration sections. */ struct section_stack { SLIST_ENTRY(section_stack) link; u_int section; /* Section ID. */ } *section_stack_ptr; SLIST_HEAD(, section_stack) section_stack_list = SLIST_HEAD_INITIALIZER(section_stack_list); const struct ac_mod *ac_mod; const struct db_mod *db_mod; struct time_param *time_param; struct debug_param *debug_param; struct parser_pbuf *pbuf; struct conf_sect_hash *conf_sect_hash; struct conf_param_hash *conf_param_hash; const struct conf_sect_hash *conf_sect_hash_ptr; const struct conf_param_hash *conf_param_hash_ptr; const ipa_conf_sect *conf_sect_tbl_ptr, *conf_sect; const ipa_conf_param *conf_param_tbl_ptr, *conf_param; int in_own_sect; /* 1, if in own ipa.conf(5) section. */ const struct get_arg_tbl *get_arg_ptr; if (mode == TEST_PARSING || mode == CMD_PARSING) use_log = real_config = 0; else real_config = 1; xvlogmsgx = vconferrx_priority; /* Set wrappers for log functions. */ mvlogmsgx = mvlogmsgx_conf_wrapper; parser_vlogmsgx = parser_vlogmsgx_wrapper; if (memfunc_init() < 0) { logmsgx(IPA_LOG_ERR, "configure: memfunc_init failed"); return -1; } if ((m_anon = mem_type_new_local(MTYPE_NAME(anonymous), "Anonymous memory", 0)) == NULL || (m_tmp = mem_type_new_local(MTYPE_NAME(tmp), "Temporal memory", 0)) == NULL || (m_ctl = mem_type_new_local(MTYPE_NAME(ctl), "Memory for ctl", 0)) == NULL || (m_cmd = mem_type_new_local(MTYPE_NAME(cmd), "Memory for commands", 0)) == NULL || (m_parser = mem_type_new_local(MTYPE_NAME(parser), "Memory allocated by parser", MEMTYPE_FLAGS)) == NULL) { logmsgx(IPA_LOG_ERR, "configure: mem_type_new_local failed"); return -1; } nac_mods = ndb_mods = 0; global_ac_list = NULL; global_db_list = NULL; for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param) *debug_param->param_value = -1; SLIST_INIT(&ac_mod_list); SLIST_INIT(&db_mod_list); if ( (conf_sect_hentry_mzone = mzone_init(MZONE_NAME(conf_sect_hentry), "Config sections hash entries", 0, sizeof(struct conf_sect_hentry), CONF_SECT_HENTRY_NSIZE, CONF_SECT_HENTRY_NALLOC)) == NULL) goto parsing_failed; if ( (conf_param_hentry_mzone = mzone_init(MZONE_NAME(conf_param_hentry), "Config parameters hash entries", 0, sizeof(struct conf_param_hentry), CONF_PARAM_HENTRY_NSIZE, CONF_PARAM_HENTRY_NALLOC)) == NULL) goto parsing_failed; if (mode != RECONFIG_PARSING) { init_rules_hash(); #ifdef WITH_ANY_LIMITS init_wpid_hash(); #endif } if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", MEMFLAG_OPTIMIZE, sizeof(struct rule), RULE_NSIZE, RULE_NALLOC)) == NULL) goto parsing_failed; if ( (rulepat_mzone = mzone_init(MZONE_NAME(rulepat), "Rules patterns", 0, sizeof(struct rulepat), RULEPAT_NSIZE, RULEPAT_NALLOC)) == NULL) goto parsing_failed; STAILQ_INIT(&rulepats_list); rulepatno = 0; #ifdef WITH_RULES SLIST_INIT(&ac_gather_list); #endif nstatrules = 0; if ( (tint_mzone = mzone_init(MZONE_NAME(tint), "Time intervals", 0, sizeof(struct tint), TINT_NSIZE, TINT_NALLOC)) == NULL) goto parsing_failed; STAILQ_INIT(&tint_sets); if ( (worktime_mzone = mzone_init(MZONE_NAME(worktime), "Worktimes", 0, sizeof(struct worktime), WORKTIME_NSIZE, WORKTIME_NALLOC)) == NULL) goto parsing_failed; init_worktime_default(); SLIST_INIT(&worktimes_list); SLIST_INSERT_HEAD(&worktimes_list, &worktime_default, link); if ( (tevent_mzone = mzone_init(MZONE_NAME(tevent), "Time events", 0, sizeof(struct tevent), TEVENT_NSIZE, TEVENT_NALLOC)) == NULL) goto parsing_failed; SLIST_INIT(&tevents_list); if ( (cmd_mzone = mzone_init(MZONE_NAME(cmd), "Commands", MEMFLAG_OPTIMIZE, sizeof(struct cmd), CMD_NSIZE, CMD_NALLOC)) == NULL) goto parsing_failed; init_cmd_list(&cmds_startup); init_cmd_list(&cmds_shutdown); #ifdef WITH_LIMITS if ( (limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", MEMFLAG_OPTIMIZE, sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC)) == NULL) goto parsing_failed; global_load_limit = -1; #endif #ifdef WITH_SUBLIMITS if ( (sublimit_mzone = mzone_init(MZONE_NAME(sublimit), "Sublimits", MEMFLAG_OPTIMIZE, sizeof(struct sublimit), SUBLIMIT_NSIZE, SUBLIMIT_NALLOC)) == NULL) goto parsing_failed; #endif nstatlimits = ndynlimits = nstatsublimits = ndynsublimits = 0; #ifdef WITH_THRESHOLDS if ( (threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", MEMFLAG_OPTIMIZE, sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC)) == NULL) goto parsing_failed; global_threshold_type = -1; global_load_threshold = -1; global_threshold_time_width = 0; global_threshold_time_slice = NULL; global_threshold_below_lim = global_threshold_equal_lim = global_threshold_above_lim = 0; #endif nstatthresholds = ndynthresholds = 0; #ifdef WITH_AUTORULES if ( (autorules_marray = marray_init(MARRAY_NAME(autorules), "Autorules", 0, (void *)&autorules, sizeof(struct autorule), AUTORULE_NSIZE, AUTORULE_NALLOC)) == NULL) goto parsing_failed; nautorules = 0; rules_ptr_marray = NULL; #endif global_update_tevent = global_append_tevent = NULL; global_worktime = NULL; global_section_set = 0; only_abs_paths = keep_rules_order = posix_re_pattern = -1; for (time_param = common_time_param; time_param->param_name != NULL; ++time_param) *time_param->param_value = 0; for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param) *debug_param->param_value = -1; ctl_enable = -1; ctl_socket_path = NULL; ctl_socket_perm = 0; ctl_timeout = 0; #ifdef CTL_CHECK_CREDS if ( (ctl_acl_elem_mzone = mzone_init(MZONE_NAME(ctl_acl_elem), "ACL elements", 0, sizeof(struct ctl_acl_elem), CTL_ACL_ELEM_NSIZE, CTL_ACL_ELEM_NALLOC)) == NULL) goto parsing_failed; if ( (ctl_acl_class_mzone = mzone_init(MZONE_NAME(ctl_acl_class), "ACL classes", 0, sizeof(struct ctl_acl_class), CTL_ACL_CLASS_NSIZE, CTL_ACL_CLASS_NALLOC)) == NULL) goto parsing_failed; global_ctl_rule_acl = ctl_dump_acl = ctl_freeze_acl = ctl_stat_acl = NULL; STAILQ_INIT(&ctl_acl_classes); #endif section = section_first = section_top = IPA_CONF_SECT_ROOT; parsing_mode = mode; memfunc.m_parser = m_parser; if (real_config) logmsgx(IPA_LOG_INFO, "use configuration file %s, parsing...", ipa_conf_file); if (check_conf_file(ipa_conf_file, 0) < 0) goto parsing_failed; /* Init parser. */ if (parser_init() < 0) { logmsgx(IPA_LOG_ERR, "configure: parser_init failed"); goto parsing_failed; } /* Init first pbuf. */ if ( (pbuf = parser_new_pbuf(0)) == NULL) goto parsing_failed; pbuf->fname = ipa_conf_file; if ( (pbuf->fp = fopen(ipa_conf_file, "r")) == NULL) { conferrx("fopen(%s, \"r\"): %s", ipa_conf_file, strerror(errno)); goto parsing_failed; } if (parser_push_pbuf(pbuf) < 0) goto parsing_failed; /* Needed for log messages. */ curparam = cursect = curmodfile = NULL; if (build_conf_regexp() < 0) { conferrx("cannot build additional regular expressions needed for parsing"); goto parsing_failed; } if (init_conf_tbls((char *)NULL, mode != RECONFIG_PARSING, conf_sect_tbl, &conf_sect_hash, conf_param_tbl, &conf_param_hash) < 0) goto parsing_failed; /* Set ipa.conf(5) configuration tables. */ conf_sect_tbl_ptr = conf_sect_tbl; conf_param_tbl_ptr = conf_param_tbl; conf_sect_hash_ptr = conf_sect_hash; conf_param_hash_ptr = conf_param_hash; in_own_sect = 1; for (;;) { switch (parser_read_string()) { case 1: /* Successfully read one logical line. */ break; case 0: /* EOF of current configuration file. */ if (fclose(parser_curpbuf->fp) != 0) { conferrx("fclose(%s): %s", parser_curpbuf->fname, strerror(errno)); goto parsing_failed; } if (parser_curpbuf->fname != ipa_conf_file) mem_free(parser_curpbuf->fname, m_parser); parser_pop_pbuf(); if (parser_curpbuf != NULL) { /* Init previous file for parsing. */ if ( (parser_curpbuf->fp = fopen(parser_curpbuf->fname, "r")) == NULL) { conferrx("fopen(%s, \"r\"): %s", parser_curpbuf->fname, strerror(errno)); goto parsing_failed; } if (fseek(parser_curpbuf->fp, parser_curpbuf->foff, SEEK_SET) < 0) { conferrx("fseek(%s, %ld, SEEK_SET): %s", parser_curpbuf->fname, parser_curpbuf->foff, strerror(errno)); goto parsing_failed; } continue; } goto end_of_parsing; default: /* -1 */ goto parsing_failed; } switch (parser_token_id) { case TOKEN_ID_SECTION_BEGIN: /* Put previous section on top of sections stack. */ if ( (section_stack_ptr = mem_malloc(sizeof *section_stack_ptr, m_anon)) == NULL) { conferrx("configure: mem_malloc failed"); goto parsing_failed; } section_stack_ptr->section = section; SLIST_INSERT_HEAD(§ion_stack_list, section_stack_ptr, link); /* Get name of current section. */ cursect = parser_token; /* "section" or "prefix:section". */ if (in_own_sect) { /* Previous section is not a custom section. */ if ( (ptr = strchr(cursect, ':')) != NULL) { *ptr = '\0'; prefix = cursect; cursect = ptr + 1; /* Fix section name. */ if ( (db_mod = db_mod_by_prefix(prefix)) != NULL) { conf_sect_hash_ptr = db_mod->conf_sect_hash; conf_param_hash_ptr = db_mod->conf_param_hash; conf_sect_tbl_ptr = db_mod->ipa_db_mod->conf_sect_tbl; conf_param_tbl_ptr = db_mod->ipa_db_mod->conf_param_tbl; curmodfile = db_mod->mod_file; } else if ( (ac_mod = ac_mod_by_prefix(prefix)) != NULL) { conf_sect_hash_ptr = ac_mod->conf_sect_hash; conf_param_hash_ptr = ac_mod->conf_param_hash; conf_sect_tbl_ptr = ac_mod->ipa_ac_mod->conf_sect_tbl; conf_param_tbl_ptr = ac_mod->ipa_ac_mod->conf_param_tbl; curmodfile = ac_mod->mod_file; db_mod = NULL; } else { logconferrx("cannot find module with \"%s\" configuration prefix", prefix); goto parsing_failed; } in_own_sect = 0; } } /* Find ID of section. */ if ( (conf_sect = find_conf_sect(conf_sect_tbl_ptr, conf_sect_hash_ptr, cursect)) == NULL) { logconferrx("unknown section"); goto parsing_failed; } /* Check if current section is used in correct place. */ if ( (id_ptr = conf_sect->sect_where) != NULL) for (;; ++id_ptr) { if (*id_ptr == section) break; if (*id_ptr == 0) goto section_not_expected; } /* Check number of section's arguments. */ if (conf_sect->arg_nargs >= 0) { if (parser_nargs != conf_sect->arg_nargs) { logconferrx("wrong number of arguments (has %d, should have %d)", parser_nargs, conf_sect->arg_nargs); goto parsing_failed; } } else { if (parser_nargs < -conf_sect->arg_nargs) { logconferrx("this section should have at least %d argument%s", -conf_sect->arg_nargs, conf_sect->arg_nargs == -1 ? "" : "s"); goto parsing_failed; } } /* Validate arguments if needed. */ if (conf_sect->arg_regexp != NULL) if (regexec(conf_sect->arg_regexp, parser_args, 0, (regmatch_t *)NULL, 0) != 0) { logconferrx("wrong format of an argument"); goto parsing_failed; } section = conf_sect->sect_id; /* Register config event in module. */ if (!in_own_sect) { if (db_mod != NULL) { if (db_mod_conf_event(db_mod, IPA_CONF_EVENT_CUSTOM_SECT_BEGIN, section, (void *)NULL) < 0) goto parsing_failed; } else { if (ac_mod_conf_event(ac_mod, IPA_CONF_EVENT_CUSTOM_SECT_BEGIN, section, (void *)NULL) < 0) goto parsing_failed; } } /* Parse it. */ if (conf_sect->arg_type <= IPA_CONF_TYPE_MISC) { get_arg_ptr = &get_arg_tbl[conf_sect->arg_type]; if (get_arg_ptr->reg != NULL) if (regexec(get_arg_ptr->reg, parser_args, 0, (regmatch_t *)NULL, 0) != 0) { logconferrx("wrong format of an argument"); goto parsing_failed; } if (get_arg_ptr->get_arg(get_arg_ptr->argp) < 0) goto parsing_failed; if (conf_sect->arg_parse != NULL) if (conf_sect->arg_parse(get_arg_ptr->argp) < 0) goto parsing_failed; } else { logconferrx("internal error: unknown type %u of function's argument", conf_sect->arg_type); goto parsing_failed; } if (in_own_sect && section >= SECT_RESTART && section <= SECT_ABOVE_THRESHOLD) { /* Do additional settings for ipa.conf(5)'s sections. */ #ifdef WITH_LIMITS switch (section) { case SECT_IF_ALL_REACHED: case SECT_IF_ALL_NOT_REACHED: case SECT_IF_ANY_REACHED: case SECT_IF_ANY_NOT_REACHED: switch (section_top) { case IPA_CONF_SECT_RULE: case IPA_CONF_SECT_AUTORULE: case IPA_CONF_SECT_RULEPAT: break; default: goto section_not_expected; } prevcmdl = curcmdl; break; case SECT_IF_REACHED: case SECT_IF_NOT_REACHED: switch (section_top) { case IPA_CONF_SECT_LIMIT: case SECT_SUBLIMIT: break; default: goto section_not_expected; } prevcmdl = curcmdl; break; } #endif /* WITH_LIMITS */ if (set_curcmdl() < 0) goto parsing_failed; } break; case TOKEN_ID_PARAMETER: curparam = parser_token; /* "param" or "prefix:param". */ if (in_own_sect) { /* Current section is not a custom section. */ if ( (ptr = strchr(curparam, ':')) != NULL) { *ptr = '\0'; prefix = curparam; curparam = ptr + 1; /* Fix parameter name. */ if ( (db_mod = db_mod_by_prefix(prefix)) != NULL) { conf_param_hash_ptr = db_mod->conf_param_hash; conf_param_tbl_ptr = db_mod->ipa_db_mod->conf_param_tbl; curmodfile = db_mod->mod_file; } else if ( (ac_mod = ac_mod_by_prefix(prefix)) != NULL) { conf_param_hash_ptr = ac_mod->conf_param_hash; conf_param_tbl_ptr = ac_mod->ipa_ac_mod->conf_param_tbl; curmodfile = ac_mod->mod_file; db_mod = NULL; } else { logconferrx("cannot find module with \"%s\" configuration prefix", prefix); goto parsing_failed; } } } /* Find ID of parameter. */ if ( (conf_param = find_conf_param(conf_param_tbl_ptr, conf_param_hash_ptr, curparam)) == NULL) { logconferrx("unknown parameter"); goto parsing_failed; } /* Check if parameter is used in correct place. */ if ( (id_ptr = conf_param->param_where) != NULL) for (;; ++id_ptr) { if (*id_ptr == section) break; if (*id_ptr == 0) { logconferrx("this parameter is not expected here"); goto parsing_failed; } } /* Check number of parameters's arguments. */ if (conf_param->arg_nargs >= 0) { if (parser_nargs != conf_param->arg_nargs) { logconferrx("wrong number of arguments (has %d, should have %d)", parser_nargs, conf_param->arg_nargs); goto parsing_failed; } } else { if (parser_nargs < -conf_param->arg_nargs) { logconferrx("this parameter should have at least %d argument%s", -conf_param->arg_nargs, conf_param->arg_nargs == -1 ? "": "s"); goto parsing_failed; } } /* Validate arguments if needed. */ if (conf_param->arg_regexp != NULL) if (regexec(conf_param->arg_regexp, parser_args, 0, (regmatch_t *)NULL, 0) != 0) { logconferrx("wrong format of an argument"); goto parsing_failed; } /* Parse it. */ if (conf_param->arg_type <= IPA_CONF_TYPE_MISC) { get_arg_ptr = &get_arg_tbl[conf_param->arg_type]; if (get_arg_ptr->reg != NULL) if (regexec(get_arg_ptr->reg, parser_args, 0, (regmatch_t *)NULL, 0) != 0) { logconferrx("wrong format of an argument"); goto parsing_failed; } if (get_arg_ptr->get_arg(get_arg_ptr->argp) < 0) goto parsing_failed; if (conf_param->arg_parse != NULL) if (conf_param->arg_parse(get_arg_ptr->argp) < 0) goto parsing_failed; curparam = NULL; } else { logconferrx("internal error: unknown type %u of an argument", conf_param->arg_type); goto parsing_failed; } if (curmodfile != NULL && in_own_sect) { curmodfile = NULL; /* Was set above for logxxx() functions. */ /* Restore ipa.conf(5) parameters tables. */ conf_param_hash_ptr = conf_param_hash; conf_param_tbl_ptr = conf_param_tbl; } break; default: /* TOKEN_ID_SECTION_END */ cursect = NULL; if (!in_own_sect) { /* Register config event in module. */ if (db_mod != NULL) { if (db_mod_conf_event(db_mod, IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0) goto parsing_failed; } else { if (ac_mod_conf_event(ac_mod, IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0) goto parsing_failed; } } else { switch (section) { #ifdef WITH_RULES case IPA_CONF_SECT_RULE: section_top = IPA_CONF_SECT_ROOT; if (mod_conf_event(IPA_CONF_EVENT_RULE_END, currule->ruleno, (void *)NULL) < 0) goto parsing_failed; break; #endif case IPA_CONF_SECT_RULEPAT: section_top = IPA_CONF_SECT_ROOT; if (mod_conf_event(IPA_CONF_EVENT_RULEPAT_END, currulepat->rulepatno, (void *)NULL) < 0) goto parsing_failed; break; #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: section_top = IPA_CONF_SECT_ROOT; if (mod_conf_event(IPA_CONF_EVENT_AUTORULE_END, curautorule->aruleno, (void *)NULL) < 0) goto parsing_failed; break; #endif case IPA_CONF_SECT_GLOBAL: section_top = IPA_CONF_SECT_ROOT; #ifdef WITH_THRESHOLDS if (validate_threshold_times(global_threshold_time_width, global_threshold_time_slice) < 0) goto parsing_failed; #endif if (mod_conf_event(IPA_CONF_EVENT_GLOBAL_END, 0, (void *)NULL) < 0) goto parsing_failed; break; #ifdef WITH_LIMITS case IPA_CONF_SECT_LIMIT: section_top = section_first; if (curlimit->lim == UINT64_C(0)) { logconferrx("parameter \"limit\" must be specified"); goto parsing_failed; } (void)parser_local_sym_del("limit"); if (mod_conf_event(IPA_CONF_EVENT_LIMIT_END, curlimit->limitno, (void *)NULL) < 0) goto parsing_failed; break; # ifdef WITH_SUBLIMITS case SECT_SUBLIMIT: section_top = IPA_CONF_SECT_LIMIT; (void)parser_local_sym_del("sublimit"); break; # endif case SECT_RESTART: if (curlimit->restart.restart.upto == TEXP_UPTO_NOTSET) { logconferrx("parameter \"restart\" must be specified"); goto parsing_failed; } break; case SECT_EXPIRE: if (curlimit->expire.expire.upto == TEXP_UPTO_NOTSET) { logconferrx("parameter \"expire\" must be specified"); goto parsing_failed; } break; case SECT_IF_REACHED: case SECT_IF_NOT_REACHED: case SECT_IF_ALL_REACHED: case SECT_IF_ALL_NOT_REACHED: case SECT_IF_ANY_REACHED: case SECT_IF_ANY_NOT_REACHED: curcmdl = prevcmdl; break; #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS case IPA_CONF_SECT_THRESHOLD: section_top = section_first; if (curthreshold->thr == UINT64_C(0)) { logconferrx("parameter \"threshold\" must be specified"); goto parsing_failed; } if (validate_threshold_times(curthreshold->time_width, curthreshold->time_slice) < 0) goto parsing_failed; (void)parser_local_sym_del("threshold"); if (mod_conf_event(IPA_CONF_EVENT_THRESHOLD_END, curthreshold->thresholdno, (void *)NULL) < 0) goto parsing_failed; break; #endif /* WITH_THRESHOLDS */ } } section_stack_ptr = SLIST_FIRST(§ion_stack_list); SLIST_REMOVE_HEAD(§ion_stack_list, link); section = section_stack_ptr->section; mem_free(section_stack_ptr, m_anon); if (!in_own_sect && section < IPA_CONF_SECT_CUSTOM_OFFSET) { /* We were in custom section and previous section is not a custom section. */ curmodfile = NULL; /* Restore ipa.conf(5) configuration tables. */ conf_sect_tbl_ptr = conf_sect_tbl; conf_param_tbl_ptr = conf_param_tbl; conf_sect_hash_ptr = conf_sect_hash; conf_param_hash_ptr = conf_param_hash; in_own_sect = 1; } } } end_of_parsing: deinit_conf_tbls(0, conf_sect_tbl, conf_sect_hash, conf_param_tbl, conf_param_hash); mzone_deinit(conf_sect_hentry_mzone); mzone_deinit(conf_param_hentry_mzone); if (parser_deinit() < 0) goto parsing_failed; if (mzone_is_empty(worktime_mzone)) { mzone_deinit(worktime_mzone); mzone_deinit(tint_mzone); worktime_mzone = tint_mzone = NULL; } if (mzone_is_empty(cmd_mzone)) { mzone_deinit(cmd_mzone); cmd_mzone = NULL; } if (mzone_is_empty(rulepat_mzone)) { mzone_deinit(rulepat_mzone); rulepat_mzone = NULL; } #ifdef WITH_LIMITS if (mzone_is_empty(limit_mzone)) { mzone_deinit(limit_mzone); limit_mzone = NULL; } #endif #ifdef WITH_SUBLIMITS if (mzone_is_empty(sublimit_mzone)) { mzone_deinit(sublimit_mzone); sublimit_mzone = NULL; } #endif #ifdef WITH_THRESHOLDS if (mzone_is_empty(threshold_mzone)) { mzone_deinit(threshold_mzone); threshold_mzone = NULL; } #endif #ifdef CTL_CHECK_CREDS if (mzone_is_empty(ctl_acl_elem_mzone)) { mzone_deinit(ctl_acl_elem_mzone); ctl_acl_elem_mzone = NULL; } if (mzone_is_empty(ctl_acl_class_mzone)) { mzone_deinit(ctl_acl_class_mzone); ctl_acl_class_mzone = NULL; } #endif SLIST_FOREACH(ac_mod, &ac_mod_list, link) { if (mimic_real_config) if (ac_mod->ipa_ac_mod->conf_mimic_real() < 0) { conferrx("module %s: conf_mimic_real failed", ac_mod->mod_file); goto parsing_failed; } deinit_conf_tbls(1, ac_mod->ipa_ac_mod->conf_sect_tbl, ac_mod->conf_sect_hash, ac_mod->ipa_ac_mod->conf_param_tbl, ac_mod->conf_param_hash); if (ac_mod->ipa_ac_mod->conf_deinit() < 0) { conferrx("module %s: conf_deinit failed", ac_mod->mod_file); goto parsing_failed; } } SLIST_FOREACH(db_mod, &db_mod_list, link) { if (mimic_real_config) if (db_mod->ipa_db_mod->conf_mimic_real() < 0) { conferrx("module %s: conf_mimic_real failed", db_mod->mod_file); goto parsing_failed; } deinit_conf_tbls(1, db_mod->ipa_db_mod->conf_sect_tbl, db_mod->conf_sect_hash, db_mod->ipa_db_mod->conf_param_tbl, db_mod->conf_param_hash); if (db_mod->ipa_db_mod->conf_deinit() < 0) { conferrx("module %s: conf_deinit failed", db_mod->mod_file); goto parsing_failed; } } if (real_config) mimic_real_config = 1; if (mimic_real_config || mode == CMD_PARSING) { if (shell_path == NULL) shell_path = shell_path_default; if (shell_arg1 == NULL) shell_arg1 = shell_arg1_default; } if (mimic_real_config) { /* Set global parameters. */ global_section_set = 1; if (only_abs_paths < 0) only_abs_paths = 0; if (keep_rules_order < 0) keep_rules_order = 0; if (wakeup_time == 0) wakeup_time = WAKEUP_TIME_DEF; if (sensitive_time == 0) sensitive_time = SENSITIVE_TIME_DEF; if (global_update_tevent == NULL) if ( (global_update_tevent = find_tevent(UPDATE_TIME_DEF)) == NULL) { conferrx("find_tevent_failed for default value of \"update_time\" parameter"); goto parsing_failed; } if (global_ac_list == NULL) global_ac_list = &ac_list_null; if (global_db_list == NULL) global_db_list = &db_list_null; if (global_worktime == NULL) global_worktime = &worktime_default; #ifdef WITH_LIMITS if (global_load_limit < 0) global_load_limit = 0; #endif #ifdef WITH_THRESHOLDS if (global_threshold_type < 0) global_threshold_type = 0; if (global_load_threshold < 0) global_load_threshold = 0; if (global_threshold_below_lim == 0) global_threshold_below_lim = global_threshold_equal_lim = global_threshold_above_lim = UINT_MAX; #endif for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param) if (*debug_param->param_value < 0) *debug_param->param_value = 0; if (cmds_startup.sync_exec < 0) cmds_startup.sync_exec = 1; if (cmds_shutdown.sync_exec < 0) cmds_shutdown.sync_exec = 1; /* Inherit some settings for rulepat{} from global{}. */ STAILQ_FOREACH(rulepat, &rulepats_list, link) { if (rulepat->check_next_rulepat <= 0) { rulepat->check_next_rulepat = 0; if (rulepat->update_tevent == NULL) rulepat->update_tevent = global_update_tevent; if (rulepat->append_tevent == NULL) rulepat->append_tevent = global_append_tevent; if (rulepat->worktime == NULL) rulepat->worktime = global_worktime; if (rulepat->ac_list == NULL) rulepat->ac_list = global_ac_list; if (rulepat->db_list == NULL) rulepat->db_list = global_db_list; if (rulepat->debug_exec < 0) rulepat->debug_exec = global_debug_exec; #ifdef CTL_CHECK_CREDS if (rulepat->ctl_rule_acl == NULL) rulepat->ctl_rule_acl = global_ctl_rule_acl; #endif #ifdef WITH_LIMITS if (rulepat->debug_limit < 0) rulepat->debug_limit = global_debug_limit; if (rulepat->debug_limit_init < 0) rulepat->debug_limit_init = global_debug_limit_init; STAILQ_FOREACH(limit, &rulepat->limits, link) { inherit_limit_from_global(limit); set_sync_exec_in_limit(limit); } #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS if (rulepat->debug_threshold < 0) rulepat->debug_threshold = global_debug_threshold; if (rulepat->debug_threshold_init < 0) rulepat->debug_threshold_init = global_debug_threshold_init; STAILQ_FOREACH(threshold, &rulepat->thresholds, link) { if (inherit_threshold_from_global("rulepat", rulepat->pat, threshold) < 0) goto parsing_failed; set_sync_exec_in_threshold(threshold); } #endif /* WITH_THRESHOLDS */ } set_sync_exec_in_rulepat(rulepat); } } #ifdef WITH_AUTORULES STAILQ_INIT(&autorules_list); if (nautorules != 0) { const struct ac_list *ac_list; marray_minimize(autorules_marray); for (autorule = autorules; autorule < autorules + nautorules; ++autorule) { STAILQ_INSERT_TAIL(&autorules_list, autorule, link); ac_list = autorule->ac_list != NULL ? autorule->ac_list : global_ac_list; if (ac_list != NULL && !STAILQ_EMPTY(ac_list)) if (STAILQ_NEXT(STAILQ_FIRST(ac_list), link) != NULL) { conferrx("autorule %s: only one accounting system can be used in autorule", autorule->arule_name); goto parsing_failed; } if (mimic_real_config) { /* Inherit some settings for autorule{} from global{}. */ if (autorule->ac_list == NULL) { autorule->ac_list = global_ac_list; ac_inc_ref_count(global_ac_list); } if (autorule->update_tevent == NULL) autorule->update_tevent = global_update_tevent; if (autorule->worktime == NULL) autorule->worktime = global_worktime; set_sync_exec_in_autorule(autorule); } # ifdef WITH_LIMITS STAILQ_FOREACH(limit, &autorule->limits, link) { if (check_worktime_subset(autorule->worktime_rule, limit->worktime) < 0) { conferrx("autorule %s, limit %s: limit's worktime must be a subset of autorule's worktime_rule", autorule->arule_name, limit->limit_name); goto parsing_failed; } if (mimic_real_config) { inherit_limit_from_global(limit); set_sync_exec_in_limit(limit); } } # endif /* WITH_LIMITS */ # ifdef WITH_THRESHOLDS STAILQ_FOREACH(threshold, &autorule->thresholds, link) { if (check_worktime_subset(autorule->worktime_rule, threshold->worktime) < 0) { conferrx("autorule %s, threshold %s: threshold's worktime must be a subset of autorule's worktime_rule", autorule->arule_name, threshold->threshold_name); goto parsing_failed; } if (mimic_real_config) { if (inherit_threshold_from_global("autorule", autorule->arule_name, threshold) < 0) goto parsing_failed; set_sync_exec_in_threshold(threshold); } } # endif /* WITH_THRESHOLDS */ } } else marray_deinit(autorules_marray); #endif /* WITH_AUTORULES */ #ifdef WITH_RULES /* Init ac_gather_rev_mzone, needed only if there are static rules. */ if (!SLIST_EMPTY(&ac_gather_list)) { if ( (ac_gather_rev_mzone = mzone_init(MZONE_NAME(ac_gather_rev), "Reverse ac_gather", 0, sizeof(struct ac_gather_rev), AC_GATHER_REV_NSIZE, AC_GATHER_REV_NALLOC)) == NULL) goto parsing_failed; has_ac_gather = 1; } else { ac_gather_rev_mzone = NULL; has_ac_gather = 0; } TAILQ_FOREACH(rule, &rules_list, list) { if (init_ac_gather_rev(rule) < 0) goto parsing_failed; STAILQ_FOREACH(rulepat, &rulepats_list, link) if (regexec(&rulepat->reg, rule->rule_name, 0, (regmatch_t *)NULL, 0) == 0) { # ifdef WITH_RULES rule_wt = rule->worktime != NULL ? rule->worktime : rulepat->worktime; # endif if (mimic_real_config) { /* Inherit some settings for rule{} from rulepat{}. */ if (rule->update_tevent == NULL) rule->update_tevent = rulepat->update_tevent; if (rule->append_tevent == NULL) rule->append_tevent = rulepat->append_tevent; rule->worktime = rule_wt; if (rule->ac_list == NULL) if ( (rule->ac_list = rulepat->ac_list) != NULL) ac_inc_ref_count(rule->ac_list); if (rule->db_list == NULL) rule->db_list = rulepat->db_list; if (rule->debug_exec < 0) rule->debug_exec = rulepat->debug_exec; # ifdef WITH_LIMITS if (rule->debug_limit < 0) rule->debug_limit = rulepat->debug_limit; if (rule->debug_limit_init < 0) rule->debug_limit_init = rulepat->debug_limit_init; # endif # ifdef WITH_THRESHOLDS if (rule->debug_threshold < 0) rule->debug_threshold = rulepat->debug_threshold; if (rule->debug_threshold_init < 0) rule->debug_threshold_init = rulepat->debug_threshold_init; # endif # ifdef CTL_CHECK_CREDS if (rule->ctl_rule_acl == NULL) rule->ctl_rule_acl = rulepat->ctl_rule_acl; # endif if (mod_conf_inherit(rulepat, rule) < 0) { conferrx("configure: mod_conf_inherit failed"); goto parsing_failed; } } if (mimic_real_config || mode == CMD_PARSING) { if (rule->rc[RC_STARTUP].set == 0 && rulepat->rc[RC_STARTUP].set != 0) { if (copy_cmds_rule(rule, &rule->rc[RC_STARTUP], &rulepat->rc[RC_STARTUP]) < 0) { conferrx("rule %s: cannot copy all commands from rulepat %s { startup {}}", rule->rule_name, parser_buf_to_string(rulepat->pat)); goto parsing_failed; } rule->rc[RC_STARTUP].set = 1; } if (rule->rc[RC_SHUTDOWN].set == 0 && rulepat->rc[RC_SHUTDOWN].set != 0) { if (copy_cmds_rule(rule, &rule->rc[RC_SHUTDOWN], &rulepat->rc[RC_SHUTDOWN]) < 0) { conferrx("rule %s: cannot copy all commands from rulepat %s { shutdown {}}", rule->rule_name, parser_buf_to_string(rulepat->pat)); goto parsing_failed; } rule->rc[RC_SHUTDOWN].set = 1; } #ifdef WITH_LIMITS if (STAILQ_EMPTY(&rule->limits) && !STAILQ_EMPTY(&rulepat->limits)) { if (copy_limits(rule, &rulepat->limits, 0) < 0) { conferrx("rule %s: cannot copy all limits from rulepat %s", rule->rule_name, parser_buf_to_string(rulepat->pat)); goto parsing_failed; } rule->free_mask &= ~RULE_FREE_LIMITS; } #endif #ifdef WITH_THRESHOLDS if (STAILQ_EMPTY(&rule->thresholds) && !STAILQ_EMPTY(&rulepat->thresholds)) { if (copy_thresholds(rule, &rulepat->thresholds, 0) < 0) { conferrx("rule %s: cannot copy all thresholds from rulepat %s", rule->rule_name, parser_buf_to_string(rulepat->pat)); goto parsing_failed; } rule->free_mask &= ~RULE_FREE_THRESHOLDS; } #endif } if (rulepat->check_next_rulepat == 0) break; } if (rulepat == NULL) { # ifdef WITH_RULES rule_wt = rule->worktime != NULL ? rule->worktime : global_worktime; # endif if (mimic_real_config) { /* Inherit some settings for rule{} from global{}. */ if (rule->update_tevent == NULL) rule->update_tevent = global_update_tevent; if (rule->append_tevent == NULL) rule->append_tevent = global_append_tevent; rule->worktime = rule_wt; if (rule->ac_list == NULL) { rule->ac_list = global_ac_list; ac_inc_ref_count(rule->ac_list); } if (rule->db_list == NULL) rule->db_list = global_db_list; if (rule->debug_exec < 0) rule->debug_exec = global_debug_exec; # ifdef WITH_LIMITS if (rule->debug_limit < 0) rule->debug_limit = global_debug_limit; if (rule->debug_limit_init < 0) rule->debug_limit_init = global_debug_limit_init; # endif # ifdef WITH_THRESHOLDS if (rule->debug_threshold < 0) rule->debug_threshold = global_debug_threshold; if (rule->debug_threshold_init < 0) rule->debug_threshold_init = global_debug_threshold_init; # endif # ifdef CTL_CHECK_CREDS if (rule->ctl_rule_acl == NULL) rule->ctl_rule_acl = global_ctl_rule_acl; # endif set_sync_exec_in_rule(rule); } } # ifdef WITH_LIMITS STAILQ_FOREACH(limit, &rule->limits, link) { if (check_worktime_subset(rule_wt, limit->worktime) < 0) { conferrx("rule %s, limit %s: limit's worktime must be a subset of rule's worktime", rule->rule_name, limit->limit_name); goto parsing_failed; } if (mimic_real_config) { if (rule->free_mask & RULE_FREE_LIMITS) { /* Limits were not copied from rulepat. */ inherit_limit_from_global(limit); set_sync_exec_in_limit(limit); } if (limit->worktime == NULL) limit->worktime = rule->worktime; if (limit->db_list == NULL) limit->db_list = rule->db_list; } } # endif /* WITH_LIMITS */ # ifdef WITH_THRESHOLDS STAILQ_FOREACH(threshold, &rule->thresholds, link) { if (check_worktime_subset(rule_wt, threshold->worktime) < 0) { conferrx("rule %s, threshold %s: threshold's worktime must be a subset of rule's worktime", rule->rule_name, threshold->threshold_name); goto parsing_failed; } if (mimic_real_config) { if (rule->free_mask & RULE_FREE_THRESHOLDS) { /* Thresholds were not copied from rulepat. */ if (inherit_threshold_from_global("rule", rule->rule_name, threshold) < 0) goto parsing_failed; set_sync_exec_in_threshold(threshold); } if (threshold->worktime == NULL) threshold->worktime = rule->worktime; if (threshold->db_list == NULL) threshold->db_list = rule->db_list; } } # endif /* WITH_THRESHOLDS */ } if (ac_gather_rev_mzone != NULL) { if (mzone_is_empty(ac_gather_rev_mzone) #ifdef WITH_AUTORULES && nautorules == 0 #endif ) { mzone_deinit(ac_gather_rev_mzone); ac_gather_rev_mzone = NULL; } } #endif /* WITH_RULES */ if (real_config || mimic_real_config) { for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param) if (*debug_param->param_value < 0) *debug_param->param_value = 0; if (ctl_enable < 0) ctl_enable = 0; if (ctl_socket_path == NULL) ctl_socket_path = ctl_socket_path_default; if (ctl_socket_perm == 0) ctl_socket_perm = CTL_SOCKET_PERM_DEF; if (ctl_timeout == 0) ctl_timeout = CTL_TIMEOUT_DEF; } if (real_config) logmsgx(IPA_LOG_INFO, "loaded %u autorule(s), %u static rule(s), %u limit(s), %u sublimit(s), %u threshold(s)", nautorules, nstatrules, nstatlimits, nstatsublimits, nstatthresholds); mvlogmsgx = mvlogmsgx_wrapper; xvlogmsgx = vlogmsgx; return 0; section_not_expected: logconferrx("this section is not expected here"); parsing_failed: conferrx("configuration file(s) parsing failed!"); return -1; } /* * Unload all modules and free memory used by structures which * describe loaded modules. Note that any pointer should not references * any data in unloaded modules' memory. So, the best place when * to call this function: after memfunc_deinit() and before checking * amount of not freed memory. */ static int unload_all_mods(void) { struct ac_mod *ac_mod, *ac_mod_next; struct db_mod *db_mod, *db_mod_next; for (ac_mod = SLIST_FIRST(&ac_mod_list); ac_mod != NULL; ac_mod = ac_mod_next) { ac_mod_next = SLIST_NEXT(ac_mod, link); if (dl_close(ac_mod->mod_handle) < 0) { logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s", ac_mod->mod_file, dl_error()); return -1; } mem_free(ac_mod->mod_file, m_parser); mem_free(ac_mod, m_anon); } for (db_mod = SLIST_FIRST(&db_mod_list); db_mod != NULL; db_mod = db_mod_next) { db_mod_next = SLIST_NEXT(db_mod, link); if (dl_close(db_mod->mod_handle) < 0) { logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s", db_mod->mod_file, dl_error()); return -1; } mem_free(db_mod->mod_file, m_parser); mem_free(db_mod, m_anon); } return 0; } /* * Free all resources allocated during work. */ int free_all(void) { u_int n; if (shell_path != shell_path_default) mem_free(shell_path, m_parser); if (shell_arg1 != shell_arg1_default) mem_free(shell_arg1, m_parser); free_rules(); if (!TAILQ_EMPTY(&rules_list)) { logmsgx(IPA_LOG_ERR, "internal error: free_all: rules_list is not empty"); return -1; } if (!TAILQ_EMPTY_HEAD_VALID(&rules_list)) { logmsgx(IPA_LOG_ERR, "internal error: free_all: empty rules_list list is in inconsistent state"); return -1; } if (!rules_hash_is_empty()) { logmsgx(IPA_LOG_ERR, "internal error: free_all: rules_hash is not empty"); return -1; } if ( (n = mzone_nused(rule_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: rule_mzone is not empty: %u", n); return -1; } mzone_deinit(rule_mzone); free_rulepats(); #ifdef WITH_LIMITS if (limit_mzone != NULL) { if ( (n = mzone_nused(limit_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: limit_mzone is not empty: %u", n); return -1; } mzone_deinit(limit_mzone); } #endif #ifdef WITH_SUBLIMITS if (sublimit_mzone != NULL) { if ( (n = mzone_nused(sublimit_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: sublimit_mzone is not empty: %u", n); return -1; } mzone_deinit(sublimit_mzone); } #endif #ifdef WITH_THRESHOLDS if (threshold_mzone != NULL) { if ( (n = mzone_nused(threshold_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: threshold_mzone is not empty: %u", n); return -1; } mzone_deinit(threshold_mzone); } #endif mzone_deinit(tevent_mzone); free_cmd_list(&cmds_startup); free_cmd_list(&cmds_shutdown); if (cmd_mzone != NULL) { if ( (n = mzone_nused(cmd_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: cmd_mzone is not empty: %u", n); return -1; } mzone_deinit(cmd_mzone); } free_worktimes(); #ifdef WITH_RULES if (ac_gather_rev_mzone != NULL) { if ( (n = mzone_nused(ac_gather_rev_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: free_all: ac_gather_rev_mzone is not empty: %u", n); return -1; } mzone_deinit(ac_gather_rev_mzone); } #endif free_ac_lists(); free_db_lists(); mem_free(tdiff_buf, m_anon); mem_free(parser_str_buf, m_parser); tdiff_buf = parser_str_buf = NULL; #ifdef WITH_ANY_LIMITS mem_free(ipa_tm_buf, m_anon); ipa_tm_buf = NULL; #endif memfunc_deinit_1(0); if (unload_all_mods() < 0) { logmsgx(IPA_LOG_ERR, "free_all: cannot correctly unload all modules"); return -1; } memfunc_deinit_2(0); return 0; } /* * Reconfiguring routine. */ int reconfigure(void) { int error = 0; logmsgx(IPA_LOG_INFO, "deiniting old configuration..."); if (deinit_all() < 0) { logmsgx(IPA_LOG_ERR, "reconfigure: deinit_all failed"); error = 1; } if (!TAILQ_EMPTY(&rules_active)) { logmsgx(IPA_LOG_ERR, "internal error: reconfigure: rules_active is not empty"); return -1; } if (!TAILQ_EMPTY_HEAD_VALID(&rules_active)) { logmsgx(IPA_LOG_ERR, "internal error: reconfigure: empty rules_active list is in inconsistent state"); return -1; } if (!TAILQ_EMPTY(&rules_inactive)) { logmsgx(IPA_LOG_ERR, "internal error: reconfigure: rules_inactive is not empty"); return -1; } if (!TAILQ_EMPTY_HEAD_VALID(&rules_inactive)) { logmsgx(IPA_LOG_ERR, "internal error: reconfigure: empty rules_inactive list is in inconsistent state"); return -1; } #ifdef WITH_ANY_LIMITS if (!wpid_hash_is_empty()) { logmsgx(IPA_LOG_ERR, "internal error: reconfigure: wpid_hash is not empty"); return -1; } #endif if (free_all() < 0) { logmsgx(IPA_LOG_ERR, "reconfigure: free_all failed"); error = 1; } if (error) return -1; logmsgx(IPA_LOG_INFO, "-----------------------------------------------------"); logmsgx(IPA_LOG_INFO, "rereading configuration file..."); if (configure(RECONFIG_PARSING) < 0) { logmsgx(IPA_LOG_ERR, "reconfigure: configure failed"); return -1; } logmsgx(IPA_LOG_INFO, "using new configuration"); return 0; } static void print_time_u_int(u_int a) { const uint64_t t = a; print_time(&t); } static void show_cmd_list(const struct cmd_list *cmdl, int indent) { const struct cmd *cmd; if (cmdl->sync_exec >= 0) { printf("%*s%*s%*ssync_exec = ", conf_indent, "", indent, "", CONF_INDENT, ""); print_boolean(cmdl->sync_exec); print_eol(); } STAILQ_FOREACH(cmd, &cmdl->list, link) { printf("%*s%*s%*sexec ", conf_indent, "", indent, "", CONF_INDENT, ""); if (cmd->user != NULL) printf("%s ", cmd->user); print_string(cmd->str); print_eol(); } } static void show_commands(const struct cmd_list *cmd_list, const char *sect_name) { if (!STAILQ_EMPTY(&cmd_list->list)) { printf("%*s%s {\n", conf_indent, "", sect_name); show_cmd_list(cmd_list, 0); printf("%*s}\n", conf_indent, ""); } } static void show_rule_rc(const struct cmds_rule *rc) { int x; for (x = 0; x < 2; ++rc, ++x) if (rc->set != 0) { printf("%*s%s {\n", CONF_INDENT, "", x == RC_STARTUP ? "startup" : "shutdown"); if (!STAILQ_EMPTY(&rc->cmdl.list)) show_cmd_list(&rc->cmdl, 0); #ifdef WITH_LIMITS if (!STAILQ_EMPTY(&rc->cmdl_if_all_reached.list)) { printf("%*sif_all_reached {\n", 2 * CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_all_reached, CONF_INDENT); printf("%*s}\n", 2 * CONF_INDENT, ""); } if (!STAILQ_EMPTY(&rc->cmdl_if_all_not_reached.list)) { printf("%*sif_all_not_reached {\n", 2 * CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_all_not_reached, CONF_INDENT); printf("%*s}\n", 2 * CONF_INDENT, ""); } if (!STAILQ_EMPTY(&rc->cmdl_if_any_reached.list)) { printf("%*sif_any_reached {\n", 2 * CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_any_reached, CONF_INDENT); printf("%*s}\n", 2 * CONF_INDENT, ""); } if (!STAILQ_EMPTY(&rc->cmdl_if_any_not_reached.list)) { printf("%*sif_any_not_reached {\n", 2 * CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_any_not_reached, CONF_INDENT); printf("%*s}\n", 2 * CONF_INDENT, ""); } #endif /* WITH_LIMITS */ printf("%*s}\n", CONF_INDENT, ""); } } static void show_generic_worktime(const struct worktime *wt, const char *param_name) { u_int wday; const char *wday_char; const struct tint *tint; const struct tint_list *list; if (wt == NULL) return; wday_char = "SMTWHFA"; printf("%*s%s =", conf_indent, "", param_name); for (wday = 0; wday < DAYS_IN_WEEK; ++wday_char, ++wday) { list = wt->tint_list[wday]; if (!STAILQ_EMPTY(list)) { printf(" %c", *wday_char); tint = STAILQ_FIRST(list); if (tint->sec1 == 0 && tint->sec2 == SECONDS_IN_DAY) printf(" *"); else for (; tint != NULL; tint = STAILQ_NEXT(tint, link)) { printf(" "); printf(tint_to_buf(tint)); } } } print_eol(); } /* * Output "worktime" parameter. */ static void show_worktime(const struct worktime *wt) { show_generic_worktime(wt, "worktime"); } static void mod_conf_show(u_int sect_id, u_int no) { const struct ac_mod *ac_mod; const struct db_mod *db_mod; need_nl = 0; SLIST_FOREACH(ac_mod, &ac_mod_list, link) ac_mod->ipa_ac_mod->conf_show(sect_id, no); if (sect_id == IPA_CONF_SECT_ROOT) print_nl(); SLIST_FOREACH(db_mod, &db_mod_list, link) db_mod->ipa_db_mod->conf_show(sect_id, no); if (sect_id == IPA_CONF_SECT_ROOT) print_nl(); } static void show_ac_list(const struct ac_list *list) { const struct ac_elem *ac; if (list != NULL) { printf("%*sac_list =", CONF_INDENT, ""); if (list != &ac_list_null) STAILQ_FOREACH(ac, list, link) printf(" %s", ac->ipa_ac_mod->ac_name); else printf(" null"); print_eol(); } } static void show_db_list(const struct db_list *list) { const struct db_elem *db; if (list != NULL) { printf("%*sdb_list =", conf_indent, ""); if (list != &db_list_null) STAILQ_FOREACH(db, list, link) printf(" %s", db->ipa_db_mod->db_name); else printf(" null"); print_eol(); } } #if defined(WITH_RULES) || defined(WITH_ANY_LIMITS) static void print_info(const char *info) { if (info != NULL) { printf("%*sinfo = ", conf_indent, ""); print_string(info); print_eol(); } } #endif /* WITH_RULES || WITH_ANY_LIMITS */ #ifdef WITH_RULES static void show_ac_gather_xxx(const char *pat, const char *param_name) { if (pat != NULL) { printf("%*s%s = ", CONF_INDENT, "", param_name); print_string(pat); print_eol(); } } #endif /* WITH_RULES */ #ifdef WITH_LIMITS /* * Convert expired time to human readable string. */ static void print_texp(const struct texp *texp) { u_int t, a = texp->seconds; if (texp->upto != TEXP_UPTO_SIMPLE && texp->side == 0) printf(" +%c", texp->upto); t = a / SECONDS_IN_DAY; if (t != 0) { printf(" %dD", t); a -= t * SECONDS_IN_DAY; } if (a != 0 || (a == 0 && texp->upto == TEXP_UPTO_SIMPLE)) { printf(" "); print_time_u_int(a); } if (texp->upto != TEXP_UPTO_SIMPLE && texp->side == 1) printf(" +%c", texp->upto); } static void show_limit_rc(const struct cmds_limit *rc) { int x; for (x = 0; x < 2; ++rc, ++x) if (rc->set != 0) { printf("%*s%s {\n", conf_indent, "", x == RC_STARTUP ? "startup" : "shutdown" ); if (!STAILQ_EMPTY(&rc->cmdl.list)) show_cmd_list(&rc->cmdl, 0); if (!STAILQ_EMPTY(&rc->cmdl_if_reached.list)) { printf("%*s%*sif_reached {\n", conf_indent, "", CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_reached, CONF_INDENT); printf("%*s%*s}\n", conf_indent, "", CONF_INDENT, ""); } if (!STAILQ_EMPTY(&rc->cmdl_if_not_reached.list)) { printf("%*s%*sif_not_reached {\n", conf_indent, "", CONF_INDENT, ""); show_cmd_list(&rc->cmdl_if_not_reached, CONF_INDENT); printf("%*s%*s}\n", conf_indent, "", CONF_INDENT, ""); } printf("%*s}\n", conf_indent, ""); } } static void show_reach_sect(const struct cmd_list *cmdl) { if (!STAILQ_EMPTY(&cmdl->list)) { printf("%*sreach {\n", conf_indent, ""); show_cmd_list(cmdl, 0); printf("%*s}\n", conf_indent, ""); } } static void show_load_limit(int val) { if (val >= 0) { printf("%*sload_limit = ", conf_indent, ""); print_boolean(val); print_eol(); } } static void show_limits(const struct limits_list *list) { const struct limit *limit; #ifdef WITH_SUBLIMITS const struct sublimit *sublimit; #endif STAILQ_FOREACH(limit, list, link) { conf_indent = 2 * CONF_INDENT; printf("%*slimit %s {\n", CONF_INDENT, "", limit->limit_name); show_db_list(limit->db_list); print_info(limit->limit_info); printf("%*slimit = ", 2 * CONF_INDENT, ""); print_value(&limit->lim, limit->cnt_type); print_eol(); show_load_limit(limit->load_limit); show_worktime(limit->worktime); if (limit->restart.restart.upto != TEXP_UPTO_NOTSET) { printf("%*srestart {\n%*srestart =", 2 * CONF_INDENT, "", 3 * CONF_INDENT, ""); print_texp(&limit->restart.restart); print_eol(); show_cmd_list(&limit->restart.cmdl, 0); printf("%*s}\n", 2 * CONF_INDENT, ""); } show_reach_sect(&limit->reach); if (limit->expire.expire.upto != TEXP_UPTO_NOTSET) { printf("%*sexpire {\n%*sexpire =", 2 * CONF_INDENT, "", 3 * CONF_INDENT, ""); print_texp(&limit->expire.expire); print_eol(); show_cmd_list(&limit->expire.cmdl, 0); printf("%*s}\n", 2 * CONF_INDENT, ""); } mod_conf_show(IPA_CONF_SECT_LIMIT, limit->limitno); show_limit_rc(limit->rc); # ifdef WITH_SUBLIMITS conf_indent = 3 * CONF_INDENT; STAILQ_FOREACH(sublimit, &limit->sublimits, link) { printf("%*ssublimit ", 2 * CONF_INDENT, ""); if (sublimit->lim_per_cent != 0) printf("%u%%", sublimit->lim_per_cent); else print_value(&sublimit->lim, sublimit->cnt_type); printf(" {\n"); show_reach_sect(&sublimit->reach); show_limit_rc(sublimit->rc); printf("%*s}\n", 2 * CONF_INDENT, ""); } # endif /* WITH_SUBLIMITS */ printf("%*s}\n", CONF_INDENT, ""); } } #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS static void show_load_threshold(int val) { if (val >= 0) { printf("%*sload_threshold = ", conf_indent, ""); print_boolean(val); print_eol(); } } static void show_threshold_type(int val) { if (val >= 0) { printf("%*sthreshold_type = %d", conf_indent, "", val); print_eol(); } } static void show_threshold_time_width(u_int val) { if (val != 0) { printf("%*sthreshold_time_width = ", conf_indent, ""); print_time_u_int(val); print_eol(); } } static void show_threshold_time_slice(const struct tevent *tevent) { if (tevent != NULL) { printf("%*sthreshold_time_slice = ", conf_indent, ""); print_time_u_int(tevent->event_step); print_eol(); } } static void show_threshold_balance(u_int a, u_int b, u_int c) { if (a != 0) { printf("%*sthreshold_balance = ", conf_indent, ""); if (a != UINT_MAX) printf("%u:", a); else printf("-:"); if (b != UINT_MAX) printf("%u:", b); else printf("-:"); if (c != UINT_MAX) printf("%u", c); else printf("-"); print_eol(); } } static void show_thresholds(const struct thresholds_list *list) { const struct threshold *threshold; conf_indent = 2 * CONF_INDENT; STAILQ_FOREACH(threshold, list, link) { printf("%*sthreshold %s {\n", CONF_INDENT, "", threshold->threshold_name); show_db_list(threshold->db_list); print_info(threshold->threshold_info); printf("%*sthreshold = ", 2 * CONF_INDENT, ""); print_value(&threshold->thr, threshold->cnt_type); print_eol(); if (threshold->thr_dev != 0) { printf("%*sthreshold_deviation = ", 2 * CONF_INDENT, ""); if (threshold->thr_dev_per_cent != 0) printf("%u%%", threshold->thr_dev_per_cent); else print_value(&threshold->thr_dev, threshold->cnt_type); print_eol(); } show_threshold_type(threshold->threshold_type); show_threshold_balance(threshold->below_lim, threshold->equal_lim, threshold->above_lim); show_load_threshold(threshold->load_threshold); show_worktime(threshold->worktime); show_threshold_time_width(threshold->time_width); show_threshold_time_slice(threshold->time_slice); mod_conf_show(IPA_CONF_SECT_THRESHOLD, threshold->thresholdno); show_commands(&threshold->rc[RC_STARTUP], "startup"); show_commands(&threshold->rc[RC_SHUTDOWN], "shutdown"); show_commands(&threshold->below_threshold, "below_threshold"); show_commands(&threshold->equal_threshold, "equal_threshold"); show_commands(&threshold->above_threshold, "above_threshold"); printf("%*s}\n", CONF_INDENT, ""); } } #endif /* WITH_THRESHOLDS */ static void show_ctl_socket_perm(void) { if (ctl_socket_perm != 0) { printf("ctl_socket_perm = "); if (ctl_socket_perm & S_IWUSR) printf("u"); if (ctl_socket_perm & S_IWGRP) printf("g"); #ifdef CTL_CHECK_CREDS if (ctl_socket_perm & S_IWOTH) printf("o"); #endif print_eol(); } } #ifdef CTL_CHECK_CREDS static void show_ctl_xxx_acl(const struct ctl_acl_class *class, const char *param_name) { if (class != NULL) printf("%*s%s = %s;\n", conf_indent, "", param_name, class->class_name); } static void show_ctl_rule_acl(const struct ctl_acl_class *class) { show_ctl_xxx_acl(class, "ctl_rule_acl"); } static void show_ctl_acl_classes(void) { const struct ctl_acl_elem *elem; const struct ctl_acl_class *class; STAILQ_FOREACH(class, &ctl_acl_classes, link) { printf("ctl_acl_class = %s", class->class_name); STAILQ_FOREACH(elem, &class->list, link) { if (elem->allowed) printf(" "); else printf(" !"); if (elem->user != NULL) printf("%s", elem->user); else printf("%%%s", elem->group); } print_eol(); need_nl = 1; } } #endif /* CTL_CHECK_CREDS */ static void show_update_time(const struct tevent *tevent) { if (tevent != NULL) { printf("%*supdate_time = ", conf_indent, ""); print_time_u_int(tevent->event_step); print_eol(); } } static void show_append_time(const struct tevent *tevent) { if (tevent != NULL) { printf("%*sappend_time = ", conf_indent, ""); print_time_u_int(tevent->event_step); print_eol(); } } /* * Main function for outputting indented configuration file. */ void show_config(void) { #ifdef WITH_RULES const struct rule *rule; #endif #ifdef WITH_AUTORULES const struct autorule *autorule; #endif const struct rulepat *rulepat; const struct ac_mod *ac_mod; const struct db_mod *db_mod; const struct time_param *time_param; const struct debug_param *debug_param; printf("\ /*\n\ * This output is not identical to the original content of the configuration\n\ * file(s), this is just how ipa(8) and IPA modules see their configurations.\n\ * Any \"include\" or \"include_files\" parameters are not printed and all\n\ * macro variables are expanded.\n\ */\n\n"); if (mimic_real_config) printf("/* Mimic real configuration regime. */\n\n"); conf_indent = mod_indent = 0; SLIST_FOREACH(ac_mod, &ac_mod_list, link) { printf("ac_mod "); print_string(ac_mod->mod_file); print_eol(); need_nl = 1; } SLIST_FOREACH(db_mod, &db_mod_list, link) { printf("db_mod "); print_string(db_mod->mod_file); print_eol(); need_nl = 1; } print_nl(); for (debug_param = common_debug_param; debug_param->param_name != NULL; ++debug_param) if (*debug_param->param_value >= 0) { printf("%s = %d;\n", debug_param->param_name, *debug_param->param_value); need_nl = 1; } print_nl(); if (shell_path != NULL) { printf("shell_path = "); print_string(shell_path); print_eol(); need_nl = 1; } if (shell_arg1 != NULL) { printf("shell_arg1 = "); print_string(shell_arg1); print_eol(); need_nl = 1; } print_nl(); if (posix_re_pattern >= 0) { printf("posix_re_pattern = "); print_boolean(posix_re_pattern); print_eol(); need_nl = 1; } print_nl(); for (time_param = common_time_param; time_param->param_name != NULL; ++time_param) if (*time_param->param_value > 0) { printf("%s = ", time_param->param_name); print_time_u_int(*time_param->param_value); print_eol(); need_nl = 1; } print_nl(); if (only_abs_paths >= 0) { printf("only_abs_paths = "); print_boolean(only_abs_paths); print_eol(); need_nl = 1; } print_nl(); if (keep_rules_order >= 0) { printf("keep_rules_order = "); print_boolean(keep_rules_order); print_eol(); need_nl = 1; } print_nl(); if (ctl_enable >= 0) { printf("ctl_enable = "); print_boolean(ctl_enable); print_eol(); need_nl = 1; } if (ctl_socket_path != NULL) { printf("ctl_socket_path = "); print_string(ctl_socket_path); print_eol(); need_nl = 1; } if (ctl_timeout != 0) { printf("ctl_timeout = "); print_time_u_int(ctl_timeout); print_eol(); need_nl = 1; } show_ctl_socket_perm(); #ifdef CTL_CHECK_CREDS show_ctl_acl_classes(); show_ctl_xxx_acl(ctl_dump_acl, "ctl_dump_acl"); show_ctl_xxx_acl(ctl_freeze_acl, "ctl_freeze_acl"); show_ctl_xxx_acl(ctl_stat_acl, "ctl_stat_acl"); #endif print_nl(); mod_conf_show(IPA_CONF_SECT_ROOT, 0); if (!STAILQ_EMPTY(&cmds_startup.list)) { show_commands(&cmds_startup, "startup"); printf("\n"); } if (!STAILQ_EMPTY(&cmds_shutdown.list)) { show_commands(&cmds_shutdown, "shutdown"); printf("\n"); } if (global_section_set) { conf_indent = CONF_INDENT; printf("global {\n"); show_ac_list(global_ac_list); show_db_list(global_db_list); show_append_time(global_append_tevent); show_update_time(global_update_tevent); show_worktime(global_worktime); #ifdef WITH_LIMITS show_load_limit(global_load_limit); #endif #ifdef WITH_THRESHOLDS show_threshold_type(global_threshold_type); show_threshold_balance(global_threshold_below_lim, global_threshold_equal_lim, global_threshold_above_lim); show_load_threshold(global_load_threshold); show_threshold_time_width(global_threshold_time_width); show_threshold_time_slice(global_threshold_time_slice); #endif #ifdef CTL_CHECK_CREDS show_ctl_rule_acl(global_ctl_rule_acl); #endif for (debug_param = global_debug_param; debug_param->param_name != NULL; ++debug_param) if (*debug_param->param_value >= 0) { printf("%*s%s = %d;\n", CONF_INDENT, "", debug_param->param_name, *debug_param->param_value); need_nl = 1; } mod_conf_show(IPA_CONF_SECT_GLOBAL, 0); printf("}\n\n"); } #ifdef WITH_AUTORULES STAILQ_FOREACH(autorule, &autorules_list, link) { conf_indent = CONF_INDENT; printf("autorule %s {\n", autorule->arule_name); show_ac_list(autorule->ac_list); show_db_list(autorule->db_list); show_append_time(autorule->append_tevent); show_update_time(autorule->update_tevent); show_worktime(autorule->worktime); show_generic_worktime(autorule->worktime_rule, "worktime_rule"); # ifdef CTL_CHECK_CREDS show_ctl_rule_acl(autorule->ctl_rule_acl); # endif mod_conf_show(IPA_CONF_SECT_AUTORULE, autorule->aruleno); show_rule_rc(autorule->rc); # ifdef WITH_LIMITS if (autorule->debug_limit >= 0) printf("%*sdebug_limit = %d;\n", CONF_INDENT, "", autorule->debug_limit); if (autorule->debug_limit_init >= 0) printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "", autorule->debug_limit_init); show_limits(&autorule->limits); # endif # ifdef WITH_THRESHOLDS if (autorule->debug_threshold >= 0) printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "", autorule->debug_threshold); if (autorule->debug_threshold_init >= 0) printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "", autorule->debug_threshold_init); show_thresholds(&autorule->thresholds); # endif printf("}\n\n"); } #endif /* WITH_AUTORULES */ STAILQ_FOREACH(rulepat, &rulepats_list, link) { conf_indent = CONF_INDENT; printf("rulepat "); print_string(rulepat->pat); printf(" {\n"); if (rulepat->check_next_rulepat >= 0) { printf("%*scheck_next_rulepat = ", CONF_INDENT, ""); print_boolean(rulepat->check_next_rulepat); print_eol(); } show_ac_list(rulepat->ac_list); show_db_list(rulepat->db_list); show_append_time(rulepat->append_tevent); show_update_time(rulepat->update_tevent); show_worktime(rulepat->worktime); #ifdef CTL_CHECK_CREDS show_ctl_rule_acl(rulepat->ctl_rule_acl); #endif mod_conf_show(IPA_CONF_SECT_RULEPAT, rulepat->rulepatno); if (rulepat->debug_exec >= 0) printf("%*sdebug_exec = %d;\n", CONF_INDENT, "", rulepat->debug_exec); show_rule_rc(rulepat->rc); # ifdef WITH_LIMITS if (rulepat->debug_limit >= 0) printf("%*sdebug_limit = %d;\n", CONF_INDENT, "", rulepat->debug_limit); if (rulepat->debug_limit_init >= 0) printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "", rulepat->debug_limit_init); show_limits(&rulepat->limits); # endif # ifdef WITH_THRESHOLDS if (rulepat->debug_threshold >= 0) printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "", rulepat->debug_threshold); if (rulepat->debug_threshold_init >= 0) printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "", rulepat->debug_threshold_init); show_thresholds(&rulepat->thresholds); # endif printf("}\n\n"); } #ifdef WITH_RULES TAILQ_FOREACH(rule, &rules_list, list) { conf_indent = CONF_INDENT; printf("rule %s {\n", rule->rule_name); show_ac_list(rule->ac_list); show_ac_gather_xxx(rule->ac_gather_add_pat, "ac_gather_add"); show_ac_gather_xxx(rule->ac_gather_sub_pat, "ac_gather_sub"); show_db_list(rule->db_list); print_info(rule->rule_info); show_append_time(rule->append_tevent); show_update_time(rule->update_tevent); show_worktime(rule->worktime); # ifdef CTL_CHECK_CREDS show_ctl_rule_acl(rule->ctl_rule_acl); # endif mod_conf_show(IPA_CONF_SECT_RULE, rule->ruleno); if (rule->debug_exec >= 0) printf("%*sdebug_exec = %d;\n", CONF_INDENT, "", rule->debug_exec); show_rule_rc(rule->rc); # ifdef WITH_LIMITS if (rule->debug_limit >= 0) printf("%*sdebug_limit = %d;\n", CONF_INDENT, "", rule->debug_limit); if (rule->debug_limit_init >= 0) printf("%*sdebug_limit_init = %d;\n", CONF_INDENT, "", rule->debug_limit_init); show_limits(&rule->limits); # endif # ifdef WITH_THRESHOLDS if (rule->debug_threshold >= 0) printf("%*sdebug_threshold = %d;\n", CONF_INDENT, "", rule->debug_threshold); if (rule->debug_threshold_init >= 0) printf("%*sdebug_threshold_init = %d;\n", CONF_INDENT, "", rule->debug_threshold_init); show_thresholds(&rule->thresholds); # endif printf("}\n\n"); } #endif /* WITH_RULES */ }