diff options
Diffstat (limited to 'libseaudit/src/model.c')
-rw-r--r-- | libseaudit/src/model.c | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/libseaudit/src/model.c b/libseaudit/src/model.c new file mode 100644 index 0000000..1bc4a23 --- /dev/null +++ b/libseaudit/src/model.c @@ -0,0 +1,808 @@ +/** + * @file + * Implementation of seaudit_model_t. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "seaudit_internal.h" + +#include <apol/bst.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <libxml/uri.h> + +#define DEFAULT_MODEL_NAME "Untitled" + +struct seaudit_model +{ + char *name; + /** vector of seaudit_log_t pointers; this model will get + * messages from these logs */ + apol_vector_t *logs; + /** vector of seaudit_message_t pointers; these point into + * messages from the watched logs (only valid if dirty == 0) */ + apol_vector_t *messages; + /** vector of char * pointers; these point into malformed + * messages from the watched logs (only valid if dirty == 0) */ + apol_vector_t *malformed_messages; + /** list of messages to hide */ + apol_bst_t *hidden_messages; + /** vector of seaudit_filter_t */ + apol_vector_t *filters; + /** if more than one filter is being applied, then accept + * messages if any match or if all match */ + seaudit_filter_match_e match; + /** if a filter is being applied, then either show/hide + * messages selected by filter */ + seaudit_filter_visible_e visible; + /** vector of seaudit_sort_t, order from highest priority to lowest */ + apol_vector_t *sorts; + /** number of allow messages in the model (only valid if dirty == 0) */ + size_t num_allows; + /** number of deny messages in the model (only valid if dirty == 0) */ + size_t num_denies; + /** number of boolean changes in the model (only valid if dirty == 0) */ + size_t num_bools; + /** number of policy loads in the model (only valid if dirty == 0) */ + size_t num_loads; + /** non-zero whenever this model needs to be recalculated */ + int dirty; +}; + +/** + * Apply all of the model's filters to the message. + * + * @param model Model containing filters to apply. + * @param m Message to check. + * + * @return Non-zero if the message is accepted by the filters, 0 if not. + */ +static int model_filter_message(seaudit_model_t * model, const seaudit_message_t * m) +{ + size_t i; + int compval, filters_passed = 0; + if (apol_vector_get_size(model->filters) == 0) { + return 1; + } + for (i = 0; i < apol_vector_get_size(model->filters); i++) { + seaudit_filter_t *f = apol_vector_get_element(model->filters, i); + compval = filter_is_accepted(f, m); + if (compval) { + if (model->match == SEAUDIT_FILTER_MATCH_ANY) { + return 1; + } + filters_passed++; + } else { + if (model->match == SEAUDIT_FILTER_MATCH_ALL) { + return 0; + } + } + } + if (model->match == SEAUDIT_FILTER_MATCH_ANY) { + /* if got here, then no filters were met */ + return 0; + } + /* if got here, then all criteria were met */ + if (filters_passed) { + return 1; + } + return 0; +} + +/** + * Callback for sorting the model's messages vector. + * + * @param a First message to compare. + * @param b Second message to compare. + * @param data Pointer to the model being sorted. + * + * @return 0 if the messages are equivalent, < 0 if a is first, > 0 if + * b is first. + */ +static int message_comp(const void *a, const void *b, void *data) +{ + const seaudit_message_t *m1 = a; + const seaudit_message_t *m2 = b; + seaudit_model_t *model = data; + size_t i; + seaudit_sort_t *s; + int compval, s1, s2; + for (i = 0; i < apol_vector_get_size(model->sorts); i++) { + s = apol_vector_get_element(model->sorts, i); + s1 = sort_is_supported(s, m1); + s2 = sort_is_supported(s, m2); + if (!s1 && !s2) { + continue; + } + if (!s2) { + return -1; + } + if (!s1) { + return 1; + } + if ((compval = sort_comp(s, m1, m2)) != 0) { + return compval; + } + } + return 0; +} + +/** + * Sort the model's messages. Create two temporary vectors. The + * first holds messages that are sortable, according to the list of + * sort objects. Sort them in their priority order. The second + * vector holds messages that are not sortable; append those messages + * to the end of the first (now sorted) vector. + * + * @param log Error handling log. + * @param model Model to sort. + * + * @return 0 on successful sort, < 0 on error. + */ +static int model_sort(const seaudit_log_t * log, seaudit_model_t * model) +{ + size_t i, j, num_messages = apol_vector_get_size(model->messages); + apol_vector_t *sup = NULL, *unsup = NULL; + seaudit_message_t *m; + seaudit_sort_t *s; + int supported = 0, retval = -1, error = 0; + if (apol_vector_get_size(model->sorts) == 0) { + retval = 0; + goto cleanup; + } + + if ((sup = apol_vector_create_with_capacity(num_messages, NULL)) == NULL || + (unsup = apol_vector_create_with_capacity(num_messages, NULL)) == NULL) { + error = errno; + ERR(log, "%s", strerror(error)); + goto cleanup; + } + for (i = 0; i < num_messages; i++) { + m = apol_vector_get_element(model->messages, i); + supported = 0; + for (j = 0; j < apol_vector_get_size(model->sorts); j++) { + s = apol_vector_get_element(model->sorts, j); + if ((supported = sort_is_supported(s, m)) != 0) { + break; + } + } + if ((supported && apol_vector_append(sup, m) < 0) || (!supported && apol_vector_append(unsup, m) < 0)) { + error = errno; + ERR(log, "%s", strerror(error)); + goto cleanup; + } + } + apol_vector_sort(sup, message_comp, model); + if (apol_vector_cat(sup, unsup) < 0) { + error = errno; + ERR(log, "%s", strerror(error)); + goto cleanup; + } + apol_vector_destroy(&model->messages); + model->messages = sup; + sup = NULL; + retval = 0; + cleanup: + apol_vector_destroy(&sup); + apol_vector_destroy(&unsup); + if (retval != 0) { + errno = error; + } + return retval; +} + +/** + * Iterate through the model's messages and recalculate the number of + * each type of message is stored within. + * + * @param model Model to recalculate. + */ +static void model_recalc_stats(seaudit_model_t * model) +{ + size_t i; + seaudit_message_t *msg; + seaudit_message_type_e type; + void *v; + seaudit_avc_message_t *avc; + model->num_allows = model->num_denies = model->num_bools = model->num_loads = 0; + for (i = 0; i < apol_vector_get_size(model->messages); i++) { + msg = apol_vector_get_element(model->messages, i); + v = seaudit_message_get_data(msg, &type); + if (type == SEAUDIT_MESSAGE_TYPE_AVC) { + avc = (seaudit_avc_message_t *) v; + if (avc->msg == SEAUDIT_AVC_DENIED) { + model->num_denies++; + } else if (avc->msg == SEAUDIT_AVC_GRANTED) { + model->num_allows++; + } + } else if (type == SEAUDIT_MESSAGE_TYPE_BOOL) { + model->num_bools++; + } else if (type == SEAUDIT_MESSAGE_TYPE_LOAD) { + model->num_loads++; + } + } +} + +/** + * Recalculate all of the messages associated with a particular model, + * based upon that model's criteria. If the model is marked as not + * dirty then do nothing and return success. + * + * @param log Log to which report error messages. + * @param model Model whose messages list to refresh. + * + * @return 0 on success, < 0 on error. + */ +static int model_refresh(const seaudit_log_t * log, seaudit_model_t * model) +{ + size_t i, j; + seaudit_log_t *l; + const apol_vector_t *v; + seaudit_message_t *message; + void *result; + int error, filter_match; + + if (!model->dirty) { + return 0; + } + apol_vector_destroy(&model->messages); + apol_vector_destroy(&model->malformed_messages); + if ((model->messages = apol_vector_create(NULL)) == NULL || (model->malformed_messages = apol_vector_create(NULL)) == NULL) { + error = errno; + ERR(log, "%s", strerror(error)); + errno = error; + return -1; + } + for (i = 0; i < apol_vector_get_size(model->logs); i++) { + l = apol_vector_get_element(model->logs, i); + v = log_get_messages(l); + for (j = 0; j < apol_vector_get_size(v); j++) { + message = apol_vector_get_element(v, j); + if (apol_bst_get_element(model->hidden_messages, message, NULL, &result) == 0) { + continue; + } + filter_match = model_filter_message(model, message); + if (((filter_match && model->visible == SEAUDIT_FILTER_VISIBLE_SHOW) || + (!filter_match && model->visible == SEAUDIT_FILTER_VISIBLE_HIDE)) && + apol_vector_append(model->messages, message) < 0) { + error = errno; + ERR(log, "%s", strerror(error)); + errno = error; + return -1; + } + } + v = log_get_malformed_messages(l); + if (apol_vector_cat(model->malformed_messages, v) < 0) { + error = errno; + ERR(log, "%s", strerror(error)); + errno = error; + return -1; + } + } + if (model_sort(log, model) < 0) { + return -1; + } + model_recalc_stats(model); + model->dirty = 0; + return 0; +} + +/** + * Callback invoked when free()ing a vector of filters. + * + * @param v Filter object to free. + */ +static void filter_free(void *v) +{ + seaudit_filter_t *f = v; + seaudit_filter_destroy(&f); +} + +/** + * Callback invoked when free()ing a vector of sort objects. + * + * @param v Sort object to free. + */ +static void sort_free(void *v) +{ + seaudit_sort_t *sort = v; + seaudit_sort_destroy(&sort); +} + +seaudit_model_t *seaudit_model_create(const char *name, seaudit_log_t * log) +{ + seaudit_model_t *m = NULL; + int error; + if ((m = calloc(1, sizeof(*m))) == NULL) { + error = errno; + ERR(log, "%s", strerror(error)); + errno = error; + return NULL; + } + if (name == NULL) { + name = DEFAULT_MODEL_NAME; + } + if ((m->name = strdup(name)) == NULL || + (m->logs = apol_vector_create_with_capacity(1, NULL)) == NULL || + (m->hidden_messages = apol_bst_create(NULL, NULL)) == NULL || + (m->filters = apol_vector_create_with_capacity(1, filter_free)) == NULL || + (m->sorts = apol_vector_create_with_capacity(1, sort_free)) == NULL) { + error = errno; + seaudit_model_destroy(&m); + ERR(log, "%s", strerror(error)); + errno = error; + return NULL; + } + if (log != NULL) { + if (apol_vector_append(m->logs, log) < 0 || log_append_model(log, m)) { + error = errno; + seaudit_model_destroy(&m); + ERR(log, "%s", strerror(error)); + errno = error; + return NULL; + } + } + m->dirty = 1; + return m; +} + +static void *model_filter_dup(const void *elem, void *data) +{ + const seaudit_filter_t *filter = elem; + seaudit_model_t *model = data; + seaudit_filter_t *f; + if ((f = seaudit_filter_create_from_filter(filter)) == NULL) { + return NULL; + } + filter_set_model(f, model); + return f; +} + +static void *model_sort_dup(const void *elem, void *data __attribute__ ((unused))) +{ + const seaudit_sort_t *sort = elem; + seaudit_model_t *model = data; + seaudit_sort_t *s; + if ((s = sort_create_from_sort(sort)) == NULL) { + return NULL; + } + if (seaudit_model_append_sort(model, s) < 0) { + seaudit_sort_destroy(&s); + return NULL; + } + return s; +} + +seaudit_model_t *seaudit_model_create_from_model(const seaudit_model_t * model) +{ + seaudit_model_t *m = NULL; + int error = 0; + size_t i; + const char *name; + + if (model == NULL) { + error = EINVAL; + goto cleanup; + } + if ((m = calloc(1, sizeof(*m))) == NULL) { + error = errno; + goto cleanup; + } + if ((name = model->name) == NULL) { + name = "Untitled"; + } + if ((m->name = strdup(name)) == NULL) { + error = errno; + goto cleanup; + } + m->dirty = 1; + if ((m->logs = apol_vector_create_from_vector(model->logs, NULL, NULL, NULL)) == NULL) { + error = errno; + goto cleanup; + } + if ((m->filters = apol_vector_create_from_vector(model->filters, model_filter_dup, (void *)m, filter_free)) == NULL) { + error = errno; + goto cleanup; + } + if ((m->sorts = apol_vector_create_from_vector(model->sorts, model_sort_dup, (void *)m, sort_free)) == NULL) { + error = errno; + goto cleanup; + } + m->match = model->match; + m->visible = model->visible; + /* link this new model to the old model's logs */ + for (i = 0; i < apol_vector_get_size(m->logs); i++) { + seaudit_log_t *log = apol_vector_get_element(m->logs, i); + if (log_append_model(log, m) < 0) { + error = errno; + goto cleanup; + } + } + cleanup: + if (error != 0) { + seaudit_model_destroy(&m); + errno = error; + return NULL; + } + return m; +} + +seaudit_model_t *seaudit_model_create_from_file(const char *filename) +{ + struct filter_parse_state state; + int retval, error; + seaudit_model_t *m; + memset(&state, 0, sizeof(state)); + if ((state.filters = apol_vector_create(filter_free)) == NULL) { + return NULL; + } + retval = filter_parse_xml(&state, filename); + if (retval < 0) { + error = errno; + free(state.view_name); + apol_vector_destroy(&state.filters); + errno = errno; + return NULL; + } + if ((m = seaudit_model_create(state.view_name, NULL)) == NULL) { + error = errno; + free(state.view_name); + apol_vector_destroy(&state.filters); + errno = error; + return NULL; + } + free(state.view_name); + apol_vector_destroy(&m->filters); + m->filters = state.filters; + state.filters = NULL; + seaudit_model_set_filter_match(m, state.view_match); + seaudit_model_set_filter_visible(m, state.view_visible); + return m; +} + +void seaudit_model_destroy(seaudit_model_t ** model) +{ + size_t i; + if (model == NULL || *model == NULL) { + return; + } + for (i = 0; i < apol_vector_get_size((*model)->logs); i++) { + seaudit_log_t *l = apol_vector_get_element((*model)->logs, i); + log_remove_model(l, *model); + } + free((*model)->name); + apol_vector_destroy(&(*model)->logs); + apol_vector_destroy(&(*model)->filters); + apol_vector_destroy(&(*model)->sorts); + apol_vector_destroy(&(*model)->messages); + apol_vector_destroy(&(*model)->malformed_messages); + apol_bst_destroy(&(*model)->hidden_messages); + free(*model); + *model = NULL; +} + +int seaudit_model_save_to_file(const seaudit_model_t * model, const char *filename) +{ + FILE *file; + const char *XML_VER = "<?xml version=\"1.0\"?>\n"; + seaudit_filter_t *filter; + size_t i; + + if (model == NULL || filename == NULL) { + errno = EINVAL; + return -1; + } + if ((file = fopen(filename, "w")) == NULL) { + return -1; + } + fprintf(file, XML_VER); + fprintf(file, "<view xmlns=\"http://oss.tresys.com/projects/setools/seaudit-%s/\" name=\"%s\" match=\"%s\" show=\"%s\">\n", + FILTER_FILE_FORMAT_VERSION, model->name, + model->match == SEAUDIT_FILTER_MATCH_ALL ? "all" : "any", + model->visible == SEAUDIT_FILTER_VISIBLE_SHOW ? "true" : "false"); + for (i = 0; i < apol_vector_get_size(model->filters); i++) { + filter = apol_vector_get_element(model->filters, i); + filter_append_to_file(filter, file, 1); + } + fprintf(file, "</view>\n"); + fclose(file); + return 0; +} + +const char *seaudit_model_get_name(const seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return NULL; + } + return model->name; +} + +int seaudit_model_set_name(seaudit_model_t * model, const char *name) +{ + char *s; + if (model == NULL) { + errno = EINVAL; + return -1; + } + if (name == NULL) { + name = DEFAULT_MODEL_NAME; + } + if ((s = strdup(name)) == NULL) { + return -1; + } + free(model->name); + model->name = s; + return 0; +} + +int seaudit_model_append_log(seaudit_model_t * model, seaudit_log_t * log) +{ + if (model == NULL || log == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + if (apol_vector_append(model->logs, log) < 0 || log_append_model(log, model) < 0) { + int error = errno; + ERR(log, "%s", strerror(error)); + errno = error; + return -1; + } + model->dirty = 1; + return 0; +} + +int seaudit_model_append_filter(seaudit_model_t * model, seaudit_filter_t * filter) +{ + if (model == NULL || filter == NULL) { + errno = EINVAL; + return -1; + } + if (apol_vector_append(model->filters, filter) < 0) { + return -1; + } + filter_set_model(filter, model); + model->dirty = 1; + return 0; +} + +const apol_vector_t *seaudit_model_get_filters(const seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return NULL; + } + return model->filters; +} + +int seaudit_model_remove_filter(seaudit_model_t * model, seaudit_filter_t * filter) +{ + size_t i; + if (model == NULL || filter == NULL) { + errno = EINVAL; + return -1; + } + if (apol_vector_get_index(model->filters, filter, NULL, NULL, &i) < 0) { + errno = EINVAL; + return -1; + } + seaudit_filter_destroy(&filter); + apol_vector_remove(model->filters, i); + model->dirty = 1; + return 0; +} + +int seaudit_model_set_filter_match(seaudit_model_t * model, seaudit_filter_match_e match) +{ + if (model == NULL) { + errno = EINVAL; + return -1; + } + model->match = match; + model->dirty = 1; + return 0; +} + +seaudit_filter_match_e seaudit_model_get_filter_match(const seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return SEAUDIT_FILTER_MATCH_ALL; + } + return model->match; +} + +int seaudit_model_set_filter_visible(seaudit_model_t * model, seaudit_filter_visible_e visible) +{ + if (model == NULL) { + errno = EINVAL; + return -1; + } + model->visible = visible; + model->dirty = 1; + return 0; +} + +seaudit_filter_visible_e seaudit_model_get_filter_visible(const seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return SEAUDIT_FILTER_VISIBLE_SHOW; + } + return model->visible; +} + +int seaudit_model_append_sort(seaudit_model_t * model, seaudit_sort_t * sort) +{ + if (model == NULL || sort == NULL) { + errno = EINVAL; + return -1; + } + if (apol_vector_append(model->sorts, sort) < 0) { + return -1; + } + model->dirty = 1; + return 0; +} + +int seaudit_model_clear_sorts(seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return -1; + } + apol_vector_destroy(&model->sorts); + if ((model->sorts = apol_vector_create_with_capacity(1, sort_free)) == NULL) { + return -1; + } + model->dirty = 1; + return 0; +} + +int seaudit_model_is_changed(const seaudit_model_t * model) +{ + if (model == NULL) { + errno = EINVAL; + return -1; + } + return model->dirty; +} + +apol_vector_t *seaudit_model_get_messages(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (model_refresh(log, model) < 0) { + return NULL; + } + return apol_vector_create_from_vector(model->messages, NULL, NULL, NULL); +} + +apol_vector_t *seaudit_model_get_malformed_messages(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return NULL; + } + if (model_refresh(log, model) < 0) { + return NULL; + } + return apol_vector_create_from_vector(model->malformed_messages, NULL, NULL, NULL); +} + +void seaudit_model_hide_message(seaudit_model_t * model, const seaudit_message_t * message) +{ + if (model == NULL) { + errno = EINVAL; + return; + } + if (message == NULL) { + return; + } + if (apol_bst_insert(model->hidden_messages, (seaudit_message_t *) message, NULL) == 0) { + model->dirty = 1; + } +} + +size_t seaudit_model_get_num_allows(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return 0; + } + if (model_refresh(log, model) < 0) { + return 0; + } + return model->num_allows; +} + +size_t seaudit_model_get_num_denies(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return 0; + } + if (model_refresh(log, model) < 0) { + return 0; + } + return model->num_denies; +} + +size_t seaudit_model_get_num_bools(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return 0; + } + if (model_refresh(log, model) < 0) { + return 0; + } + return model->num_bools; +} + +size_t seaudit_model_get_num_loads(const seaudit_log_t * log, seaudit_model_t * model) +{ + if (log == NULL || model == NULL) { + ERR(log, "%s", strerror(EINVAL)); + errno = EINVAL; + return 0; + } + if (model_refresh(log, model) < 0) { + return 0; + } + return model->num_loads; +} + +/******************** protected functions below ********************/ + +void model_remove_log(seaudit_model_t * model, seaudit_log_t * log) +{ + size_t i; + if (apol_vector_get_index(model->logs, log, NULL, NULL, &i) == 0) { + apol_vector_remove(model->logs, i); + model->dirty = 1; + } +} + +void model_notify_log_changed(seaudit_model_t * model, seaudit_log_t * log) +{ + size_t i; + if (apol_vector_get_index(model->logs, log, NULL, NULL, &i) == 0) { + model->dirty = 1; + } +} + +void model_notify_filter_changed(seaudit_model_t * model, seaudit_filter_t * filter) +{ + size_t i; + if (apol_vector_get_index(model->filters, filter, NULL, NULL, &i) == 0) { + model->dirty = 1; + } +} |