/** * @file * Implementation of seaudit report generator. * * @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 #include #include #include #include #include #include #include #include #define CONFIG_FILE "seaudit-report.conf" #define STYLESHEET_FILE "seaudit-report.css" #define LINE_MAX 1024 struct seaudit_report { /** output format for the report */ seaudit_report_format_e format; /** path to configuration file, or NULL to use system configuration */ char *config; /** path to HTML stylesheet, or NULL to use system stylesheet */ char *stylesheet; /** if non-zero, then use a stylesheet when generating HTML reports */ int use_stylesheet; /** if non-zero, then print malformed messages */ int malformed; /** model from which messages will be obtained */ seaudit_model_t *model; }; static const char *seaudit_report_node_names[] = { "seaudit-report", "standard-section", "custom-section", "view", NULL }; static const char *seaudit_standard_section_names[] = { "PolicyLoads", "EnforcementToggles", "PolicyBooleans", "Statistics", "AllowListing", "DenyListing", NULL }; seaudit_report_t *seaudit_report_create(seaudit_model_t * model) { seaudit_report_t *r = calloc(1, sizeof(*r)); if (r == NULL) { return NULL; } r->model = model; return r; } void seaudit_report_destroy(seaudit_report_t ** report) { if (report == NULL || *report == NULL) { return; } free((*report)->config); free((*report)->stylesheet); free(*report); *report = NULL; } int seaudit_report_set_format(const seaudit_log_t * log, seaudit_report_t * report, seaudit_report_format_e format) { if (report == NULL) { ERR(log, "%s", strerror(EINVAL)); errno = EINVAL; return -1; } report->format = format; return 0; } /** * Set the report's configuration file to the default system file. */ static int report_set_default_configuration(const seaudit_log_t * log, seaudit_report_t * report) { char *config_dir = apol_file_find(CONFIG_FILE); int error; if (config_dir == NULL) { error = errno; ERR(log, "%s", "Could not find default configuration file."); errno = error; return -1; } if (asprintf(&report->config, "%s/%s", config_dir, CONFIG_FILE) < 0) { error = errno; report->config = NULL; free(config_dir); ERR(log, "%s", strerror(error)); errno = error; return -1; } free(config_dir); /* check if can read the file */ if (access(report->config, R_OK) != 0) { error = errno; ERR(log, "Could not read default config file %s.", report->config); errno = error; return -1; } return 0; } int seaudit_report_set_configuration(const seaudit_log_t * log, seaudit_report_t * report, const char *file) { if (report == NULL) { ERR(log, "%s", strerror(EINVAL)); errno = EINVAL; return -1; } free(report->config); report->config = NULL; if (file == NULL) { return report_set_default_configuration(log, report); } else { if ((report->config = strdup(file)) == NULL) { int error = errno; ERR(log, "%s", strerror(error)); errno = error; return -1; } return 0; } } /** * Set the report's stylesheet to the default system stylesheet. */ static int report_set_default_stylesheet(const seaudit_log_t * log, seaudit_report_t * report) { char *dir = apol_file_find(STYLESHEET_FILE); int error; if (dir == NULL) { error = errno; ERR(log, "%s", "Could not find default stylesheet."); errno = error; return -1; } if (asprintf(&report->stylesheet, "%s/%s", dir, STYLESHEET_FILE) < 0) { error = errno; report->stylesheet = NULL; free(dir); ERR(log, "%s", strerror(error)); errno = error; return -1; } free(dir); return 0; } int seaudit_report_set_stylesheet(const seaudit_log_t * log, seaudit_report_t * report, const char *file, const int use_stylesheet) { if (report == NULL) { ERR(log, "%s", strerror(EINVAL)); errno = EINVAL; return -1; } free(report->stylesheet); report->stylesheet = NULL; report->use_stylesheet = use_stylesheet; if (file == NULL) { return report_set_default_stylesheet(log, report); } else { if ((report->stylesheet = strdup(file)) == NULL) { return -1; int error = errno; ERR(log, "%s", strerror(error)); errno = error; return -1; } return 0; } } int seaudit_report_set_malformed(const seaudit_log_t * log, seaudit_report_t * report, const int do_malformed) { if (report == NULL) { ERR(log, "%s", strerror(EINVAL)); errno = EINVAL; return -1; } report->malformed = do_malformed; return 0; } /** * Insert the contents of the stylesheet into the output file. If it * is not readable then generate a warning. This is not an error * because the stylesheet is not strictly necessary. */ static int report_import_html_stylesheet(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { char line[LINE_MAX], *line_ptr = NULL; FILE *fp; if (report->use_stylesheet) { fp = fopen(report->stylesheet, "r"); if (fp == NULL) { WARN(log, "Cannot open stylesheet file %s.", report->stylesheet); return 1; } fprintf(outfile, "\n"); fclose(fp); free(line_ptr); } return 0; } static int report_print_header(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { time_t ltime; time(<ime); if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "\n"); fprintf(outfile, "\n\n"); if (report_import_html_stylesheet(log, report, outfile) < 0) { return -1; } fprintf(outfile, "seaudit-report\n\n"); fprintf(outfile, "\n"); fprintf(outfile, "# Report generated by seaudit-report on %s
\n", ctime(<ime)); } else { fprintf(outfile, "# Begin\n\n"); fprintf(outfile, "# Report generated by seaudit-report on %s\n", ctime(<ime)); } return 0; } static int report_print_footer(const seaudit_report_t * report, FILE * outfile) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "\n\n"); } else { fprintf(outfile, "# End\n"); } return 0; } static int report_is_valid_node_name(const char *name) { size_t i; for (i = 0; seaudit_report_node_names[i] != NULL; i++) if (strcmp(seaudit_report_node_names[i], name) == 0) return 1; return 0; } static int report_is_valid_section_name(const char *name) { size_t i; for (i = 0; seaudit_standard_section_names[i] != NULL; i++) if (strcmp(seaudit_standard_section_names[i], name) == 0) return 1; return 0; } static int report_parse_seaudit_report(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)), xmlTextReaderPtr reader, xmlChar ** id_value __attribute__ ((unused)), xmlChar ** title_value) { int rt, error; xmlChar *name = NULL; if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) { /* Parse attributes */ rt = xmlTextReaderMoveToNextAttribute(reader); while (rt > 0) { name = xmlTextReaderName(reader); if (name == NULL) { error = errno; ERR(log, "%s", "Attribute name unavailable."); errno = error; return -1; } if (strcmp((char *)name, "title") == 0) { *title_value = xmlTextReaderValue(reader); } xmlFree(name); rt = xmlTextReaderMoveToNextAttribute(reader); } if (rt < 0) { error = errno; ERR(log, "%s", "Error parsing attribute for seaudit-report node."); errno = error; return -1; } } return 0; } static int report_parse_standard_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)), xmlTextReaderPtr reader, xmlChar ** id_value, xmlChar ** title_value) { int rt, error; xmlChar *name = NULL; if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) { /* Parse attributes */ rt = xmlTextReaderMoveToNextAttribute(reader); while (rt > 0) { name = xmlTextReaderName(reader); if (name == NULL) { error = errno; ERR(log, "%s", "Attribute name unavailable."); errno = error; return -1; } if (strcmp((char *)name, "id") == 0) { *id_value = xmlTextReaderValue(reader); } else if (strcmp((char *)name, "title") == 0) { *title_value = xmlTextReaderValue(reader); } xmlFree(name); rt = xmlTextReaderMoveToNextAttribute(reader); } if (rt < 0) { error = errno; ERR(log, "%s", "Error parsing attribute for standard-section node."); errno = error; return -1; } } return 0; } static int report_parse_custom_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)), xmlTextReaderPtr reader, xmlChar ** title_value) { int rt, error; xmlChar *name = NULL; if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) { /* Parse attributes */ rt = xmlTextReaderMoveToNextAttribute(reader); while (rt > 0) { name = xmlTextReaderName(reader); if (name == NULL) { error = errno; ERR(log, "%s", "Attribute name unavailable."); errno = error; return -1; } if (strcmp((char *)name, "title") == 0) { *title_value = xmlTextReaderValue(reader); } xmlFree(name); rt = xmlTextReaderMoveToNextAttribute(reader); } if (rt < 0) { error = errno; ERR(log, "%s", "Error parsing attribute for custom-section node."); errno = error; return -1; } } return 0; } /** * Allocate and return a filter for setenforce toggles. (Actually, it * can't filter on permissions.) */ static seaudit_filter_t *report_enforce_toggle_filter_create(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused))) { seaudit_filter_t *filter = NULL; apol_vector_t *type_v = NULL, *class_v; int retval = -1, error = 0; char *tgt_type = "security_t"; char *obj_class = "security"; if ((filter = seaudit_filter_create(NULL)) == NULL) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } if ((type_v = apol_vector_create_with_capacity(1, NULL)) == NULL || apol_vector_append(type_v, tgt_type) < 0 || seaudit_filter_set_target_type(filter, type_v) < 0) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } if ((class_v = apol_vector_create_with_capacity(1, NULL)) == NULL || apol_vector_append(class_v, obj_class) < 0 || seaudit_filter_set_target_class(filter, class_v) < 0) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } retval = 0; cleanup: apol_vector_destroy(&type_v); apol_vector_destroy(&class_v); if (retval != 0) { seaudit_filter_destroy(&filter); errno = error; return NULL; } return filter; } static int report_print_enforce_toggles(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { seaudit_filter_t *filter = NULL; seaudit_model_t *dup_model = NULL; size_t i, j, num_setenforce = 0; apol_vector_t *v = NULL; seaudit_message_t *msg; seaudit_avc_message_t *avc; seaudit_message_type_e type; char *s; char *perm = "setenforce"; int retval = -1, error = 0; if ((filter = report_enforce_toggle_filter_create(log, report)) == NULL) { error = errno; goto cleanup; } if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL || seaudit_model_append_filter(dup_model, filter) < 0) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } filter = NULL; /* Loop through and get the number of avc allow messages with * the setenforce permission. */ v = seaudit_model_get_messages(log, dup_model); for (i = 0; i < apol_vector_get_size(v); i++) { msg = apol_vector_get_element(v, i); avc = seaudit_message_get_data(msg, &type); if (type != SEAUDIT_MESSAGE_TYPE_AVC || avc->msg == SEAUDIT_AVC_DENIED) continue; if (apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) == 0) { /* Increment number of setenforce messages */ num_setenforce++; } } /* Since we cannot filter by setenforce permission within the * view, we do so manually within the following for loop. */ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "Number of messages: %zd
\n
\n", num_setenforce); else fprintf(outfile, "Number of messages: %zd\n\n", num_setenforce); for (i = 0; i < apol_vector_get_size(v); i++) { msg = apol_vector_get_element(v, i); avc = seaudit_message_get_data(msg, &type); if (type != SEAUDIT_MESSAGE_TYPE_AVC || avc->msg == SEAUDIT_AVC_DENIED || apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) < 0) { continue; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { s = seaudit_message_to_string_html(msg); } else { s = seaudit_message_to_string(msg); } if (s == NULL) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } fputs(s, outfile); fputc('\n', outfile); free(s); } retval = 0; cleanup: apol_vector_destroy(&v); seaudit_filter_destroy(&filter); seaudit_model_destroy(&dup_model); if (error != 0) { errno = error; } return retval; } static int report_print_policy_booleans(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { size_t i, num = seaudit_model_get_num_bools(log, report->model); apol_vector_t *v = seaudit_model_get_messages(log, report->model); seaudit_message_t *m; seaudit_message_type_e type; char *s; if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "Number of messages: %zd
\n
\n", num); else fprintf(outfile, "Number of messages: %zd\n\n", num); for (i = 0; i < apol_vector_get_size(v); i++) { m = apol_vector_get_element(v, i); seaudit_message_get_data(m, &type); if (type == SEAUDIT_MESSAGE_TYPE_BOOL) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { s = seaudit_message_to_string_html(m); } else { s = seaudit_message_to_string(m); } if (s == NULL) { int error = errno; apol_vector_destroy(&v); ERR(log, "%s", strerror(error)); errno = error; return -1; } fputs(s, outfile); fputc('\n', outfile); free(s); } } apol_vector_destroy(&v); return 0; } static int report_print_policy_loads(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { size_t i, num = seaudit_model_get_num_loads(log, report->model); apol_vector_t *v = seaudit_model_get_messages(log, report->model); seaudit_message_t *m; seaudit_message_type_e type; char *s; if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "Number of messages: %zd
\n
\n", num); else fprintf(outfile, "Number of messages: %zd\n\n", num); for (i = 0; i < apol_vector_get_size(v); i++) { m = apol_vector_get_element(v, i); seaudit_message_get_data(m, &type); if (type == SEAUDIT_MESSAGE_TYPE_LOAD) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { s = seaudit_message_to_string_html(m); } else { s = seaudit_message_to_string(m); } if (s == NULL) { int error = errno; apol_vector_destroy(&v); ERR(log, "%s", strerror(error)); errno = error; return -1; } fputs(s, outfile); fputc('\n', outfile); free(s); } } apol_vector_destroy(&v); return 0; } static int report_print_avc_listing(const seaudit_log_t * log, const seaudit_report_t * report, seaudit_avc_message_type_e avc_type, FILE * outfile) { size_t i, num; apol_vector_t *v = seaudit_model_get_messages(log, report->model); seaudit_message_t *m; seaudit_avc_message_t *avc; seaudit_message_type_e type; char *s; if (avc_type == SEAUDIT_AVC_GRANTED) { num = seaudit_model_get_num_allows(log, report->model); } else { num = seaudit_model_get_num_denies(log, report->model); } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "Number of messages: %zd
\n
\n", num); else fprintf(outfile, "Number of messages: %zd\n\n", num); for (i = 0; i < apol_vector_get_size(v); i++) { m = apol_vector_get_element(v, i); avc = seaudit_message_get_data(m, &type); if (type == SEAUDIT_MESSAGE_TYPE_AVC && avc->msg == avc_type) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { s = seaudit_message_to_string_html(m); } else { s = seaudit_message_to_string(m); } if (s == NULL) { int error = errno; apol_vector_destroy(&v); ERR(log, "%s", strerror(error)); errno = error; return -1; } fputs(s, outfile); fputc('\n', outfile); free(s); } } apol_vector_destroy(&v); return 0; } static int report_print_stats(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { apol_vector_t *v = seaudit_model_get_messages(log, report->model); size_t num_messages = apol_vector_get_size(v); apol_vector_destroy(&v); if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "Number of total messages: %zd
\n", num_messages); fprintf(outfile, "Number of policy load messages: %zd
\n", seaudit_model_get_num_loads(log, report->model)); fprintf(outfile, "Number of policy boolean messages: %zd
\n", seaudit_model_get_num_bools(log, report->model)); fprintf(outfile, "Number of allow messages: %zd
\n", seaudit_model_get_num_allows(log, report->model)); fprintf(outfile, "Number of denied messages: %zd
\n", seaudit_model_get_num_denies(log, report->model)); } else { fprintf(outfile, "Number of total messages: %zd\n", num_messages); fprintf(outfile, "Number of policy load messages: %zd\n", seaudit_model_get_num_loads(log, report->model)); fprintf(outfile, "Number of policy boolean messages: %zd\n", seaudit_model_get_num_bools(log, report->model)); fprintf(outfile, "Number of allow messages: %zd\n", seaudit_model_get_num_allows(log, report->model)); fprintf(outfile, "Number of denied messages: %zd\n", seaudit_model_get_num_denies(log, report->model)); } return 0; } static int report_print_standard_section(const seaudit_log_t * log, const seaudit_report_t * report, xmlChar * id, xmlChar * title, FILE * outfile) { size_t sz, len, i; int rt = 0; if (!report_is_valid_section_name((char *)id)) { ERR(log, "%s", "Invalid standard section ID."); errno = EINVAL; return -1; } sz = strlen((char *)id); if (title != NULL) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "

%s

\n", title); } else { fprintf(outfile, "%s\n", title); len = strlen((char *)title); for (i = 0; i < len; i++) { fprintf(outfile, "-"); } fprintf(outfile, "\n"); } } if (strncasecmp((char *)id, "PolicyLoads", sz) == 0) { rt = report_print_policy_loads(log, report, outfile); } else if (strncasecmp((char *)id, "EnforcementToggles", sz) == 0) { rt = report_print_enforce_toggles(log, report, outfile); } else if (strncasecmp((char *)id, "PolicyBooleans", sz) == 0) { rt = report_print_policy_booleans(log, report, outfile); } else if (strncasecmp((char *)id, "AllowListing", sz) == 0) { rt = report_print_avc_listing(log, report, SEAUDIT_AVC_GRANTED, outfile); } else if (strncasecmp((char *)id, "DenyListing", sz) == 0) { rt = report_print_avc_listing(log, report, SEAUDIT_AVC_DENIED, outfile); } else if (strncasecmp((char *)id, "Statistics", sz) == 0) { rt = report_print_stats(log, report, outfile); } if (rt < 0) { return rt; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "
\n"); else fprintf(outfile, "\n"); return 0; } static int report_print_loaded_view(const seaudit_log_t * log, const seaudit_report_t * report, xmlChar * view_filePath, FILE * outfile) { size_t i, filters_added = 0; apol_vector_t *loaded_filters = NULL; seaudit_model_t *dup_model = NULL; seaudit_filter_t *filter; seaudit_message_t *msg; char *s; apol_vector_t *v = NULL; int retval = -1, error = 0; if ((loaded_filters = seaudit_filter_create_from_file((char *)view_filePath)) == NULL) { error = errno; ERR(log, "Error parsing file %s.", view_filePath); goto cleanup; } if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } for (i = 0; i < apol_vector_get_size(loaded_filters); i++, filters_added++) { filter = apol_vector_get_element(loaded_filters, i); if (seaudit_model_append_filter(dup_model, filter) < 0) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } } if ((v = seaudit_model_get_messages(log, dup_model)) == NULL) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "View file: %s
\n", view_filePath); fprintf(outfile, "Number of messages: %zd
\n
\n", apol_vector_get_size(v)); } else { fprintf(outfile, "View file: %s\n", view_filePath); fprintf(outfile, "Number of messages: %zd\n\n", apol_vector_get_size(v)); } for (i = 0; i < apol_vector_get_size(v); i++) { msg = apol_vector_get_element(v, i); if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { s = seaudit_message_to_string_html(msg); } else { s = seaudit_message_to_string(msg); } if (s == NULL) { error = errno; ERR(log, "%s", strerror(error)); goto cleanup; } fputs(s, outfile); fputc('\n', outfile); free(s); } retval = 0; cleanup: /* only destroy filters that were not added to the model * (recall that model takes ownership of filters) */ if (loaded_filters != NULL) { for (i = filters_added; i < apol_vector_get_size(loaded_filters); i++) { filter = apol_vector_get_element(loaded_filters, i); seaudit_filter_destroy(&filter); } apol_vector_destroy(&loaded_filters); } seaudit_model_destroy(&dup_model); apol_vector_destroy(&v); if (error != 0) { errno = error; } return retval; } static int report_print_custom_section(const seaudit_log_t * log, const seaudit_report_t * report, xmlTextReaderPtr reader, xmlChar * title, FILE * outfile) { size_t len, i; int rt, error = 0, retval = -1, end_of_element = 0; xmlChar *view_filePath = NULL, *name = NULL; if (title != NULL) { if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "

%s

\n", title); } else { fprintf(outfile, "%s\n", title); len = strlen((char *)title); for (i = 0; i < len; i++) { fprintf(outfile, "-"); } fprintf(outfile, "\n"); } } /* Moves the position of the current instance to the next node * in the stream, which should be a view node */ rt = xmlTextReaderRead(reader); while (rt == 1) { /* Read inner child view node(s) */ name = xmlTextReaderName(reader); if (name == NULL) { error = errno; ERR(log, "%s", "Unavailable node name within."); goto cleanup; } /* We have reached the end-of-element for the * custom-section node (indicated by 15) */ if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 15) { xmlFree(name); end_of_element = 1; break; } if (strcmp((char *)name, "view") == 0 && xmlTextReaderNodeType(reader) == 1 && xmlTextReaderHasAttributes(reader)) { view_filePath = xmlTextReaderGetAttribute(reader, (const xmlChar *)"file"); if (view_filePath == NULL) { error = errno; ERR(log, "%s", "Error getting file attribute for view node."); goto cleanup; } if (report_print_loaded_view(log, report, view_filePath, outfile) < 0) { error = errno; goto cleanup; } xmlFree(view_filePath); } xmlFree(name); rt = xmlTextReaderRead(reader); } if (!end_of_element && rt != 0) { error = EIO; ERR(log, "Error parsing config file %s. (rt:%d)", report->config, rt); goto cleanup; } if (!end_of_element) { error = EIO; ERR(log, "%s", "Encountered end of file before finding end of element for custom-section node."); goto cleanup;; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "
\n"); else fprintf(outfile, "\n"); return 0; cleanup: if (view_filePath) xmlFree(view_filePath); if (name) xmlFree(name); if (error != 0) { errno = error; } return retval; } static int report_process_xmlNode(const seaudit_log_t * log, const seaudit_report_t * report, xmlTextReaderPtr reader, FILE * outfile) { xmlChar *name = NULL, *id_attr = NULL, *title_attr = NULL; int retval = -1, error = 0; if ((name = xmlTextReaderName(reader)) == NULL) { error = errno; ERR(log, "%s", "Unavailable node name."); goto cleanup; } if (!report_is_valid_node_name((char *)name)) { retval = 0; goto cleanup; } if (strcmp((char *)name, "seaudit-report") == 0 && xmlTextReaderNodeType(reader) == 1) { if (report_parse_seaudit_report(log, report, reader, &id_attr, &title_attr) < 0) { error = errno; goto cleanup; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "

Title: %s

\n", title_attr); } else { fprintf(outfile, "Title: %s\n", title_attr); } } else if (strcmp((char *)name, "standard-section") == 0 && xmlTextReaderNodeType(reader) == 1) { if (report_parse_standard_attribs(log, report, reader, &id_attr, &title_attr) < 0) { error = errno; goto cleanup; } if (id_attr == NULL) { ERR(log, "%s", "Missing required id attribute for standard section node."); error = EIO; goto cleanup; } /* NOTE: If a title wasn't provided, we still continue. */ if (report_print_standard_section(log, report, id_attr, title_attr, outfile) < 0) { error = errno; goto cleanup; } } else if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 1) { if (report_parse_custom_attribs(log, report, reader, &title_attr) < 0) { error = errno; goto cleanup; } /* NOTE: If a title wasn't provided, we still continue. */ if (report_print_custom_section(log, report, reader, title_attr, outfile) < 0) { error = errno; goto cleanup; } } retval = 0; cleanup: xmlFree(name); xmlFree(id_attr); xmlFree(title_attr); if (retval < 0) { errno = error; } return retval; } static int report_print_malformed(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile) { size_t i, len; apol_vector_t *v = seaudit_model_get_malformed_messages(log, report->model); if (v == NULL) { return -1; } if (report->format == SEAUDIT_REPORT_FORMAT_HTML) { fprintf(outfile, "Malformed messages\n"); fprintf(outfile, "
\n
\n"); } else { fprintf(outfile, "Malformed messages\n"); len = strlen("Malformed messages\n"); for (i = 0; i < len; i++) { fprintf(outfile, "-"); } fprintf(outfile, "\n"); } for (i = 0; i < apol_vector_get_size(v); i++) { char *malformed_msg; malformed_msg = apol_vector_get_element(v, i); if (report->format == SEAUDIT_REPORT_FORMAT_HTML) fprintf(outfile, "%s
\n", malformed_msg); else fprintf(outfile, "%s\n", malformed_msg); } fprintf(outfile, "\n"); apol_vector_destroy(&v); return 0; } int seaudit_report_write(const seaudit_log_t * log, const seaudit_report_t * report, const char *out_file) { xmlTextReaderPtr reader; FILE *outfile = NULL; int rt, retval = -1, error = 0; /* Set/Open the output stream */ if (out_file == NULL) { outfile = stdout; } else { if ((outfile = fopen(out_file, "w+")) == NULL) { error = errno; ERR(log, "Could not open %s for writing.", out_file); goto cleanup; } } /* Print report header */ if (report_print_header(log, report, outfile) < 0) { error = errno; goto cleanup; } /* Parse the xml config file and output report */ reader = xmlNewTextReaderFilename(report->config); if (reader == NULL) { error = errno; ERR(log, "Unable to open config file (%s).", report->config); goto cleanup; } rt = xmlTextReaderRead(reader); while (rt == 1) { report_process_xmlNode(log, report, reader, outfile); rt = xmlTextReaderRead(reader); } error = errno; xmlFreeTextReader(reader); if (rt != 0) { ERR(log, "Failed to parse config file %s.", report->config); goto cleanup; } if (report->malformed && report_print_malformed(log, report, outfile) < 0) { error = errno; goto cleanup; } report_print_footer(report, outfile); retval = 0; cleanup: if (outfile != NULL) { fclose(outfile); } if (retval < 0) { errno = error; } return retval; }