/*- * Copyright (c) 2003-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: sdb_db_mod.c,v 1.3 2006/03/21 20:24:45 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipa_sdb.h" #include "version.h" #if IPA_DB_MOD_API_VERSION != 1 # error This module supports only ipa_db_mod API version 1 #endif #define DB_MOD_API_VERSION 1 /* Supported ipa_db_mod API. */ #if IPA_MEMFUNC_API_VERSION != 1 # error This module supports only ipa_memfunc API version 1 #endif #define MEMFUNC_API_VERSION 1 /* Supported memfunc API. */ #ifdef WITH_PTHREAD # define MOD_FLAGS IPA_MOD_FLAG_PTHREAD_SAFE #else # define MOD_FLAGS 0 #endif #define MEMFLAG_OPTIMIZE IPA_MEMFUNC_FLAG_OPTIMIZE #define MOD_NAME "ipa_db_sdb" #define MOD_DB_NAME "sdb" #define MOD_CONF_PREFIX "sdb" #define MARRAY_NAME(x) MOD_NAME ":" #x #define MZONE_NAME(x) MOD_NAME ":" #x #define MTYPE_NAME(x) MOD_NAME ":m_" #x extern struct ipa_db_mod IPA_DB_MOD_ENTRY(ipa_db_sdb); /* Is defined at the end of this file. */ static const ipa_suppfunc *suppfunc; /* Exported support functions. */ static const ipa_memfunc *memfunc; /* Exported memory manipulation functions. */ static ipa_mem_type *m_parser; /* Memory from parser. */ static ipa_mem_type *m_anon; /* Anonymous memory allocations. */ static ipa_mem_type *m_tmp; /* Temporary memory allocations. */ static void *(*mem_malloc)(size_t, ipa_mem_type *); static void *(*mem_realloc)(void *, size_t, ipa_mem_type *); static void (*mem_free)(void *, ipa_mem_type *); static ipa_marray *(*marray_init)(const char *, const char *, u_int, void **, size_t, u_int, u_int); static void (*marray_deinit)(ipa_marray *); static int (*marray_alloc)(ipa_marray *, u_int *, int); static void (*marray_free)(ipa_marray *, u_int); static void (*marray_minimize)(ipa_marray *); static int (*marray_check_index)(ipa_marray *, u_int); static ipa_mzone *(*mzone_init)(const char *, const char *, u_int, size_t, u_int, u_int); static void (*mzone_deinit)(ipa_mzone *); static void *(*mzone_alloc)(ipa_mzone *); static void (*mzone_free)(ipa_mzone *, void *); static u_int (*mzone_nused)(ipa_mzone *); static int config_section = 0; /* 1, if there was sdb: {} section. */ static int allow_symlinks; /* sdb: { allow_symlinks } */ static int check_version; /* sdb: { check_version } */ #define DB_RULE_DIR_PERM_UG (S_IRWXU|S_IRGRP|S_IXGRP) /* 0750 */ #define DB_RULE_DIR_PERM_U S_IRWXU /* 0700 */ #define DB_DIR_PERM (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)/* 0755 */ #define DB_FILE_PERM (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) /* 0644 */ #define DB_MAIN_DIR_PERM DB_DIR_PERM /* 0755 */ #define PERM_MASK(x) ((x) & (S_IRWXU|S_IRWXG|S_IRWXO)) /* 0777 & x */ #define NREC_PER_READ 200 /* Number of records to read per one read(2). */ /* * One "db_group" parameter. */ struct db_group { gid_t gid; /* GID of database files. */ int set; /* 0 not set, 1 set, 2 set and named. */ mode_t dir_mode; /* Mode of the database directory. */ }; static char *global_db_dir; /* global { sdb:db_dir } */ static int global_close_fd; /* global { sdb:close_fd } */ static struct db_group global_db_group; /* global { sdb:db_group } */ static char *db_dir_default = IPA_SDB_DB_DIR; /* Default database directory. */ static uid_t proc_uid; /* Current process PID. */ static gid_t proc_gid; /* Current process GID. */ #define RULE_NSIZE 30 #define RULE_NALLOC 20 #ifdef WITH_LIMITS /* * Module's data for limit{}. * If fd < 0, then limit is not initialized or is inactive. */ struct limit { const char *limit_name; /* Limit's name. */ u_int limitno; /* Limit order number. */ int fd; /* Database file descriptor. */ int start_year; /* Year when limit was started. */ int start_mon; /* Month when limit was started.*/ struct ipa_sdb_limit_record rec;/* Current limit's record in database. */ struct ipa_sdb_limit_record_update *rec_update; /* Used in db_update_limit. */ char *db_file; /* Database file for limit. */ char *limit_dir; /* Directory for limit. */ }; static ipa_mzone *limit_mzone; /* Mzone for all limits. */ #define LIMIT_NSIZE RULE_NSIZE #define LIMIT_NALLOC RULE_NALLOC #define LIMIT(r, i) ((r)->limits[i]) /* Pointer to limit by number. */ static int conf_has_limit; /* Nonzero, if conf file has limit. */ #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS /* * Module's data for threshold{}. * If fd < 0, then threshold is not initialized or is inactive. */ struct threshold { const char *threshold_name;/* Threshold's name. */ u_int thresholdno; /* Threshold order number. */ int fd; /* Database file descriptor. */ char *db_file; /* Database file for trheshold. */ char *threshold_dir; /* Directory for threshold. */ }; static ipa_mzone *threshold_mzone; /* Mzone for all thresholds. */ #define THRESHOLD_NSIZE RULE_NSIZE #define THRESHOLD_NALLOC RULE_NALLOC #define THRESHOLD(r, i) ((r)->thresholds[i]) /* Pointer to threshold by number. */ static int conf_has_threshold; /* Nonzero, if conf file has threshold. */ #endif /* WITH_THRESHOLDS */ #define RULE_FREE_DB_DIR 0x01 /* Need to free db_dir in struct rule{}. */ #define RULE_INITED 0x02 /* Rule was inited in init_rule(). */ /* * Module's data for rule{}. * If fd < 0, then rule is not initialized or is inacitve. */ struct rule { const char *rule_name; /* Rule's name. */ u_int ruleno; /* Number of rule. */ #ifdef WITH_LIMITS struct limit **limits; /* Array of pointers to rule's limits. */ u_int nlimits; /* Number of limits. */ char *limits_dir; /* Directory for limits. */ #endif #ifdef WITH_THRESHOLDS struct threshold **thresholds; /* Array of pointers to rule's thresholds. */ u_int nthresholds; /* Number of thresholds. */ char *thresholds_dir;/* Directory for thresholds. */ #endif int fd; /* Database file descriptor. */ int cur_year; /* Current year. */ int cur_mon; /* Current month. */ char *db_dir; /* rule { sdb:db_dir } */ struct db_group db_group; /* rule { sdb:db_group } */ int close_fd; /* rule { sdb:close_fd } */ char *db_file; /* Database file for rule. */ char *rule_dir; /* Directory for rule. */ u_int ref_count; /* Reference counter. */ u_int flags; /* RULE_xxx flags. */ TAILQ_ENTRY(rule) link; /* For list of all rules. */ }; static struct rule **rules_ptr; /* Array of pointers to rules. */ static u_int nstatrules; /* Number of static rules. */ static u_int nalloced; /* Number of allocated rules. */ static u_int ninited; /* Number of inited rules. */ static ipa_mzone *rule_mzone; /* Mzone for all rules. */ static ipa_marray *rules_ptr_marray; /* Marray for array of pointers to rules.*/ #define RULE(i) rules_ptr[i] /* Pointer to rule by number. */ static TAILQ_HEAD(, rule) rules_list; /* List of all rules. */ #ifdef WITH_AUTORULES /* * Module's data for each autorule{}. */ struct autorule { char *db_dir; /* autorule { sdb:db_dir } */ struct db_group db_group; /* autorule { sdb:db_group } */ int close_fd; /* autorule { sdb:close_fd } */ }; static struct autorule *curautorule; /* Current autorule. */ static struct autorule *autorules; /* Array of autorules. */ static u_int nautorules; /* Number of autorules. */ static ipa_marray *autorules_marray; /* Marray of autorules.. */ #define AUTORULE_NSIZE 10 #define AUTORULE_NALLOC 10 #define AUTORULE(i) &autorules[i] /* Pointer to autorule by number. */ #endif /* WITH_AUTORULES */ static const char *currulename; /* Name of current rule. */ #ifdef WITH_RULES static int in_rule; /* 1, if we are in rule{} section. */ static struct rule *currule; /* Current rule. */ #endif static int (*stat_func)(const char *, struct stat *); /* stat() or lstat(). */ static const char *stat_func_name; /* "stat" or "lstat". */ #define cnt_from_base(cnt, c_high, c_low) do { \ (cnt) = ntohl(c_high); \ (cnt) <<= 32; \ (cnt) += ntohl(c_low); \ } while (/* CONSTCOND */ 0) #define cnt_to_base(cnt, c_high, c_low) do { \ (c_high) = htonl((uint32_t)((cnt) >> 32)); \ (c_low) = htonl((uint32_t)(cnt)); \ } while (/* CONSTCOND */ 0) static void logmsg(int, const char *, ...) ATTR_FORMAT(printf, 2, 3); static void logmsgx(int, const char *, ...) ATTR_FORMAT(printf, 2, 3); static void logconferr(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void logconferrx(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void print_param_args(const char *, ...) ATTR_FORMAT(printf, 1, 2); static int mem_asprintf(ipa_mem_type *, char **, const char *, ...) ATTR_FORMAT(printf, 3, 4); /* * A wrapper for imported logmsg() function. */ static void logmsg(int priority, const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logmsg(MOD_NAME, priority, errno, format, ap); va_end(ap); } /* * A wrapper for imported logmsg() function, * but error code is always zero here. */ static void logmsgx(int priority, const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logmsg(MOD_NAME, priority, 0, format, ap); va_end(ap); } /* * A wrapper for imported logconferr() function. */ static void logconferr(const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logconferr(MOD_NAME, errno, format, ap); va_end(ap); } /* * A wrapper for imported logconferr() function, * but error code is always zero here. */ static void logconferrx(const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->logconferr(MOD_NAME, 0, format, ap); va_end(ap); } /* * A wrapper for imported print_param_args() function. */ static void print_param_args(const char *format, ...) { va_list ap; va_start(ap, format); suppfunc->print_param_args(format, ap); va_end(ap); } /* * A wrapper for imported print_param_name() function. */ static void print_param_name(const char *name) { suppfunc->print_param_name(MOD_CONF_PREFIX, name); } /* * A wrapper for imported print_param_end() function. */ static void print_param_end(void) { suppfunc->print_param_end(); } static int mem_asprintf(ipa_mem_type *mem_type, char **bufp, const char *format, ...) { int ret; va_list ap; va_start(ap, format); ret = memfunc->mem_vasprintf(mem_type, bufp, format, ap); va_end(ap); return ret; } #ifdef WITH_ANY_LIMITS /* * A wrapper for read(2) syscall. */ static ssize_t readn(int fd, const char *fname, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; for (;;) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) { logmsg(IPA_LOG_WARNING, "readn: read(%s)", fname); nread = 0; } else { logmsg(IPA_LOG_ERR, "readn: read(%s)", fname); return -1; } } else if (nread == 0) break; /* EOF */ nleft -= nread; if (nleft == 0) break; ptr += nread; logmsgx(IPA_LOG_WARNING, "readn: read: short read from %s", fname); } return n - nleft; } #endif /* WITH_ANY_LIMITS */ /* * A wraper for write(2) system call. */ static ssize_t writen(int fd, const char *fname, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; for (;;) { if ( (nwritten = write(fd, ptr, nleft)) < 0) { if (errno == EINTR) { logmsg(IPA_LOG_WARNING, "writen: write(%s)", fname); nwritten = 0; } else { logmsg(IPA_LOG_ERR, "writen: write(%s)", fname); return -1; } } nleft -= nwritten; if (nleft == 0) break; ptr += nwritten; logmsgx(IPA_LOG_WARNING, "writen: write: short write to %s", fname); } return n - nleft; } static struct rule * alloc_rule(u_int ruleno) { struct rule *rule; if ( (rule = mzone_alloc(rule_mzone)) == NULL) { logconferrx("alloc_rule: mzone_alloc failed"); return NULL; } rule->rule_name = currulename; rule->ruleno = ruleno; #ifdef WITH_LIMITS rule->limits = NULL; rule->nlimits = 0; rule->limits_dir = NULL; #endif #ifdef WITH_THRESHOLDS rule->thresholds = NULL; rule->nthresholds = 0; rule->thresholds_dir = NULL; #endif /* * Is used in cases when rule->close_fd == 0 and need to check * if file is opened. */ rule->fd = -1; /* Means, that rule->db_file was not build. */ rule->cur_year = 0; rule->db_group.set = 0; rule->close_fd = -1; rule->db_dir = rule->db_file = rule->rule_dir = NULL; rule->ref_count = 1; rule->flags = 0; if (marray_alloc(rules_ptr_marray, &ruleno, 1) < 0) { logconferrx("alloc_rule: marray_alloc failed"); mzone_free(rule_mzone, rule); return NULL; } RULE(ruleno) = rule; TAILQ_INSERT_TAIL(&rules_list, rule, link); ++nalloced; return rule; } #ifdef WITH_RULES static struct rule * alloc_currule(void) { return currule = alloc_rule(nstatrules - 1); } #endif #ifdef WITH_AUTORULES static struct autorule * alloc_autorule(u_int autoruleno) { struct autorule *autorule; if (marray_alloc(autorules_marray, &autoruleno, 1) < 0) { logconferrx("alloc_autorule: marray_alloc failed"); return NULL; } autorule = AUTORULE(autoruleno); autorule->db_dir = NULL; autorule->db_group.set = 0; autorule->close_fd = -1; return autorule; } #endif /* * Parse "allow_symlinks" parameter. */ static int parse_allow_symlinks(void *arg) { allow_symlinks = *(int *)arg; return 0; } /* * Parse "check_version" parameter. */ static int parse_check_version(void *arg) { check_version = *(int *)arg; return 0; } /* * Parse "sdb:" section. */ /* ARGSUSED */ static int parse_config_section(void *arg ATTR_UNUSED) { config_section = 1; return 0; } /* * Parse "db_dir" parameter. */ static int parse_db_dir(void *arg) { char *str = *(char **)arg, **dstp; if (*str == '\0') { logconferrx("argument should be a non-empty string"); return -1; } if (*str != '/') { logconferrx("directory should be given with absolute path"); return -1; } #ifdef WITH_RULES if (in_rule) { if (currule == NULL) if (alloc_currule() == NULL) return -1; dstp = &currule->db_dir; currule->flags |= RULE_FREE_DB_DIR; } else #endif #ifdef WITH_AUTORULES if (curautorule != NULL) dstp = &curautorule->db_dir; else #endif dstp = &global_db_dir; mem_free(*dstp, m_parser); *dstp = str; return 0; } /* * Convert string to u_int. * Assumed that sizeof(u_long) >= sizeof(u_int). * The first character in a string should be checked * for '-' before calling this function (remember that * strtoul() and strtoull() work with negative values). */ static int strto_u_int(u_int *result, const char *nptr, char **endptr) { u_long val_ul; errno = 0; val_ul = strtoul(nptr, endptr, 10); if (errno != 0) { logconferr("strtoul"); return -1; } if (val_ul > UINT_MAX) { logconferrx("too big value for u_int"); return -1; } *result = (u_int)val_ul; return 0; } /* * Parse "db_group" parameter. */ static int parse_db_group(void *arg) { char *endptr; const char *group; int set; u_int gid; struct db_group *db_group; const struct group *grp; group = *(char **)arg; errno = 0; if ( (grp = getgrnam(group)) == NULL) { if (errno != 0) { logconferr("getgrnam(%s)", group); return -1; } if (isdigit(*group) == 0) { logconferrx("cannot find group \"%s\"", group); return -1; } if (strto_u_int(&gid, group, &endptr) < 0) return -1; if (*endptr != '\0') { logconferrx("cannot find group \"%s\"", group); return -1; } set = 1; } else { gid = grp->gr_gid; set = 2; } endgrent(); #ifdef WITH_RULES if (in_rule) { if (currule == NULL) if (alloc_currule() == NULL) return -1; db_group = &currule->db_group; } else #endif #ifdef WITH_AUTORULES if (curautorule != NULL) db_group = &curautorule->db_group; else #endif db_group = &global_db_group; db_group->gid = (uid_t)gid; db_group->set = set; db_group->dir_mode = DB_RULE_DIR_PERM_UG; return 0; } /* * Parse "close_fd" parameter. */ static int parse_close_fd(void *arg) { #ifdef WITH_RULES if (in_rule) { if (currule == NULL) if (alloc_currule() == NULL) return -1; currule->close_fd = *(int *)arg; } else #endif #ifdef WITH_AUTORULES if (curautorule != NULL) curautorule->close_fd = *(int *)arg; else #endif global_close_fd = *(int *)arg; return 0; } static void show_db_dir(const char *db_dir) { if (db_dir != NULL) { print_param_name("db_dir"); suppfunc->print_string(db_dir); print_param_end(); } } static void show_close_fd(int val) { if (val >= 0) { print_param_name("close_fd"); suppfunc->print_boolean(val); print_param_end(); } } static void show_db_group(const struct db_group *db_group) { const struct group *grp; if (db_group->set) { print_param_name("db_group"); if (db_group->set == 2) { if ( (grp = getgrgid(db_group->gid)) == NULL) print_param_args("%lu", (u_long)db_group->gid); else print_param_args("%s", grp->gr_name); } else print_param_args("%lu", (u_long)db_group->gid); print_param_end(); } } static int conf_init(void) { suppfunc = IPA_DB_MOD_ENTRY(ipa_db_sdb).suppfunc; memfunc = IPA_DB_MOD_ENTRY(ipa_db_sdb).memfunc; if (memfunc->api_ver != MEMFUNC_API_VERSION) { logconferrx("conf_init: module understands memfunc API version %u, exported memfunc API version is %u", MEMFUNC_API_VERSION, memfunc->api_ver); return -1; } m_parser = memfunc->m_parser; if ((m_anon = memfunc->mem_type_new(MTYPE_NAME(anon), "Anonymous memory", 0)) == NULL || (m_tmp = memfunc->mem_type_new(MTYPE_NAME(tmp), "Temporal memory", 0)) == NULL) { logconferrx("conf_init: mem_type_new failed"); return -1; } mem_malloc = memfunc->mem_malloc; mem_realloc = memfunc->mem_realloc; mem_free = memfunc->mem_free; marray_init = memfunc->marray_init; marray_deinit = memfunc->marray_deinit; marray_alloc = memfunc->marray_alloc; marray_free = memfunc->marray_free; marray_minimize = memfunc->marray_minimize; marray_check_index = memfunc->marray_check_index; mzone_init = memfunc->mzone_init; mzone_deinit = memfunc->mzone_deinit; mzone_alloc = memfunc->mzone_alloc; mzone_free = memfunc->mzone_free; mzone_nused = memfunc->mzone_nused; if ( (rules_ptr_marray = marray_init(MARRAY_NAME(rules_ptr), "Pointers to rules", 0, (void *)&rules_ptr, sizeof(struct rule *), RULE_NSIZE, RULE_NALLOC)) == NULL #ifdef WITH_AUTORULES || (autorules_marray = marray_init(MARRAY_NAME(autorules), "Autorules", 0, (void *)&autorules, sizeof(struct autorule), AUTORULE_NSIZE, AUTORULE_NALLOC)) == NULL #endif ) { logconferrx("conf_init: marray_init failed"); return -1; } if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", MEMFLAG_OPTIMIZE, sizeof(struct rule), RULE_NSIZE, RULE_NALLOC)) == NULL) { logconferrx("conf_init: mzone_init failed"); return -1; } nstatrules = nalloced = ninited = 0; #ifdef WITH_RULES in_rule = 0; #endif #ifdef WITH_AUTORULES nautorules = 0; curautorule = NULL; #endif global_db_dir = NULL; global_close_fd = allow_symlinks = check_version = -1; global_db_group.set = 0; #ifdef WITH_LIMITS conf_has_limit = 0; #endif #ifdef WITH_THRESHOLDS conf_has_threshold = 0; #endif TAILQ_INIT(&rules_list); return 0; } static int conf_deinit(void) { /* Just to prevent of using incorrect link in alloc_rule(). */ currulename = NULL; return 0; } static void conf_real(void) { #ifdef WITH_AUTORULES struct autorule *autorule; #endif if (global_db_dir == NULL) global_db_dir = db_dir_default; if (global_close_fd < 0) global_close_fd = 0; if (allow_symlinks < 0) allow_symlinks = 0; if (check_version < 0) check_version = 1; #ifdef WITH_AUTORULES for (autorule = autorules; autorule < autorules + nautorules; ++autorule) { if (autorule->db_dir == NULL) autorule->db_dir = global_db_dir; if (!autorule->db_group.set) autorule->db_group = global_db_group; if (autorule->close_fd < 0) autorule->close_fd = global_close_fd; } #endif } static int conf_mimic_real(void) { #ifdef WITH_RULES struct rule *rule; #endif conf_real(); config_section = 1; #ifdef WITH_RULES TAILQ_FOREACH(rule, &rules_list, link) { if (rule->db_dir == NULL) rule->db_dir = global_db_dir; if (!rule->db_group.set) rule->db_group = global_db_group; if (rule->close_fd < 0) rule->close_fd = global_close_fd; } #endif return 0; } static void show_boolean(const char *param_name, int boolean) { if (boolean >= 0) { suppfunc->print_param_name((char *)NULL, param_name); suppfunc->print_boolean(boolean); print_param_end(); } } static void conf_show(u_int sect_id, u_int no) { #ifdef WITH_RULES const struct rule *rule; #endif #ifdef WITH_AUTORULES const struct autorule *autorule; #endif switch (sect_id) { case IPA_CONF_SECT_ROOT: if (config_section) { suppfunc->print_sect_name(MOD_DB_NAME, ""); suppfunc->print_sect_begin(); suppfunc->set_indent(1); show_boolean("allow_symlinks", allow_symlinks); show_boolean("check_version", check_version); suppfunc->set_indent(0); suppfunc->print_sect_end(); } break; case IPA_CONF_SECT_GLOBAL: show_db_dir(global_db_dir); show_db_group(&global_db_group); show_close_fd(global_close_fd); break; #ifdef WITH_RULES case IPA_CONF_SECT_RULE: if (marray_check_index(rules_ptr_marray, no)) { rule = RULE(no); show_db_dir(rule->db_dir); show_db_group(&rule->db_group); show_close_fd(rule->close_fd); } break; #endif #ifdef WITH_AUTORULES case IPA_CONF_SECT_AUTORULE: autorule = AUTORULE(no); show_db_dir(autorule->db_dir); show_db_group(&autorule->db_group); show_close_fd(autorule->close_fd); break; #endif } } static int conf_event(u_int event, u_int no, const void *arg) { switch (event) { #ifdef WITH_RULES case IPA_CONF_EVENT_RULE_BEGIN: nstatrules = no + 1; in_rule = 1; currule = NULL; currulename = arg; break; case IPA_CONF_EVENT_RULE_END: in_rule = 0; break; #endif #ifdef WITH_AUTORULES case IPA_CONF_EVENT_AUTORULE_BEGIN: if ( (curautorule = alloc_autorule(no)) == NULL) return -1; nautorules = no + 1; break; case IPA_CONF_EVENT_AUTORULE_END: curautorule = NULL; break; #endif #ifdef WITH_LIMITS case IPA_CONF_EVENT_LIMIT_BEGIN: conf_has_limit = 1; break; #endif #ifdef WITH_THRESHOLDS case IPA_CONF_EVENT_THRESHOLD_BEGIN: conf_has_threshold = 1; break; #endif } return 0; } static int create_db_file(const char *path) { int fd; if ( (fd = open(path, O_RDWR|O_CREAT, DB_FILE_PERM)) < 0) { logmsg(IPA_LOG_ERR, "create_db_file: open(%s, O_RDWR|O_CREAT, 0%03o)", path, DB_FILE_PERM); return -1; } if (fchown(fd, proc_uid, proc_gid) < 0) { logmsg(IPA_LOG_ERR, "create_db_file: fchown(%s, %lu, %lu)", path, (u_long)proc_uid, (u_long)proc_gid); return -1; } return fd; } /* * Create or unlink info file in the database. */ static int init_info_file(const char *path, const char *info) { char *infopath; int fd; ssize_t len; if (mem_asprintf(m_tmp, &infopath, "%s/"IPA_SDB_INFO_FILE, path) < 0) { logmsgx(IPA_LOG_ERR, "init_info_file: mem_asprintf failed"); return -1; } if (info != NULL) { if ( (fd = create_db_file(infopath)) < 0) { logmsgx(IPA_LOG_ERR, "init_info_file: create_db_file failed"); goto fail; } len = strlen(info); if (writen(fd, infopath, info, len) != len) { logmsgx(IPA_LOG_ERR, "init_info_file: write failed"); goto fail_close; } if (ftruncate(fd, (off_t)len) < 0) { logmsg(IPA_LOG_ERR, "init_info_file: ftruncate(%s, %lu)", infopath, (u_long)len); goto fail_close; } if (close(fd) < 0) { logmsg(IPA_LOG_ERR, "init_info_file: close(%s)", infopath); goto fail; } } else { if (unlink(infopath) < 0) if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_info_file: unlink(%s)", infopath); goto fail; } } mem_free(infopath, m_tmp); return 0; fail_close: if (close(fd) < 0) logmsg(IPA_LOG_ERR, "init_info_file: close(%s)", infopath); fail: mem_free(infopath, m_tmp); return -1; } #ifdef WITH_ANY_LIMITS static int create_db_dir(const char *path) { if (mkdir(path, DB_DIR_PERM) < 0) { logmsg(IPA_LOG_ERR, "create_db_dir: mkdir(%s, 0%03o)", path, DB_DIR_PERM); return -1; } if (chown(path, proc_uid, proc_gid) < 0) { logmsg(IPA_LOG_ERR, "create_db_dir: chown(%s, %lu, %lu)", path, (u_long)proc_uid, (u_long)proc_gid); return -1; } return 0; } #endif /* WITH_ANY_LIMITS */ static int check_dir(const char *path, const struct stat *statbuf) { if (!S_ISDIR(statbuf->st_mode)) { logmsgx(IPA_LOG_ERR, "check_dir: %s: is expected to be a directory", path); return -1; } return 0; } static int check_file(const char *path, const struct stat *statbuf) { if (!S_ISREG(statbuf->st_mode)) { logmsgx(IPA_LOG_ERR, "check_file: %s: is expected to be a regular file", path); return -1; } return 0; } static int check_db_file(const char *fname, const struct stat *statbuf) { if (check_file(fname, statbuf) < 0) { logmsgx(IPA_LOG_ERR, "check_db_file: check_file failed"); return -1; } if (statbuf->st_uid != proc_uid) { logmsgx(IPA_LOG_ERR, "check_db_file: file %s: file's UID %lu, process's UID %lu: wrong owner of the file", fname, (u_long)statbuf->st_uid, (u_long)proc_uid); return -1; } if (statbuf->st_mode & (S_IWGRP|S_IWOTH)) { logmsgx(IPA_LOG_ERR, "check_db_file: file %s: should not be writable by group or other users", fname); return -1; } return 0; } static int check_db_dir(const char *db_dir) { int newbase, nwritten; u_int version; char *version_file; FILE *fp; struct stat statbuf; /* Create and check main database directory. */ if (stat_func(db_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "check_db_dir: %s(%s)", stat_func_name, db_dir); return -1; } if (mkdir(db_dir, DB_MAIN_DIR_PERM) < 0) { logmsg(IPA_LOG_ERR, "check_db_dir: mkdir(%s, 0%03o)", db_dir, DB_MAIN_DIR_PERM); return -1; } logmsgx(IPA_LOG_INFO, "database directory %s was created", db_dir); newbase = 1; } else { if (check_dir(db_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "check_db_dir: check_dir failed"); return -1; } if (statbuf.st_uid != proc_uid) { logmsgx(IPA_LOG_ERR, "check_db_dir: directory %s: directory's UID %lu, process UID %lu: wrong owner of the directory", db_dir, (u_long)statbuf.st_uid, (u_long)proc_uid); return -1; } if (statbuf.st_mode & (S_IWGRP|S_IWOTH)) { logmsgx(IPA_LOG_ERR, "check_db_dir: directory %s: should not be writable by group or other users", db_dir); return -1; } newbase = 0; } if (check_version) { /* Create and check database format version file. */ if (mem_asprintf(m_tmp, &version_file, "%s/"IPA_SDB_VERSION_FILE, db_dir) < 0) { logmsgx(IPA_LOG_ERR, "check_db_dir: mem_asprintf failed"); return -1; } fp = NULL; if (stat_func(version_file, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "check_db_dir: %s(%s)", stat_func_name, version_file); goto failed; } if (!newbase) logmsgx(IPA_LOG_WARNING, "check_db_dir: file %s does not exist, cannot check version of format of %s database", version_file, db_dir); if ( (fp = fopen(version_file, "w")) == NULL) { logmsg(IPA_LOG_ERR, "check_db_dir: fopen(%s, \"w\")", version_file); goto failed; } nwritten = fprintf(fp, "%u", IPA_SDB_FORMAT_VERSION); if (nwritten < 0) { logmsg(IPA_LOG_ERR, "check_db_dir: fprintf(%s)", version_file); goto failed; } if (nwritten != IPA_SDB_FORMAT_VERSION_LEN) { logmsgx(IPA_LOG_ERR, "check_db_dir: fprintf(%s): short write, written %d of %d bytes", version_file, nwritten, IPA_SDB_FORMAT_VERSION_LEN); goto failed; } } else { if (check_db_file(version_file, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "check_db_dir: check_db_file failed"); goto failed; } if ( (fp = fopen(version_file, "r")) == NULL) { logmsg(IPA_LOG_ERR, "check_db_dir: fopen(%s, \"r\")", version_file); goto failed; } if (fscanf(fp, "%u", &version) != 1) { logmsg(IPA_LOG_ERR, "check_db_dir: fscanf(%s, \"%%u\") failed", version_file); goto failed; } if (version != IPA_SDB_FORMAT_VERSION) { logmsgx(IPA_LOG_ERR, "check_db_dir: version of format of %s database is %u, current version of format is %u", db_dir, version, IPA_SDB_FORMAT_VERSION); goto failed; } } if (fclose(fp) != 0) { logmsg(IPA_LOG_ERR, "check_db_dir: fclose(%s)", version_file); fp = NULL; goto failed; } mem_free(version_file, m_tmp); } return 0; failed: if (fp != NULL) if (fclose(fp) != 0) logmsg(IPA_LOG_ERR, "check_db_dir: fclose(%s)", version_file); mem_free(version_file, m_tmp); return -1; } /* * General initialization of the database. */ static int db_pre_init(void) { #ifdef WITH_AUTORULES const struct autorule *autorule; #endif logmsgx(IPA_LOG_INFO, "initing SDB database, version "IPA_SDB_VERSION); proc_uid = getuid(); proc_gid = getgid(); if (!global_db_group.set) { global_db_group.dir_mode = DB_RULE_DIR_PERM_U; global_db_group.gid = proc_gid; } conf_real(); if (allow_symlinks > 0) { stat_func = stat; stat_func_name = "stat"; } else { stat_func = lstat; stat_func_name = "lstat"; } if (check_db_dir(global_db_dir) < 0) { logmsgx(IPA_LOG_ERR, "db_pre_init: check_db_dir failed"); return -1; } #ifdef WITH_AUTORULES for (autorule = autorules; autorule < autorules + nautorules; ++autorule) if (autorule->db_dir != global_db_dir) if (check_db_dir(autorule->db_dir) < 0) { logmsgx(IPA_LOG_ERR, "db_pre_init: check_db_dir failed"); return -1; } /* Try to minimize autorules_marray. */ if (nautorules != 0) marray_minimize(autorules_marray); else { marray_deinit(autorules_marray); autorules_marray = NULL; } #endif /* Try to minimize rules_ptr_marray. */ if (nstatrules != 0) marray_minimize(rules_ptr_marray); /* * Heuristic decition, if configuration file has limits or * thresholds, then it is very likely that this database will * be used for them, another variant is to check limit_mzone * and threshold_mzone in each db_init_*() call. */ #ifdef WITH_LIMITS if (conf_has_limit) { if ( (limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", MEMFLAG_OPTIMIZE, sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC)) == NULL) { logmsgx(IPA_LOG_ERR, "db_pre_init: mzone_init failed"); return -1; } } else limit_mzone = NULL; #endif #ifdef WITH_THRESHOLDS if (conf_has_threshold) { if ( (threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", MEMFLAG_OPTIMIZE, sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC)) == NULL) { logmsgx(IPA_LOG_ERR, "db_pre_init: mzone_init failed"); return -1; } } else threshold_mzone = NULL; #endif return 0; } static int db_init(void) { #ifdef WITH_RULES const struct rule *rule; if (nalloced != ninited) TAILQ_FOREACH(rule, &rules_list, link) if (!(rule->flags & RULE_INITED)) logmsgx(IPA_LOG_WARNING, "rule %s: this rule has some module's parameter, but it does not use my database", rule->rule_name); #endif return 0; } static void free_rule(struct rule *rule) { rule->ref_count--; if (rule->ref_count == 0) { TAILQ_REMOVE(&rules_list, rule, link); if (rule->flags & RULE_FREE_DB_DIR) mem_free(rule->db_dir, m_parser); #ifdef WITH_LIMITS mem_free(rule->limits, m_anon); mem_free(rule->limits_dir, m_anon); #endif #ifdef WITH_THRESHOLDS mem_free(rule->thresholds, m_anon); mem_free(rule->thresholds_dir, m_anon); #endif mem_free(rule->db_file, m_anon); mem_free(rule->rule_dir, m_anon); marray_free(rules_ptr_marray, rule->ruleno); mzone_free(rule_mzone, rule); --nalloced; --ninited; } } #ifdef WITH_RULES /* * The same as free_rule(), but free only those memory, which * could be allocated during configuration phase. */ static void free_rule_fast(struct rule *rule) { rule->ref_count--; if (rule->ref_count == 0) { if (rule->flags & RULE_FREE_DB_DIR) mem_free(rule->db_dir, m_parser); marray_free(rules_ptr_marray, rule->ruleno); mzone_free(rule_mzone, rule); --nalloced; } } #endif static int init_rule(u_int ruleno, const char *rule_name, const char *rule_info) { mode_t perm; struct rule *rule; struct stat statbuf; rule = RULE(ruleno); rule->rule_name = rule_name; if (mem_asprintf(m_anon, &rule->rule_dir, "%s/%s", rule->db_dir, rule_name) < 0) { logmsgx(IPA_LOG_ERR, "init_rule: mem_asprintf failed"); goto failed; } if (stat_func(rule->rule_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_rule: %s(%s)", stat_func_name, rule->rule_dir); goto failed; } if (mkdir(rule->rule_dir, rule->db_group.dir_mode) < 0) { logmsg(IPA_LOG_ERR, "init_rule: mkdir(%s, 0%03o)", rule->rule_dir, (u_int)rule->db_group.dir_mode); goto failed; } if (chown(rule->rule_dir, proc_uid, rule->db_group.gid) < 0) { logmsg(IPA_LOG_ERR, "init_rule: chown(%s, %lu, %lu)", rule->rule_dir, (u_long)proc_uid, (u_long)rule->db_group.gid); goto failed; } logmsgx(IPA_LOG_INFO, "rule database directory %s was created", rule->rule_dir); } else { if (check_dir(rule->rule_dir, &statbuf) < 0) goto failed; if (statbuf.st_uid != proc_uid) { logmsgx(IPA_LOG_ERR, "init_rule: directory %s: directory's UID %lu, process's UID %lu: wrong owner of the directory", rule->rule_dir, (u_long)statbuf.st_uid, (u_long)proc_uid); goto failed; } perm = PERM_MASK(statbuf.st_mode); if (perm & (S_IWGRP|S_IWOTH)) { logmsgx(IPA_LOG_ERR, "init_rule: directory %s: should not be writable by group or other users", rule->rule_dir); goto failed; } if (statbuf.st_gid != rule->db_group.gid) { logmsgx(IPA_LOG_WARNING, "init_rule: directory %s has incorrect GID %lu, changing GID to %lu", rule->rule_dir, (u_long)statbuf.st_gid, (u_long)rule->db_group.gid); if (chown(rule->rule_dir, proc_uid, rule->db_group.gid) < 0) { logmsg(IPA_LOG_ERR, "init_rule: chown(%s, %lu, %lu)", rule->rule_dir, (u_long)proc_uid, (u_long)rule->db_group.gid); goto failed; } } if (perm != rule->db_group.dir_mode) { logmsgx(IPA_LOG_WARNING, "init_rule: directory %s has wrong mode 0%03o, changing mode to 0%03o", rule->rule_dir, (u_int)perm, (u_int)rule->db_group.dir_mode); if (chmod(rule->rule_dir, rule->db_group.dir_mode) < 0) { logmsg(IPA_LOG_ERR, "init_rule: chmod(%s, 0%03o)", rule->rule_dir, (u_int)rule->db_group.dir_mode); goto failed; } } } if (init_info_file(rule->rule_dir, rule_info) < 0) { logmsgx(IPA_LOG_ERR, "init_rule: init_info_file failed"); goto failed; } #ifdef WITH_LIMITS if (mem_asprintf(m_anon, &rule->limits_dir, "%s/"IPA_SDB_LIMITS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_rule: mem_asprintf failed"); goto failed; } if (rmdir(rule->limits_dir) < 0) switch (errno) { case ENOENT: case ENOTEMPTY: break; default: logmsg(IPA_LOG_ERR, "init_rule: rmdir(%s)", rule->limits_dir); goto failed; } #endif #ifdef WITH_THRESHOLDS if (mem_asprintf(m_anon, &rule->thresholds_dir, "%s/"IPA_SDB_THRESHOLDS_DIR, rule->rule_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_rule: mem_asprintf failed"); goto failed; } if (rmdir(rule->thresholds_dir) < 0) switch (errno) { case ENOENT: case ENOTEMPTY: break; default: logmsg(IPA_LOG_ERR, "init_rule: rmdir(%s)", rule->thresholds_dir); goto failed; } #endif rule->flags |= RULE_INITED; ++ninited; return 0; failed: free_rule(rule); return -1; } #ifdef WITH_RULES /* * Init the database for a static rule. */ static int db_init_statrule(u_int ruleno, const char *rule_name, const char *rule_info) { struct rule *rule; if (!marray_check_index(rules_ptr_marray, ruleno)) { if ( (rule = alloc_rule(ruleno)) == NULL) { logmsgx(IPA_LOG_ERR, "db_init_statrule: alloc_rule failed"); return -1; } } else rule = RULE(ruleno); /* Inherit settings from global{}. */ if (rule->db_dir == NULL) { rule->db_dir = global_db_dir; rule->flags &= ~RULE_FREE_DB_DIR; } else { if (check_db_dir(rule->db_dir) < 0) { logmsgx(IPA_LOG_ERR, "db_init_statrule: check_db_dir failed"); return -1; } } if (!rule->db_group.set) rule->db_group = global_db_group; if (rule->close_fd < 0) rule->close_fd = global_close_fd; return init_rule(ruleno, rule_name, rule_info); } #else # define db_init_statrule NULL #endif /* WITH_RULES */ #ifdef WITH_AUTORULES /* * Init the database for a dynamic rule. */ static int db_init_dynrule(u_int autoruleno, u_int ruleno, const char *rule_name, const char *rule_info) { struct rule *rule; const struct autorule *autorule; if ( (rule = alloc_rule(ruleno)) == NULL) { logmsgx(IPA_LOG_ERR, "db_init_dynrule: alloc_rule failed"); return -1; } autorule = AUTORULE(autoruleno); /* Inherit settings from autorule. */ rule->db_dir = autorule->db_dir; rule->flags &= ~RULE_FREE_DB_DIR; rule->db_group = autorule->db_group; rule->close_fd = autorule->close_fd; return init_rule(ruleno, rule_name, rule_info); } #else # define db_init_dynrule NULL #endif /* WITH_AUTORULES */ #ifdef WITH_LIMITS static void free_limit(struct rule *rule, struct limit *limit) { mem_free(limit->db_file, m_anon); mem_free(limit->limit_dir, m_anon); LIMIT(rule, limit->limitno) = NULL; mzone_free(limit_mzone, limit); free_rule(rule); } static int init_limit(struct rule *rule, u_int limitno, const char *limit_name, const char *limit_info) { struct limit *limit, **limits; struct stat statbuf; if (rule->nlimits == 0) { /* Check the directory for limits. */ if (stat_func(rule->limits_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_limit: %s(%s)", stat_func_name, rule->limits_dir); goto failed; } if (create_db_dir(rule->limits_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: create_db_dir failed"); goto failed; } } else if (check_dir(rule->limits_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: check_db_dir failed"); goto failed; } } if (rule->nlimits < limitno + 1) { if ( (limits = mem_realloc(rule->limits, (limitno + 1) * sizeof *rule->limits, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "init_limit: mem_realloc failed"); goto failed; } rule->limits = limits; rule->nlimits = limitno + 1; } else { logmsgx(IPA_LOG_ERR, "init_limit: unxpected order number %u of limit", limitno); goto failed; } if ( (limit = mzone_alloc(limit_mzone)) == NULL) { logmsgx(IPA_LOG_ERR, "init_limit: mzone_alloc failed"); LIMIT(rule, limitno) = NULL; goto failed; } LIMIT(rule, limitno) = limit; limit->limit_name = limit_name; limit->limitno = limitno; limit->db_file = limit->limit_dir = NULL; if (mem_asprintf(m_anon, &limit->limit_dir, "%s/%s", rule->limits_dir, limit_name) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: mem_asprintf failed"); goto limit_failed; } if (stat_func(limit->limit_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_limit: %s(%s)", stat_func_name, limit->limit_dir); goto limit_failed; } if (create_db_dir(limit->limit_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: create_db_dir failed"); goto limit_failed; } } else if (check_dir(limit->limit_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: check_dir failed"); goto limit_failed; } if (init_info_file(limit->limit_dir, limit_info) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: init_info_file failed"); goto limit_failed; } if (mem_asprintf(m_anon, &limit->db_file, "%s/"IPA_SDB_LIMIT_CURRENT, limit->limit_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_limit: mem_asprintf failed"); goto limit_failed; } /* * Is used in cases when rule->close_fd == 0 and need to check * if file is opened. */ limit->fd = -1; /* Means, ``current'' was not linked to database file. */ limit->start_year = 0; limit->rec_update = (struct ipa_sdb_limit_record_update *)&limit->rec.updated; return 0; limit_failed: free_limit(rule, limit); return -1; failed: free_rule(rule); return -1; } #endif /* WITH_LIMITS */ #if defined(WITH_LIMITS) && defined(WITH_RULES) /* * Init the database for a static limit. */ static int db_init_statlimit(u_int ruleno, const char *rule_name, const char *rule_info, u_int limitno, const char *limit_name, const char *limit_info) { struct rule *rule; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (db_init_statrule(ruleno, rule_name, rule_info) < 0) { logmsgx(IPA_LOG_ERR, "db_init_statlimit: db_init_statrule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } return init_limit(rule, limitno, limit_name, limit_info); } #else # define db_init_statlimit NULL #endif /* WITH_LIMITS && WITH_RULES */ #if defined(WITH_LIMITS) && defined(WITH_AUTORULES) /* * Init the database for a dynamic limit. */ static int db_init_dynlimit(u_int autoruleno, u_int ruleno, const char *rule_name, const char *rule_info, u_int limitno, const char *limit_name, const char *limit_info) { struct rule *rule; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (db_init_dynrule(autoruleno, ruleno, rule_name, rule_info) < 0) { logmsgx(IPA_LOG_ERR, "db_init_dynlimit: db_init_dynrule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } return init_limit(rule, limitno, limit_name, limit_info); } #else # define db_init_dynlimit NULL #endif /* WITH_LIMITS && WITH_AUTORULES */ #ifdef WITH_THRESHOLDS static void free_threshold(struct rule *rule, struct threshold *threshold) { mem_free(threshold->db_file, m_anon); mem_free(threshold->threshold_dir, m_anon); THRESHOLD(rule, threshold->thresholdno) = NULL; mzone_free(threshold_mzone, threshold); free_rule(rule); } static int init_threshold(struct rule *rule, u_int thresholdno, const char *threshold_name, const char *threshold_info) { struct threshold *threshold, **thresholds; struct stat statbuf; if (rule->nthresholds == 0) { /* Check the directory for thresholds. */ if (stat_func(rule->thresholds_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_threshold: %s(%s)", stat_func_name, rule->thresholds_dir); goto failed; } if (create_db_dir(rule->thresholds_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: create_db_dir failed"); goto failed; } } else if (check_dir(rule->thresholds_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: check_dir failed"); goto failed; } } if (rule->nthresholds < thresholdno + 1) { if ( (thresholds = mem_realloc(rule->thresholds, (thresholdno + 1) * sizeof *rule->thresholds, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "init_threshold: mem_realloc failed"); goto failed; } rule->thresholds = thresholds; rule->nthresholds = thresholdno + 1; } else { logmsgx(IPA_LOG_ERR, "init_threshold: unxpected order number %u of threshold", thresholdno); goto failed; } if ( (threshold = mzone_alloc(threshold_mzone)) == NULL) { logmsgx(IPA_LOG_ERR, "init_threshold: mzone_alloc failed"); THRESHOLD(rule, thresholdno) = NULL; goto failed; } THRESHOLD(rule, thresholdno) = threshold; threshold->threshold_name = threshold_name; threshold->thresholdno = thresholdno; threshold->db_file = threshold->threshold_dir = NULL; if (mem_asprintf(m_anon, &threshold->threshold_dir, "%s/%s", rule->thresholds_dir, threshold_name) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: mem_asprintf failed"); goto threshold_failed; } if (stat_func(threshold->threshold_dir, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "init_threshold: %s(%s)", stat_func_name, threshold->threshold_dir); goto threshold_failed; } if (create_db_dir(threshold->threshold_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: create_db_dir failed"); goto threshold_failed; } } else if (check_dir(threshold->threshold_dir, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: check_dir failed"); goto threshold_failed; } if (init_info_file(threshold->threshold_dir, threshold_info) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: init_info_file failed"); goto threshold_failed; } if (mem_asprintf(m_anon, &threshold->db_file, "%s/"IPA_SDB_THRESHOLD_STATE, threshold->threshold_dir) < 0) { logmsgx(IPA_LOG_ERR, "init_threshold: mem_asprintf failed"); goto threshold_failed; } if ( (threshold->fd = create_db_file(threshold->db_file)) < 0) { logmsgx(IPA_LOG_ERR, "init_threshoold: create_db_file failed"); goto threshold_failed; } if (rule->close_fd) if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "init_threshold: close(%s)", threshold->db_file); goto threshold_failed; } return 0; threshold_failed: free_threshold(rule, threshold); return -1; failed: free_rule(rule); return -1; } #endif /* WITH_THRESHOLDS */ #if defined(WITH_THRESHOLDS) && defined(WITH_RULES) /* * Init the database for a static threshold. */ static int db_init_statthreshold(u_int ruleno, const char *rule_name, const char *rule_info, u_int thresholdno, const char *threshold_name, const char *threshold_info) { struct rule *rule; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (db_init_statrule(ruleno, rule_name, rule_info) < 0) { logmsgx(IPA_LOG_ERR, "db_init_statthreshold: db_init_statrule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } return init_threshold(rule, thresholdno, threshold_name, threshold_info); } #else # define db_init_statthreshold NULL #endif /* WITH_THRESHOLDS && WITH_RULES */ #if defined(WITH_THRESHOLDS) && defined(WITH_AUTORULES) /* * Init the database for a dynamic threshold. */ static int db_init_dynthreshold(u_int autoruleno, u_int ruleno, const char *rule_name, const char *rule_info, u_int thresholdno, const char *threshold_name, const char *threshold_info) { struct rule *rule; if (!marray_check_index(rules_ptr_marray, ruleno)) { if (db_init_dynrule(autoruleno, ruleno, rule_name, rule_info) < 0) { logmsgx(IPA_LOG_ERR, "db_init_dynthreshold: db_init_dynrule failed"); return -1; } rule = RULE(ruleno); } else { rule = RULE(ruleno); rule->ref_count++; } return init_threshold(rule, thresholdno, threshold_name, threshold_info); } #else # define db_init_dynthreshold NULL #endif /* WITH_THRESHOLDS && WITH_AUTORULES */ #ifdef WITH_ANY_LIMITS /* * Convert machine independent ipa_sdb_date{} values to ipa_tm{}. */ static void copy_db_date_to_tm(const ipa_sdb_date *date, ipa_tm *tm) { tm->tm_year = ntohs(date->year); tm->tm_mon = date->mon; tm->tm_mday = date->mday; tm->tm_hour = date->hour; tm->tm_min = date->min; tm->tm_sec = date->sec; } /* * Convert ipa_tm{} values to machine independent ipa_sdb_date{}. */ static void copy_tm_to_db_date(const ipa_tm *tm, ipa_sdb_date *date) { date->year = htons((u_short)tm->tm_year); date->mon = tm->tm_mon; date->mday = tm->tm_mday; date->hour = tm->tm_hour; date->min = tm->tm_min; date->sec = tm->tm_sec; } #endif /* WITH_ANY_LIMITS */ #ifdef WITH_LIMITS /* * Return limit state. */ static int db_get_limit_state(u_int ruleno, u_int limitno, struct ipa_limit_state *state) { const struct rule *rule; struct limit *limit; struct stat statbuf; struct ipa_sdb_limit_record rec; rule = RULE(ruleno); limit = LIMIT(rule, limitno); state->lim = UINT64_C(0); if (stat_func(limit->db_file, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "db_get_limit_state: %s(%s)", stat_func_name, limit->db_file); return -1; } } else { if (check_file(limit->db_file, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "db_get_limit_state: check_file failed"); return -1; } if (rule->close_fd || limit->fd < 0) if ( (limit->fd = open(limit->db_file, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_get_limit_state: open(%s, O_RDWR)", limit->db_file); return -1; } if (statbuf.st_size != 0) { if (statbuf.st_size % sizeof rec != 0) { logmsgx(IPA_LOG_ERR, "db_get_limit_state: wrong size of the database file %s", limit->db_file); goto failed; } if (lseek(limit->fd, -(off_t)sizeof(rec), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_get_limit_state: lseek(%s, -%lu, SEEK_END)", limit->db_file, (u_long)sizeof(rec)); goto failed; } if (readn(limit->fd, limit->db_file, &rec, sizeof rec) != sizeof rec) { logmsgx(IPA_LOG_ERR, "db_get_limit_state: readn failed"); goto failed; } cnt_from_base(state->lim, rec.l_high, rec.l_low); cnt_from_base(state->cnt, rec.c_high, rec.c_low); state->event_date_set = 0; if (rec.set & IPA_SDB_LIMIT_EVENT_START_SET) { state->event_date_set |= IPA_LIMIT_EVENT_START_SET; copy_db_date_to_tm(&rec.start, &state->event_date[IPA_LIMIT_EVENT_START]); } if (rec.set & IPA_SDB_LIMIT_EVENT_RESTART_SET) { state->event_date_set |= IPA_LIMIT_EVENT_RESTART_SET; copy_db_date_to_tm(&rec.restart, &state->event_date[IPA_LIMIT_EVENT_RESTART]); } if (rec.set & IPA_SDB_LIMIT_EVENT_RESTART_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_RESTART_EXEC_SET; copy_db_date_to_tm(&rec.restart_exec, &state->event_date[IPA_LIMIT_EVENT_RESTART_EXEC]); } if (rec.set & IPA_SDB_LIMIT_EVENT_REACH_SET) { state->event_date_set |= IPA_LIMIT_EVENT_REACH_SET; copy_db_date_to_tm(&rec.reach, &state->event_date[IPA_LIMIT_EVENT_REACH]); } if (rec.set & IPA_SDB_LIMIT_EVENT_REACH_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_REACH_EXEC_SET; copy_db_date_to_tm(&rec.reach_exec, &state->event_date[IPA_LIMIT_EVENT_REACH_EXEC]); } if (rec.set & IPA_SDB_LIMIT_EVENT_EXPIRE_SET) { state->event_date_set |= IPA_LIMIT_EVENT_EXPIRE_SET; copy_db_date_to_tm(&rec.expire, &state->event_date[IPA_LIMIT_EVENT_EXPIRE]); } if (rec.set & IPA_SDB_LIMIT_EVENT_EXPIRE_EXEC_SET) { state->event_date_set |= IPA_LIMIT_EVENT_EXPIRE_EXEC_SET; copy_db_date_to_tm(&rec.expire_exec, &state->event_date[IPA_LIMIT_EVENT_EXPIRE_EXEC]); } if (rec.set & IPA_SDB_LIMIT_EVENT_UPDATED_SET) { state->event_date_set |= IPA_LIMIT_EVENT_UPDATED_SET; copy_db_date_to_tm(&rec.updated, &state->event_date[IPA_LIMIT_EVENT_UPDATED]); } } if (rule->close_fd) if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_get_limit_state: close(%s)", limit->db_file); return -1; } } return 0; failed: if (rule->close_fd) if (close(limit->fd) < 0) logmsg(IPA_LOG_ERR, "db_get_limit_state: close(%s)", limit->db_file); return -1; } /* * Move statistics from /current to /yyyymm file. */ static int archive_limit(const struct limit *limit) { char *path; int fdsrc, fddst; size_t src_size, recs_size; ssize_t nread; ipa_tm start_tm; struct stat statbuf; struct ipa_sdb_limit_record *recs, rec1; if ( (fdsrc = open(limit->db_file, O_RDONLY)) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: open(%s, O_RDONLY)", limit->db_file); return -1; } if (readn(fdsrc, limit->db_file, &rec1, sizeof rec1) != sizeof rec1) { logmsgx(IPA_LOG_ERR, "archive_limit: readn from %s failed", limit->db_file); return -1; } if (close(fdsrc) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: close(%s)", limit->db_file); return -1; } copy_db_date_to_tm(&rec1.start, &start_tm); /* Build the name for the year/month file. */ if (mem_asprintf(m_tmp, &path, "%s/%u%02u", limit->limit_dir, start_tm.tm_year, start_tm.tm_mon) < 0) { logmsgx(IPA_LOG_ERR, "archive_limit: mem_asprintf failed"); return -1; } recs = NULL; fdsrc = fddst = -1; if (stat_func(path, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "archive_limit: %s(%s)", stat_func_name, path); goto failed; } logmsgx(IPA_LOG_INFO, "archive_limit: rename file %s to %s", limit->db_file, path); if (rename(limit->db_file, path) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: rename(%s, %s)", limit->db_file, path); goto failed; } } else { if (check_file(path, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "archive_limit: check_file failed"); goto failed; } logmsgx(IPA_LOG_WARNING, "archive_limit: file %s already exists, merging current statistics to it", path); if (stat_func(limit->db_file, &statbuf) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: %s(%s)", stat_func_name, limit->db_file); goto failed; } src_size = statbuf.st_size; recs_size = src_size < (NREC_PER_READ * sizeof *recs) ? src_size : (NREC_PER_READ * sizeof *recs); if ( (recs = mem_malloc(recs_size, m_tmp)) == NULL) { logmsgx(IPA_LOG_ERR, "archive_limit: mem_malloc failed"); goto failed; } if ( (fdsrc = open(limit->db_file, O_RDONLY)) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: open(%s, O_RDONLY)", limit->db_file); goto failed; } if ( (fddst = open(path, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: open(%s, O_WRONLY)", path); goto failed; } if (lseek(fddst, (off_t)0, SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "archive_limit: lseek(%s, 0, SEEK_END)", path); goto failed; } nread = recs_size; while ( (nread = readn(fdsrc, limit->db_file, recs, nread)) > 0) { if (nread % sizeof(*recs) != 0) { logmsgx(IPA_LOG_ERR, "archive_limit: readn from %s: short read %ld", limit->db_file, (long)nread); continue; } if (writen(fddst, path, recs, nread) != nread) { logmsgx(IPA_LOG_ERR, "archive_limit: writen to %s failed", path); goto failed; } if ( (src_size -= nread) == 0) break; nread = src_size < recs_size ? src_size : recs_size; } switch (nread) { case 0: logmsgx(IPA_LOG_WARNING, "archive_limit: readn from %s retruned 0", limit->db_file); break; case -1: logmsgx(IPA_LOG_ERR, "archive_limit: readn from %s failed", limit->db_file); goto failed; } if (close(fdsrc) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: close(%s)", limit->db_file); fdsrc = -1; goto failed; } fdsrc = -1; if (close(fddst) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: close(%s)", path); fddst = -1; goto failed; } fddst = -1; if (unlink(limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "archive_limit: unlink(%s)", limit->db_file); goto failed; } } mem_free(path, m_tmp); mem_free(recs, m_tmp); return 0; failed: if (fdsrc >= 0) if (close(fdsrc) < 0) logmsg(IPA_LOG_ERR, "archive_limit: close(%s)", limit->db_file); if (fddst >= 0) if (close(fddst) < 0) logmsg(IPA_LOG_ERR, "archive_limit: close(%s)", path); mem_free(path, m_tmp); mem_free(recs, m_tmp); return -1; } /* * Set limit state. */ static int db_set_limit_state(u_int ruleno, u_int limitno, const struct ipa_limit_state *state, int new_state) { char *path; int file_is_empty = 0; const struct rule *rule; struct limit *limit; const ipa_tm *start_tm; struct stat statbuf1, statbuf2; struct ipa_sdb_limit_record *rec; if (!(state->event_date_set & IPA_LIMIT_EVENT_START_SET)) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: EVENT_START is not set"); return -1; } rule = RULE(ruleno); limit = LIMIT(rule, limitno); rec = &limit->rec; start_tm = &state->event_date[IPA_LIMIT_EVENT_START]; if (start_tm->tm_year != limit->start_year || start_tm->tm_mon != limit->start_mon) { if (!rule->close_fd && limit->fd >= 0) { if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: close(%s)", limit->db_file); return -1; } limit->fd = -1; } /* Build the name for the year/month file. */ if (mem_asprintf(m_tmp, &path, "%s/%d%02d", limit->limit_dir, start_tm->tm_year, start_tm->tm_mon) < 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: mem_asprintf failed"); return -1; } if (stat_func(path, &statbuf1) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "db_set_limit_state: %s(%s)", stat_func_name, path); goto failed; } /* There isn't a file for year/month --> create it. */ if ( (limit->fd = create_db_file(path)) < 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: create_db_file failed"); goto failed; } if (stat_func(path, &statbuf1) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: %s(%s)", stat_func_name, path); goto failed; } file_is_empty = 1; } else { /* There is a file for year/month. */ if (check_file(path, &statbuf1) < 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: check_file failed"); goto failed; } if (statbuf1.st_size != 0) { if (statbuf1.st_size % sizeof *rec != 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: wrong size of the database file %s", path); goto failed; } } else file_is_empty = 1; if ( (limit->fd = open(path, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: open(%s, O_RDWR)", path); goto failed; } } if (stat_func(limit->db_file, &statbuf2) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "db_set_limit_state: %s(%s)", stat_func_name, limit->db_file); goto failed; } /* There isn't ``current'' file -> link it to year/month file. */ if (link(path, limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: link(%s, %s)", path, limit->db_file); goto failed; } } else { /* There is ``current'' file. */ if (statbuf2.st_nlink == 1) { logmsgx(IPA_LOG_WARNING, "db_set_limit_state: file %s has only one hard link, fixing...", limit->db_file); if (statbuf2.st_size == 0) { logmsgx(IPA_LOG_WARNING, "db_set_limit_state: file %s is empty, unlinking it", limit->db_file); if (unlink(limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: unlink(%s)", limit->db_file); goto failed; } } else { if (statbuf2.st_size % sizeof *rec != 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: wrong size of the database file %s", limit->db_file); goto failed; } if (archive_limit(limit) < 0) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: archive_limit failed"); goto failed; } } /* Link ``current'' file to the year/month file. */ if (link(path, limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: link(%s, %s)", path, limit->db_file); goto failed; } } else if (statbuf1.st_dev != statbuf2.st_dev || statbuf1.st_ino != statbuf2.st_ino) { if (unlink(limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: unlink(%s)", limit->db_file); goto failed; } /* Link ``current'' file to the year/month file. */ if (link(path, limit->db_file) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: link(%s, %s)", path, limit->db_file); goto failed; } } } mem_free(path, m_tmp); limit->start_year = start_tm->tm_year; limit->start_mon = start_tm->tm_mon; } else { if (rule->close_fd) if ( (limit->fd = open(limit->db_file, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: fopen(%s, O_RDWR)", limit->db_file); return -1; } } /* Set the new limit state. */ cnt_to_base(state->cnt, rec->c_high, rec->c_low); cnt_to_base(state->lim, rec->l_high, rec->l_low); /* Was checked above. */ rec->set = IPA_SDB_LIMIT_EVENT_START_SET; copy_tm_to_db_date(start_tm, &rec->start); if (state->event_date_set & IPA_LIMIT_EVENT_RESTART_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_RESTART_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_RESTART], &rec->restart); } if (state->event_date_set & IPA_LIMIT_EVENT_RESTART_EXEC_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_RESTART_EXEC_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_RESTART_EXEC], &rec->restart_exec); } if (state->event_date_set & IPA_LIMIT_EVENT_REACH_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_REACH_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_REACH], &rec->reach); } if (state->event_date_set & IPA_LIMIT_EVENT_REACH_EXEC_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_REACH_EXEC_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_REACH_EXEC], &rec->reach_exec); } if (state->event_date_set & IPA_LIMIT_EVENT_EXPIRE_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_EXPIRE_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_EXPIRE], &rec->expire); } if (state->event_date_set & IPA_LIMIT_EVENT_EXPIRE_EXEC_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_EXPIRE_EXEC_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_EXPIRE_EXEC], &rec->expire_exec); } if (state->event_date_set & IPA_LIMIT_EVENT_UPDATED_SET) { rec->set |= IPA_SDB_LIMIT_EVENT_UPDATED_SET; copy_tm_to_db_date(&state->event_date[IPA_LIMIT_EVENT_UPDATED], &rec->updated); } if (new_state) { if (lseek(limit->fd, (off_t)0, SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_set_limit_state: lseek(%s, 0, SEEK_END)", limit->db_file); return -1; } } else if (!file_is_empty) { if (lseek(limit->fd, -(off_t)sizeof(*rec), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_set_limit_state: lseek(%s, -%lu, SEEK_END)", limit->db_file, (u_long)sizeof(*rec)); return -1; } } if (writen(limit->fd, limit->db_file, rec, sizeof *rec) != sizeof *rec) { logmsgx(IPA_LOG_ERR, "db_set_limit_state: writen failed"); return -1; } if (rule->close_fd) if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_state: close(%s)", limit->db_file); return -1; } return 0; failed: mem_free(path, m_tmp); if (rule->close_fd && limit->fd >= 0) if (close(limit->fd) < 0) logmsg(IPA_LOG_ERR, "db_set_limit_state: close(%s)", limit->db_file); return -1; } /* * Deinit a limit. */ static int db_deinit_limit(u_int ruleno, u_int limitno) { int error = 0; struct rule *rule; struct limit *limit; if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ rule = RULE(ruleno); if (limitno < rule->nlimits) { /* * Such limit was registered, but probably * free_limit() was already called for it. */ limit = LIMIT(rule, limitno); if (limit != NULL) { if (!rule->close_fd && limit->fd >= 0) if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_deinit_limit: close(%s)", limit->db_file); error = -1; } free_limit(rule, limit); } } } return error; } /* * Update statistics for a limit. */ static int db_update_limit(u_int ruleno, u_int limitno, const uint64_t *cnt, const ipa_tm *ctm) { const struct rule *rule; struct limit *limit; struct ipa_sdb_limit_record_update *rec_update; rule = RULE(ruleno); limit = LIMIT(rule, limitno); rec_update = limit->rec_update; if (rule->close_fd) if ( (limit->fd = open(limit->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_update_limit: fopen(%s, O_WRONLY)", limit->db_file); return -1; } if (lseek(limit->fd, -(off_t)sizeof(*rec_update), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_update_limit: lseek(%s, -%lu, SEEK_END)", limit->db_file, (u_long)sizeof(*rec_update)); goto failed; } copy_tm_to_db_date(ctm, &rec_update->updated); cnt_to_base(*cnt, rec_update->c_high, rec_update->c_low); if (writen(limit->fd, limit->db_file, rec_update, sizeof *rec_update) != sizeof *rec_update) { logmsgx(IPA_LOG_ERR, "db_update_limit: writen failed"); goto failed; } if (rule->close_fd) if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_update_limit: close(%s)", limit->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(limit->fd) < 0) logmsg(IPA_LOG_ERR, "db_update_limit: close(%s)", limit->db_file); return -1; } /* * Register a limit's event. */ static int db_limit_event(u_int ruleno, u_int limitno, u_int event, const ipa_tm *etm, const ipa_tm *ctm) { const struct rule *rule; struct limit *limit; struct ipa_sdb_limit_record *rec; rule = RULE(ruleno); limit = LIMIT(rule, limitno); rec = &limit->rec; switch (event) { case IPA_LIMIT_EVENT_RESTART_EXEC: copy_tm_to_db_date(etm, &rec->restart_exec); rec->set |= IPA_LIMIT_EVENT_RESTART_EXEC_SET; break; case IPA_LIMIT_EVENT_REACH: copy_tm_to_db_date(etm, &rec->reach); rec->set |= IPA_LIMIT_EVENT_REACH_SET; break; case IPA_LIMIT_EVENT_REACH_EXEC: copy_tm_to_db_date(etm, &rec->reach_exec); rec->set |= IPA_LIMIT_EVENT_REACH_EXEC_SET; break; case IPA_LIMIT_EVENT_EXPIRE: copy_tm_to_db_date(etm, &rec->expire); rec->set |= IPA_LIMIT_EVENT_EXPIRE_SET; break; case IPA_LIMIT_EVENT_EXPIRE_EXEC: copy_tm_to_db_date(etm, &rec->expire_exec); rec->set |= IPA_LIMIT_EVENT_EXPIRE_EXEC_SET; break; default: logmsgx(IPA_LOG_ERR, "db_limit_event: unexpected limit event %u for db_set_limit_event", event); return -1; } if (rule->close_fd) if ( (limit->fd = open(limit->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_limit_event: open(%s, O_WRONLY)", limit->db_file); return -1; } if (lseek(limit->fd, -(off_t)sizeof(*rec), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_limit_event: lseek(%s, -%lu, SEEK_END)", limit->db_file, (u_long)sizeof(*rec)); goto failed; } copy_tm_to_db_date(ctm, &rec->updated); if (writen(limit->fd, limit->db_file, rec, sizeof *rec) != sizeof *rec) { logmsgx(IPA_LOG_ERR, "db_limit_event: writen failed"); goto failed; } if (rule->close_fd) if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_limit_event: close(%s)", limit->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(limit->fd) < 0) logmsg(IPA_LOG_ERR, "db_limit_event: close(%s)", limit->db_file); return -1; } static int db_set_limit_active(u_int ruleno, u_int limitno, int active) { const struct rule *rule; struct limit *limit; rule = RULE(ruleno); if (rule->close_fd) /* Database file is already closed. */ return 0; limit = LIMIT(rule, limitno); if (limit->start_year == 0) /* ``current'' was not linked to database file. */ return 0; if (active == 0) { /* * Set limite->fd to -1, since db_deinit_limit() * can be called for inactive limit, and there we * check limit->fd. */ if (close(limit->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_active: close(%s)", limit->db_file); limit->fd = -1; return -1; } limit->fd = -1; } else { /* Set limit active, and rule->close_fd == 0. */ if ( (limit->fd = open(limit->db_file, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_set_limit_active: open(%s, O_RDWR)", limit->db_file); return -1; } } return 0; } #else # define db_get_limit_state NULL # define db_set_limit_state NULL # define db_deinit_limit NULL # define db_update_limit NULL # define db_limit_event NULL # define db_set_limit_active NULL #endif /* WITH_LIMITS */ /* * Deinit a rule */ static int db_deinit_rule(u_int ruleno) { int error = 0; struct rule *rule; if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ rule = RULE(ruleno); if (!rule->close_fd && rule->fd >= 0) if (close(rule->fd) < 0) { logmsg(IPA_LOG_ERR, "db_deinit_rule: close(%s)", rule->db_file); error = -1; } free_rule(rule); } return error; } /* * Deinit database. */ static int db_deinit(void) { u_int n; struct rule *rule; #ifdef WITH_AUTORULES struct autorule *autorule; #endif logmsgx(IPA_LOG_INFO, "deiniting SDB database"); if (global_db_dir != db_dir_default) mem_free(global_db_dir, m_parser); if (nalloced != 0) TAILQ_FOREACH(rule, &rules_list, link) { if (rule->flags & RULE_INITED) { logmsgx(IPA_LOG_ERR, "internal error: db_deinit: rule %s: rule was inited, but was not deinited (ref_count %u)", rule->rule_name, rule->ref_count); return -1; } #ifdef WITH_RULES free_rule_fast(rule); if (nalloced == 0) break; #endif } if ( (n = memfunc->marray_nused(rules_ptr_marray)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: db_deinit: rules_ptr_marray is not empty: %u", n); return -1; } marray_deinit(rules_ptr_marray); #ifdef WITH_AUTORULES if (nautorules != 0) for (autorule = autorules; autorule < autorules + nautorules; ++autorule) if (autorule->db_dir != global_db_dir) mem_free(autorule->db_dir, m_parser); marray_deinit(autorules_marray); #endif if ( (n = mzone_nused(rule_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: db_deinit: rule_mzone is not empty: %u", n); return -1; } mzone_deinit(rule_mzone); #ifdef WITH_LIMITS if (limit_mzone != NULL) { if ( (n = mzone_nused(limit_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: db_deinit: limit_mzone is not empty: %u", n); return -1; } mzone_deinit(limit_mzone); } #endif #ifdef WITH_THRESHOLDS if (threshold_mzone != NULL) { if ( (n = mzone_nused(threshold_mzone)) != 0) { logmsgx(IPA_LOG_ERR, "internal error: db_deinit: threshold_mzone is not empty: %u", n); return -1; } mzone_deinit(threshold_mzone); } #endif return 0; } /* * Append a new record for a rule to the database. */ static int db_append_rule(u_int ruleno, const uint64_t *cnt, const ipa_tm *ctm) { struct rule *rule; struct stat statbuf; struct ipa_sdb_rule_record rec; rule = RULE(ruleno); if (rule->cur_year != ctm->tm_year || rule->cur_mon != ctm->tm_mon) { /* New year, or new month came. */ if (!rule->close_fd && rule->fd >= 0) if (close(rule->fd) < 0) { logmsg(IPA_LOG_ERR, "db_append_rule: close(%s)", rule->db_file); return -1; } mem_free(rule->db_file, m_anon); if (mem_asprintf(m_anon, &rule->db_file, "%s/%d%02d", rule->rule_dir, ctm->tm_year, ctm->tm_mon) < 0) { logmsgx(IPA_LOG_ERR, "db_append_rule: mem_asprintf failed"); return -1; } if (stat_func(rule->db_file, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "db_append_rule: %s(%s)", stat_func_name, rule->db_file); return -1; } if ( (rule->fd = create_db_file(rule->db_file)) < 0) { logmsgx(IPA_LOG_ERR, "db_append_rule: create_db_file failed"); return -1; } } else { if (check_file(rule->db_file, &statbuf) < 0) { logmsgx(IPA_LOG_ERR, "db_append_rule: check_file failed"); return -1; } if ( (rule->fd = open(rule->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_append_rule: open(%s, O_WRONLY)", rule->db_file); return -1; } if (statbuf.st_size % sizeof rec != 0) { logmsgx(IPA_LOG_ERR, "db_append_rule: wrong size of the database file %s", rule->db_file); goto failed; } } rule->cur_year = ctm->tm_year; rule->cur_mon = ctm->tm_mon; } else { if (rule->close_fd) if ( (rule->fd = open(rule->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_append_rule: open(%s, O_WRONLY)", rule->db_file); return -1; } } if (lseek(rule->fd, (off_t)0, SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_append_rule: lseek(%s, 0, SEEK_END)", rule->db_file); goto failed; } rec.mday = ctm->tm_mday; rec.h1 = rec.h2 = ctm->tm_hour; rec.m1 = rec.m2 = ctm->tm_min; rec.s1 = rec.s2 = ctm->tm_sec; cnt_to_base(*cnt, rec.c_high, rec.c_low); if (writen(rule->fd, rule->db_file, &rec, sizeof rec) != sizeof rec) { logmsgx(IPA_LOG_ERR, "db_append_rule: writen failed"); goto failed; } if (rule->close_fd) if (close(rule->fd) < 0) { logmsg(IPA_LOG_ERR, "db_append_rule: close(%s)", rule->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(rule->fd) < 0) logmsg(IPA_LOG_ERR, "db_append_rule: close(%s)", rule->db_file); return -1; } /* * Update statistics for a rule. */ static int db_update_rule(u_int ruleno, const uint64_t *cnt, const ipa_tm *ctm) { struct rule *rule; struct ipa_sdb_rule_record_update update_rec; rule = RULE(ruleno); if (rule->close_fd) if ( (rule->fd = open(rule->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_update_rule: open(%s, O_WRONLY)", rule->db_file); return -1; } if (lseek(rule->fd, -(off_t)sizeof(update_rec), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_update_rule: lseek(%s, -%lu, SEEK_END)", rule->db_file, (u_long)sizeof(update_rec)); goto failed; } update_rec.h2 = ctm->tm_hour; update_rec.m2 = ctm->tm_min; update_rec.s2 = ctm->tm_sec; cnt_to_base(*cnt, update_rec.c_high, update_rec.c_low); if (writen(rule->fd, rule->db_file, &update_rec, sizeof update_rec) != sizeof update_rec) { logmsgx(IPA_LOG_ERR, "db_update_rule: writen failed"); goto failed; } if (rule->close_fd) if (close(rule->fd) < 0) { logmsg(IPA_LOG_ERR, "db_update_rule: close(%s)", rule->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(rule->fd) < 0) logmsg(IPA_LOG_ERR, "db_update_rule: close(%s)", rule->db_file); return -1; } static int db_set_rule_active(u_int ruleno, int active) { struct rule *rule; rule = RULE(ruleno); if (rule->close_fd) /* Database file is already closed. */ return 0; if (rule->cur_year == 0) /* rule->db_file was not build. */ return 0; if (active == 0) { /* * Set rule->fd to -1, since db_deinit_rule() * can be called for inactive rule, and there we * check rule->fd. */ if (close(rule->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_rule_active: close(%s)", rule->db_file); rule->fd = -1; return -1; } rule->fd = -1; } else { /* Set rule active, and rule->close_fd == 0. */ if ( (rule->fd = open(rule->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_set_rule_active: open(%s, O_WRONLY)", rule->db_file); return -1; } } return 0; } #ifdef WITH_THRESHOLDS static int db_get_threshold_state(u_int ruleno, u_int thresholdno, struct ipa_threshold_state *state) { const struct rule *rule; struct threshold *threshold; struct stat statbuf; struct ipa_sdb_threshold_record rec; rule = RULE(ruleno); threshold = THRESHOLD(rule, thresholdno); state->thr = UINT64_C(0); if (stat_func(threshold->db_file, &statbuf) < 0) { if (errno != ENOENT) { logmsg(IPA_LOG_ERR, "db_get_threshold_state: %s(%s)", stat_func_name, threshold->db_file); return -1; } } else { if (check_file(threshold->db_file, &statbuf) < 0) { logmsg(IPA_LOG_ERR, "db_get_threshold_state: check_file failed"); return -1; } if (rule->close_fd || threshold->fd < 0) if ( (threshold->fd = open(threshold->db_file, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_get_threshold_state: open(%s, O_RDWR)", threshold->db_file); return -1; } if (statbuf.st_size != 0) { if (statbuf.st_size != sizeof rec) { logmsgx(IPA_LOG_ERR, "db_get_threshold_state: wrong size of the database file %s", threshold->db_file); goto failed; } if (lseek(threshold->fd, (off_t)0, SEEK_SET) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_get_threshold_state: lseek(%s, 0, SEEK_SET)", threshold->db_file); goto failed; } if (readn(threshold->fd, threshold->db_file, &rec, sizeof rec) != sizeof rec) { logmsgx(IPA_LOG_ERR, "db_get_threshold_state: readn failed"); goto failed; } cnt_from_base(state->thr, rec.t_high, rec.t_low); cnt_from_base(state->cnt, rec.c_high, rec.c_low); copy_db_date_to_tm(&rec.tm_from, &state->tm_from); copy_db_date_to_tm(&rec.tm_updated, &state->tm_updated); } if (rule->close_fd) if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "db_get_threshold_state: close(%s)", threshold->db_file); return -1; } } return 0; failed: if (rule->close_fd) if (close(threshold->fd) < 0) logmsg(IPA_LOG_ERR, "db_get_threshold_state: close(%s)", threshold->db_file); return -1; } static int db_set_threshold_state(u_int ruleno, u_int thresholdno, const struct ipa_threshold_state *state) { const struct rule *rule; struct threshold *threshold; struct ipa_sdb_threshold_record rec; rule = RULE(ruleno); threshold = THRESHOLD(rule, thresholdno); if (rule->close_fd) if ( (threshold->fd = open(threshold->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_set_threshold_state: open(%s, O_WRONLY)", threshold->db_file); return -1; } cnt_to_base(state->thr, rec.t_high, rec.t_low); cnt_to_base(state->cnt, rec.c_high, rec.c_low); copy_tm_to_db_date(&state->tm_from, &rec.tm_from); copy_tm_to_db_date(&state->tm_updated, &rec.tm_updated); if (lseek(threshold->fd, (off_t)0, SEEK_SET) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_set_threshold_state: lseek(%s, 0, SEEK_SET)", threshold->db_file); goto failed; } if (writen(threshold->fd, threshold->db_file, &rec, sizeof rec) != sizeof rec) { logmsgx(IPA_LOG_ERR, "db_set_threshold_state: writen failed"); goto failed; } if (rule->close_fd) if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_threshold_state: close(%s)", threshold->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(threshold->fd) < 0) logmsg(IPA_LOG_ERR, "db_set_threshold_state: close(%s)", threshold->db_file); return -1; } /* * Deinit a threshold. */ static int db_deinit_threshold(u_int ruleno, u_int thresholdno) { int error = 0; struct rule *rule; struct threshold *threshold; if (marray_check_index(rules_ptr_marray, ruleno)) { /* Such rule was registered. */ rule = RULE(ruleno); if (thresholdno < rule->nthresholds) { /* * Such threshold was registered, but probably * free_threshold() was already called for it. */ threshold = THRESHOLD(rule, thresholdno); if (threshold != NULL) { if (!rule->close_fd && threshold->fd >= 0) if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "db_deinit_threshold: close(%s)", threshold->db_file); error = -1; } free_threshold(rule, threshold); } } } return error; } static int db_update_threshold(u_int ruleno, u_int thresholdno, const uint64_t *cnt, const ipa_tm *tm_from, const ipa_tm *tm_updated) { const struct rule *rule; struct threshold *threshold; struct ipa_sdb_threshold_record_update update_rec; rule = RULE(ruleno); threshold = THRESHOLD(rule, thresholdno); if (rule->close_fd) if ( (threshold->fd = open(threshold->db_file, O_WRONLY)) < 0) { logmsg(IPA_LOG_ERR, "db_update_threshold: fopen(%s, O_WRONLY)", threshold->db_file); return -1; } if (lseek(threshold->fd, -(off_t)sizeof(update_rec), SEEK_END) == (off_t)-1) { logmsg(IPA_LOG_ERR, "db_update_threshold: lseek(%s, -%lu, SEEK_END)", threshold->db_file, (u_long)sizeof(update_rec)); goto failed; } cnt_to_base(*cnt, update_rec.c_high, update_rec.c_low); copy_tm_to_db_date(tm_from, &update_rec.tm_from); copy_tm_to_db_date(tm_updated, &update_rec.tm_updated); if (writen(threshold->fd, threshold->db_file, &update_rec, sizeof update_rec) != sizeof update_rec) { logmsgx(IPA_LOG_ERR, "db_update_threshold: writen failed"); goto failed; } if (rule->close_fd) if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "db_update_threshold: close(%s)", threshold->db_file); return -1; } return 0; failed: if (rule->close_fd) if (close(threshold->fd) < 0) logmsg(IPA_LOG_ERR, "db_update_threshold: close(%s)", threshold->db_file); return -1; } static int db_set_threshold_active(u_int ruleno, u_int thresholdno, int active) { const struct rule *rule; struct threshold *threshold; rule = RULE(ruleno); if (rule->close_fd) /* Database file is already closed. */ return 0; threshold = THRESHOLD(rule, thresholdno); if (active == 0) { /* * Set threshold inactive and database file is opened * in db_init_threshold(). Set threshold->fd to -1, * since db_deinit_threshold() can be called for inactive * threshold and there we check threshold->fd. */ if (close(threshold->fd) < 0) { logmsg(IPA_LOG_ERR, "db_set_threshold_active: close(%s)", threshold->db_file); threshold->fd = -1; return -1; } threshold->fd = -1; } else { /* Set threshold active, and rule->close_fd == 0. */ if ( (threshold->fd = open(threshold->db_file, O_RDWR)) < 0) { logmsg(IPA_LOG_ERR, "db_set_threshold_active: open(%s, O_RDWR)", threshold->db_file); return -1; } } return 0; } #else # define db_get_threshold_state NULL # define db_set_threshold_state NULL # define db_deinit_threshold NULL # define db_update_threshold NULL # define db_set_threshold_active NULL #endif /* WITH_THRESHOLDS */ #define SDB_CONF_ROOT_SECT (IPA_CONF_SECT_CUSTOM_OFFSET + 1) static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 }; static const u_int sect_cust_root[] = { SDB_CONF_ROOT_SECT, 0 }; static const u_int sect_any_rule[] = { IPA_CONF_SECT_GLOBAL, #ifdef WITH_RULES IPA_CONF_SECT_RULE, #endif #ifdef WITH_AUTORULES IPA_CONF_SECT_AUTORULE, #endif 0 }; static ipa_conf_sect conf_sect_tbl[] = { { "", SDB_CONF_ROOT_SECT, 0, NULL, NULL, IPA_CONF_TYPE_MISC, sect_root, parse_config_section }, { NULL, 0, 0, NULL, NULL, IPA_CONF_TYPE_MISC, NULL, NULL } }; static ipa_conf_param conf_param_tbl[] = { { "db_dir", 1, NULL, NULL, IPA_CONF_TYPE_STRING, sect_any_rule, parse_db_dir }, { "db_group", 1, NULL, NULL, IPA_CONF_TYPE_MISC, sect_any_rule, parse_db_group }, { "close_fd", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_any_rule, parse_close_fd }, { "allow_symlinks", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_cust_root, parse_allow_symlinks }, { "check_version", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN, sect_cust_root, parse_check_version }, { NULL, 0, NULL, NULL, IPA_CONF_TYPE_MISC, NULL, NULL } }; struct ipa_db_mod IPA_DB_MOD_ENTRY(ipa_db_sdb) = { DB_MOD_API_VERSION, /* api_ver */ MOD_FLAGS, /* mod_flags */ MOD_DB_NAME, /* db_name */ NULL, /* suppfunc */ NULL, /* memfunc */ MOD_CONF_PREFIX, /* conf_prefix */ conf_sect_tbl, /* conf_sect_tbl */ conf_param_tbl, /* conf_param_tbl */ conf_init, /* conf_init */ conf_deinit, /* conf_deinit */ conf_event, /* conf_event */ conf_mimic_real, /* conf_mimic_real */ NULL, /* conf_inherit */ conf_show, /* conf_show */ db_pre_init, /* db_pre_init */ db_init_dynrule, /* db_init_dynrule */ db_init_statrule, /* db_init_statrule */ db_init_dynlimit, /* db_init_dynlimit */ db_init_statlimit, /* db_init_statlimit */ db_init_dynthreshold, /* db_init_dynthreshold */ db_init_statthreshold, /* db_init_statthreshold */ db_init, /* db_init */ db_get_limit_state, /* db_get_limit_state */ db_set_limit_state, /* db_set_limit_state */ db_get_threshold_state, /* db_get_threshold_state */ db_set_threshold_state, /* db_set_threshold_state */ db_deinit_threshold, /* db_deinit_threshold */ db_deinit_limit, /* db_deinit_limit */ db_deinit_rule, /* db_deinit_rule */ db_deinit, /* db_deinit */ db_append_rule, /* db_append_rule */ db_update_rule, /* db_update_rule */ db_update_limit, /* db_update_limit */ db_limit_event, /* db_limit_event */ db_update_threshold, /* db_update_threshold */ db_set_rule_active, /* db_set_rule_active */ db_set_limit_active, /* db_set_limit_active */ db_set_threshold_active /* db_set_threshold_active */ };