report.c

Go to the documentation of this file.
00001 /**
00002  *  @file
00003  *  Implementation of seaudit report generator.
00004  *
00005  *  @author Jeremy A. Mowery jmowery@tresys.com
00006  *  @author Jason Tang jtang@tresys.com
00007  *
00008  *  Copyright (C) 2004-2007 Tresys Technology, LLC
00009  *
00010  *  This library is free software; you can redistribute it and/or
00011  *  modify it under the terms of the GNU Lesser General Public
00012  *  License as published by the Free Software Foundation; either
00013  *  version 2.1 of the License, or (at your option) any later version.
00014  *
00015  *  This library is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  *  Lesser General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU Lesser General Public
00021  *  License along with this library; if not, write to the Free Software
00022  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00023  */
00024 
00025 #include "seaudit_internal.h"
00026 
00027 #include <seaudit/report.h>
00028 
00029 #include <apol/util.h>
00030 #include <libxml/xmlreader.h>
00031 
00032 #include <assert.h>
00033 #include <errno.h>
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <unistd.h>
00038 
00039 #define CONFIG_FILE "seaudit-report.conf"
00040 #define STYLESHEET_FILE "seaudit-report.css"
00041 #define LINE_MAX 1024
00042 
00043 struct seaudit_report
00044 {
00045         /** output format for the report */
00046         seaudit_report_format_e format;
00047         /** path to configuration file, or NULL to use system configuration */
00048         char *config;
00049         /** path to HTML stylesheet, or NULL to use system stylesheet */
00050         char *stylesheet;
00051         /** if non-zero, then use a stylesheet when generating HTML reports */
00052         int use_stylesheet;
00053         /** if non-zero, then print malformed messages */
00054         int malformed;
00055         /** model from which messages will be obtained */
00056         seaudit_model_t *model;
00057 };
00058 
00059 static const char *seaudit_report_node_names[] = {
00060         "seaudit-report",
00061         "standard-section",
00062         "custom-section",
00063         "view",
00064         NULL
00065 };
00066 
00067 static const char *seaudit_standard_section_names[] = {
00068         "PolicyLoads",
00069         "EnforcementToggles",
00070         "PolicyBooleans",
00071         "Statistics",
00072         "AllowListing",
00073         "DenyListing",
00074         NULL
00075 };
00076 
00077 seaudit_report_t *seaudit_report_create(seaudit_model_t * model)
00078 {
00079         seaudit_report_t *r = calloc(1, sizeof(*r));
00080         if (r == NULL) {
00081                 return NULL;
00082         }
00083         r->model = model;
00084         return r;
00085 }
00086 
00087 void seaudit_report_destroy(seaudit_report_t ** report)
00088 {
00089         if (report == NULL || *report == NULL) {
00090                 return;
00091         }
00092         free((*report)->config);
00093         free((*report)->stylesheet);
00094         free(*report);
00095         *report = NULL;
00096 }
00097 
00098 int seaudit_report_set_format(const seaudit_log_t * log, seaudit_report_t * report, seaudit_report_format_e format)
00099 {
00100         if (report == NULL) {
00101                 ERR(log, "%s", strerror(EINVAL));
00102                 errno = EINVAL;
00103                 return -1;
00104         }
00105         report->format = format;
00106         return 0;
00107 }
00108 
00109 /**
00110  * Set the report's configuration file to the default system file.
00111  */
00112 static int report_set_default_configuration(const seaudit_log_t * log, seaudit_report_t * report)
00113 {
00114         char *config_dir = apol_file_find(CONFIG_FILE);
00115         int error;
00116 
00117         if (config_dir == NULL) {
00118                 error = errno;
00119                 ERR(log, "%s", "Could not find default configuration file.");
00120                 errno = error;
00121                 return -1;
00122         }
00123         if (asprintf(&report->config, "%s/%s", config_dir, CONFIG_FILE) < 0) {
00124                 error = errno;
00125                 report->config = NULL;
00126                 free(config_dir);
00127                 ERR(log, "%s", strerror(error));
00128                 errno = error;
00129                 return -1;
00130         }
00131         free(config_dir);
00132 
00133         /* check if can read the file */
00134         if (access(report->config, R_OK) != 0) {
00135                 error = errno;
00136                 ERR(log, "Could not read default config file %s.", report->config);
00137                 errno = error;
00138                 return -1;
00139         }
00140         return 0;
00141 }
00142 
00143 int seaudit_report_set_configuration(const seaudit_log_t * log, seaudit_report_t * report, const char *file)
00144 {
00145         if (report == NULL) {
00146                 ERR(log, "%s", strerror(EINVAL));
00147                 errno = EINVAL;
00148                 return -1;
00149         }
00150         free(report->config);
00151         report->config = NULL;
00152         if (file == NULL) {
00153                 return report_set_default_configuration(log, report);
00154         } else {
00155                 if ((report->config = strdup(file)) == NULL) {
00156                         int error = errno;
00157                         ERR(log, "%s", strerror(error));
00158                         errno = error;
00159                         return -1;
00160                 }
00161                 return 0;
00162         }
00163 }
00164 
00165 /**
00166  * Set the report's stylesheet to the default system stylesheet.
00167  */
00168 static int report_set_default_stylesheet(const seaudit_log_t * log, seaudit_report_t * report)
00169 {
00170         char *dir = apol_file_find(STYLESHEET_FILE);
00171         int error;
00172         if (dir == NULL) {
00173                 error = errno;
00174                 ERR(log, "%s", "Could not find default stylesheet.");
00175                 errno = error;
00176                 return -1;
00177         }
00178 
00179         if (asprintf(&report->stylesheet, "%s/%s", dir, STYLESHEET_FILE) < 0) {
00180                 error = errno;
00181                 report->stylesheet = NULL;
00182                 free(dir);
00183                 ERR(log, "%s", strerror(error));
00184                 errno = error;
00185                 return -1;
00186         }
00187         free(dir);
00188 
00189         return 0;
00190 }
00191 
00192 int seaudit_report_set_stylesheet(const seaudit_log_t * log, seaudit_report_t * report, const char *file, const int use_stylesheet)
00193 {
00194         if (report == NULL) {
00195                 ERR(log, "%s", strerror(EINVAL));
00196                 errno = EINVAL;
00197                 return -1;
00198         }
00199         free(report->stylesheet);
00200         report->stylesheet = NULL;
00201         report->use_stylesheet = use_stylesheet;
00202         if (file == NULL) {
00203                 return report_set_default_stylesheet(log, report);
00204         } else {
00205                 if ((report->stylesheet = strdup(file)) == NULL) {
00206                         return -1;
00207                         int error = errno;
00208                         ERR(log, "%s", strerror(error));
00209                         errno = error;
00210                         return -1;
00211                 }
00212                 return 0;
00213         }
00214 }
00215 
00216 int seaudit_report_set_malformed(const seaudit_log_t * log, seaudit_report_t * report, const int do_malformed)
00217 {
00218         if (report == NULL) {
00219                 ERR(log, "%s", strerror(EINVAL));
00220                 errno = EINVAL;
00221                 return -1;
00222         }
00223         report->malformed = do_malformed;
00224         return 0;
00225 }
00226 
00227 /**
00228  * Insert the contents of the stylesheet into the output file.  If it
00229  * is not readable then generate a warning.  This is not an error
00230  * because the stylesheet is not strictly necessary.
00231  */
00232 static int report_import_html_stylesheet(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00233 {
00234         char line[LINE_MAX], *line_ptr = NULL;
00235         FILE *fp;
00236 
00237         if (report->use_stylesheet) {
00238                 fp = fopen(report->stylesheet, "r");
00239                 if (fp == NULL) {
00240                         WARN(log, "Cannot open stylesheet file %s.", report->stylesheet);
00241                         return 1;
00242                 }
00243                 fprintf(outfile, "<style type=\"text/css\">\n");
00244 
00245                 while (fgets(line, LINE_MAX, fp) != NULL) {
00246                         free(line_ptr);
00247                         line_ptr = NULL;
00248                         if ((line_ptr = strdup(line)) == NULL) {
00249                                 int error = errno;
00250                                 free(line_ptr);
00251                                 fclose(fp);
00252                                 ERR(log, "%s", strerror(error));
00253                                 errno = error;
00254                                 return -1;
00255                         }
00256                         apol_str_trim(line_ptr);
00257                         if (line_ptr[0] == '#' || apol_str_is_only_white_space(line_ptr))
00258                                 continue;
00259                         fprintf(outfile, "%s\n", line_ptr);
00260                 }
00261                 fprintf(outfile, "</style>\n");
00262                 fclose(fp);
00263                 free(line_ptr);
00264         }
00265         return 0;
00266 }
00267 
00268 static int report_print_header(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00269 {
00270         time_t ltime;
00271 
00272         time(&ltime);
00273         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00274                 fprintf(outfile, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n");
00275                 fprintf(outfile, "<html>\n<head>\n");
00276                 if (report_import_html_stylesheet(log, report, outfile) < 0) {
00277                         return -1;
00278                 }
00279                 fprintf(outfile, "<title>seaudit-report</title>\n</head>\n");
00280                 fprintf(outfile, "<body>\n");
00281                 fprintf(outfile, "<b class=\"report_date\"># Report generated by seaudit-report on %s</b><br>\n", ctime(&ltime));
00282         } else {
00283                 fprintf(outfile, "# Begin\n\n");
00284                 fprintf(outfile, "# Report generated by seaudit-report on %s\n", ctime(&ltime));
00285         }
00286         return 0;
00287 }
00288 
00289 static int report_print_footer(const seaudit_report_t * report, FILE * outfile)
00290 {
00291         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00292                 fprintf(outfile, "</body>\n</html>\n");
00293         } else {
00294                 fprintf(outfile, "# End\n");
00295         }
00296         return 0;
00297 }
00298 
00299 static int report_is_valid_node_name(const char *name)
00300 {
00301         size_t i;
00302         for (i = 0; seaudit_report_node_names[i] != NULL; i++)
00303                 if (strcmp(seaudit_report_node_names[i], name) == 0)
00304                         return 1;
00305         return 0;
00306 }
00307 
00308 static int report_is_valid_section_name(const char *name)
00309 {
00310         size_t i;
00311         for (i = 0; seaudit_standard_section_names[i] != NULL; i++)
00312                 if (strcmp(seaudit_standard_section_names[i], name) == 0)
00313                         return 1;
00314         return 0;
00315 }
00316 
00317 static int report_parse_seaudit_report(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
00318                                        xmlTextReaderPtr reader, xmlChar ** id_value
00319                                        __attribute__ ((unused)), xmlChar ** title_value)
00320 {
00321         int rt, error;
00322         xmlChar *name = NULL;
00323 
00324         if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
00325                 /* Parse attributes */
00326                 rt = xmlTextReaderMoveToNextAttribute(reader);
00327                 while (rt > 0) {
00328                         name = xmlTextReaderName(reader);
00329                         if (name == NULL) {
00330                                 error = errno;
00331                                 ERR(log, "%s", "Attribute name unavailable.");
00332                                 errno = error;
00333                                 return -1;
00334                         }
00335                         if (strcmp((char *)name, "title") == 0) {
00336                                 *title_value = xmlTextReaderValue(reader);
00337                         }
00338 
00339                         xmlFree(name);
00340                         rt = xmlTextReaderMoveToNextAttribute(reader);
00341                 }
00342                 if (rt < 0) {
00343                         error = errno;
00344                         ERR(log, "%s", "Error parsing attribute for seaudit-report node.");
00345                         errno = error;
00346                         return -1;
00347                 }
00348         }
00349         return 0;
00350 }
00351 
00352 static int report_parse_standard_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
00353                                          xmlTextReaderPtr reader, xmlChar ** id_value, xmlChar ** title_value)
00354 {
00355         int rt, error;
00356         xmlChar *name = NULL;
00357 
00358         if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
00359                 /* Parse attributes */
00360                 rt = xmlTextReaderMoveToNextAttribute(reader);
00361                 while (rt > 0) {
00362                         name = xmlTextReaderName(reader);
00363                         if (name == NULL) {
00364                                 error = errno;
00365                                 ERR(log, "%s", "Attribute name unavailable.");
00366                                 errno = error;
00367                                 return -1;
00368                         }
00369                         if (strcmp((char *)name, "id") == 0) {
00370                                 *id_value = xmlTextReaderValue(reader);
00371                         } else if (strcmp((char *)name, "title") == 0) {
00372                                 *title_value = xmlTextReaderValue(reader);
00373                         }
00374                         xmlFree(name);
00375                         rt = xmlTextReaderMoveToNextAttribute(reader);
00376                 }
00377                 if (rt < 0) {
00378                         error = errno;
00379                         ERR(log, "%s", "Error parsing attribute for standard-section node.");
00380                         errno = error;
00381                         return -1;
00382                 }
00383         }
00384         return 0;
00385 }
00386 
00387 static int report_parse_custom_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
00388                                        xmlTextReaderPtr reader, xmlChar ** title_value)
00389 {
00390         int rt, error;
00391         xmlChar *name = NULL;
00392 
00393         if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
00394                 /* Parse attributes */
00395                 rt = xmlTextReaderMoveToNextAttribute(reader);
00396                 while (rt > 0) {
00397                         name = xmlTextReaderName(reader);
00398                         if (name == NULL) {
00399                                 error = errno;
00400                                 ERR(log, "%s", "Attribute name unavailable.");
00401                                 errno = error;
00402                                 return -1;
00403                         }
00404                         if (strcmp((char *)name, "title") == 0) {
00405                                 *title_value = xmlTextReaderValue(reader);
00406                         }
00407 
00408                         xmlFree(name);
00409                         rt = xmlTextReaderMoveToNextAttribute(reader);
00410                 }
00411                 if (rt < 0) {
00412                         error = errno;
00413                         ERR(log, "%s", "Error parsing attribute for custom-section node.");
00414                         errno = error;
00415                         return -1;
00416                 }
00417         }
00418         return 0;
00419 }
00420 
00421 /**
00422  * Allocate and return a filter for setenforce toggles.  (Actually, it
00423  * can't filter on permissions.)
00424  */
00425 static seaudit_filter_t *report_enforce_toggle_filter_create(const seaudit_log_t * log, const seaudit_report_t * report
00426                                                              __attribute__ ((unused)))
00427 {
00428         seaudit_filter_t *filter = NULL;
00429         apol_vector_t *type_v = NULL, *class_v;
00430         int retval = -1, error = 0;
00431         char *tgt_type = "security_t";
00432         char *obj_class = "security";
00433 
00434         if ((filter = seaudit_filter_create(NULL)) == NULL) {
00435                 error = errno;
00436                 ERR(log, "%s", strerror(error));
00437                 goto cleanup;
00438         }
00439         if ((type_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
00440             apol_vector_append(type_v, tgt_type) < 0 || seaudit_filter_set_target_type(filter, type_v) < 0) {
00441                 error = errno;
00442                 ERR(log, "%s", strerror(error));
00443                 goto cleanup;
00444         }
00445         if ((class_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
00446             apol_vector_append(class_v, obj_class) < 0 || seaudit_filter_set_target_class(filter, class_v) < 0) {
00447                 error = errno;
00448                 ERR(log, "%s", strerror(error));
00449                 goto cleanup;
00450         }
00451         retval = 0;
00452       cleanup:
00453         apol_vector_destroy(&type_v);
00454         apol_vector_destroy(&class_v);
00455         if (retval != 0) {
00456                 seaudit_filter_destroy(&filter);
00457                 errno = error;
00458                 return NULL;
00459         }
00460         return filter;
00461 }
00462 
00463 static int report_print_enforce_toggles(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00464 {
00465         seaudit_filter_t *filter = NULL;
00466         seaudit_model_t *dup_model = NULL;
00467         size_t i, j, num_setenforce = 0;
00468         apol_vector_t *v = NULL;
00469         seaudit_message_t *msg;
00470         seaudit_avc_message_t *avc;
00471         seaudit_message_type_e type;
00472         char *s;
00473         char *perm = "setenforce";
00474         int retval = -1, error = 0;
00475 
00476         if ((filter = report_enforce_toggle_filter_create(log, report)) == NULL) {
00477                 error = errno;
00478                 goto cleanup;
00479         }
00480         if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL ||
00481             seaudit_model_append_filter(dup_model, filter) < 0) {
00482                 error = errno;
00483                 ERR(log, "%s", strerror(error));
00484                 goto cleanup;
00485         }
00486         filter = NULL;
00487         /* Loop through and get the number of avc allow messages with
00488          * the setenforce permission. */
00489         v = seaudit_model_get_messages(log, dup_model);
00490         for (i = 0; i < apol_vector_get_size(v); i++) {
00491                 msg = apol_vector_get_element(v, i);
00492                 avc = seaudit_message_get_data(msg, &type);
00493                 if (type != SEAUDIT_MESSAGE_TYPE_AVC || avc->msg == SEAUDIT_AVC_DENIED)
00494                         continue;
00495                 if (apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) == 0) {
00496                         /* Increment number of setenforce messages */
00497                         num_setenforce++;
00498                 }
00499         }
00500 
00501         /* Since we cannot filter by setenforce permission within the
00502          * view, we do so manually within the following for loop. */
00503         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00504                 fprintf(outfile,
00505                         "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
00506                         num_setenforce);
00507         else
00508                 fprintf(outfile, "Number of messages: %zd\n\n", num_setenforce);
00509 
00510         for (i = 0; i < apol_vector_get_size(v); i++) {
00511                 msg = apol_vector_get_element(v, i);
00512                 avc = seaudit_message_get_data(msg, &type);
00513                 if (type != SEAUDIT_MESSAGE_TYPE_AVC ||
00514                     avc->msg == SEAUDIT_AVC_DENIED || apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) < 0) {
00515                         continue;
00516                 }
00517                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00518                         s = seaudit_message_to_string_html(msg);
00519                 } else {
00520                         s = seaudit_message_to_string(msg);
00521                 }
00522                 if (s == NULL) {
00523                         error = errno;
00524                         ERR(log, "%s", strerror(error));
00525                         goto cleanup;
00526                 }
00527                 fputs(s, outfile);
00528                 fputc('\n', outfile);
00529                 free(s);
00530         }
00531         retval = 0;
00532       cleanup:
00533         apol_vector_destroy(&v);
00534         seaudit_filter_destroy(&filter);
00535         seaudit_model_destroy(&dup_model);
00536         if (error != 0) {
00537                 errno = error;
00538         }
00539         return retval;
00540 }
00541 
00542 static int report_print_policy_booleans(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00543 {
00544         size_t i, num = seaudit_model_get_num_bools(log, report->model);
00545         apol_vector_t *v = seaudit_model_get_messages(log, report->model);
00546         seaudit_message_t *m;
00547         seaudit_message_type_e type;
00548         char *s;
00549         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00550                 fprintf(outfile,
00551                         "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
00552                         num);
00553         else
00554                 fprintf(outfile, "Number of messages: %zd\n\n", num);
00555 
00556         for (i = 0; i < apol_vector_get_size(v); i++) {
00557                 m = apol_vector_get_element(v, i);
00558                 seaudit_message_get_data(m, &type);
00559                 if (type == SEAUDIT_MESSAGE_TYPE_BOOL) {
00560                         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00561                                 s = seaudit_message_to_string_html(m);
00562                         } else {
00563                                 s = seaudit_message_to_string(m);
00564                         }
00565                         if (s == NULL) {
00566                                 int error = errno;
00567                                 apol_vector_destroy(&v);
00568                                 ERR(log, "%s", strerror(error));
00569                                 errno = error;
00570                                 return -1;
00571                         }
00572                         fputs(s, outfile);
00573                         fputc('\n', outfile);
00574                         free(s);
00575                 }
00576         }
00577         apol_vector_destroy(&v);
00578         return 0;
00579 }
00580 
00581 static int report_print_policy_loads(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00582 {
00583         size_t i, num = seaudit_model_get_num_loads(log, report->model);
00584         apol_vector_t *v = seaudit_model_get_messages(log, report->model);
00585         seaudit_message_t *m;
00586         seaudit_message_type_e type;
00587         char *s;
00588         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00589                 fprintf(outfile,
00590                         "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
00591                         num);
00592         else
00593                 fprintf(outfile, "Number of messages: %zd\n\n", num);
00594 
00595         for (i = 0; i < apol_vector_get_size(v); i++) {
00596                 m = apol_vector_get_element(v, i);
00597                 seaudit_message_get_data(m, &type);
00598                 if (type == SEAUDIT_MESSAGE_TYPE_LOAD) {
00599                         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00600                                 s = seaudit_message_to_string_html(m);
00601                         } else {
00602                                 s = seaudit_message_to_string(m);
00603                         }
00604                         if (s == NULL) {
00605                                 int error = errno;
00606                                 apol_vector_destroy(&v);
00607                                 ERR(log, "%s", strerror(error));
00608                                 errno = error;
00609                                 return -1;
00610                         }
00611                         fputs(s, outfile);
00612                         fputc('\n', outfile);
00613                         free(s);
00614                 }
00615         }
00616         apol_vector_destroy(&v);
00617         return 0;
00618 }
00619 
00620 static int report_print_avc_listing(const seaudit_log_t * log, const seaudit_report_t * report, seaudit_avc_message_type_e avc_type,
00621                                     FILE * outfile)
00622 {
00623         size_t i, num;
00624         apol_vector_t *v = seaudit_model_get_messages(log, report->model);
00625         seaudit_message_t *m;
00626         seaudit_avc_message_t *avc;
00627         seaudit_message_type_e type;
00628         char *s;
00629         if (avc_type == SEAUDIT_AVC_GRANTED) {
00630                 num = seaudit_model_get_num_allows(log, report->model);
00631         } else {
00632                 num = seaudit_model_get_num_denies(log, report->model);
00633         }
00634         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00635                 fprintf(outfile,
00636                         "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
00637                         num);
00638         else
00639                 fprintf(outfile, "Number of messages: %zd\n\n", num);
00640 
00641         for (i = 0; i < apol_vector_get_size(v); i++) {
00642                 m = apol_vector_get_element(v, i);
00643                 avc = seaudit_message_get_data(m, &type);
00644                 if (type == SEAUDIT_MESSAGE_TYPE_AVC && avc->msg == avc_type) {
00645                         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00646                                 s = seaudit_message_to_string_html(m);
00647                         } else {
00648                                 s = seaudit_message_to_string(m);
00649                         }
00650                         if (s == NULL) {
00651                                 int error = errno;
00652                                 apol_vector_destroy(&v);
00653                                 ERR(log, "%s", strerror(error));
00654                                 errno = error;
00655                                 return -1;
00656                         }
00657                         fputs(s, outfile);
00658                         fputc('\n', outfile);
00659                         free(s);
00660                 }
00661         }
00662         apol_vector_destroy(&v);
00663         return 0;
00664 }
00665 
00666 static int report_print_stats(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00667 {
00668         apol_vector_t *v = seaudit_model_get_messages(log, report->model);
00669         size_t num_messages = apol_vector_get_size(v);
00670         apol_vector_destroy(&v);
00671         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00672                 fprintf(outfile,
00673                         "<font class=\"stats_label\">Number of total messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
00674                         num_messages);
00675                 fprintf(outfile,
00676                         "<font class=\"stats_label\">Number of policy load messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
00677                         seaudit_model_get_num_loads(log, report->model));
00678                 fprintf(outfile,
00679                         "<font class=\"stats_label\">Number of policy boolean messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
00680                         seaudit_model_get_num_bools(log, report->model));
00681                 fprintf(outfile,
00682                         "<font class=\"stats_label\">Number of allow messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
00683                         seaudit_model_get_num_allows(log, report->model));
00684                 fprintf(outfile,
00685                         "<font class=\"stats_label\">Number of denied messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
00686                         seaudit_model_get_num_denies(log, report->model));
00687         } else {
00688                 fprintf(outfile, "Number of total messages: %zd\n", num_messages);
00689                 fprintf(outfile, "Number of policy load messages: %zd\n", seaudit_model_get_num_loads(log, report->model));
00690                 fprintf(outfile, "Number of policy boolean messages: %zd\n", seaudit_model_get_num_bools(log, report->model));
00691                 fprintf(outfile, "Number of allow messages: %zd\n", seaudit_model_get_num_allows(log, report->model));
00692                 fprintf(outfile, "Number of denied messages: %zd\n", seaudit_model_get_num_denies(log, report->model));
00693         }
00694         return 0;
00695 }
00696 
00697 static int report_print_standard_section(const seaudit_log_t * log, const seaudit_report_t * report,
00698                                          xmlChar * id, xmlChar * title, FILE * outfile)
00699 {
00700         size_t sz, len, i;
00701         int rt = 0;
00702 
00703         if (!report_is_valid_section_name((char *)id)) {
00704                 ERR(log, "%s", "Invalid standard section ID.");
00705                 errno = EINVAL;
00706                 return -1;
00707         }
00708         sz = strlen((char *)id);
00709         if (title != NULL) {
00710                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00711                         fprintf(outfile, "<h2 class=\"standard_section_title\"><u>%s</h2></u>\n", title);
00712                 } else {
00713                         fprintf(outfile, "%s\n", title);
00714                         len = strlen((char *)title);
00715                         for (i = 0; i < len; i++) {
00716                                 fprintf(outfile, "-");
00717                         }
00718                         fprintf(outfile, "\n");
00719                 }
00720         }
00721         if (strncasecmp((char *)id, "PolicyLoads", sz) == 0) {
00722                 rt = report_print_policy_loads(log, report, outfile);
00723         } else if (strncasecmp((char *)id, "EnforcementToggles", sz) == 0) {
00724                 rt = report_print_enforce_toggles(log, report, outfile);
00725         } else if (strncasecmp((char *)id, "PolicyBooleans", sz) == 0) {
00726                 rt = report_print_policy_booleans(log, report, outfile);
00727         } else if (strncasecmp((char *)id, "AllowListing", sz) == 0) {
00728                 rt = report_print_avc_listing(log, report, SEAUDIT_AVC_GRANTED, outfile);
00729         } else if (strncasecmp((char *)id, "DenyListing", sz) == 0) {
00730                 rt = report_print_avc_listing(log, report, SEAUDIT_AVC_DENIED, outfile);
00731         } else if (strncasecmp((char *)id, "Statistics", sz) == 0) {
00732                 rt = report_print_stats(log, report, outfile);
00733         }
00734         if (rt < 0) {
00735                 return rt;
00736         }
00737 
00738         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00739                 fprintf(outfile, "<br>\n");
00740         else
00741                 fprintf(outfile, "\n");
00742 
00743         return 0;
00744 }
00745 
00746 static int report_print_loaded_view(const seaudit_log_t * log, const seaudit_report_t * report, xmlChar * view_filePath,
00747                                     FILE * outfile)
00748 {
00749         size_t i, filters_added = 0;
00750         apol_vector_t *loaded_filters = NULL;
00751         seaudit_model_t *dup_model = NULL;
00752         seaudit_filter_t *filter;
00753         seaudit_message_t *msg;
00754         char *s;
00755         apol_vector_t *v = NULL;
00756         int retval = -1, error = 0;
00757 
00758         if ((loaded_filters = seaudit_filter_create_from_file((char *)view_filePath)) == NULL) {
00759                 error = errno;
00760                 ERR(log, "Error parsing file %s.", view_filePath);
00761                 goto cleanup;
00762         }
00763         if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL) {
00764                 error = errno;
00765                 ERR(log, "%s", strerror(error));
00766                 goto cleanup;
00767         }
00768         for (i = 0; i < apol_vector_get_size(loaded_filters); i++, filters_added++) {
00769                 filter = apol_vector_get_element(loaded_filters, i);
00770                 if (seaudit_model_append_filter(dup_model, filter) < 0) {
00771                         error = errno;
00772                         ERR(log, "%s", strerror(error));
00773                         goto cleanup;
00774                 }
00775         }
00776         if ((v = seaudit_model_get_messages(log, dup_model)) == NULL) {
00777                 error = errno;
00778                 ERR(log, "%s", strerror(error));
00779                 goto cleanup;
00780         }
00781         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00782                 fprintf(outfile, "View file: %s<br>\n", view_filePath);
00783                 fprintf(outfile,
00784                         "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
00785                         apol_vector_get_size(v));
00786         } else {
00787                 fprintf(outfile, "View file: %s\n", view_filePath);
00788                 fprintf(outfile, "Number of messages: %zd\n\n", apol_vector_get_size(v));
00789         }
00790 
00791         for (i = 0; i < apol_vector_get_size(v); i++) {
00792                 msg = apol_vector_get_element(v, i);
00793                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00794                         s = seaudit_message_to_string_html(msg);
00795                 } else {
00796                         s = seaudit_message_to_string(msg);
00797                 }
00798                 if (s == NULL) {
00799                         error = errno;
00800                         ERR(log, "%s", strerror(error));
00801                         goto cleanup;
00802                 }
00803                 fputs(s, outfile);
00804                 fputc('\n', outfile);
00805                 free(s);
00806         }
00807         retval = 0;
00808       cleanup:
00809         /* only destroy filters that were not added to the model
00810          * (recall that model takes ownership of filters) */
00811         if (loaded_filters != NULL) {
00812                 for (i = filters_added; i < apol_vector_get_size(loaded_filters); i++) {
00813                         filter = apol_vector_get_element(loaded_filters, i);
00814                         seaudit_filter_destroy(&filter);
00815                 }
00816                 apol_vector_destroy(&loaded_filters);
00817         }
00818         seaudit_model_destroy(&dup_model);
00819         apol_vector_destroy(&v);
00820         if (error != 0) {
00821                 errno = error;
00822         }
00823         return retval;
00824 }
00825 
00826 static int report_print_custom_section(const seaudit_log_t * log, const seaudit_report_t * report,
00827                                        xmlTextReaderPtr reader, xmlChar * title, FILE * outfile)
00828 {
00829         size_t len, i;
00830         int rt, error = 0, retval = -1, end_of_element = 0;
00831         xmlChar *view_filePath = NULL, *name = NULL;
00832 
00833         if (title != NULL) {
00834                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00835                         fprintf(outfile, "<h2 class=\"custom_section_title\"><u>%s</h2></u>\n", title);
00836                 } else {
00837                         fprintf(outfile, "%s\n", title);
00838                         len = strlen((char *)title);
00839                         for (i = 0; i < len; i++) {
00840                                 fprintf(outfile, "-");
00841                         }
00842                         fprintf(outfile, "\n");
00843                 }
00844         }
00845 
00846         /* Moves the position of the current instance to the next node
00847          * in the stream, which should be a view node */
00848         rt = xmlTextReaderRead(reader);
00849         while (rt == 1) {
00850                 /* Read inner child view node(s) */
00851                 name = xmlTextReaderName(reader);
00852                 if (name == NULL) {
00853                         error = errno;
00854                         ERR(log, "%s", "Unavailable node name within.");
00855                         goto cleanup;
00856                 }
00857                 /* We have reached the end-of-element for the
00858                  * custom-section node (indicated by 15) */
00859                 if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 15) {
00860                         xmlFree(name);
00861                         end_of_element = 1;
00862                         break;
00863                 }
00864                 if (strcmp((char *)name, "view") == 0 && xmlTextReaderNodeType(reader) == 1 && xmlTextReaderHasAttributes(reader)) {
00865                         view_filePath = xmlTextReaderGetAttribute(reader, (const xmlChar *)"file");
00866                         if (view_filePath == NULL) {
00867                                 error = errno;
00868                                 ERR(log, "%s", "Error getting file attribute for view node.");
00869                                 goto cleanup;
00870                         }
00871                         if (report_print_loaded_view(log, report, view_filePath, outfile) < 0) {
00872                                 error = errno;
00873                                 goto cleanup;
00874                         }
00875                         xmlFree(view_filePath);
00876                 }
00877                 xmlFree(name);
00878                 rt = xmlTextReaderRead(reader);
00879         }
00880         if (!end_of_element && rt != 0) {
00881                 error = EIO;
00882                 ERR(log, "Error parsing config file %s. (rt:%d)", report->config, rt);
00883                 goto cleanup;
00884         }
00885 
00886         if (!end_of_element) {
00887                 error = EIO;
00888                 ERR(log, "%s", "Encountered end of file before finding end of element for custom-section node.");
00889                 goto cleanup;;
00890         }
00891         if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00892                 fprintf(outfile, "<br>\n");
00893         else
00894                 fprintf(outfile, "\n");
00895 
00896         return 0;
00897       cleanup:
00898         if (view_filePath)
00899                 xmlFree(view_filePath);
00900         if (name)
00901                 xmlFree(name);
00902         if (error != 0) {
00903                 errno = error;
00904         }
00905         return retval;
00906 }
00907 
00908 static int report_process_xmlNode(const seaudit_log_t * log, const seaudit_report_t * report, xmlTextReaderPtr reader,
00909                                   FILE * outfile)
00910 {
00911         xmlChar *name = NULL, *id_attr = NULL, *title_attr = NULL;
00912         int retval = -1, error = 0;
00913 
00914         if ((name = xmlTextReaderName(reader)) == NULL) {
00915                 error = errno;
00916                 ERR(log, "%s", "Unavailable node name.");
00917                 goto cleanup;
00918         }
00919 
00920         if (!report_is_valid_node_name((char *)name)) {
00921                 retval = 0;
00922                 goto cleanup;
00923         }
00924 
00925         if (strcmp((char *)name, "seaudit-report") == 0 && xmlTextReaderNodeType(reader) == 1) {
00926                 if (report_parse_seaudit_report(log, report, reader, &id_attr, &title_attr) < 0) {
00927                         error = errno;
00928                         goto cleanup;
00929                 }
00930                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00931                         fprintf(outfile, "<h1 class=\"report_title\">Title: %s</h1>\n", title_attr);
00932                 } else {
00933                         fprintf(outfile, "Title: %s\n", title_attr);
00934                 }
00935         } else if (strcmp((char *)name, "standard-section") == 0 && xmlTextReaderNodeType(reader) == 1) {
00936                 if (report_parse_standard_attribs(log, report, reader, &id_attr, &title_attr) < 0) {
00937                         error = errno;
00938                         goto cleanup;
00939                 }
00940                 if (id_attr == NULL) {
00941                         ERR(log, "%s", "Missing required id attribute for standard section node.");
00942                         error = EIO;
00943                         goto cleanup;
00944                 }
00945                 /* NOTE: If a title wasn't provided, we still continue. */
00946                 if (report_print_standard_section(log, report, id_attr, title_attr, outfile) < 0) {
00947                         error = errno;
00948                         goto cleanup;
00949                 }
00950         } else if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 1) {
00951                 if (report_parse_custom_attribs(log, report, reader, &title_attr) < 0) {
00952                         error = errno;
00953                         goto cleanup;
00954                 }
00955                 /* NOTE: If a title wasn't provided, we still continue. */
00956                 if (report_print_custom_section(log, report, reader, title_attr, outfile) < 0) {
00957                         error = errno;
00958                         goto cleanup;
00959                 }
00960         }
00961 
00962         retval = 0;
00963       cleanup:
00964         xmlFree(name);
00965         xmlFree(id_attr);
00966         xmlFree(title_attr);
00967         if (retval < 0) {
00968                 errno = error;
00969         }
00970         return retval;
00971 }
00972 
00973 static int report_print_malformed(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
00974 {
00975         size_t i, len;
00976         apol_vector_t *v = seaudit_model_get_malformed_messages(log, report->model);
00977         if (v == NULL) {
00978                 return -1;
00979         }
00980         if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
00981                 fprintf(outfile, "<b><u>Malformed messages</b></u>\n");
00982                 fprintf(outfile, "<br>\n<br>\n");
00983         } else {
00984                 fprintf(outfile, "Malformed messages\n");
00985                 len = strlen("Malformed messages\n");
00986                 for (i = 0; i < len; i++) {
00987                         fprintf(outfile, "-");
00988                 }
00989                 fprintf(outfile, "\n");
00990         }
00991         for (i = 0; i < apol_vector_get_size(v); i++) {
00992                 char *malformed_msg;
00993                 malformed_msg = apol_vector_get_element(v, i);
00994                 if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
00995                         fprintf(outfile, "%s<br>\n", malformed_msg);
00996                 else
00997                         fprintf(outfile, "%s\n", malformed_msg);
00998         }
00999         fprintf(outfile, "\n");
01000         apol_vector_destroy(&v);
01001         return 0;
01002 }
01003 
01004 int seaudit_report_write(const seaudit_log_t * log, const seaudit_report_t * report, const char *out_file)
01005 {
01006         xmlTextReaderPtr reader;
01007         FILE *outfile = NULL;
01008         int rt, retval = -1, error = 0;
01009 
01010         /* Set/Open the output stream */
01011         if (out_file == NULL) {
01012                 outfile = stdout;
01013         } else {
01014                 if ((outfile = fopen(out_file, "w+")) == NULL) {
01015                         error = errno;
01016                         ERR(log, "Could not open %s for writing.", out_file);
01017                         goto cleanup;
01018                 }
01019         }
01020 
01021         /* Print report header */
01022         if (report_print_header(log, report, outfile) < 0) {
01023                 error = errno;
01024                 goto cleanup;
01025         }
01026 
01027         /* Parse the xml config file and output report */
01028         reader = xmlNewTextReaderFilename(report->config);
01029         if (reader == NULL) {
01030                 error = errno;
01031                 ERR(log, "Unable to open config file (%s).", report->config);
01032                 goto cleanup;
01033         }
01034         rt = xmlTextReaderRead(reader);
01035         while (rt == 1) {
01036                 report_process_xmlNode(log, report, reader, outfile);
01037                 rt = xmlTextReaderRead(reader);
01038         }
01039         error = errno;
01040         xmlFreeTextReader(reader);
01041         if (rt != 0) {
01042                 ERR(log, "Failed to parse config file %s.", report->config);
01043                 goto cleanup;
01044         }
01045         if (report->malformed && report_print_malformed(log, report, outfile) < 0) {
01046                 error = errno;
01047                 goto cleanup;
01048         }
01049         report_print_footer(report, outfile);
01050 
01051         retval = 0;
01052       cleanup:
01053         if (outfile != NULL) {
01054                 fclose(outfile);
01055         }
01056         if (retval < 0) {
01057                 errno = error;
01058         }
01059         return retval;
01060 }