/** * @file * Implementation of the storage class preferences_t. * * @author Jeremy A. Mowery jmowery@tresys.com * @author Jason Tang jtang@tresys.com * @author Jeremy Solt jsolt@tresys.com * * Copyright (C) 2003-2007 Tresys Technology, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "preferences.h" #include #include #include #include #include /** default frequency, in milliseconds, to poll log file for changes */ #define DEFAULT_LOG_UPDATE_INTERVAL 1000 /** maximum number of recent log files and recent policy files to remember */ #define MAX_RECENT_ENTRIES 5 /** name of the user's seaudit personal preferences file */ #define USER_SEAUDIT_CONF ".seaudit" /** name of the system seaudit preference file */ #define SYSTEM_SEAUDIT_CONF "dot_seaudit" struct visible_field { preference_field_e id; const char *field; int visible; }; static const struct visible_field default_visible_fields[] = { {HOST_FIELD, "host_field", 1}, {MESSAGE_FIELD, "msg_field", 1}, {DATE_FIELD, "date_field", 1}, {SUSER_FIELD, "src_usr_field", 0}, {SROLE_FIELD, "src_role_field", 0}, {STYPE_FIELD, "src_type_field", 1}, {SMLS_LVL_FIELD, "src_mls_lvl_field", 0}, {SMLS_CLR_FIELD, "src_mls_clr_field", 0}, {TUSER_FIELD, "tgt_usr_field", 0}, {TROLE_FIELD, "tgt_role_field", 0}, {TTYPE_FIELD, "tgt_type_field", 1}, {TMLS_LVL_FIELD, "tgt_mls_lvl_field", 0}, {TMLS_CLR_FIELD, "tgt_mls_clr_field", 0}, {OBJCLASS_FIELD, "obj_class_field", 1}, {PERM_FIELD, "perm_field", 1}, {EXECUTABLE_FIELD, "exe_field", 1}, {COMMAND_FIELD, "comm_field", 1}, {NAME_FIELD, "name_field", 0}, {PID_FIELD, "pid_field", 0}, {INODE_FIELD, "inode_field", 0}, {PATH_FIELD, "path_field", 0}, {OTHER_FIELD, "other_field", 1} }; static const size_t num_visible_fields = sizeof(default_visible_fields) / sizeof(default_visible_fields[0]); struct preferences { /** path to default system log file */ char *log; /** path to default policy */ apol_policy_path_t *policy; /** default path when writing reports */ char *report; /** default path to the stylesheet, used during report writing */ char *stylesheet; /** vector of paths (strings) to recently opened log files */ apol_vector_t *recent_log_files; /** vector of apol_policy_path_t objects to recently opened policies */ apol_vector_t *recent_policy_files; /** non-zero if seaudit should poll the log file for changes */ int real_time_log; /** frequency, in milliesconds, to poll log file */ int real_time_interval; struct visible_field *fields; }; void preferences_apol_policy_path_free(void *elem) { apol_policy_path_t *path = elem; apol_policy_path_destroy(&path); } /** * Parse the old-style recent policies list (a ':' separated list of * paths) into the recent_policy_files field. */ static int preferences_parse_old_recent_files(preferences_t * prefs, const char *s) { apol_vector_t *v = NULL; size_t i; char *base; apol_policy_path_t *path; int error = 0; if ((v = apol_str_split(s, ":")) == NULL) { error = errno; goto cleanup; } for (i = 0; i < apol_vector_get_size(v); i++) { base = apol_vector_get_element(v, i); if ((path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, base, NULL)) == NULL || apol_vector_append(prefs->recent_policy_files, path) < 0) { error = errno; apol_policy_path_destroy(&path); goto cleanup; } } cleanup: apol_vector_destroy(&v); if (error != 0) { errno = error; return -1; } return 0; } /** * Parse the new recent policy files, which now spans across multiple * lines. */ static int preferences_parse_new_recent_files(preferences_t * prefs, FILE * f, int num_files) { int count; for (count = 0; count < num_files; count++) { char *var_name, *value = NULL; apol_policy_path_t *p = NULL; if (asprintf(&var_name, "RECENT_POLICY_PATH_%d", count) < 0) { return -1; } value = apol_config_get_var(var_name, f); free(var_name); if (value == NULL || (p = apol_policy_path_create_from_string(value)) == NULL || apol_vector_append(prefs->recent_policy_files, p) < 0) { free(value); apol_policy_path_destroy(&p); return -1; } free(value); } return 0; } preferences_t *preferences_create(void) { preferences_t *prefs = NULL; FILE *file = NULL; char *path = NULL, *value; apol_vector_t *v = NULL; size_t i, j; int error = 0; if ((prefs = calloc(1, sizeof(*prefs))) == NULL || (prefs->log = strdup("")) == NULL || (prefs->report = strdup("")) == NULL || (prefs->stylesheet = strdup("")) == NULL || (prefs->recent_log_files = apol_vector_create(free)) == NULL || (prefs->recent_policy_files = apol_vector_create(preferences_apol_policy_path_free)) == NULL || (prefs->fields = calloc(num_visible_fields, sizeof(struct visible_field))) == NULL) { error = errno; goto cleanup; } prefs->real_time_interval = DEFAULT_LOG_UPDATE_INTERVAL; memcpy(prefs->fields, default_visible_fields, num_visible_fields * sizeof(struct visible_field)); path = apol_file_find_user_config(USER_SEAUDIT_CONF); if (!path) { if ((path = apol_file_find_path(SYSTEM_SEAUDIT_CONF)) == NULL) { return prefs; } } if ((file = fopen(path, "r")) == NULL) { error = errno; goto cleanup; } if ((value = apol_config_get_var("DEFAULT_LOG_FILE", file)) != NULL) { free(prefs->log); prefs->log = value; } if ((value = apol_config_get_var("DEFAULT_POLICY_FILE", file)) != NULL) { apol_policy_path_destroy(&prefs->policy); if (apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, value, NULL) == NULL) { error = errno; free(value); goto cleanup; } free(value); } if ((value = apol_config_get_var("DEFAULT_POLICY_PATH", file)) != NULL) { apol_policy_path_destroy(&prefs->policy); if ((prefs->policy = apol_policy_path_create_from_string(value)) == NULL) { error = errno; free(value); goto cleanup; } free(value); } if ((value = apol_config_get_var("DEFAULT_REPORT_CONFIG_FILE", file)) != NULL) { free(prefs->report); prefs->report = value; } if ((value = apol_config_get_var("DEFAULT_REPORT_CSS_FILE", file)) != NULL) { free(prefs->stylesheet); prefs->stylesheet = value; } if ((value = apol_config_get_var("RECENT_LOG_FILES", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) { error = errno; free(value); goto cleanup; } free(value); apol_vector_destroy(&prefs->recent_log_files); prefs->recent_log_files = v; /* test if there exists the new recent list that contains * module filenames */ if ((value = apol_config_get_var("RECENT_POLICY_PATH_FILES", file)) != NULL) { if (preferences_parse_new_recent_files(prefs, file, atoi(value)) < 0) { error = errno; free(value); goto cleanup; } } else { /* use older style that could only handle monolithic policies */ if ((value = apol_config_get_var("RECENT_POLICY_FILES", file)) == NULL || preferences_parse_old_recent_files(prefs, value) < 0) { error = errno; free(value); goto cleanup; } } free(value); if ((value = apol_config_get_var("LOG_COLUMNS_HIDDEN", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) { error = errno; goto cleanup; } for (j = 0; j < num_visible_fields; j++) { prefs->fields[j].visible = 1; } for (i = 0; i < apol_vector_get_size(v); i++) { char *s = apol_vector_get_element(v, i); for (j = 0; j < num_visible_fields; j++) { if (strcmp(s, prefs->fields[j].field) == 0) { prefs->fields[j].visible = 0; break; } } } free(value); apol_vector_destroy(&v); value = apol_config_get_var("REAL_TIME_LOG_MONITORING", file); if (value != NULL && value[0] != '0') { prefs->real_time_log = 1; } free(value); value = apol_config_get_var("REAL_TIME_LOG_UPDATE_INTERVAL", file); if (value != NULL) { prefs->real_time_interval = atoi(value); } free(value); cleanup: free(path); if (file != NULL) { fclose(file); } if (error != 0) { preferences_destroy(&prefs); errno = error; return NULL; } return prefs; } void preferences_destroy(preferences_t ** prefs) { if (prefs != NULL && *prefs != NULL) { free((*prefs)->log); apol_policy_path_destroy(&(*prefs)->policy); free((*prefs)->report); free((*prefs)->stylesheet); apol_vector_destroy(&(*prefs)->recent_log_files); apol_vector_destroy(&(*prefs)->recent_policy_files); free((*prefs)->fields); free(*prefs); *prefs = NULL; } } int preferences_write_to_conf_file(preferences_t * prefs) { FILE *file = NULL; char *home, *conf_file = NULL, *value; apol_vector_t *hidden_fields = NULL; size_t i; int retval = 0, error = 0; /* we need to open ~/.seaudit */ home = getenv("HOME"); if (!home) { error = EBADRQC; goto cleanup; } if (asprintf(&conf_file, "%s/%s", home, USER_SEAUDIT_CONF) < 0) { error = errno; goto cleanup; } if ((file = fopen(conf_file, "w")) == NULL) { error = errno; goto cleanup; } fprintf(file, "# configuration file for seaudit - an audit log tool for Security Enhanced Linux.\n"); fprintf(file, "# this file is auto-generated\n\n"); if (strcmp(prefs->log, "") != 0) { fprintf(file, "DEFAULT_LOG_FILE %s\n", prefs->log); } if (prefs->policy != NULL) { value = apol_policy_path_to_string(prefs->policy); if (value == NULL) { error = errno; goto cleanup; } fprintf(file, "DEFAULT_POLICY_PATH %s\n", value); free(value); } if (strcmp(prefs->report, "") != 0) { fprintf(file, "DEFAULT_REPORT_CONFIG_FILE %s\n", prefs->report); } if (strcmp(prefs->stylesheet, "") != 0) { fprintf(file, "DEFAULT_REPORT_CSS_FILE %s\n", prefs->stylesheet); } if ((value = apol_str_join(prefs->recent_log_files, ":")) == NULL) { error = errno; goto cleanup; } fprintf(file, "RECENT_LOG_FILES %s\n", value); free(value); fprintf(file, "RECENT_POLICY_PATH_FILES %zd\n", apol_vector_get_size(prefs->recent_policy_files)); for (i = 0; i < apol_vector_get_size(prefs->recent_policy_files); i++) { apol_policy_path_t *p = apol_vector_get_element(prefs->recent_policy_files, i); if ((value = apol_policy_path_to_string(p)) == NULL) { error = errno; goto cleanup; } fprintf(file, "RECENT_POLICY_PATH_%zd %s\n", i, value); free(value); } if ((hidden_fields = apol_vector_create(NULL)) == NULL) { error = errno; goto cleanup; } for (i = 0; i < num_visible_fields; i++) { if (!prefs->fields[i].visible && apol_vector_append(hidden_fields, (char *)prefs->fields[i].field) < 0) { error = errno; goto cleanup; } } if ((value = apol_str_join(hidden_fields, ":")) == NULL) { error = errno; goto cleanup; } fprintf(file, "LOG_COLUMNS_HIDDEN %s\n", value); free(value); fprintf(file, "REAL_TIME_LOG_MONITORING %d\n", prefs->real_time_log); fprintf(file, "REAL_TIME_LOG_UPDATE_INTERVAL %d\n", prefs->real_time_interval); retval = 0; cleanup: free(conf_file); apol_vector_destroy(&hidden_fields); if (file != NULL) { fclose(file); } errno = error; return retval; } int preferences_is_column_visible(preferences_t * prefs, preference_field_e id) { size_t i; for (i = 0; i < num_visible_fields; i++) { if (prefs->fields[i].id == id) { return prefs->fields[i].visible; } } assert(0); return -1; } void preferences_set_column_visible(preferences_t * prefs, preference_field_e id, int visible) { size_t i; for (i = 0; i < num_visible_fields; i++) { if (prefs->fields[i].id == id) { prefs->fields[i].visible = visible; return; } } assert(0); } int preferences_set_log(preferences_t * prefs, const char *log) { char *s; if ((s = strdup(log)) == NULL) { return -1; } free(prefs->log); prefs->log = s; return 0; } const char *preferences_get_log(preferences_t * prefs) { return prefs->log; } int preferences_set_policy(preferences_t * prefs, const apol_policy_path_t * policy) { apol_policy_path_t *new_policy; if ((new_policy = apol_policy_path_create_from_policy_path(policy)) == NULL) { return -1; } apol_policy_path_destroy(&prefs->policy); prefs->policy = new_policy; return 0; } const apol_policy_path_t *preferences_get_policy(preferences_t * prefs) { return prefs->policy; } int preferences_set_report(preferences_t * prefs, const char *report) { char *s; if ((s = strdup(report)) == NULL) { return -1; } free(prefs->report); prefs->report = s; return 0; } const char *preferences_get_report(preferences_t * prefs) { return prefs->report; } int preferences_set_stylesheet(preferences_t * prefs, const char *stylesheet) { char *s; if ((s = strdup(stylesheet)) == NULL) { return -1; } free(prefs->stylesheet); prefs->stylesheet = s; return 0; } const char *preferences_get_stylesheet(preferences_t * prefs) { return prefs->stylesheet; } void preferences_set_real_time_at_startup(preferences_t * prefs, int startup) { prefs->real_time_log = startup; } int preferences_get_real_time_at_startup(preferences_t * prefs) { return prefs->real_time_log; } void preferences_set_real_time_interval(preferences_t * prefs, int interval) { if (interval <= 0) { prefs->real_time_interval = 0; } else { prefs->real_time_interval = interval; } } int preferences_get_real_time_interval(preferences_t * prefs) { return prefs->real_time_interval; } /** * Add an entry to a vector, discarding the oldest entry if the vector * size is too large. */ static int prefs_add_recent_vector(apol_vector_t * v, const char *entry) { size_t i; char *s; if (apol_vector_get_index(v, (void *)entry, apol_str_strcmp, NULL, &i) == 0) { return 0; } if ((s = strdup(entry)) == NULL || apol_vector_append(v, s) < 0) { int error = errno; free(s); errno = error; return -1; } if (apol_vector_get_size(v) >= MAX_RECENT_ENTRIES) { s = apol_vector_get_element(v, 0); free(s); return apol_vector_remove(v, 0); } return 0; } int preferences_add_recent_log(preferences_t * prefs, const char *log) { return prefs_add_recent_vector(prefs->recent_log_files, log); } apol_vector_t *preferences_get_recent_logs(preferences_t * prefs) { return prefs->recent_log_files; } static int preferences_policy_path_compare(const void *a, const void *b, void *data __attribute__ ((unused))) { return apol_policy_path_compare((const apol_policy_path_t *)a, (const apol_policy_path_t *)b); } int preferences_add_recent_policy(preferences_t * prefs, const apol_policy_path_t * policy) { size_t i; apol_policy_path_t *p = NULL; if (apol_vector_get_index(prefs->recent_policy_files, policy, preferences_policy_path_compare, NULL, &i) == 0) { return 0; } if ((p = apol_policy_path_create_from_policy_path(policy)) == NULL || apol_vector_append(prefs->recent_policy_files, p) < 0) { int error = errno; apol_policy_path_destroy(&p); errno = error; return -1; } if (apol_vector_get_size(prefs->recent_policy_files) >= MAX_RECENT_ENTRIES) { p = apol_vector_get_element(prefs->recent_policy_files, 0); apol_policy_path_destroy(&p); return apol_vector_remove(prefs->recent_policy_files, 0); } return 0; } apol_vector_t *preferences_get_recent_policies(preferences_t * prefs) { return prefs->recent_policy_files; }