/*- * Copyright (c) 2000-2003 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_db.c,v 1.1.4.2 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "parser.h" #include "ipa_ac.h" #include "ipa_db.h" #include "ipa_cmd.h" #include "ipa_ctl.h" #include "ipa_time.h" #include "ipa_conf.h" #include "ipa_log.h" #include "ipa_main.h" #include "ipa_rules.h" #include "ipa_autorules.h" u_int ndb_mods; /* Number of db_mods. */ int debug_db_null; /* debug_db_null parameter. */ const struct db_list *global_db_list; /* global { db_list } */ /* * db_sets is the list of all used "db_list" parameters. * If some rules use the same "db_list" parameter, then * they share the same struct db_list{}. */ struct db_sets db_sets = SLIST_HEAD_INITIALIZER(db_sets); /* * List of all database modules. */ struct db_mod_list db_mod_list; /* * Find a database module by configuration prefix. */ struct db_mod * db_mod_by_prefix(const char *prefix) { struct db_mod *db_mod; SLIST_FOREACH(db_mod, &db_mod_list, link) if (strcmp(db_mod->ipa_db_mod->conf_prefix, prefix) == 0) break; return db_mod; } /* * Find a database module by a name. */ struct db_mod * db_mod_by_name(const char *name) { struct db_mod *db_mod; SLIST_FOREACH(db_mod, &db_mod_list, link) if (strcmp(db_mod->ipa_db_mod->db_name, name) == 0) return db_mod; return NULL; } /* * Release memory held by db_set, including struct db_set{}. */ void free_db_set(struct db_set *set) { struct db_elem *db, *db_next; for (db = STAILQ_FIRST(&set->list); db != NULL; db = db_next) { db_next = STAILQ_NEXT(db, link); mem_free(db, m_anon); } mem_free(set, m_anon); } /* * Release memory held by all db_set. */ void free_db_lists(void) { struct db_set *set; while ( (set = SLIST_FIRST(&db_sets)) != NULL) { SLIST_REMOVE_HEAD(&db_sets, link); free_db_set(set); } } #ifdef WITH_LIMITS # ifdef WITH_RULES /* * Init limits for one static rule in databases. */ static int db_init_statlimits(const struct rule *rule) { struct limit *limit; const struct db_elem *db; STAILQ_FOREACH(limit, &rule->limits, link) { if (!STAILQ_EMPTY(limit->db_list)) { STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_init_statlimit != NULL) { if (db->ipa_db_mod->db_init_statlimit( rule->ruleno, rule->rule_name, rule->rule_info, limit->limitno, limit->limit_name, limit->limit_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s) failed", db->mod_file, rule->rule_name, limit->limit_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s): module does not support limits from static rules", db->mod_file, rule->rule_name, limit->limit_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database", rule->rule_name, limit->limit_name); } mem_free(limit->limit_info, m_parser); limit->limit_info = NULL; } return 0; } # endif /* WITH_RULES */ # ifdef WITH_AUTORULES /* * Init limits for one dynamic rule in databases. */ static int db_init_dynlimits(const struct autorule *autorule, const struct rule *rule) { struct limit *limit; const struct db_elem *db; STAILQ_FOREACH(limit, &rule->limits, link) { if (!STAILQ_EMPTY(limit->db_list)) { STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_init_dynlimit != NULL) { if (db->ipa_db_mod->db_init_dynlimit( autorule->aruleno, rule->ruleno, rule->rule_name, rule->rule_info, limit->limitno, limit->limit_name, limit->limit_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s) failed", db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s): module does not support limits from dynamic rules", db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database", rule->rule_name, limit->limit_name); } } return 0; } # endif /* WITH_AUTORULES */ /* * Deinit limit in one rule in databases. */ int db_deinit_limit(const struct rule *rule, const struct limit *limit) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_deinit_limit != NULL) if (db->ipa_db_mod->db_deinit_limit(rule->ruleno, limit->limitno) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_deinit_limit(%s, %s) failed", db->mod_file, rule->rule_name, limit->limit_name); error = -1; } return error; } /* * Update statistics for one limit. */ int db_update_limit(const struct rule *rule, const struct limit *limit) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_update_limit(rule->ruleno, limit->limitno, &limit->cnt, &curdate) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_update_limit(%s, %s) failed", db->mod_file, rule->rule_name, limit->limit_name); error = -1; } return error; } /* * Register limit event for one limit. */ int db_limit_event(const struct rule *rule, struct limit *limit, u_int event, const ipa_tm *tm) { int error = 0; const struct db_elem *db; if (ctl_enable) { limit->event_date_set |= 1 << event; limit->event_date[event] = *tm; } STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_limit_event(rule->ruleno, limit->limitno, event, tm, &curdate) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_limit_event(%s, %s, %s) failed", db->mod_file, rule->rule_name, limit->limit_name, limit_event_msg[event]); error = -1; } return error; } /* * Get a state of the limit: use first database which * supports db_get_limit_state method and save its name in * db_name. If any of databases used by the limit doesn't * support db_get_limit_state method, then set db_name to NULL. */ int db_get_limit_state(const struct rule *rule, const struct limit *limit, struct ipa_limit_state *state, const char **db_name) { const struct db_elem *db; STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_get_limit_state != NULL) { if (db->ipa_db_mod->db_get_limit_state(rule->ruleno, limit->limitno, state) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_get_limit_state(%s, %s) failed", db->mod_file, rule->rule_name, limit->limit_name); return -1; } *db_name = db->ipa_db_mod->db_name; return 0; } *db_name = NULL; return 0; } /* * Set a state of the limit in databases. */ int db_set_limit_state(const struct rule *rule, struct limit *limit, struct ipa_limit_state *state, int new_state) { int error = 0; const struct db_elem *db; state->event_date_set |= IPA_LIMIT_EVENT_UPDATED_SET; state->event_date[IPA_LIMIT_EVENT_UPDATED] = curdate; if (ctl_enable) { limit->event_date_set = state->event_date_set; memcpy(&limit->event_date, &state->event_date, sizeof limit->event_date); } STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_set_limit_state(rule->ruleno, limit->limitno, state, new_state) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_state(%s, %s, %d) failed", db->mod_file, rule->rule_name, limit->limit_name, new_state); error = -1; } return error; } /* * Set a limit active/inactive in databases it uses. */ int db_set_limit_active(const struct rule *rule, const struct limit *limit, int active) { const struct db_elem *db; /* Limit uses own db_list. */ STAILQ_FOREACH(db, limit->db_list, link) if (db->ipa_db_mod->db_set_limit_active != NULL) if (db->ipa_db_mod->db_set_limit_active(rule->ruleno, limit->limitno, active) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_active(%s, %s, %d) failed", db->mod_file, rule->rule_name, limit->limit_name, active); return -1; } return 0; } #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS # ifdef WITH_RULES /* * Init thresholds for one static rule in databases. */ static int db_init_statthresholds(const struct rule *rule) { struct threshold *threshold; const struct db_elem *db; STAILQ_FOREACH(threshold, &rule->thresholds, link) { if (!STAILQ_EMPTY(threshold->db_list)) { STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_init_statthreshold != NULL) { if (db->ipa_db_mod->db_init_statthreshold( rule->ruleno, rule->rule_name, rule->rule_info, threshold->thresholdno, threshold->threshold_name, threshold->threshold_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s) failed", db->mod_file, rule->rule_name, threshold->threshold_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s): module does not support thresholds from static rules", db->mod_file, rule->rule_name, threshold->threshold_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database", rule->rule_name, threshold->threshold_name); } mem_free(threshold->threshold_info, m_parser); threshold->threshold_info = NULL; } return 0; } # endif /* WITH_RULES */ # ifdef WITH_AUTORULES /* * Init thresholds for one dynamic rule in databases. */ static int db_init_dynthresholds(const struct autorule *autorule, const struct rule *rule) { struct threshold *threshold; const struct db_elem *db; STAILQ_FOREACH(threshold, &rule->thresholds, link) { if (!STAILQ_EMPTY(threshold->db_list)) { STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_init_dynthreshold != NULL) { if (db->ipa_db_mod->db_init_dynthreshold( autorule->aruleno, rule->ruleno, rule->rule_name, rule->rule_info, threshold->thresholdno, threshold->threshold_name, threshold->threshold_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s) failed", db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s): module does not support thresholds from dynamic rules", db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database", rule->rule_name, threshold->threshold_name); } } return 0; } # endif /* WITH_AUTORULES */ /* * Deinit threshold in one rule in databases. */ int db_deinit_threshold(const struct rule *rule, const struct threshold *threshold) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_deinit_threshold != NULL) if (db->ipa_db_mod->db_deinit_threshold(rule->ruleno, threshold->thresholdno) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_deinit_threshold(%s, %s) failed", db->mod_file, rule->rule_name, threshold->threshold_name); error = -1; } return error; } /* * Update statistics for one threshold in databases. */ int db_update_threshold(const struct rule *rule, const struct threshold *threshold) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_update_threshold(rule->ruleno, threshold->thresholdno, &threshold->cnt, &threshold->tm_from, &threshold->tm_updated) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_update_threshold(%s, %s) failed", db->mod_file, rule->rule_name, threshold->threshold_name); error = -1; } return error; } /* * Get a state of the threshold: use first database which * supports db_get_threshold_state method and save its name in * db_name. If any of databases used by the threshold doesn't * support db_get_threshold_state method, then set db_name to NULL. */ int db_get_threshold_state(const struct rule *rule, const struct threshold *threshold, struct ipa_threshold_state *state, const char **db_name) { const struct db_elem *db; STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_get_threshold_state != NULL) { if (db->ipa_db_mod->db_get_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_get_threshold_state(%s, %s) failed", db->mod_file, rule->rule_name, threshold->threshold_name); return -1; } *db_name = db->ipa_db_mod->db_name; return 0; } *db_name = NULL; return 0; } /* * Set a state of the threshold in databases. */ int db_set_threshold_state(const struct rule *rule, const struct threshold *threshold, const struct ipa_threshold_state *state) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_set_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_state(%s, %s) failed", db->mod_file, rule->rule_name, threshold->threshold_name); error = -1; } return error; } /* * Set a threshold active/inactive in databases it uses. */ int db_set_threshold_active(const struct rule *rule, const struct threshold *threshold, int active) { const struct db_elem *db; /* Threshold uses own db_list. */ STAILQ_FOREACH(db, threshold->db_list, link) if (db->ipa_db_mod->db_set_threshold_active != NULL) if (db->ipa_db_mod->db_set_threshold_active(rule->ruleno, threshold->thresholdno, active) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_active(%s, %s, %d) failed", db->mod_file, rule->rule_name, threshold->threshold_name, active); return -1; } return 0; } #endif /* WITH_THRESHOLDS */ #ifdef WITH_RULES /* * Init one static rule in databases, also init its limits and thresholds. */ int db_init_statrule(struct rule *rule) { const struct db_elem *db; if (!STAILQ_EMPTY(rule->db_list)) { STAILQ_FOREACH(db, rule->db_list, link) if (db->ipa_db_mod->db_init_statrule != NULL) { if (db->ipa_db_mod->db_init_statrule(rule->ruleno, rule->rule_name, rule->rule_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s) failed", db->mod_file, rule->rule_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s): module does not support static rules", db->mod_file, rule->rule_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database", rule->rule_name); } mem_free(rule->rule_info, m_parser); rule->rule_info = NULL; #ifdef WITH_LIMITS if (db_init_statlimits(rule) < 0) return -1; #endif #ifdef WITH_THRESHOLDS if (db_init_statthresholds(rule) < 0) return -1; #endif return 0; } #endif /* WITH_RULES */ /* * Pre-init all db_mods. */ int pre_init_db_mods(void) { const struct db_mod *db_mod; SLIST_FOREACH(db_mod, &db_mod_list, link) if (db_mod->ipa_db_mod->db_pre_init() < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_pre_init failed", db_mod->mod_file); return -1; } return 0; } /* * Init all db_mods. */ int init_db_mods(void) { const struct db_mod *db_mod; SLIST_FOREACH(db_mod, &db_mod_list, link) if (db_mod->ipa_db_mod->db_init() < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init failed", db_mod->mod_file); return -1; } return 0; } /* * Deinit all db_mods. */ int deinit_db_mods(void) { int error = 0; const struct db_mod *db_mod; SLIST_FOREACH(db_mod, &db_mod_list, link) if (db_mod->ipa_db_mod->db_deinit() < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_deinit failed", db_mod->mod_file); error = -1; } return error; } /* * Deinit one rule in databases. */ int db_deinit_rule(const struct rule *rule) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, rule->db_list, link) { if (db->ipa_db_mod->db_deinit_rule(rule->ruleno) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_deinit_rule(%s) failed", db->mod_file, rule->rule_name); error = -1; } } return error; } /* * Update statistics for one rule. */ int db_update_rule(const struct rule *rule, const uint64_t *cnt) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, rule->db_list, link) if (db->ipa_db_mod->db_update_rule(rule->ruleno, cnt, &curdate) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_update_rule(%s) failed", db->mod_file, rule->rule_name); error = -1; } return error; } /* * Append an accounting record for one rule. */ int db_append_rule(struct rule *rule, const uint64_t *cnt, int flush_cnt) { int error = 0; const struct db_elem *db; STAILQ_FOREACH(db, rule->db_list, link) if (db->ipa_db_mod->db_append_rule(rule->ruleno, cnt, &curdate) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_append_rule(%s) failed", db->mod_file, rule->rule_name); error = -1; } if (rule->append_tevent != NULL) rule->append_sec = rule->append_tevent->event_sec; if (flush_cnt) rule->cnt = UINT64_C(0); return error; } /* * Set a rule active/inactive in databases it uses. */ int db_set_rule_active(const struct rule *rule, int active) { const struct db_elem *db; STAILQ_FOREACH(db, rule->db_list, link) if (db->ipa_db_mod->db_set_rule_active != NULL) if (db->ipa_db_mod->db_set_rule_active(rule->ruleno, active) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_set_rule_active(%s, %d) failed", db->mod_file, rule->rule_name, active); return -1; } return 0; } #ifdef WITH_AUTORULES /* * Init one dynamic rule in databases, also init its limits and thresholds. */ int db_init_dynrule(const struct autorule *autorule, struct rule *rule) { const struct db_elem *db; if (!STAILQ_EMPTY(rule->db_list)) { STAILQ_FOREACH(db, rule->db_list, link) if (db->ipa_db_mod->db_init_dynrule != NULL) { if (db->ipa_db_mod->db_init_dynrule(autorule->aruleno, rule->ruleno, rule->rule_name, rule->rule_info) < 0) { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s) failed", db->mod_file, autorule->arule_name, rule->rule_name); return -1; } } else { logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s): module does not support dynamic rules", db->mod_file, autorule->arule_name, rule->rule_name); return -1; } } else { if (debug_db_null) logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database", rule->rule_name); } mem_free(rule->rule_info, m_anon); rule->rule_info = NULL; #ifdef WITH_LIMITS if (db_init_dynlimits(autorule, rule) < 0) return -1; #endif #ifdef WITH_THRESHOLDS if (db_init_dynthresholds(autorule, rule) < 0) return -1; #endif return 0; } #endif /* WITH_AUTORULES */