From 47be9ff57e72906660bb62a515222f482131e1fb Mon Sep 17 00:00:00 2001 From: Miroslav Grepl Date: Fri, 11 Apr 2014 09:37:53 +0200 Subject: Create setools-3.3.7 git repo --- seaudit/Makefile.am | 90 + seaudit/dot_seaudit.in | 12 + seaudit/filter_view.c | 1127 +++++ seaudit/filter_view.h | 42 + seaudit/message_view.c | 1341 ++++++ seaudit/message_view.h | 178 + seaudit/modify_view.c | 327 ++ seaudit/modify_view.h | 41 + seaudit/open_policy_window.c | 469 +++ seaudit/open_policy_window.h | 46 + seaudit/policy_components_view.c | 373 ++ seaudit/policy_components_view.h | 56 + seaudit/policy_view.c | 573 +++ seaudit/policy_view.h | 77 + seaudit/preferences.c | 585 +++ seaudit/preferences.h | 274 ++ seaudit/preferences_view.c | 310 ++ seaudit/preferences_view.h | 44 + seaudit/progress.c | 200 + seaudit/progress.h | 139 + seaudit/report_window.c | 261 ++ seaudit/report_window.h | 41 + seaudit/seaudit-report-group.conf | 3 + seaudit/seaudit-report-service.conf | 5 + seaudit/seaudit-report-service.in | 24 + seaudit/seaudit-report.c | 246 ++ seaudit/seaudit-report.conf | 62 + seaudit/seaudit-report.css | 178 + seaudit/seaudit-small.png | Bin 0 -> 336 bytes seaudit/seaudit.c | 418 ++ seaudit/seaudit.glade | 7713 +++++++++++++++++++++++++++++++++++ seaudit/seaudit.gladep | 7 + seaudit/seaudit.h | 231 ++ seaudit/seaudit.png | Bin 0 -> 992 bytes seaudit/seaudit.xcf | Bin 0 -> 7462 bytes seaudit/seaudit_help.txt | 293 ++ seaudit/toplevel.c | 1179 ++++++ seaudit/toplevel.h | 263 ++ seaudit/utilgui.c | 134 + seaudit/utilgui.h | 106 + 40 files changed, 17468 insertions(+) create mode 100644 seaudit/Makefile.am create mode 100644 seaudit/dot_seaudit.in create mode 100644 seaudit/filter_view.c create mode 100644 seaudit/filter_view.h create mode 100644 seaudit/message_view.c create mode 100644 seaudit/message_view.h create mode 100644 seaudit/modify_view.c create mode 100644 seaudit/modify_view.h create mode 100644 seaudit/open_policy_window.c create mode 100644 seaudit/open_policy_window.h create mode 100644 seaudit/policy_components_view.c create mode 100644 seaudit/policy_components_view.h create mode 100644 seaudit/policy_view.c create mode 100644 seaudit/policy_view.h create mode 100644 seaudit/preferences.c create mode 100644 seaudit/preferences.h create mode 100644 seaudit/preferences_view.c create mode 100644 seaudit/preferences_view.h create mode 100644 seaudit/progress.c create mode 100644 seaudit/progress.h create mode 100644 seaudit/report_window.c create mode 100644 seaudit/report_window.h create mode 100644 seaudit/seaudit-report-group.conf create mode 100644 seaudit/seaudit-report-service.conf create mode 100644 seaudit/seaudit-report-service.in create mode 100644 seaudit/seaudit-report.c create mode 100644 seaudit/seaudit-report.conf create mode 100644 seaudit/seaudit-report.css create mode 100644 seaudit/seaudit-small.png create mode 100644 seaudit/seaudit.c create mode 100644 seaudit/seaudit.glade create mode 100644 seaudit/seaudit.gladep create mode 100644 seaudit/seaudit.h create mode 100644 seaudit/seaudit.png create mode 100644 seaudit/seaudit.xcf create mode 100644 seaudit/seaudit_help.txt create mode 100644 seaudit/toplevel.c create mode 100644 seaudit/toplevel.h create mode 100644 seaudit/utilgui.c create mode 100644 seaudit/utilgui.h (limited to 'seaudit') diff --git a/seaudit/Makefile.am b/seaudit/Makefile.am new file mode 100644 index 0000000..1987c99 --- /dev/null +++ b/seaudit/Makefile.am @@ -0,0 +1,90 @@ +setoolsdir = @setoolsdir@ +bin_PROGRAMS = seaudit-report +sbin_PROGRAMS = seaudit + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEAUDIT_CFLAGS@ + +seaudit_CFLAGS = $(AM_CFLAGS) \ + @GTK_CFLAGS@ @PIXBUF_CFLAGS@ @GLADE_CFLAGS@ @GTHREAD_CFLAGS@ +seaudit_report_CFLAGS = $(AM_CFLAGS) -DAPOL_INSTALL_DIR='"${setoolsdir}"' + +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +# need the -rdynamic flag below - glade uses dlopen() upon seaudit callbacks +seaudit_LDFLAGS = $(AM_LDFLAGS) \ + @GTK_LIBS@ @PIXBUF_LIBS@ @GLADE_LIBS@ @GTHREAD_LIBS@ -rdynamic + +LDADD = @SELINUX_LIB_FLAG@ @SEAUDIT_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ + +dist_setools_DATA = \ + seaudit.glade \ + seaudit_help.txt \ + seaudit-report.conf \ + seaudit-report.css \ + seaudit.png seaudit-small.png + +nodist_setools_DATA = \ + dot_seaudit \ + seaudit-report-service + +seaudit_SOURCES = \ + filter_view.c filter_view.h \ + message_view.c message_view.h \ + modify_view.c modify_view.h \ + open_policy_window.c open_policy_window.h \ + policy_components_view.c policy_components_view.h \ + policy_view.c policy_view.h \ + preferences.c preferences.h \ + preferences_view.c preferences_view.h \ + progress.c progress.h \ + report_window.c report_window.h \ + seaudit.c seaudit.h \ + toplevel.c toplevel.h \ + utilgui.c utilgui.h + +seaudit_DEPENDENCIES = $(top_builddir)/libseaudit/src/libseaudit.so \ + $(top_builddir)/libapol/src/libapol.so \ + $(top_builddir)/libqpol/src/libqpol.so + +dot_seaudit: dot_seaudit.in Makefile + sed -e 's|\@setoolsdir\@|$(setoolsdir)|g' $< > $@ + +seaudit_report_SOURCES = seaudit-report.c +seaudit_report_DEPENDENCIES = $(top_builddir)/libseaudit/src/libseaudit.so \ + $(top_builddir)/libapol/src/libapol.so \ + $(top_builddir)/libqpol/src/libqpol.so + +logwatch = $(DESTDIR)/etc/logwatch +LOGWATCH_GROUP = $(logwatch)/conf/logfiles +LOGWATCH_SERVICE = $(logwatch)/conf/services +LOGWATCH_FILTER = $(logwatch)/scripts/services + +dist_noinst_DATA = dot_seaudit.in \ + seaudit-report-group.conf \ + seaudit-report-service.conf \ + seaudit-report-service.in + +seaudit-report-service: seaudit-report-service.in Makefile + sed -e 's|\@bindir\@|$(bindir)|g' $< > $@ + +install-logwatch: $(dist_noinst_DATA) seaudit-report-service + mkdir -p -- $(LOGWATCH_GROUP) + install -m 644 seaudit-report-group.conf $(LOGWATCH_GROUP) + mkdir -p -- $(LOGWATCH_SERVICE) + install -m 644 seaudit-report-service.conf $(LOGWATCH_SERVICE) + mkdir -p -- $(LOGWATCH_FILTER) + install -m 755 seaudit-report-service $(LOGWATCH_FILTER) + +$(top_builddir)/libapol/src/libapol.so: + $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@) + +$(top_builddir)/libqpol/src/libqpol.so: + $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@) + +$(top_builddir)/libsefs/src/libsefs.so: + $(MAKE) -C $(top_builddir)/libsefs/src $(notdir $@) + +.PHONY: install-logwatch + +CLEANFILES = dot_seaudit seaudit-report-service diff --git a/seaudit/dot_seaudit.in b/seaudit/dot_seaudit.in new file mode 100644 index 0000000..2852819 --- /dev/null +++ b/seaudit/dot_seaudit.in @@ -0,0 +1,12 @@ +# Configuration file for seaudit - an audit log tool for Security +# Enhanced Linux. This file is auto-generated by the build system. + +DEFAULT_LOG_FILE /var/log/audit/audit.log +DEFAULT_POLICY_FILE +DEFAULT_REPORT_CONFIG_FILE @setoolsdir@/seaudit-report.conf +DEFAULT_REPORT_CSS_FILE @setoolsdir@/seaudit-report.css +RECENT_LOG_FILES +RECENT_POLICY_FILES +LOG_COLUMNS_HIDDEN path_field:src_usr_field:src_role_field:tgt_usr_field:tgt_role_field:inode_field:pid_field: +REAL_TIME_LOG_MONITORING 0 +REAL_TIME_UPDATE_INTERVAL 1000 diff --git a/seaudit/filter_view.c b/seaudit/filter_view.c new file mode 100644 index 0000000..e0d8745 --- /dev/null +++ b/seaudit/filter_view.c @@ -0,0 +1,1127 @@ +/** + * @file + * Run the dialog to modify a particular filter. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "filter_view.h" +#include "policy_components_view.h" + +#include +#include +#include +#include +#include +#include +#include + +struct context_item +{ + GtkButton *button; + GtkEntry *entry; + apol_vector_t *items; +}; + +struct date_item +{ + GtkComboBox *month; + GtkSpinButton *day, *hour, *minute, *second; + GtkFrame *frame; +}; + +struct filter_view +{ + toplevel_t *top; + seaudit_filter_t *filter; + GladeXML *xml; + + GtkDialog *dialog; + + GtkEntry *name_entry; + GtkComboBox *match_combo; + + struct context_item suser, srole, stype, smls_lvl, smls_clr, tuser, trole, ttype, tmls_lvl, tmls_clr, obj_class; + GtkButton *context_clear_button; + + GtkEntry *ipaddr_entry, *port_entry, *netif_entry, *exe_entry, *path_entry, *host_entry, *comm_entry; + GtkComboBox *message_combo; + GtkButton *other_clear_button; + + GtkRadioButton *date_none_radio, *date_before_radio, *date_after_radio, *date_between_radio; + struct date_item dates[2]; + GtkTextBuffer *description_buffer; +}; + +/** + * Initialize pointers to widgets on the context tab. + */ +static void filter_view_init_widgets_context(struct filter_view *fv) +{ + fv->suser.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSUserButton")); + fv->srole.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSRoleButton")); + fv->stype.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSTypeButton")); + fv->smls_lvl.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSMLSLVLButton")); + fv->smls_clr.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSMLSCLRButton")); + fv->tuser.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTUserButton")); + fv->trole.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTRoleButton")); + fv->ttype.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTTypeButton")); + fv->tmls_lvl.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTMLSLVLButton")); + fv->tmls_clr.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTMLSCLRButton")); + fv->obj_class.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewClassButton")); + assert(fv->suser.button != NULL && fv->srole.button != NULL && fv->stype.button != NULL && fv->smls_lvl.button != NULL && fv->smls_clr.button != NULL && + fv->tuser.button != NULL && fv->trole.button != NULL && fv->ttype.button != NULL && fv->tmls_lvl.button != NULL && fv->tmls_clr.button != NULL && fv->obj_class.button != NULL); + + fv->suser.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSUserEntry")); + fv->srole.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSRoleEntry")); + fv->stype.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSTypeEntry")); + fv->smls_lvl.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSMLSLVLEntry")); + fv->smls_clr.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSMLSCLREntry")); + fv->tuser.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTUserEntry")); + fv->trole.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTRoleEntry")); + fv->ttype.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTTypeEntry")); + fv->tmls_lvl.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTMLSLVLEntry")); + fv->tmls_clr.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTMLSCLREntry")); + fv->obj_class.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewClassEntry")); + assert(fv->suser.entry != NULL && fv->srole.entry != NULL && fv->stype.entry != NULL && fv->smls_lvl.entry != NULL && fv->smls_clr.entry != NULL && + fv->tuser.entry != NULL && fv->trole.entry != NULL && fv->ttype.entry != NULL && fv->tmls_lvl.entry != NULL && fv->tmls_clr.entry != NULL && fv->obj_class.entry != NULL); + g_object_set_data(G_OBJECT(fv->suser.entry), "data", &fv->suser); + g_object_set_data(G_OBJECT(fv->srole.entry), "data", &fv->srole); + g_object_set_data(G_OBJECT(fv->stype.entry), "data", &fv->stype); + g_object_set_data(G_OBJECT(fv->smls_lvl.entry), "data", &fv->smls_lvl); + g_object_set_data(G_OBJECT(fv->smls_clr.entry), "data", &fv->smls_clr); + g_object_set_data(G_OBJECT(fv->tuser.entry), "data", &fv->tuser); + g_object_set_data(G_OBJECT(fv->trole.entry), "data", &fv->trole); + g_object_set_data(G_OBJECT(fv->ttype.entry), "data", &fv->ttype); + g_object_set_data(G_OBJECT(fv->tmls_lvl.entry), "data", &fv->tmls_lvl); + g_object_set_data(G_OBJECT(fv->tmls_clr.entry), "data", &fv->tmls_clr); + g_object_set_data(G_OBJECT(fv->obj_class.entry), "data", &fv->obj_class); + + fv->context_clear_button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewContextClearButton")); + assert(fv->context_clear_button != NULL); +} + +/** + * Initialize pointers to widgets on the other tab. + */ +static void filter_view_init_widgets_other(struct filter_view *fv) +{ + fv->ipaddr_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewIPAddrEntry")); + fv->port_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewPortEntry")); + fv->netif_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewNetIfEntry")); + assert(fv->ipaddr_entry != NULL && fv->port_entry != NULL && fv->netif_entry != NULL); + + fv->exe_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewExeEntry")); + fv->path_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewPathEntry")); + fv->host_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewHostEntry")); + fv->comm_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewCommEntry")); + assert(fv->exe_entry != NULL && fv->path_entry != NULL && fv->host_entry != NULL && fv->comm_entry != NULL); + + fv->message_combo = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, "FilterViewMessageCombo")); + assert(fv->message_combo != NULL); + + fv->other_clear_button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewOtherClearButton")); + assert(fv->other_clear_button != NULL); +} + +/** + * Initialize pointers to widgets on the date tab. + */ +static void filter_view_init_widgets_date(struct filter_view *fv) +{ + static const char *widgets[2][6] = { + {"FilterViewDateStartFrame", "FilterViewDateStartMonthCombo", "FilterViewDateStartDaySpin", + "FilterViewDateStartHourSpin", "FilterViewDateStartMinuteSpin", "FilterViewDateStartSecondSpin"}, + {"FilterViewDateEndFrame", "FilterViewDateEndMonthCombo", "FilterViewDateEndDaySpin", + "FilterViewDateEndHourSpin", "FilterViewDateEndMinuteSpin", "FilterViewDateEndSecondSpin"} + }; + size_t i; + fv->date_none_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateNoneRadio")); + fv->date_before_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateBeforeRadio")); + fv->date_after_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateAfterRadio")); + fv->date_between_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateBetweenRadio")); + assert(fv->date_none_radio != NULL && fv->date_before_radio != NULL && fv->date_after_radio != NULL + && fv->date_between_radio != NULL); + + for (i = 0; i < 2; i++) { + fv->dates[i].frame = GTK_FRAME(glade_xml_get_widget(fv->xml, widgets[i][0])); + fv->dates[i].month = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, widgets[i][1])); + fv->dates[i].day = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][2])); + fv->dates[i].hour = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][3])); + fv->dates[i].minute = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][4])); + fv->dates[i].second = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][5])); + assert(fv->dates[i].frame != NULL && fv->dates[i].month != NULL && fv->dates[i].day != NULL && + fv->dates[i].hour != NULL && fv->dates[i].minute != NULL && fv->dates[i].second != NULL); + } +} + +static void filter_view_init_widgets(struct filter_view *fv, GtkWindow * parent) +{ + GtkTextView *description_view; + + fv->dialog = GTK_DIALOG(glade_xml_get_widget(fv->xml, "FilterWindow")); + assert(fv->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(fv->dialog), parent); + + fv->name_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewNameEntry")); + fv->match_combo = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, "FilterViewMatchCombo")); + assert(fv->name_entry != NULL && fv->match_combo); + + filter_view_init_widgets_context(fv); + filter_view_init_widgets_other(fv); + filter_view_init_widgets_date(fv); + + fv->description_buffer = gtk_text_buffer_new(NULL); +#ifdef GTK_2_8 + g_object_ref_sink(fv->description_buffer); +#endif + description_view = GTK_TEXT_VIEW(glade_xml_get_widget(fv->xml, "FilterViewDescView")); + assert(description_view != NULL); + gtk_text_view_set_buffer(description_view, fv->description_buffer); +} + +/********** functions that copies filter object values to widget **********/ + +/** + * Get the vector of strings from the accessor function. If the + * vector is NULL then clear the entry's contents; otherwies set the + * entry to the vector of strings, comma delimited. + */ +static void filter_view_context_item_to_entry(struct filter_view *fv, struct context_item *item) +{ + if (item->items == NULL) { + gtk_entry_set_text(item->entry, ""); + } else { + GString *s = g_string_new(""); + size_t i; + for (i = 0; i < apol_vector_get_size(item->items); i++) { + char *t = apol_vector_get_element(item->items, i); + if (i > 0) { + g_string_append(s, ", "); + } + g_string_append(s, t); + } + gtk_entry_set_text(item->entry, s->str); + g_string_free(s, TRUE); + } +} + +static void filter_view_context_items_to_entries(struct filter_view *fv) +{ + filter_view_context_item_to_entry(fv, &fv->suser); + filter_view_context_item_to_entry(fv, &fv->srole); + filter_view_context_item_to_entry(fv, &fv->stype); + filter_view_context_item_to_entry(fv, &fv->smls_lvl); + filter_view_context_item_to_entry(fv, &fv->smls_clr); + filter_view_context_item_to_entry(fv, &fv->tuser); + filter_view_context_item_to_entry(fv, &fv->trole); + filter_view_context_item_to_entry(fv, &fv->ttype); + filter_view_context_item_to_entry(fv, &fv->tmls_lvl); + filter_view_context_item_to_entry(fv, &fv->tmls_clr); + filter_view_context_item_to_entry(fv, &fv->obj_class); +} + +static void filter_view_init_context(struct filter_view *fv) +{ + const apol_vector_t *v; + v = seaudit_filter_get_source_user(fv->filter); + if (v != NULL && (fv->suser.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_source_role(fv->filter); + if (v != NULL && (fv->srole.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_source_type(fv->filter); + if (v != NULL && (fv->stype.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_source_mls_lvl(fv->filter); + if (v != NULL && (fv->smls_lvl.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_source_mls_clr(fv->filter); + if (v != NULL && (fv->smls_clr.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + + v = seaudit_filter_get_target_user(fv->filter); + if (v != NULL && (fv->tuser.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_target_role(fv->filter); + if (v != NULL && (fv->trole.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_target_type(fv->filter); + if (v != NULL && (fv->ttype.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_target_mls_lvl(fv->filter); + if (v != NULL && (fv->tmls_lvl.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + v = seaudit_filter_get_target_mls_clr(fv->filter); + if (v != NULL && (fv->tmls_clr.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + + v = seaudit_filter_get_target_class(fv->filter); + if (v != NULL && (fv->obj_class.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) { + toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno)); + return; + } + filter_view_context_items_to_entries(fv); +} + +/** + * Get the string from the accessor function. If the returned string + * is NULL then clear the entry's contents; otherwise set the entry to + * the returned string. + */ +static void filter_view_init_entry(struct filter_view *fv, const char *(*accessor) (const seaudit_filter_t *), GtkEntry * entry) +{ + const char *s = accessor(fv->filter); + if (s == NULL) { + s = ""; + } + gtk_entry_set_text(entry, s); +} + +static void filter_view_init_other(struct filter_view *fv) +{ + char s[32]; + filter_view_init_entry(fv, seaudit_filter_get_anyaddr, fv->ipaddr_entry); + if (seaudit_filter_get_anyport(fv->filter) <= 0) { + s[0] = '\0'; + } else { + snprintf(s, 32, "%d", seaudit_filter_get_anyport(fv->filter)); + } + gtk_entry_set_text(fv->port_entry, s); + filter_view_init_entry(fv, seaudit_filter_get_netif, fv->netif_entry); + filter_view_init_entry(fv, seaudit_filter_get_executable, fv->exe_entry); + filter_view_init_entry(fv, seaudit_filter_get_path, fv->path_entry); + filter_view_init_entry(fv, seaudit_filter_get_host, fv->host_entry); + filter_view_init_entry(fv, seaudit_filter_get_command, fv->comm_entry); + switch (seaudit_filter_get_message_type(fv->filter)) { + case SEAUDIT_AVC_DENIED: + gtk_combo_box_set_active(fv->message_combo, 1); + break; + case SEAUDIT_AVC_GRANTED: + gtk_combo_box_set_active(fv->message_combo, 2); + break; + default: + gtk_combo_box_set_active(fv->message_combo, 0); + } +} + +static void filter_view_init_date(struct filter_view *fv) +{ + const struct tm *start, *end; + struct tm values[2]; + int has_value[2] = { 0, 0 }; + seaudit_filter_date_match_e match; + size_t i; + + seaudit_filter_get_date(fv->filter, &start, &end, &match); + if (start == NULL && end == NULL) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_none_radio), TRUE); + } else { + if (match == SEAUDIT_FILTER_DATE_MATCH_BEFORE) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_before_radio), TRUE); + } else if (match == SEAUDIT_FILTER_DATE_MATCH_AFTER) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_after_radio), TRUE); + } + memcpy(values + 0, start, sizeof(values[0])); + has_value[0] = 1; + } + if (match == SEAUDIT_FILTER_DATE_MATCH_BETWEEN) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_between_radio), TRUE); + memcpy(values + 1, end, sizeof(values[1])); + has_value[1] = 1; + } + for (i = 0; i < 2; i++) { + if (has_value[i]) { + gtk_combo_box_set_active(fv->dates[i].month, values[i].tm_mon); + gtk_spin_button_set_value(fv->dates[i].day, values[i].tm_mday); + gtk_spin_button_set_value(fv->dates[i].hour, values[i].tm_hour); + gtk_spin_button_set_value(fv->dates[i].minute, values[i].tm_min); + gtk_spin_button_set_value(fv->dates[i].second, values[i].tm_sec); + } else { + gtk_combo_box_set_active(fv->dates[i].month, 0); + } + } +} + +/** + * Copy values from seaudit filter object to GTK+ widgets. + */ +static void filter_view_init_dialog(struct filter_view *fv) +{ + const char *name = seaudit_filter_get_name(fv->filter); + const char *desc = seaudit_filter_get_description(fv->filter);; + if (name == NULL) { + name = "Untitled"; + } + gtk_entry_set_text(fv->name_entry, name); + gtk_combo_box_set_active(fv->match_combo, seaudit_filter_get_match(fv->filter)); + + filter_view_init_context(fv); + filter_view_init_other(fv); + filter_view_init_date(fv); + + if (desc == NULL) { + desc = ""; + } + gtk_text_buffer_set_text(fv->description_buffer, desc, -1); +} + +/********** functions that copies widget values to filter object **********/ + +static void filter_view_apply_context(struct filter_view *fv) +{ + if (seaudit_filter_set_source_user(fv->filter, fv->suser.items) < 0 || + seaudit_filter_set_source_role(fv->filter, fv->srole.items) < 0 || + seaudit_filter_set_source_type(fv->filter, fv->stype.items) < 0 || + seaudit_filter_set_source_mls_lvl(fv->filter, fv->smls_lvl.items) < 0 || + seaudit_filter_set_source_mls_clr(fv->filter, fv->smls_clr.items) < 0 || + seaudit_filter_set_target_user(fv->filter, fv->tuser.items) < 0 || + seaudit_filter_set_target_role(fv->filter, fv->trole.items) < 0 || + seaudit_filter_set_target_type(fv->filter, fv->ttype.items) < 0 || + seaudit_filter_set_target_mls_lvl(fv->filter, fv->tmls_lvl.items) < 0 || + seaudit_filter_set_target_mls_clr(fv->filter, fv->tmls_clr.items) < 0 || + seaudit_filter_set_target_class(fv->filter, fv->obj_class.items) < 0) { + toplevel_ERR(fv->top, "Error applying context: %s", strerror(errno)); + } +} + +/** + * If the entry is empty, then call the modifier function passing NULL + * as the second parameter. Else call the function with the entry's + * contents. + */ +static void filter_view_apply_entry(struct filter_view *fv, GtkEntry * entry, int (*modifier) (seaudit_filter_t *, const char *)) +{ + const char *s = gtk_entry_get_text(entry); + if (strcmp(s, "") == 0) { + s = NULL; + } + if (modifier(fv->filter, s) < 0) { + toplevel_ERR(fv->top, "Error apply settings: %s", strerror(errno)); + } +} + +/** + * Copy values from the other tab to filter object. + */ +static void filter_view_apply_other(struct filter_view *fv) +{ + const char *s; + int port = 0; + seaudit_avc_message_type_e message_type; + + filter_view_apply_entry(fv, fv->ipaddr_entry, seaudit_filter_set_anyaddr); + s = gtk_entry_get_text(fv->port_entry); + if (strcmp(s, "") != 0) { + port = atoi(s); + } + if (seaudit_filter_set_anyport(fv->filter, port) < 0) { + toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno)); + return; + } + filter_view_apply_entry(fv, fv->netif_entry, seaudit_filter_set_netif); + filter_view_apply_entry(fv, fv->exe_entry, seaudit_filter_set_executable); + filter_view_apply_entry(fv, fv->path_entry, seaudit_filter_set_path); + filter_view_apply_entry(fv, fv->host_entry, seaudit_filter_set_host); + filter_view_apply_entry(fv, fv->comm_entry, seaudit_filter_set_command); + switch (gtk_combo_box_get_active(fv->message_combo)) { + case 1: + message_type = SEAUDIT_AVC_DENIED; + break; + case 2: + message_type = SEAUDIT_AVC_GRANTED; + break; + default: + message_type = SEAUDIT_AVC_UNKNOWN; + } + if (seaudit_filter_set_message_type(fv->filter, message_type) < 0) { + toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno)); + return; + } +} + +/** + * Returns which date radio button is active: + * + * -1 if date_none_radio, + * else something that can be casted to seaudit_filter_date_match + */ +static int filter_view_get_date_match(struct filter_view *fv) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_before_radio))) { + return SEAUDIT_FILTER_DATE_MATCH_BEFORE; + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_after_radio))) { + return SEAUDIT_FILTER_DATE_MATCH_AFTER; + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_between_radio))) { + return SEAUDIT_FILTER_DATE_MATCH_BETWEEN; + } + return -1; +} + +/** + * Copy values from date tab to the seaudit filter object. + */ +static void filter_view_apply_date(struct filter_view *fv) +{ + struct tm tm[2]; + size_t i; + int date_match = filter_view_get_date_match(fv); + memset(&tm, 0, sizeof(tm)); + for (i = 0; i < 2; i++) { + tm[i].tm_mon = gtk_combo_box_get_active(fv->dates[i].month); + tm[i].tm_mday = gtk_spin_button_get_value_as_int(fv->dates[i].day); + tm[i].tm_year = 0; + tm[i].tm_hour = gtk_spin_button_get_value_as_int(fv->dates[i].hour); + tm[i].tm_min = gtk_spin_button_get_value_as_int(fv->dates[i].minute); + tm[i].tm_sec = gtk_spin_button_get_value_as_int(fv->dates[i].second); + } + if (date_match < 0) { + seaudit_filter_set_date(fv->filter, NULL, NULL, 0); + } else { + seaudit_filter_set_date(fv->filter, tm + 0, tm + 1, (seaudit_filter_date_match_e) date_match); + } +} + +/** + * Copy values from GTK+ widgets to the seaudit filter object. + */ +static void filter_view_apply(struct filter_view *fv) +{ + GtkTextIter start, end; + char *s; + seaudit_filter_match_e match = SEAUDIT_FILTER_MATCH_ALL; + + filter_view_apply_entry(fv, fv->name_entry, seaudit_filter_set_name); + if (gtk_combo_box_get_active(fv->match_combo) == 1) { + match = SEAUDIT_FILTER_MATCH_ANY; + } + if (seaudit_filter_set_match(fv->filter, match) < 0) { + toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno)); + } + + filter_view_apply_context(fv); + filter_view_apply_other(fv); + filter_view_apply_date(fv); + + gtk_text_buffer_get_bounds(fv->description_buffer, &start, &end); + s = gtk_text_buffer_get_text(fv->description_buffer, &start, &end, FALSE); + if (strcmp(s, "") == 0) { + free(s); + s = NULL; + } + if (seaudit_filter_set_description(fv->filter, s) < 0) { + toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno)); + } + free(s); +} + +/******************** signal handlers for dialog ********************/ + +/** + * Return a list of users within the currently loaded policy, sorted + * alphabetically. If there is no policy loaded then return NULL. + */ +static apol_vector_t *filter_view_get_policy_users(struct filter_view *fv) +{ + apol_vector_t *policy_items = NULL, *v = NULL; + apol_policy_t *p = toplevel_get_policy(fv->top); + size_t i; + if (p == NULL) { + return NULL; + } + if (apol_user_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(fv->top, "Error getting a list of policy users: %s", strerror(errno)); + apol_vector_destroy(&policy_items); + return NULL; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_user_t *e = apol_vector_get_element(v, i); + const char *name; + qpol_user_get_name(apol_policy_get_qpol(p), e, &name); + if (apol_vector_append(policy_items, (void *)name) < 0) { + toplevel_ERR(fv->top, "Error getting a list of policy users: %s", strerror(errno)); + apol_vector_destroy(&v); + apol_vector_destroy(&policy_items); + } + } + apol_vector_destroy(&v); + apol_vector_sort(policy_items, apol_str_strcmp, NULL); + return policy_items; +} + +/** + * Return a list of roles within the currently loaded policy, sorted + * alphabetically. If there is no policy loaded then return NULL. + */ +static apol_vector_t *filter_view_get_policy_roles(struct filter_view *fv) +{ + apol_vector_t *policy_items = NULL, *v = NULL; + apol_policy_t *p = toplevel_get_policy(fv->top); + size_t i; + if (p == NULL) { + return NULL; + } + if (apol_role_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(fv->top, "Error getting a list of policy roles: %s", strerror(errno)); + apol_vector_destroy(&policy_items); + return NULL; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_role_t *e = apol_vector_get_element(v, i); + const char *name; + qpol_role_get_name(apol_policy_get_qpol(p), e, &name); + if (apol_vector_append(policy_items, (void *)name) < 0) { + toplevel_ERR(fv->top, "Error getting a list of policy roles: %s", strerror(errno)); + apol_vector_destroy(&v); + apol_vector_destroy(&policy_items); + } + } + apol_vector_destroy(&v); + apol_vector_sort(policy_items, apol_str_strcmp, NULL); + return policy_items; +} + +/** + * Return a list of types (not attributes nor aliases) within the + * currently loaded policy, sorted alphabetically. If there is no + * policy loaded then return NULL. + */ +static apol_vector_t *filter_view_get_policy_types(struct filter_view *fv) +{ + apol_vector_t *policy_items = NULL, *v = NULL; + apol_policy_t *p = toplevel_get_policy(fv->top); + size_t i; + if (p == NULL) { + return NULL; + } + if (apol_type_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(fv->top, "Error getting a list of policy types: %s", strerror(errno)); + apol_vector_destroy(&policy_items); + return NULL; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_type_t *e = apol_vector_get_element(v, i); + const char *name; + qpol_type_get_name(apol_policy_get_qpol(p), e, &name); + if (apol_vector_append(policy_items, (void *)name) < 0) { + toplevel_ERR(fv->top, "Error getting a list of policy types: %s", strerror(errno)); + apol_vector_destroy(&v); + apol_vector_destroy(&policy_items); + } + } + apol_vector_destroy(&v); + apol_vector_sort(policy_items, apol_str_strcmp, NULL); + return policy_items; +} + +/** + * Return a list of mls levels/clearance (not aliases) within the + * currently loaded policy, sorted alphabetically. If there is no + * policy loaded then return NULL. + */ +static apol_vector_t *filter_view_get_policy_mls_lvl(struct filter_view *fv) +{ + apol_vector_t *policy_items = NULL, *v = NULL; + apol_policy_t *p = toplevel_get_policy(fv->top); + const qpol_iterator_t **cats = NULL; + + size_t i; + + if (p == NULL) { + return NULL; + } + + if (apol_level_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(&free)) == NULL) { + toplevel_ERR(fv->top, "Error getting a list of policy mls levels: %s", strerror(errno)); + apol_vector_destroy(&policy_items); + return NULL; + } + + for (i = 0; i < apol_vector_get_size(v); i++) + { + const char *name = NULL; + const char *mls = malloc(100*sizeof(mls)); + + const char *cat_name1 = NULL, *cat_name2 = NULL; + uint32_t *cat_val1, *cat_val2; + const char *cat_low = NULL; + const char *cat_high = NULL; + bool isrange = false, isrange_end = false, isempty = true; + size_t k; + qpol_cat_t *c = NULL; + const qpol_level_t *e = apol_vector_get_element(v, i); + + qpol_level_get_name(apol_policy_get_qpol(p), e, &name); + strcpy(mls, name); + if (qpol_level_get_cat_iter(apol_policy_get_qpol(p), e, &cats) < 0){ + toplevel_ERR(fv->top, "Error getting categories for level: %s", strerror(errno)); + qpol_iterator_destroy(&cats); + } + qpol_iterator_get_size(cats, &k); + + if (k > 0){ + strcat(mls, ":"); + isempty = true; + } + if (qpol_iterator_get_item(cats, &c) < 0){ + toplevel_ERR(fv->top, "Error getting category: %s", strerror(errno)); + qpol_iterator_destroy(&cats); + } + qpol_cat_get_value(apol_policy_get_qpol(p), c, &cat_val1); + qpol_cat_get_name(apol_policy_get_qpol(p), c, &cat_name1); + isrange = false; + isrange_end = false; + + for (; !qpol_iterator_end(cats); qpol_iterator_next(cats)) + { + if (qpol_iterator_get_item(cats, &c) < 0){ + toplevel_ERR(fv->top, "Error getting category: %s", strerror(errno)); + qpol_iterator_destroy(&cats); + } + qpol_cat_get_value(apol_policy_get_qpol(p), c, &cat_val2); + qpol_cat_get_name(apol_policy_get_qpol(p), c, &cat_name2); + if (((int)cat_val2 == ((int)cat_val1 + 1)) && (isrange == false)) + { + cat_low = cat_name1; + strcat(mls, cat_low); + strcat(mls, "."); + isrange = true; + isempty = false; + } + if ((isrange == true) && ((int)cat_val2 == ((int)cat_val1 + 1))) + { + cat_high = cat_name2; + } + else if ((isrange == true) && ((int)cat_val2 != ((int)cat_val1 + 1))) + { + cat_high = cat_name1; + isrange_end = true; + strcat(mls, cat_high); + isempty=false; + } + if ((isrange == false) && (isempty == false) && ((int)cat_val2 != ((int)cat_val1 + 1))) + { + strcat(mls, ","); + strcat(mls, cat_name2); + } + cat_val1 = cat_val2; + cat_name1 = cat_name2; + + } + if ((isrange == true) && (isrange_end == false)) + { + strcat(mls, cat_high); + } + if (apol_vector_append(policy_items, (void *)mls) < 0) { + toplevel_ERR(fv->top, "Error getting a list of policy mls levels: %s", strerror(errno)); + apol_vector_destroy(&v); + apol_vector_destroy(&policy_items); + } + } + + apol_vector_destroy(&v); + qpol_iterator_destroy(&cats); + apol_vector_sort(policy_items, apol_str_strcmp, NULL); + return policy_items; +} + +/** + * Return a list of object classeswithin the currently loaded policy, + * sorted alphabetically. If there is no policy loaded then return + * NULL. + */ +static apol_vector_t *filter_view_get_policy_classes(struct filter_view *fv) +{ + apol_vector_t *policy_items = NULL, *v = NULL; + apol_policy_t *p = toplevel_get_policy(fv->top); + size_t i; + if (p == NULL) { + return NULL; + } + if (apol_class_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(fv->top, "Error getting a list of policy classes: %s", strerror(errno)); + apol_vector_destroy(&policy_items); + return NULL; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + const qpol_class_t *e = apol_vector_get_element(v, i); + const char *name; + qpol_class_get_name(apol_policy_get_qpol(p), e, &name); + if (apol_vector_append(policy_items, (void *)name) < 0) { + toplevel_ERR(fv->top, "Error getting a list of policy classes: %s", strerror(errno)); + apol_vector_destroy(&v); + apol_vector_destroy(&policy_items); + } + } + apol_vector_destroy(&v); + apol_vector_sort(policy_items, apol_str_strcmp, NULL); + return policy_items; +} + +static void filter_view_on_suser_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_users(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_users(fv); + fv->suser.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source User Items", log_items, policy_items, + fv->suser.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->suser); +} + +static void filter_view_on_srole_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_roles(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_roles(fv); + fv->srole.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source Role Items", log_items, policy_items, + fv->srole.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->srole); +} + +static void filter_view_on_stype_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_types(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_types(fv); + fv->stype.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source Type Items", log_items, policy_items, + fv->stype.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->stype); +} + +static void filter_view_on_smls_lvl_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + + apol_vector_t *log_items = toplevel_get_log_mls_lvl(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv); + fv->smls_lvl.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source MLS Level Items", log_items, policy_items, + fv->smls_lvl.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->smls_lvl); +} + +static void filter_view_on_smls_clr_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_mls_clr(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv); + fv->smls_clr.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source MLS Clearance Items", log_items, policy_items, + fv->smls_clr.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->smls_clr); +} + +static void filter_view_on_tuser_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_users(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_users(fv); + fv->tuser.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target User Items", log_items, policy_items, + fv->tuser.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->tuser); +} + +static void filter_view_on_trole_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_roles(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_roles(fv); + fv->trole.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target Role Items", log_items, policy_items, + fv->trole.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->trole); +} + +static void filter_view_on_ttype_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_types(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_types(fv); + fv->ttype.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target Type Items", log_items, policy_items, + fv->ttype.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->ttype); +} + +static void filter_view_on_tmls_lvl_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_mls_lvl(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv); + fv->tmls_lvl.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target MLS Level Items", log_items, policy_items, + fv->tmls_lvl.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->tmls_lvl); +} + +static void filter_view_on_tmls_clr_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_mls_clr(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv); + fv->tmls_clr.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target MLS Clearance Items", log_items, policy_items, + fv->tmls_clr.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->tmls_clr); +} + +static void filter_view_on_class_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + apol_vector_t *log_items = toplevel_get_log_classes(fv->top); + apol_vector_t *policy_items = filter_view_get_policy_classes(fv); + fv->obj_class.items = + policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Object Class Items", log_items, policy_items, + fv->obj_class.items); + apol_vector_destroy(&log_items); + apol_vector_destroy(&policy_items); + filter_view_context_item_to_entry(fv, &fv->obj_class); +} + +/** + * Whenever the user finished manually editing a context entry, + * convert the entry's string into the underlying vector. + */ +static gboolean filter_view_on_entry_focus_out(GtkWidget * widget, GdkEventFocus * event + __attribute__ ((unused)), gpointer user_data) +{ + struct context_item *item = g_object_get_data(G_OBJECT(widget), "data"); + struct filter_view *fv = (struct filter_view *)user_data; + gchar **strs = g_strsplit(gtk_entry_get_text(GTK_ENTRY(widget)), ",", -1); + gchar *s; + size_t i = 0; + char *t; + apol_vector_t *new_v = NULL; + while (1) { + s = strs[i++]; + if (s == NULL) { + break; + } + if (new_v == NULL && (new_v = apol_vector_create(free)) == NULL) { + toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno)); + break; + } + if ((t = strdup(s)) == NULL) { + toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno)); + free(t); + break; + } + apol_str_trim(t); + if (apol_vector_append(new_v, t) < 0) { + toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno)); + free(t); + break; + } + } + g_strfreev(strs); + apol_vector_destroy(&item->items); + item->items = new_v; + filter_view_context_item_to_entry(fv, item); + return FALSE; +} + +static void filter_view_destroy_context_vectors(struct filter_view *fv) +{ + apol_vector_destroy(&fv->suser.items); + apol_vector_destroy(&fv->srole.items); + apol_vector_destroy(&fv->stype.items); + apol_vector_destroy(&fv->smls_lvl.items); + apol_vector_destroy(&fv->smls_clr.items); + apol_vector_destroy(&fv->tuser.items); + apol_vector_destroy(&fv->trole.items); + apol_vector_destroy(&fv->ttype.items); + apol_vector_destroy(&fv->tmls_lvl.items); + apol_vector_destroy(&fv->tmls_clr.items); + apol_vector_destroy(&fv->obj_class.items); +} + +static void filter_view_on_context_clear_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + filter_view_destroy_context_vectors(fv); + filter_view_context_items_to_entries(fv); +} + +static void filter_view_init_context_signals(struct filter_view *fv) +{ + g_signal_connect(fv->suser.button, "clicked", G_CALLBACK(filter_view_on_suser_context_click), fv); + g_signal_connect(fv->suser.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->srole.button, "clicked", G_CALLBACK(filter_view_on_srole_context_click), fv); + g_signal_connect(fv->srole.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->stype.button, "clicked", G_CALLBACK(filter_view_on_stype_context_click), fv); + g_signal_connect(fv->stype.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->smls_lvl.button, "clicked", G_CALLBACK(filter_view_on_smls_lvl_context_click), fv); + g_signal_connect(fv->smls_lvl.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->smls_clr.button, "clicked", G_CALLBACK(filter_view_on_smls_clr_context_click), fv); + g_signal_connect(fv->smls_clr.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->tuser.button, "clicked", G_CALLBACK(filter_view_on_tuser_context_click), fv); + g_signal_connect(fv->tuser.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->trole.button, "clicked", G_CALLBACK(filter_view_on_trole_context_click), fv); + g_signal_connect(fv->trole.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->ttype.button, "clicked", G_CALLBACK(filter_view_on_ttype_context_click), fv); + g_signal_connect(fv->ttype.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->tmls_lvl.button, "clicked", G_CALLBACK(filter_view_on_tmls_lvl_context_click), fv); + g_signal_connect(fv->tmls_lvl.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->tmls_clr.button, "clicked", G_CALLBACK(filter_view_on_tmls_clr_context_click), fv); + g_signal_connect(fv->tmls_clr.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->obj_class.button, "clicked", G_CALLBACK(filter_view_on_class_context_click), fv); + g_signal_connect(fv->obj_class.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv); + g_signal_connect(fv->context_clear_button, "clicked", G_CALLBACK(filter_view_on_context_clear_click), fv); +} + +static void filter_view_on_other_clear_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + gtk_entry_set_text(fv->ipaddr_entry, ""); + gtk_entry_set_text(fv->port_entry, ""); + gtk_entry_set_text(fv->netif_entry, ""); + gtk_entry_set_text(fv->exe_entry, ""); + gtk_entry_set_text(fv->path_entry, ""); + gtk_entry_set_text(fv->host_entry, ""); + gtk_entry_set_text(fv->comm_entry, ""); + gtk_combo_box_set_active(fv->message_combo, 0); +} + +static void filter_view_on_date_toggle(GtkToggleButton * widget, gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + int match; + /* clicking on the radio buttons emit two toggle signals, one for + * the original button and one for the new one. thus only need to + * handle half of all signals */ + if (!gtk_toggle_button_get_active(widget)) { + return; + } + match = filter_view_get_date_match(fv); + gtk_widget_set_sensitive(GTK_WIDGET(fv->dates[0].frame), (match != -1)); + gtk_widget_set_sensitive(GTK_WIDGET(fv->dates[1].frame), (match == SEAUDIT_FILTER_DATE_MATCH_BETWEEN)); +} + +/* Given the year and the month set the spin button to have the + correct number of days for that month */ +static void filter_view_date_set_number_days(int month, GtkSpinButton * button) +{ + static const int days[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int cur_day; + /* get the current day because set_range moves the current day + * by the difference minus 1 day. e.g., going from jan 20 to + * february the day would automatically become 18 */ + cur_day = gtk_spin_button_get_value_as_int(button); + gtk_spin_button_set_range(button, 1, days[month]); + /* return to current day, or to the max value allowed in range */ + gtk_spin_button_set_value(button, cur_day); +} + +static void filter_view_on_month0_change(GtkComboBox * widget, gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + filter_view_date_set_number_days(gtk_combo_box_get_active(widget), fv->dates[0].day); +} + +static void filter_view_on_month1_change(GtkComboBox * widget, gpointer user_data) +{ + struct filter_view *fv = (struct filter_view *)user_data; + filter_view_date_set_number_days(gtk_combo_box_get_active(widget), fv->dates[1].day); +} + +static void filter_view_init_signals(struct filter_view *fv) +{ + filter_view_init_context_signals(fv); + g_signal_connect(fv->other_clear_button, "clicked", G_CALLBACK(filter_view_on_other_clear_click), fv); + g_signal_connect(fv->date_none_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv); + g_signal_connect(fv->date_before_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv); + g_signal_connect(fv->date_after_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv); + g_signal_connect(fv->date_between_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv); + g_signal_connect(fv->dates[0].month, "changed", G_CALLBACK(filter_view_on_month0_change), fv); + g_signal_connect(fv->dates[1].month, "changed", G_CALLBACK(filter_view_on_month1_change), fv); +} + +/******************** public function below ********************/ + +void filter_view_run(seaudit_filter_t * filter, toplevel_t * top, GtkWindow * parent) +{ + struct filter_view fv; + gint response; + + memset(&fv, 0, sizeof(fv)); + fv.top = top; + fv.filter = filter; + fv.xml = glade_xml_new(toplevel_get_glade_xml(top), "FilterWindow", NULL); + filter_view_init_widgets(&fv, parent); + filter_view_init_signals(&fv); + filter_view_init_dialog(&fv); + do { + response = gtk_dialog_run(fv.dialog); + } while (response != GTK_RESPONSE_CLOSE); + + filter_view_apply(&fv); + g_object_unref(fv.description_buffer); + gtk_widget_destroy(GTK_WIDGET(fv.dialog)); + filter_view_destroy_context_vectors(&fv); +} diff --git a/seaudit/filter_view.h b/seaudit/filter_view.h new file mode 100644 index 0000000..d94ece3 --- /dev/null +++ b/seaudit/filter_view.h @@ -0,0 +1,42 @@ +/** + * @file + * Dialog that allows the user to modify a particular filter. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FILTER_VIEW_H +#define FILTER_VIEW_H + +#include "toplevel.h" +#include +#include + +/** + * Display and run a dialog that allows the user to modify a single + * filter. + * + * @param top Toplevel containing message view. + * @param view Message view to modify. + * @param parent Parent window upon which to center this dialog. + */ +void filter_view_run(seaudit_filter_t * filter, toplevel_t * top, GtkWindow * parent); + +#endif diff --git a/seaudit/message_view.c b/seaudit/message_view.c new file mode 100644 index 0000000..64d625f --- /dev/null +++ b/seaudit/message_view.c @@ -0,0 +1,1341 @@ +/** + * @file + * Implementation of the view for a libseaudit model. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "message_view.h" +#include "modify_view.h" +#include "utilgui.h" + +#include +#include +#include +#include +#include +#include + +/** + * A custom model that implements the interfaces GtkTreeModel and + * GtkTreeSortable. + */ +typedef struct message_view_store +{ + /** this must be the first field, to satisfy glib */ + GObject parent; + /** pointer to the store's controller */ + message_view_t *view; + /** vector of seaudit_message_t, as returned by + * seaudit_model_get_messages() */ + apol_vector_t *messages; + /** column that is currently being sorted; use OTHER_FIELD to + * indicate no sorting */ + gint sort_field; + /** current sort direction, either 1 or ascending or -1 for + * descending */ + int sort_dir; + /** unique integer for each instance of a model */ + gint stamp; +} message_view_store_t; + +typedef struct message_view_store_class +{ + GObjectClass parent_class; +} message_view_store_class_t; + +static GType message_view_store_get_type(void); +#define SEAUDIT_TYPE_MESSAGE_VIEW_STORE (message_view_store_get_type()) +#define SEAUDIT_IS_MESSAGE_VIEW_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAUDIT_TYPE_MESSAGE_VIEW_STORE)) + +struct message_view +{ + seaudit_model_t *model; + toplevel_t *top; + /** toplevel of the view, currently a scrolled_window */ + GtkWidget *w; + /** actual GTK+ tree view widget that displays the rows and + * columns of message data */ + GtkTreeView *view; + /** GTK+ store that models messages within the tree */ + message_view_store_t *store; + /** filename for when this view was saved (could be NULL) */ + char *filename; + /** most recent filename for exported messages (could be NULL) */ + char *export_filename; +}; + +typedef seaudit_sort_t *(*sort_generator_fn_t) (int direction); + +struct view_column_record +{ + preference_field_e id; + const char *name; + const char *sample_text; + sort_generator_fn_t sort; +}; + +static const struct view_column_record column_data[] = { + {HOST_FIELD, "Hostname", "Hostname", seaudit_sort_by_host}, + {MESSAGE_FIELD, "Message", "Message", seaudit_sort_by_message_type}, + {DATE_FIELD, "Date", "Jan 01 00:00:00", seaudit_sort_by_date}, + {SUSER_FIELD, "Source\nUser", "Source", seaudit_sort_by_source_user}, + {SROLE_FIELD, "Source\nRole", "Source", seaudit_sort_by_source_role}, + {STYPE_FIELD, "Source\nType", "unlabeled_t", seaudit_sort_by_source_type}, + {SMLS_LVL_FIELD, "Source\nMLS Level", "MLS Level", seaudit_sort_by_source_mls_lvl}, + {SMLS_CLR_FIELD, "Source\nMLS Clearance", "MLS Clearance", seaudit_sort_by_source_mls_clr}, + {TUSER_FIELD, "Target\nUser", "Target", seaudit_sort_by_target_user}, + {TROLE_FIELD, "Target\nRole", "Target", seaudit_sort_by_target_role}, + {TTYPE_FIELD, "Target\nType", "unlabeled_t", seaudit_sort_by_target_type}, + {TMLS_LVL_FIELD, "Target\nMLS Level", "MLS Level", seaudit_sort_by_target_mls_lvl}, + {TMLS_CLR_FIELD, "Target\nMLS Clearance", "MLS Clearance", seaudit_sort_by_target_mls_clr}, + {OBJCLASS_FIELD, "Object\nClass", "Object", seaudit_sort_by_object_class}, + {PERM_FIELD, "Permission", "Permission", seaudit_sort_by_permission}, + {EXECUTABLE_FIELD, "Executable", "/usr/bin/cat", seaudit_sort_by_executable}, + {COMMAND_FIELD, "Command", "/usr/bin/cat", seaudit_sort_by_command}, + {NAME_FIELD, "Name", "iceweasel", seaudit_sort_by_name}, + {PID_FIELD, "PID", "12345", seaudit_sort_by_pid}, + {INODE_FIELD, "Inode", "123456", seaudit_sort_by_inode}, + {PATH_FIELD, "Path", "/home/gburdell/foo", seaudit_sort_by_path}, + {OTHER_FIELD, "Other", "Lorem ipsum dolor sit amet, consectetur", NULL} +}; + +static const size_t num_columns = sizeof(column_data) / sizeof(column_data[0]); + +/** + * (Re)sort the view based upon which column is clicked. If already + * sorting on this column, then reverse the sort direction. Also + * update the sort indicator for this column. + */ +static gboolean message_view_on_column_click(GtkTreeViewColumn * column, gpointer user_data) +{ + gint column_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column id")); + message_view_t *view = (message_view_t *) user_data; + int dir = 0; + seaudit_sort_t *sort; + GtkTreeViewColumn *prev_column; + if (column_id == view->store->sort_field) { + dir = view->store->sort_dir * -1; + } else { + dir = 1; + } + + if ((sort = column_data[(preference_field_e) column_id].sort(dir)) == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return TRUE; + } + seaudit_model_clear_sorts(view->model); + if (seaudit_model_append_sort(view->model, sort) < 0) { + seaudit_sort_destroy(&sort); + toplevel_ERR(view->top, "%s", strerror(errno)); + } + prev_column = gtk_tree_view_get_column(view->view, view->store->sort_field); + if (prev_column != NULL) { + gtk_tree_view_column_set_sort_indicator(prev_column, FALSE); + } + gtk_tree_view_column_set_sort_indicator(column, TRUE); + if (dir > 0) { + gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING); + } else { + gtk_tree_view_column_set_sort_order(column, GTK_SORT_DESCENDING); + } + + view->store->sort_field = column_id; + view->store->sort_dir = dir; + message_view_update_rows(view); + return TRUE; +} + +/*************** implementation of a custom GtkTreeModel ***************/ + +static GObjectClass *parent_class = NULL; + +static void message_view_store_init(message_view_store_t * m); +static void message_view_store_class_init(message_view_store_class_t * c); +static void message_view_store_tree_init(GtkTreeModelIface * iface); +static void message_view_store_finalize(GObject * object); +static GtkTreeModelFlags message_view_store_get_flags(GtkTreeModel * tree_model); +static gint message_view_store_get_n_columns(GtkTreeModel * tree_model); +static GType message_view_store_get_column_type(GtkTreeModel * tree_model, gint index); +static gboolean message_view_store_get_iter(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path); +static GtkTreePath *message_view_store_get_path(GtkTreeModel * tree_model, GtkTreeIter * iter); +static void message_view_store_get_value(GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value); +static gboolean message_view_store_iter_next(GtkTreeModel * tree_model, GtkTreeIter * iter); +static gboolean message_view_store_iter_children(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent); +static gboolean message_view_store_iter_has_child(GtkTreeModel * tree_model, GtkTreeIter * iter); +static gint message_view_store_iter_n_children(GtkTreeModel * tree_model, GtkTreeIter * iter); +static gboolean message_view_store_iter_nth_child(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n); +static gboolean message_view_store_iter_parent(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * child); + +static GType message_view_store_get_type(void) +{ + static GType store_type = 0; + static const GTypeInfo store_info = { + sizeof(message_view_store_class_t), + NULL, + NULL, + (GClassInitFunc) message_view_store_class_init, + NULL, + NULL, + sizeof(message_view_store_t), + 0, + (GInstanceInitFunc) message_view_store_init + }; + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) message_view_store_tree_init, + NULL, + NULL + }; + + if (store_type) + return store_type; + + store_type = g_type_register_static(G_TYPE_OBJECT, "message_view_store", &store_info, (GTypeFlags) 0); + g_type_add_interface_static(store_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + return store_type; +} + +static void message_view_store_init(message_view_store_t * m) +{ + static int next_stamp = 0; + m->messages = NULL; + m->sort_field = OTHER_FIELD; + m->sort_dir = 1; + m->stamp = next_stamp++; +} + +static void message_view_store_class_init(message_view_store_class_t * c) +{ + GObjectClass *object_class; + parent_class = g_type_class_peek_parent(c); + object_class = (GObjectClass *) c; + object_class->finalize = message_view_store_finalize; +} + +static void message_view_store_tree_init(GtkTreeModelIface * iface) +{ + iface->get_flags = message_view_store_get_flags; + iface->get_n_columns = message_view_store_get_n_columns; + iface->get_column_type = message_view_store_get_column_type; + iface->get_iter = message_view_store_get_iter; + iface->get_path = message_view_store_get_path; + iface->get_value = message_view_store_get_value; + iface->iter_next = message_view_store_iter_next; + iface->iter_children = message_view_store_iter_children; + iface->iter_has_child = message_view_store_iter_has_child; + iface->iter_n_children = message_view_store_iter_n_children; + iface->iter_nth_child = message_view_store_iter_nth_child; + iface->iter_parent = message_view_store_iter_parent; +} + +static void message_view_store_finalize(GObject * object) +{ + (*parent_class->finalize) (object); +} + +static GtkTreeModelFlags message_view_store_get_flags(GtkTreeModel * tree_model) +{ + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), 0); + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static gint message_view_store_get_n_columns(GtkTreeModel * tree_model __attribute__ ((unused))) +{ + return OTHER_FIELD + 1; +} + +static GType message_view_store_get_column_type(GtkTreeModel * tree_model, gint idx __attribute__ ((unused))) +{ + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), G_TYPE_INVALID); + /* everything is a string for now */ + return G_TYPE_STRING; +} + +static gboolean message_view_store_get_iter(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path) +{ + gint i; + message_view_store_t *store = (message_view_store_t *) tree_model; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE); + g_return_val_if_fail(gtk_tree_path_get_depth(path) > 0, FALSE); + i = gtk_tree_path_get_indices(path)[0]; + if (i >= apol_vector_get_size(store->messages)) + return FALSE; + + iter->stamp = store->stamp; + iter->user_data = apol_vector_get_element(store->messages, i); + iter->user_data2 = GINT_TO_POINTER(i); + iter->user_data3 = store->view; + return TRUE; +} + +static GtkTreePath *message_view_store_get_path(GtkTreeModel * tree_model, GtkTreeIter * iter) +{ + GtkTreePath *retval; + message_view_store_t *store = (message_view_store_t *) tree_model; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), NULL); + g_return_val_if_fail(iter->stamp == store->stamp, NULL); + retval = gtk_tree_path_new(); + gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data2)); + return retval; +} + +/** + * Given a string, check that it is UTF8 legal. If not, or if the + * string is NULL, then return an empty string. Otherwise return the + * original string. + */ +static void message_view_to_utf8(GValue * value, const char *s) +{ + if (s == NULL || !g_utf8_validate(s, -1, NULL)) { + g_value_set_string(value, ""); + } + g_value_set_string(value, s); +} + +static void message_view_store_get_value(GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value) +{ + message_view_store_t *store; + message_view_t *view; + seaudit_message_t *m; + seaudit_message_type_e type; + void *data; + seaudit_avc_message_t *avc; + g_return_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model)); + g_return_if_fail(iter != NULL); + g_return_if_fail(column <= OTHER_FIELD); + g_value_init(value, G_TYPE_STRING); + store = (message_view_store_t *) tree_model; + view = store->view; + m = (seaudit_message_t *) iter->user_data; + data = seaudit_message_get_data(m, &type); + preference_field_e field = column; + + switch (field) { + case HOST_FIELD: + { + message_view_to_utf8(value, seaudit_message_get_host(m)); + return; + } + case MESSAGE_FIELD: + { + char *message = "Invalid"; + switch (type) { + case SEAUDIT_MESSAGE_TYPE_BOOL: + { + message = "Boolean"; + break; + } + case SEAUDIT_MESSAGE_TYPE_LOAD: + { + message = "Load"; + break; + } + case SEAUDIT_MESSAGE_TYPE_AVC: + { + avc = (seaudit_avc_message_t *) data; + seaudit_avc_message_type_e avc_type; + avc_type = seaudit_avc_message_get_message_type(avc); + switch (avc_type) { + case SEAUDIT_AVC_DENIED: + { + message = "Denied"; + break; + } + case SEAUDIT_AVC_GRANTED: + { + message = "Granted"; + break; + } + default: + { + /* should never get here */ + toplevel_ERR(view->top, "Got an invalid AVC message type %d!", avc_type); + assert(0); + return; + } + } + break; + } + default: + { + /* should never get here */ + toplevel_ERR(view->top, "Got an invalid message type %d!", type); + assert(0); + return; + } + } + message_view_to_utf8(value, message); + return; + } + case DATE_FIELD: + { + const struct tm *tm = seaudit_message_get_time(m); + char date[256]; + /* check to see if we have been given a valid year, if + * so display, otherwise no year displayed */ + if (tm->tm_year == 0) { + strftime(date, 256, "%b %d %H:%M:%S", tm); + } else { + strftime(date, 256, "%b %d %H:%M:%S %Y", tm); + } + message_view_to_utf8(value, date); + return; + } + case OTHER_FIELD: + { + char *other = seaudit_message_to_misc_string(m);; + if (other == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + message_view_to_utf8(value, other); + free(other); + return; + } + default: /* FALLTHROUGH */ + break; + } + + if (type != SEAUDIT_MESSAGE_TYPE_AVC) { + /* the rest of the columns are blank for non-AVC + * messages */ + message_view_to_utf8(value, ""); + return; + } + avc = (seaudit_avc_message_t *) data; + + switch (field) { + case SUSER_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_source_user(avc)); + return; + } + case SROLE_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_source_role(avc)); + return; + } + case STYPE_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_source_type(avc)); + return; + } + case SMLS_LVL_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_source_mls_lvl(avc)); + return; + } + case SMLS_CLR_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_source_mls_clr(avc)); + return; + } + case TUSER_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_target_user(avc)); + return; + } + case TROLE_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_target_role(avc)); + return; + } + case TTYPE_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_target_type(avc)); + return; + } + case TMLS_LVL_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_target_mls_lvl(avc)); + return; + } + case TMLS_CLR_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_target_mls_clr(avc)); + return; + } + case OBJCLASS_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_object_class(avc)); + return; + } + case PERM_FIELD: + { + const apol_vector_t *perms = seaudit_avc_message_get_perm(avc); + char *perm = NULL; + size_t i, len = 0; + for (i = 0; perms != NULL && i < apol_vector_get_size(perms); i++) { + char *p = apol_vector_get_element(perms, i); + if (apol_str_appendf(&perm, &len, "%s%s", (i > 0 ? "," : ""), p) < 0) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + } + message_view_to_utf8(value, perm); + free(perm); + return; + } + case EXECUTABLE_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_exe(avc)); + return; + } + case COMMAND_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_comm(avc)); + return; + } + case NAME_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_name(avc)); + return; + } + case PID_FIELD: + { + char *s; + if (asprintf(&s, "%u", seaudit_avc_message_get_pid(avc)) < 0) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + message_view_to_utf8(value, s); + free(s); + return; + } + case INODE_FIELD: + { + char *s; + if (asprintf(&s, "%lu", seaudit_avc_message_get_inode(avc)) < 0) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + message_view_to_utf8(value, s); + free(s); + return; + } + case PATH_FIELD: + { + message_view_to_utf8(value, seaudit_avc_message_get_path(avc)); + return; + } + default: /* FALLTHROUGH */ + break; + } + /* should never get here */ + toplevel_ERR(view->top, "Got an invalid column %d!", field); + assert(0); +} + +static gboolean message_view_store_iter_next(GtkTreeModel * tree_model, GtkTreeIter * iter) +{ + gint i; + message_view_store_t *store = (message_view_store_t *) tree_model; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE); + g_return_val_if_fail(iter->stamp == store->stamp, FALSE); + if (iter == NULL || iter->user_data == NULL) + return FALSE; + i = GPOINTER_TO_INT(iter->user_data2) + 1; + if (i >= apol_vector_get_size(store->messages)) { + return FALSE; + } + iter->user_data = apol_vector_get_element(store->messages, i); + iter->user_data2 = GINT_TO_POINTER(i); + iter->user_data3 = store->view; + return TRUE; +} + +static gboolean message_view_store_iter_children(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent) +{ + message_view_store_t *store; + g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE); + if (parent) + return FALSE; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE); + + /* set iterator to first row, if possible */ + store = (message_view_store_t *) tree_model; + if (store->messages == NULL || apol_vector_get_size(store->messages) == 0) + return FALSE; + + iter->stamp = store->stamp; + iter->user_data = apol_vector_get_element(store->messages, 0); + iter->user_data2 = GINT_TO_POINTER(0); + iter->user_data3 = store->view; + return TRUE; +} + +static gboolean message_view_store_iter_has_child(GtkTreeModel * tree_model __attribute__ ((unused)), GtkTreeIter * iter + __attribute__ ((unused))) +{ + return FALSE; +} + +static gint message_view_store_iter_n_children(GtkTreeModel * tree_model, GtkTreeIter * iter) +{ + message_view_store_t *store; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), -1); + g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0); + store = (message_view_store_t *) tree_model; + /* return the number of rows, if iterator is at the top; + * otherwise return 0 because this store is just a list */ + if (iter != NULL || store->messages == NULL) { + return 0; + } + return apol_vector_get_size(store->messages); +} + +static gboolean message_view_store_iter_nth_child(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n) +{ + message_view_store_t *store; + g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE); + store = (message_view_store_t *) tree_model; + if (store->messages == NULL || parent != NULL) { + return FALSE; + } + if (n >= apol_vector_get_size(store->messages)) { + return FALSE; + } + iter->stamp = store->stamp; + iter->user_data = apol_vector_get_element(store->messages, n); + iter->user_data2 = GINT_TO_POINTER(n); + iter->user_data3 = store->view; + return TRUE; +} + +static gboolean message_view_store_iter_parent(GtkTreeModel * tree_model __attribute__ ((unused)), GtkTreeIter * iter + __attribute__ ((unused)), GtkTreeIter * child __attribute__ ((unused))) +{ + return FALSE; +} + +/*************** end of custom GtkTreeModel implementation ***************/ + +/*************** message_view_messages_vector() callbacks ******************/ +#define LBACK 1 +#define LFORWARD 2 +#define LNOOP 255 + +typedef struct _msg_user_data +{ + message_view_t *view; + apol_vector_t *messages; + GtkDialog *dialog; + GtkTextBuffer *buffer; + gint handle_id; +} _msg_user_data_t; + +static void message_view_dialog_change(GtkTreeModel * tree_model, + GtkTreePath * path __attribute__ ((unused)), + GtkTreeIter * iter __attribute__ ((unused)), gpointer user_data) +{ + _msg_user_data_t *d = (_msg_user_data_t *) user_data; + /* Disconnect this signal handler after it fires. It's one + * shot, nothing should be able to bring the next and previous + * buttons back from the dead if the view ever changes. + */ + g_signal_handler_disconnect(tree_model, d->handle_id); + d->view = NULL; + gtk_dialog_set_response_sensitive(d->dialog, LBACK, FALSE); + gtk_dialog_set_response_sensitive(d->dialog, LFORWARD, FALSE); +} + +static void message_view_dialog_response(GtkDialog * dialog, gint response, gpointer user_data) +{ + _msg_user_data_t *d = (_msg_user_data_t *) user_data; + GtkTreePath *p; + GtkTreeIter tree_iter; + GtkTextIter text_iter; + size_t i; + gboolean go_back = FALSE; + gboolean go_forward = FALSE; + + gtk_text_buffer_set_text(d->buffer, "", -1); + p = apol_vector_get_element(d->messages, 0); + assert(p != NULL); + switch (response) { + case LNOOP: + break; /* no-op response, display and test only */ + case LBACK: + gtk_tree_path_prev(p); + break; + case LFORWARD: + gtk_tree_path_next(p); + break; + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CLOSE: + apol_vector_destroy(&d->messages); + if (d->view != NULL) { + g_signal_handler_disconnect(d->view->store, d->handle_id); + } + free(d); + gtk_widget_destroy(GTK_WIDGET(dialog)); + return; + default: + /* should never get here */ + toplevel_ERR(d->view->top, "Unhandled response type (%d).\n", response); + assert(0); + return; + } + assert(d->view != NULL); + + /* determine if the forward and backward buttons should be + enabled or not */ + if (apol_vector_get_size(d->messages) == 1) { + GtkTreePath *dupe_p = gtk_tree_path_copy(p); + if (dupe_p == NULL) { + toplevel_ERR(d->view->top, "%s", strerror(errno)); + return; + } + go_back = gtk_tree_path_prev(dupe_p); + gtk_tree_path_free(dupe_p); + + message_view_store_get_iter(GTK_TREE_MODEL(d->view->store), &tree_iter, p); + go_forward = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->view->store), &tree_iter); + } + gtk_dialog_set_response_sensitive(dialog, LBACK, go_back); + gtk_dialog_set_response_sensitive(dialog, LFORWARD, go_forward); + + gtk_text_buffer_get_start_iter(d->buffer, &text_iter); + for (i = 0; i < apol_vector_get_size(d->messages); i++) { + char *s; + p = apol_vector_get_element(d->messages, i); + message_view_store_get_iter(GTK_TREE_MODEL(d->view->store), &tree_iter, p); + if ((s = seaudit_message_to_string(tree_iter.user_data)) == NULL) { + toplevel_ERR(d->view->top, "%s", strerror(errno)); + continue; + } + + gtk_text_buffer_insert(d->buffer, &text_iter, s, -1); + gtk_text_buffer_insert(d->buffer, &text_iter, "\n", -1); + free(s); + } +} + +/** + * Show all messages within the messages vector (vector of + * GtkTreePaths into the view's store). + * + * Callback function cb_view_message takes ownership of messages + * vector and state. + */ +static void message_view_messages_vector(message_view_t * view, apol_vector_t * messages) +{ + GtkWidget *window = NULL; + GtkWidget *scroll; + GtkWidget *text_view; + GtkTextBuffer *buffer; + _msg_user_data_t *state; + + state = malloc(sizeof(_msg_user_data_t)); + if (state == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + apol_vector_destroy(&messages); + return; + } + + window = gtk_dialog_new_with_buttons("View Messages", + toplevel_get_window(view->top), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_GO_BACK, LBACK, + GTK_STOCK_GO_FORWARD, LFORWARD, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE); + gtk_window_set_modal(GTK_WINDOW(window), FALSE); + scroll = gtk_scrolled_window_new(NULL, NULL); + text_view = gtk_text_view_new(); + gtk_window_set_default_size(GTK_WINDOW(window), 480, 300); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll); + gtk_container_add(GTK_CONTAINER(scroll), text_view); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD); + gtk_widget_show(text_view); + gtk_widget_show(scroll); + + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); + state->view = view; + state->messages = messages; + state->dialog = GTK_DIALOG(window); + state->buffer = buffer; + g_signal_connect(window, "response", G_CALLBACK(message_view_dialog_response), state); + state->handle_id = g_signal_connect(view->store, "row-changed", G_CALLBACK(message_view_dialog_change), state); + message_view_dialog_response(GTK_DIALOG(window), LNOOP, state); + + gtk_widget_show_all(GTK_WIDGET(window)); +} + +/******************** handlers for right click menu ********************/ + +static void message_view_popup_on_view_message_activate(GtkMenuItem * menuitem, gpointer user_data __attribute__ ((unused))) +{ + message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object"); + message_view_entire_message(v); +} + +static void message_view_popup_on_find_terules_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object"); + toplevel_find_terules(v->top, (seaudit_message_t *) user_data); +} + +static void message_view_popup_on_export_selected_messages_activate(GtkMenuItem * menuitem, gpointer user_data + __attribute__ ((unused))) +{ + message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object"); + message_view_export_selected_messages(v); +} + +static void message_view_popup_menu(GtkWidget * treeview, GdkEventButton * event, message_view_t * view, + seaudit_message_t * message) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gint num_selected_rows = gtk_tree_selection_count_selected_rows(selection); + GtkWidget *menu, *menuitem, *menuitem2, *menuitem3; + int button, event_time; + + menu = gtk_menu_new(); + if (num_selected_rows == 1) { + menuitem = gtk_menu_item_new_with_label("View Selected Message"); + menuitem3 = gtk_menu_item_new_with_label("Export Selected Message..."); + } else { + menuitem = gtk_menu_item_new_with_label("View Selected Messages"); + menuitem3 = gtk_menu_item_new_with_label("Export Selected Messages..."); + } + menuitem2 = gtk_menu_item_new_with_label("Find TERules using Message..."); + g_signal_connect(menuitem, "activate", (GCallback) message_view_popup_on_view_message_activate, message); + g_signal_connect(menuitem2, "activate", (GCallback) message_view_popup_on_find_terules_activate, message); + g_signal_connect(menuitem3, "activate", (GCallback) message_view_popup_on_export_selected_messages_activate, NULL); + g_object_set_data(G_OBJECT(menuitem), "view-object", view); + g_object_set_data(G_OBJECT(menuitem2), "view-object", view); + g_object_set_data(G_OBJECT(menuitem3), "view-object", view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem3); + gtk_widget_show_all(menu); + if (toplevel_get_policy(view->top) == NULL) { + gtk_widget_set_sensitive(menuitem2, FALSE); + } + + if (event) { + button = event->button; + event_time = event->time; + } else { + button = 0; + event_time = gtk_get_current_event_time(); + } + gtk_menu_attach_to_widget(GTK_MENU(menu), treeview, NULL); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time); +} + +static gboolean message_view_delayed_selection_menu_item(gpointer data) +{ + message_view_t *view = (message_view_t *) data; + toplevel_update_selection_menu_item(view->top); + return FALSE; +} + +static gboolean message_view_on_button_press(GtkWidget * treeview, GdkEventButton * event, gpointer user_data) +{ + message_view_t *view = (message_view_t *) user_data; + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + GtkTreePath *path = NULL; + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y, &path, NULL, NULL, NULL)) { + return FALSE; + } + /* if the right click occurred on an unselected row, remove + * all selections and select the item under the pointer */ + if (!gtk_tree_selection_path_is_selected(selection, path)) { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_path(selection, path); + } + message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path); + /* popup a menu for the row that was clicked */ + message_view_popup_menu(treeview, event, view, (seaudit_message_t *) iter.user_data); + return TRUE; + } else if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + /* n.b.: rows can be selected but never deselected. + * delay updating the menu, for upon the first click + * there is not a selection yet */ + g_idle_add(&message_view_delayed_selection_menu_item, view); + return FALSE; + } else if (event->type == GDK_2BUTTON_PRESS && event->button == 1){ + /* Show message on double click */ + message_view_entire_message(view); + } + return FALSE; +} + +static void message_view_gtk_tree_path_free(gpointer data, gpointer user_data __attribute__ ((unused))) +{ + gtk_tree_path_free((GtkTreePath *) data); +} + +static gboolean message_view_on_popup_menu(GtkWidget * treeview, gpointer user_data) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL); + message_view_t *view = (message_view_t *) user_data; + GtkTreePath *path; + GtkTreeIter iter; + if (glist == NULL) { + return FALSE; + } + path = g_list_nth_data(glist, 0); + message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + message_view_popup_menu(treeview, NULL, view, (seaudit_message_t *) iter.user_data); + return TRUE; +} + +static void message_view_on_row_activate(GtkTreeView * tree_view __attribute__ ((unused)), GtkTreePath * path + __attribute__ ((unused)), GtkTreeViewColumn * column + __attribute__ ((unused)), gpointer user_data) +{ + message_view_t *view = (message_view_t *) user_data; + toplevel_update_selection_menu_item(view->top); +} + +/******************** other public functions below ********************/ + +message_view_t *message_view_create(toplevel_t * top, seaudit_model_t * model, const char *filename) +{ + message_view_t *view; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + size_t i; + + if ((view = calloc(1, sizeof(*view))) == NULL || (filename != NULL && (view->filename = strdup(filename)) == NULL)) { + int error = errno; + toplevel_ERR(top, "%s", strerror(error)); + message_view_destroy(&view); + errno = error; + return NULL; + } + view->top = top; + view->model = model; + view->store = (message_view_store_t *) g_object_new(SEAUDIT_TYPE_MESSAGE_VIEW_STORE, NULL); + view->store->view = view; + view->store->sort_field = OTHER_FIELD; + view->store->sort_dir = 1; + view->w = gtk_scrolled_window_new(NULL, NULL); + view->view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(view->store))); + selection = gtk_tree_view_get_selection(view->view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_container_add(GTK_CONTAINER(view->w), GTK_WIDGET(view->view)); + gtk_widget_show(GTK_WIDGET(view->view)); + gtk_widget_show(view->w); + + renderer = gtk_cell_renderer_text_new(); + for (i = 0; i < num_columns; i++) { + struct view_column_record r = column_data[i]; + PangoLayout *layout = gtk_widget_create_pango_layout(GTK_WIDGET(view->view), r.sample_text); + gint width; + GtkTreeViewColumn *column; + pango_layout_get_pixel_size(layout, &width, NULL); + g_object_unref(G_OBJECT(layout)); + width += 12; + column = gtk_tree_view_column_new_with_attributes(r.name, renderer, "text", r.id, NULL); + gtk_tree_view_column_set_clickable(column, TRUE); + gtk_tree_view_column_set_resizable(column, TRUE); + if (r.sort != NULL) { + g_object_set_data(G_OBJECT(column), "column id", GINT_TO_POINTER(r.id)); + g_signal_connect_after(G_OBJECT(column), "clicked", G_CALLBACK(message_view_on_column_click), view); + } + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width(column, width); + gtk_tree_view_append_column(view->view, column); + } + + g_signal_connect(G_OBJECT(view->view), "button-press-event", G_CALLBACK(message_view_on_button_press), view); + g_signal_connect(G_OBJECT(view->view), "popup-menu", G_CALLBACK(message_view_on_popup_menu), view); + g_signal_connect(G_OBJECT(view->view), "row-activated", G_CALLBACK(message_view_on_row_activate), view); + message_view_update_visible_columns(view); + message_view_update_rows(view); + return view; +} + +void message_view_destroy(message_view_t ** view) +{ + if (view != NULL && *view != NULL) { + /* emit a signal to force all message view dialogs to + disable their scroll buttons. need to pass a + non-NULL path in the signal handler to make GTK + shut up */ + GtkTreePath *path = gtk_tree_path_new_first(); + GtkTreeIter iter; + gtk_tree_model_get_iter(GTK_TREE_MODEL((*view)->store), &iter, path); + g_signal_emit_by_name((*view)->store, "row-changed", path, &iter); + gtk_tree_path_free(path); + seaudit_model_destroy(&(*view)->model); + apol_vector_destroy(&((*view)->store->messages)); + g_free((*view)->filename); + g_free((*view)->export_filename); + /* let glib handle destruction of object */ + g_object_unref((*view)->store); + free(*view); + *view = NULL; + } +} + +seaudit_model_t *message_view_get_model(message_view_t * view) +{ + return view->model; +} + +void message_view_set_model(message_view_t * view, seaudit_model_t * model) +{ + seaudit_model_destroy(&view->model); + view->model = model; + toplevel_update_tabs(view->top); + message_view_update_rows(view); +} + +GtkWidget *message_view_get_view(message_view_t * view) +{ + return view->w; +} + +size_t message_view_get_num_log_messages(message_view_t * view) +{ + if (view->store->messages == NULL) { + return 0; + } + return apol_vector_get_size(view->store->messages); +} + +gboolean message_view_is_message_selected(message_view_t * view) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view); + GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL); + if (glist == NULL) { + return FALSE; + } + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + return TRUE; +} + +void message_view_vector_gtk_tree_path_free(void *elem) +{ + GtkTreePath *path = (GtkTreePath *) elem; + gtk_tree_path_free(path); +} + +void message_view_entire_message(message_view_t * view) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view); + GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL); + GList *l; + apol_vector_t *messages; + if (glist == NULL) { + return; + } + if ((messages = apol_vector_create(message_view_vector_gtk_tree_path_free)) == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + return; + } + for (l = glist; l != NULL; l = l->next) { + GtkTreePath *path = gtk_tree_path_copy((GtkTreePath *) l->data); + if (path == NULL || apol_vector_append(messages, path) < 0) { + toplevel_ERR(view->top, "%s", strerror(errno)); + gtk_tree_path_free(path); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + apol_vector_destroy(&messages); + return; + } + } + /* the following function takes ownership of messages vector */ + message_view_messages_vector(view, messages); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); +} + +void message_view_save(message_view_t * view) +{ + if (view->filename == NULL) { + GtkWindow *parent = toplevel_get_window(view->top); + char *path = util_save_file(parent, "Save View", NULL); + if (path == NULL) { + return; + } + view->filename = path; + } + if (seaudit_model_save_to_file(view->model, view->filename) < 0) { + toplevel_ERR(view->top, "Error saving view: %s", strerror(errno)); + } +} + +void message_view_saveas(message_view_t * view) +{ + GtkWindow *parent = toplevel_get_window(view->top); + char *path = util_save_file(parent, "Save View As", view->filename); + if (path == NULL) { + return; + } + g_free(view->filename); + view->filename = path; + if (seaudit_model_save_to_file(view->model, view->filename) < 0) { + toplevel_ERR(view->top, "Error saving view: %s", strerror(errno)); + } +} + +void message_view_modify(message_view_t * view) +{ + if (modify_view_run(view->top, view)) { + toplevel_update_status_bar(view->top); + } +} + +void message_view_clear(message_view_t * view) +{ + size_t i; + for (i = 0; i < apol_vector_get_size(view->store->messages); i++) { + seaudit_message_t *m = apol_vector_get_element(view->store->messages, i); + seaudit_model_hide_message(view->model, m); + } + message_view_update_rows(view); +} + +/** + * Write to a file all messages in the given vector. Upon success, + * update the view object's export filename. + * + * @param view View containing messages to write. + * @param path Destination to write file, overwriting existing files + * as necessary. + * @param messages Vector of seaudit_message_t. + */ +static void message_view_export_messages_vector(message_view_t * view, char *path, apol_vector_t * messages) +{ + FILE *f = NULL; + size_t i; + g_free(view->export_filename); + view->export_filename = path; + if ((f = fopen(path, "w")) == NULL) { + toplevel_ERR(view->top, "Could not open %s for writing.", path); + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(messages); i++) { + seaudit_message_t *m = apol_vector_get_element(messages, i); + char *s = seaudit_message_to_string(m); + if (s == NULL || fprintf(f, "%s\n", s) < 0) { + toplevel_ERR(view->top, "Error writing string: %s", strerror(errno)); + goto cleanup; + } + free(s); + } + cleanup: + if (f != NULL) { + fclose(f); + } +} + +void message_view_export_all_messages(message_view_t * view) +{ + GtkWindow *parent = toplevel_get_window(view->top); + char *path = util_save_file(parent, "Export Messages", view->export_filename); + apol_vector_t *messages = view->store->messages; + if (path == NULL) { + return; + } + message_view_export_messages_vector(view, path, messages); +} + +void message_view_export_selected_messages(message_view_t * view) +{ + GtkWindow *parent = toplevel_get_window(view->top); + char *path; + GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view); + GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL); + GList *l; + apol_vector_t *messages; + if (glist == NULL) { + return; + } + path = util_save_file(parent, "Export Selected Messages", view->export_filename); + if (path == NULL) { + return; + } + if ((messages = apol_vector_create(NULL)) == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + return; + } + for (l = glist; l != NULL; l = l->next) { + GtkTreePath *tree_path = (GtkTreePath *) l->data; + GtkTreeIter iter; + message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, tree_path); + if (apol_vector_append(messages, iter.user_data) < 0) { + toplevel_ERR(view->top, "%s", strerror(errno)); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + apol_vector_destroy(&messages); + return; + } + } + message_view_export_messages_vector(view, path, messages); + g_list_foreach(glist, message_view_gtk_tree_path_free, NULL); + g_list_free(glist); + apol_vector_destroy(&messages); +} + +/** + * Given the name of a column, return its column record data. + */ +static const struct view_column_record *get_record(const char *name) +{ + size_t i; + for (i = 0; i < num_columns; i++) { + const struct view_column_record *r = column_data + i; + if (strcmp(r->name, name) == 0) { + return r; + } + } + return NULL; +} + +void message_view_update_visible_columns(message_view_t * view) +{ + GList *columns, *c; + preferences_t *prefs = toplevel_get_prefs(view->top); + columns = gtk_tree_view_get_columns(view->view); + c = columns; + while (c != NULL) { + GtkTreeViewColumn *vc = GTK_TREE_VIEW_COLUMN(c->data); + const gchar *title = gtk_tree_view_column_get_title(vc); + const struct view_column_record *r = get_record(title); + if (preferences_is_column_visible(prefs, r->id)) { + gtk_tree_view_column_set_visible(vc, TRUE); + } else { + gtk_tree_view_column_set_visible(vc, FALSE); + } + c = g_list_next(c); + } + g_list_free(columns); +} + +void message_view_update_rows(message_view_t * view) +{ + /* remove all existing rows, then insert them back into the + * view according to the model. automatically scroll to the + * same seleceted row(s). */ + GtkTreeSelection *selection; + GList *rows, *r, *selected = NULL; + GtkTreePath *path; + GtkTreeIter iter; + seaudit_log_t *log; + size_t i, num_old_messages = 0, num_new_messages = 0, num_changed; + int first_scroll = 0; + + if (!seaudit_model_is_changed(view->model)) { + return; + } + + /* convert the current selection into a GList of message + * pointers */ + selection = gtk_tree_view_get_selection(view->view); + rows = gtk_tree_selection_get_selected_rows(selection, NULL); + for (r = rows; r != NULL; r = r->next) { + path = (GtkTreePath *) r->data; + message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path); + selected = g_list_prepend(selected, iter.user_data); + } + g_list_foreach(rows, message_view_gtk_tree_path_free, NULL); + g_list_free(rows); + + log = toplevel_get_log(view->top); + if (view->store->messages != NULL) { + num_old_messages = apol_vector_get_size(view->store->messages); + } + apol_vector_destroy(&view->store->messages); + if (log != NULL) { + view->store->messages = seaudit_model_get_messages(log, view->model); + num_new_messages = apol_vector_get_size(view->store->messages); + } + gtk_tree_selection_unselect_all(selection); + + /* mark which rows have been changed/removed/inserted. do + * this as a single pass, rather than a two pass + * mark-and-sweep, for GTK+ tree views can be somewhat slow */ + num_changed = num_old_messages; + if (num_new_messages < num_changed) { + num_changed = num_new_messages; + } + for (i = 0; i < num_changed; i++) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, i); + iter.user_data = apol_vector_get_element(view->store->messages, i); + iter.user_data2 = GINT_TO_POINTER(i); + iter.user_data3 = view; + gtk_tree_model_row_changed(GTK_TREE_MODEL(view->store), path, &iter); + for (r = selected; r != NULL; r = r->next) { + if (r->data == iter.user_data) { + gtk_tree_selection_select_iter(selection, &iter); + if (!first_scroll) { + gtk_tree_view_scroll_to_cell(view->view, path, NULL, FALSE, 0.0, 0.0); + first_scroll = 1; + } + break; + } + } + gtk_tree_path_free(path); + } + if (num_old_messages > num_changed) { + /* delete in reverse order, else indices get renumbered */ + for (i = num_old_messages; i > num_changed; i--) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, i - 1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(view->store), path); + gtk_tree_path_free(path); + } + } else { + for (; i < num_new_messages; i++) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, i); + iter.user_data = apol_vector_get_element(view->store->messages, i); + iter.user_data2 = GINT_TO_POINTER(i); + iter.user_data3 = view; + gtk_tree_model_row_inserted(GTK_TREE_MODEL(view->store), path, &iter); + for (r = selected; r != NULL; r = r->next) { + if (r->data == iter.user_data) { + gtk_tree_selection_select_iter(selection, &iter); + if (!first_scroll) { + gtk_tree_view_scroll_to_cell(view->view, path, NULL, FALSE, 0.0, 0.0); + first_scroll = 1; + } + break; + } + } + gtk_tree_path_free(path); + } + } + g_list_free(selected); +} diff --git a/seaudit/message_view.h b/seaudit/message_view.h new file mode 100644 index 0000000..5b9bdd6 --- /dev/null +++ b/seaudit/message_view.h @@ -0,0 +1,178 @@ +/** + * @file + * Declaration of a single tab within the main notebook, showing + * all messages within a libseaudit model. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGE_VIEW_H +#define MESSAGE_VIEW_H + +#include "toplevel.h" + +#include +#include + +typedef struct message_view message_view_t; + +/** + * Allocate a new view for a particular model. + * + * @param top Handle to the controlling toplevel widget. + * @param model libseaudit model to display. The view takes ownership + * of the model afterwards. + * @param filename Initial filename for the view, or NULL if none. + * This function will duplicate the string. + * + * @return A newly allocated view, or NULL upon error. The caller is + * responsible for calling message_view_destroy() afterwards. + */ +message_view_t *message_view_create(toplevel_t * top, seaudit_model_t * model, const char *filename); + +/** + * Destroy a view and free its memory. This does nothing if the + * pointer is set to NULL. + * + * @param view Reference to a toplevel object. Afterwards the pointer + * will be set to NULL. + */ +void message_view_destroy(message_view_t ** view); + +/** + * Get the message view's model. If the caller changes the model then + * he is responsible for calling message_view_update_rows() to update + * the view. + * + * @param view View whose model to obtain. + * + * @return View's model. + */ +seaudit_model_t *message_view_get_model(message_view_t * view); + +/** + * Replace a message view's model with a different model. The + * previous model will be destroyed. Afterwards the view will update + * its rows. + * + * @param view View to modify. + * @param libseaudit model to display. The view takes ownership + * of the model afterwards. + */ +void message_view_set_model(message_view_t * view, seaudit_model_t * model); + +/** + * Get the message view's widget display. This widget will be placed + * in a container for the user to see. + * + * @param view View whose widget to obtain. + * + * @return View's widget. + */ +GtkWidget *message_view_get_view(message_view_t * view); + +/** + * Return the number of messages currently in this view. + * + * @param view View object to query. + * + * @return Number of log messages, or 0 if no model is associated with + * the view. + */ +size_t message_view_get_num_log_messages(message_view_t * view); + +/** + * Return TRUE if the message view has one or more messages selected, + * FALSE if not. + * + * @param view View object to query. + * + * @return TRUE if any messages are selected. + */ +gboolean message_view_is_message_selected(message_view_t * view); + +/** + * Save the current view to disk. + * + * @param view View to save. + */ +void message_view_save(message_view_t * view); + +/** + * Save the current view to disk under a new filename. + * + * @param view View to save. + */ +void message_view_saveas(message_view_t * view); + +/** + * Modify the settings for this view. + * + * @param view View to modify. + */ +void message_view_modify(message_view_t * view); + +/** + * Clear all messages from this view. + * + * @param view View to clear. + */ +void message_view_clear(message_view_t * view); + +/** + * Export to file all messages in a particular view. + * + * @param view View whose messages to export. + */ +void message_view_export_all_messages(message_view_t * view); + +/** + * Export to file all messages selected in a particular view. + * + * @param view View whose messages to export. + */ +void message_view_export_selected_messages(message_view_t * view); + +/** + * Open a dialog that shows an approximation of the message(s) + * currently selected. + * + * @param view View whose messages to show. + */ +void message_view_entire_message(message_view_t * view); + +/** + * Show/hide columns in a view based upon the user's current + * preferences. + * + * @param view View's columns to update. + */ +void message_view_update_visible_columns(message_view_t * view); + +/** + * (Re)synchronize the messages displayed in a view with its + * underlying model. This needs to be called when a model's filter + * changes or if new messages are found within the model's log. + * + * @param view View's rows to update. + */ +void message_view_update_rows(message_view_t * view); + +#endif diff --git a/seaudit/modify_view.c b/seaudit/modify_view.c new file mode 100644 index 0000000..abfbcc4 --- /dev/null +++ b/seaudit/modify_view.c @@ -0,0 +1,327 @@ +/** + * @file + * Run the dialog to modify a view. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "filter_view.h" +#include "modify_view.h" +#include "utilgui.h" + +#include +#include +#include +#include + +struct modify_view +{ + toplevel_t *top; + message_view_t *view; + GladeXML *xml; + /** the model currently being modified -- note that this is a + * deep copy of the message_view's model */ + seaudit_model_t *model; + + /** model containing a list of filter names, needs to be + * destroyed afterwords */ + GtkListStore *filter_store; + GtkDialog *dialog; + GtkEntry *name_entry; + GtkComboBox *visible_combo, *match_combo; + GtkTreeView *filter_view; + GtkButton *add_button, *edit_button, *remove_button, *import_button, *export_button; + + /** keep track of most recent filter filename */ + char *filter_filename; +}; + +/** + * Return the currently selected filter, or NULL if no filter is + * selected. + */ +static seaudit_filter_t *modify_view_get_current_filter(struct modify_view *mv) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view); + GtkTreeIter iter; + seaudit_filter_t *filter; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + return NULL; + } + gtk_tree_model_get(GTK_TREE_MODEL(mv->filter_store), &iter, 0, &filter, -1); + return filter; +} + +/** + * Rebuild the filter store, preserving the current selection if + * possible. + */ +static void modify_view_update_filter_store(struct modify_view *mv) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view); + GtkTreeIter old_iter, iter; + gboolean selection_existed = gtk_tree_selection_get_selected(selection, NULL, &old_iter); + const apol_vector_t *filters = seaudit_model_get_filters(mv->model); + size_t i; + gtk_list_store_clear(mv->filter_store); + for (i = 0; i < apol_vector_get_size(filters); i++) { + seaudit_filter_t *filter = apol_vector_get_element(filters, i); + gtk_list_store_append(mv->filter_store, &iter); + gtk_list_store_set(mv->filter_store, &iter, 0, filter, 1, seaudit_filter_get_name(filter), -1); + } + /* initially select the last thing, then reset selection */ + if (i > 0) { + gtk_tree_selection_select_iter(selection, &iter); + } + if (selection_existed && gtk_list_store_iter_is_valid(mv->filter_store, &old_iter)) { + gtk_tree_selection_select_iter(selection, &old_iter); + } +} + +static void modify_view_on_selection_change(GtkTreeSelection * selection, gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL); + gtk_widget_set_sensitive(GTK_WIDGET(mv->edit_button), sens); + gtk_widget_set_sensitive(GTK_WIDGET(mv->remove_button), sens); + gtk_widget_set_sensitive(GTK_WIDGET(mv->export_button), sens); +} + +static void modify_view_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + seaudit_filter_t *filter = NULL; + GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view); + + if ((filter = seaudit_filter_create("Untitled")) == NULL || (seaudit_model_append_filter(mv->model, filter) < 0)) { + toplevel_ERR(mv->top, "Error adding new filter: %s", strerror(errno)); + seaudit_filter_destroy(&filter); + return; + } + /* select the filter that was just created, by removing the + * selection and letting the following function select it */ + gtk_tree_selection_unselect_all(selection); + modify_view_update_filter_store(mv); + filter_view_run(filter, mv->top, GTK_WINDOW(mv->dialog)); + modify_view_update_filter_store(mv); +} + +static void modify_view_on_edit_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + seaudit_filter_t *filter = modify_view_get_current_filter(mv); + assert(filter != NULL); + filter_view_run(filter, mv->top, GTK_WINDOW(mv->dialog)); + modify_view_update_filter_store(mv); +} + +static void modify_view_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + seaudit_filter_t *filter = modify_view_get_current_filter(mv); + assert(filter != NULL); + seaudit_model_remove_filter(mv->model, filter); + modify_view_update_filter_store(mv); +} + +static void modify_view_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + apol_vector_t *paths = util_open_file(GTK_WINDOW(mv->dialog), "Import Filter", mv->filter_filename, 0); + apol_vector_t *filters; + size_t i; + if (paths == NULL) { + return; + } + free(mv->filter_filename); + if ((mv->filter_filename = strdup(apol_vector_get_element(paths, 0))) == NULL) { + toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno)); + apol_vector_destroy(&paths); + return; + } + apol_vector_destroy(&paths); + if ((filters = seaudit_filter_create_from_file(mv->filter_filename)) == NULL) { + toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno)); + apol_vector_destroy(&paths); + return; + } + for (i = 0; i < apol_vector_get_size(filters); i++) { + seaudit_filter_t *filter = apol_vector_get_element(filters, i); + seaudit_filter_t *new_f = NULL; + if ((new_f = seaudit_filter_create_from_filter(filter)) == NULL || + seaudit_model_append_filter(mv->model, new_f) < 0) { + toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno)); + seaudit_filter_destroy(&new_f); + apol_vector_destroy(&filters); + return; + } + modify_view_update_filter_store(mv); + } + apol_vector_destroy(&filters); +} + +static void modify_view_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct modify_view *mv = (struct modify_view *)user_data; + char *path = util_save_file(GTK_WINDOW(mv->dialog), "Export Filter", mv->filter_filename); + seaudit_filter_t *filter = modify_view_get_current_filter(mv); + assert(filter != NULL); + if (path == NULL) { + return; + } + g_free(mv->filter_filename); + mv->filter_filename = path; + if (seaudit_filter_save_to_file(filter, mv->filter_filename) < 0) { + toplevel_ERR(mv->top, "Error exporting filter: %s", strerror(errno)); + } +} + +/** + * Make libglade calls to fill in struct modify_view widget + * references. + */ +static void modify_view_init_widgets(struct modify_view *mv) +{ + mv->dialog = GTK_DIALOG(glade_xml_get_widget(mv->xml, "ModifyViewWindow")); + assert(mv->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(mv->dialog), toplevel_get_window(mv->top)); + + mv->name_entry = GTK_ENTRY(glade_xml_get_widget(mv->xml, "ModifyViewNameEntry")); + assert(mv->name_entry != NULL); + + mv->visible_combo = GTK_COMBO_BOX(glade_xml_get_widget(mv->xml, "ModifyViewVisibleCombo")); + mv->match_combo = GTK_COMBO_BOX(glade_xml_get_widget(mv->xml, "ModifyViewMatchCombo")); + assert(mv->visible_combo != NULL && mv->match_combo != NULL); + + mv->filter_view = GTK_TREE_VIEW(glade_xml_get_widget(mv->xml, "ModifyViewFilterView")); + assert(mv->filter_view != NULL); + mv->filter_store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); + gtk_tree_view_set_model(mv->filter_view, GTK_TREE_MODEL(mv->filter_store)); + + mv->add_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewAddButton")); + mv->edit_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewEditButton")); + mv->remove_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewRemoveButton")); + mv->import_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewImportButton")); + mv->export_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewExportButton")); + assert(mv->add_button != NULL && mv->edit_button != NULL && mv->remove_button != NULL && + mv->import_button != NULL && mv->export_button != NULL); +} + +static void modify_view_init_signals(struct modify_view *mv) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Filter names", renderer, "text", 1, NULL); + gtk_tree_view_column_set_clickable(column, FALSE); + gtk_tree_view_column_set_resizable(column, FALSE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_visible(column, TRUE); + gtk_tree_view_append_column(mv->filter_view, column); + + selection = gtk_tree_view_get_selection(mv->filter_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + g_signal_connect(selection, "changed", G_CALLBACK(modify_view_on_selection_change), mv); + + g_signal_connect(mv->add_button, "clicked", G_CALLBACK(modify_view_on_add_click), mv); + g_signal_connect(mv->edit_button, "clicked", G_CALLBACK(modify_view_on_edit_click), mv); + g_signal_connect(mv->remove_button, "clicked", G_CALLBACK(modify_view_on_remove_click), mv); + g_signal_connect(mv->import_button, "clicked", G_CALLBACK(modify_view_on_import_click), mv); + g_signal_connect(mv->export_button, "clicked", G_CALLBACK(modify_view_on_export_click), mv); +} + +/** + * Set up the window to reflect the current view's values. + */ +static void modify_view_init_dialog(struct modify_view *mv) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view); + GtkTreeIter iter; + + gtk_entry_set_text(mv->name_entry, seaudit_model_get_name(mv->model)); + + gtk_combo_box_set_active(mv->visible_combo, seaudit_model_get_filter_visible(mv->model)); + gtk_combo_box_set_active(mv->match_combo, seaudit_model_get_filter_match(mv->model)); + + gtk_tree_selection_unselect_all(selection); + modify_view_update_filter_store(mv); + /* automatically select the first filter upon dialog + * startup */ + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mv->filter_store), &iter)) { + gtk_tree_selection_select_iter(selection, &iter); + } +} + +int modify_view_run(toplevel_t * top, message_view_t * view) +{ + struct modify_view mv; + seaudit_model_t *orig_model = message_view_get_model(view); + gint response; + int retval; + + memset(&mv, 0, sizeof(mv)); + mv.top = top; + mv.view = view; + mv.xml = glade_xml_new(toplevel_get_glade_xml(top), "ModifyViewWindow", NULL); + mv.filter_filename = NULL; + if ((mv.model = seaudit_model_create_from_model(orig_model)) == NULL) { + toplevel_ERR(mv.top, "Error duplicating model: %s", strerror(errno)); + return 0; + } + modify_view_init_widgets(&mv); + modify_view_init_signals(&mv); + modify_view_init_dialog(&mv); + + do { + response = gtk_dialog_run(mv.dialog); + const gchar *text = gtk_entry_get_text(mv.name_entry); + + if (seaudit_model_set_name(mv.model, text) < 0) { + toplevel_ERR(mv.top, "Could not set name: %s", strerror(errno)); + } + seaudit_model_set_filter_visible(mv.model, gtk_combo_box_get_active(mv.visible_combo)); + seaudit_model_set_filter_match(mv.model, gtk_combo_box_get_active(mv.match_combo)); + if (response == GTK_RESPONSE_APPLY) { + seaudit_model_t *new_model; + if ((new_model = seaudit_model_create_from_model(mv.model)) == NULL) { + toplevel_ERR(mv.top, "Error applying model: %s", strerror(errno)); + break; + } + message_view_set_model(mv.view, new_model); + toplevel_update_status_bar(mv.top); + } + } while (response == GTK_RESPONSE_APPLY); + + if (response == GTK_RESPONSE_OK) { + message_view_set_model(mv.view, mv.model); + retval = 1; + } else { + seaudit_model_destroy(&mv.model); + retval = 0; + } + gtk_widget_destroy(GTK_WIDGET(mv.dialog)); + g_object_unref(mv.filter_store); + return retval; +} diff --git a/seaudit/modify_view.h b/seaudit/modify_view.h new file mode 100644 index 0000000..06993f3 --- /dev/null +++ b/seaudit/modify_view.h @@ -0,0 +1,41 @@ +/** + * @file + * Dialog that allows the user to modify the current view. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MODIFY_VIEW_H +#define MODIFY_VIEW_H + +#include "toplevel.h" +#include "message_view.h" + +/** + * Display and run a dialog that allows the user to modify a view. + * + * @param top Toplevel containing message view. + * @param view Message view to modify. + * + * @return Non-zero if the view changed, zero if not. + */ +int modify_view_run(toplevel_t * top, message_view_t * view); + +#endif diff --git a/seaudit/open_policy_window.c b/seaudit/open_policy_window.c new file mode 100644 index 0000000..ca8a511 --- /dev/null +++ b/seaudit/open_policy_window.c @@ -0,0 +1,469 @@ +/** + * @file + * Run the dialog to allow the user to open a policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "open_policy_window.h" +#include "utilgui.h" + +#include +#include +#include +#include +#include +#include +#include + +struct open_policy +{ + GladeXML *xml; + toplevel_t *top; + char *last_module_path; + GtkDialog *dialog; + + GtkRadioButton *monolithic_radio, *modular_radio; + GtkLabel *main_label; + + GtkHBox *bottom_hbox; + GtkListStore *module_store; + + GtkEntry *base_entry; + GtkButton *base_browse_button; + + GtkTreeView *module_view; + GtkButton *add_button, *remove_button, *import_button, *export_button; + GtkButton *ok_button; +}; + +enum module_columns +{ + PATH_COLUMN = 0, NAME_COLUMN, VERSION_COLUMN, NUM_COLUMNS +}; + +/** + * Sort columns in alphabetical order. + */ +static gint open_policy_sort(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data) +{ + GValue value_a = { 0 }, value_b = { + 0}; + const char *name_a, *name_b; + int retval, column_id = GPOINTER_TO_INT(user_data); + + gtk_tree_model_get_value(model, a, column_id, &value_a); + gtk_tree_model_get_value(model, b, column_id, &value_b); + name_a = g_value_get_string(&value_a); + name_b = g_value_get_string(&value_b); + retval = strcmp(name_a, name_b); + g_value_unset(&value_a); + g_value_unset(&value_b); + return retval; +} + +static void open_policy_init_widgets(struct open_policy *op) +{ + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + op->dialog = GTK_DIALOG(glade_xml_get_widget(op->xml, "PolicyOpenWindow")); + assert(op->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(op->dialog), toplevel_get_window(op->top)); + + op->monolithic_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, "monolithic radio")); + op->modular_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, "modular radio")); + op->main_label = GTK_LABEL(glade_xml_get_widget(op->xml, "main filename label")); + assert(op->monolithic_radio != NULL && op->modular_radio != NULL && op->main_label != NULL); + + op->base_entry = GTK_ENTRY(glade_xml_get_widget(op->xml, "base entry")); + op->base_browse_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "base browse")); + assert(op->base_entry != NULL && op->base_browse_button != NULL); + + op->bottom_hbox = GTK_HBOX(glade_xml_get_widget(op->xml, "hbox.3")); + assert(op->bottom_hbox != NULL); + + op->module_view = GTK_TREE_VIEW(glade_xml_get_widget(op->xml, "module view")); + assert(op->module_view != NULL); + op->module_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + gtk_tree_view_set_model(op->module_view, GTK_TREE_MODEL(op->module_store)); + + op->add_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module add button")); + op->remove_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module remove button")); + op->import_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module list import button")); + op->export_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module list export button")); + op->ok_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "ok button")); + assert(op->add_button != NULL && op->remove_button != NULL && + op->import_button != NULL && op->export_button != NULL && op->ok_button != NULL); + + selection = gtk_tree_view_get_selection(op->module_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + column = gtk_tree_view_column_new_with_attributes("Module", renderer, "text", NAME_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(op->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), NAME_COLUMN, open_policy_sort, (gpointer) NAME_COLUMN, + NULL); + + column = gtk_tree_view_column_new_with_attributes("Version", renderer, "text", VERSION_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, VERSION_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(op->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), VERSION_COLUMN, open_policy_sort, + (gpointer) VERSION_COLUMN, NULL); + + column = gtk_tree_view_column_new_with_attributes("Path", renderer, "text", PATH_COLUMN, NULL); + gtk_tree_view_column_set_sort_column_id(column, PATH_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(op->module_view, column); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), PATH_COLUMN, open_policy_sort, (gpointer) PATH_COLUMN, + NULL); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(op->module_store), NAME_COLUMN, GTK_SORT_ASCENDING); +} + +static void open_policy_on_policy_type_toggle(GtkToggleButton * widget, gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + /* clicking on the radio buttons emit two toggle signals, one for + * the original button and one for the new one. thus only need to + * handle half of all signals */ + if (!gtk_toggle_button_get_active(widget)) { + return; + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->monolithic_radio))) { + gtk_widget_set_sensitive(GTK_WIDGET(op->bottom_hbox), FALSE); + gtk_label_set_markup(op->main_label, "Policy Filename:"); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(op->bottom_hbox), TRUE); + gtk_label_set_markup(op->main_label, "Base Filename:"); + } +} + +static void open_policy_on_entry_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + gboolean sens = FALSE; + if (strcmp(gtk_entry_get_text(op->base_entry), "") != 0) { + sens = TRUE; + } + gtk_widget_set_sensitive(GTK_WIDGET(op->export_button), sens); + gtk_widget_set_sensitive(GTK_WIDGET(op->ok_button), sens); +} + +static void open_policy_on_base_browse_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + const char *current_path = gtk_entry_get_text(op->base_entry); + char *title; + apol_vector_t *paths; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->monolithic_radio))) { + title = "Open Monolithic Policy"; + } else { + title = "Open Modular Policy"; + } + if (strcmp(current_path, "") == 0) { + current_path = NULL; + } + if ((paths = util_open_file(GTK_WINDOW(op->dialog), title, current_path, 0)) == NULL) { + return; + } + gtk_entry_set_text(op->base_entry, apol_vector_get_element(paths, 0)); + apol_vector_destroy(&paths); +} + +/** + * Attempt to load a module and retrieve its name and version. Upon + * success add an entry to the list store. + * + * @return 0 on success, < 0 on error. + */ +static int open_policy_load_module(struct open_policy *op, const char *path) +{ + const char *module_name, *version_string; + int module_type; + qpol_module_t *module = NULL; + GtkTreeIter iter; + + /* check if modulue was already loaded */ + gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(op->module_store), &iter); + while (iter_valid) { + char *s; + gtk_tree_model_get(GTK_TREE_MODEL(op->module_store), &iter, PATH_COLUMN, &s, -1); + if (strcmp(s, path) == 0) { + toplevel_ERR(op->top, "Module %s was already added.", path); + return -1; + } + iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(op->module_store), &iter); + } + if ((qpol_module_create_from_file(path, &module)) < 0) { + toplevel_ERR(op->top, "Error opening module %s: %s", path, strerror(errno)); + return -1; + } + if (qpol_module_get_name(module, &module_name) < 0 || + qpol_module_get_version(module, &version_string) < 0 || qpol_module_get_type(module, &module_type) < 0) { + toplevel_ERR(op->top, "Error reading module %s: %s", path, strerror(errno)); + qpol_module_destroy(&module); + return -1; + } + if (module_type != QPOL_MODULE_OTHER) { + toplevel_ERR(op->top, "%s is not a loadable module.", path); + qpol_module_destroy(&module); + return -1; + } + gtk_list_store_append(op->module_store, &iter); + gtk_list_store_set(op->module_store, &iter, PATH_COLUMN, path, NAME_COLUMN, module_name, VERSION_COLUMN, version_string, + -1); + qpol_module_destroy(&module); + return 0; +} + +static void open_policy_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + apol_vector_t *paths; + const char *path = NULL, *prev_path; + size_t i; + if ((prev_path = op->last_module_path) == NULL) { + prev_path = gtk_entry_get_text(op->base_entry); + if (strcmp(prev_path, "") == 0) { + prev_path = NULL; + } + } + paths = util_open_file(GTK_WINDOW(op->dialog), "Open Module", prev_path, 1); + if (paths == NULL) { + return; + } + int all_succeed = 1; + for (i = 0; i < apol_vector_get_size(paths); i++) { + path = apol_vector_get_element(paths, i); + if (open_policy_load_module(op, path) < 0) { + all_succeed = 0; + } + } + if (all_succeed) { + assert(path != NULL); + free(op->last_module_path); + op->last_module_path = strdup(path); + } + apol_vector_destroy(&paths); +} + +static void open_policy_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + GtkTreeSelection *selection = gtk_tree_view_get_selection(op->module_view); + GtkTreeIter iter; + char *path; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + return; + } + gtk_tree_model_get(GTK_TREE_MODEL(op->module_store), &iter, 0, &path, -1); + gtk_list_store_remove(op->module_store, &iter); +} + +static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data); +static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data); + +static void open_policy_on_selection_change(GtkTreeSelection * selection, gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL); + gtk_widget_set_sensitive(GTK_WIDGET(op->remove_button), sens); +} + +static void open_policy_init_signals(struct open_policy *op) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(op->module_view); + g_signal_connect(op->monolithic_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), op); + g_signal_connect(op->modular_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), op); + g_signal_connect(op->base_entry, "event-after", G_CALLBACK(open_policy_on_entry_event_after), op); + g_signal_connect(op->base_browse_button, "clicked", G_CALLBACK(open_policy_on_base_browse_click), op); + g_signal_connect(selection, "changed", G_CALLBACK(open_policy_on_selection_change), op); + g_signal_connect(op->add_button, "clicked", G_CALLBACK(open_policy_on_add_click), op); + g_signal_connect(op->remove_button, "clicked", G_CALLBACK(open_policy_on_remove_click), op); + g_signal_connect(op->import_button, "clicked", G_CALLBACK(open_policy_on_import_click), op); + g_signal_connect(op->export_button, "clicked", G_CALLBACK(open_policy_on_export_click), op); +} + +static void open_policy_init_values(struct open_policy *op, const apol_policy_path_t * path) +{ + if (path != NULL) { + apol_policy_path_type_e path_type = apol_policy_path_get_type(path); + const char *primary_path = apol_policy_path_get_primary(path); + gtk_entry_set_text(op->base_entry, primary_path); + gtk_list_store_clear(op->module_store); + if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(op->monolithic_radio), TRUE); + } else if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) { + const apol_vector_t *modules = apol_policy_path_get_modules(path); + size_t i; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(op->modular_radio), TRUE); + for (i = 0; i < apol_vector_get_size(modules); i++) { + char *module_path = apol_vector_get_element(modules, i); + if (open_policy_load_module(op, module_path) < 0) { + break; + } + } + } else { + /* should never get here */ + toplevel_ERR(op->top, "Unknown policy path type %d.", path_type); + } + } +} + +/** + * Build the policy path corresponding to the user's inputs on this + * dialog. + * + * @return path for the dialog, or NULL upon error. The caller must + * call apol_policy_path_destroy() afterwards. + */ +static apol_policy_path_t *open_policy_build_path(struct open_policy *op) +{ + const char *primary_path = gtk_entry_get_text(op->base_entry); + apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + apol_vector_t *modules = NULL; + apol_policy_path_t *path = NULL; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->modular_radio))) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + GtkTreeIter iter; + if ((modules = apol_vector_create(free)) == NULL) { + toplevel_ERR(op->top, "%s", strerror(errno)); + return NULL; + } + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(op->module_store), &iter)) { + do { + GValue value = { 0 }; + char *module_path; + gtk_tree_model_get_value(GTK_TREE_MODEL(op->module_store), &iter, PATH_COLUMN, &value); + module_path = g_value_dup_string(&value); + g_value_unset(&value); + if (apol_vector_append(modules, module_path) < 0) { + toplevel_ERR(op->top, "%s", strerror(errno)); + free(module_path); + apol_vector_destroy(&modules); + return NULL; + } + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(op->module_store), &iter)); + } + } + path = apol_policy_path_create(path_type, primary_path, modules); + apol_vector_destroy(&modules); + if (path == NULL) { + toplevel_ERR(op->top, "%s", strerror(errno)); + return NULL; + } + return path; +} + +static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + apol_vector_t *paths = NULL; + apol_policy_path_t *ppath = NULL; + const char *path = NULL, *prev_path; + if ((prev_path = op->last_module_path) == NULL) { + prev_path = gtk_entry_get_text(op->base_entry); + if (strcmp(prev_path, "") == 0) { + prev_path = NULL; + } + } + paths = util_open_file(GTK_WINDOW(op->dialog), "Import Policy List", prev_path, 1); + if (paths == NULL) { + return; + } + path = apol_vector_get_element(paths, 0); + ppath = apol_policy_path_create_from_file(path); + if (ppath == NULL) { + toplevel_ERR(op->top, "Error importing policy list %s: %s", path, strerror(errno)); + goto cleanup; + } + open_policy_init_values(op, ppath); + free(op->last_module_path); + op->last_module_path = strdup(path); + cleanup: + apol_vector_destroy(&paths); + apol_policy_path_destroy(&ppath); +} + +static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + struct open_policy *op = (struct open_policy *)user_data; + char *path = util_save_file(GTK_WINDOW(op->dialog), "Export Policy List", NULL); + apol_policy_path_t *ppath = NULL; + if (path == NULL) { + return; + } + ppath = open_policy_build_path(op); + if (ppath == NULL) { + goto cleanup; + } + if (apol_policy_path_to_file(ppath, path) < 0) { + toplevel_ERR(op->top, "Error exporting policy list %s: %s", path, strerror(errno)); + } + cleanup: + g_free(path); + apol_policy_path_destroy(&ppath); +} + +void open_policy_window_run(toplevel_t * top, const apol_policy_path_t * path, apol_policy_path_t ** selection) +{ + struct open_policy op; + gint response; + apol_policy_path_t *input; + + memset(&op, 0, sizeof(op)); + op.top = top; + op.xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyOpenWindow", NULL); + + open_policy_init_widgets(&op); + open_policy_init_signals(&op); + open_policy_init_values(&op, path); + if (selection != NULL) { + *selection = NULL; + } + + while (1) { + response = gtk_dialog_run(op.dialog); + if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) { + break; + } + if ((input = open_policy_build_path(&op)) == NULL) { + continue; + } + if (selection == NULL) { + if (toplevel_open_policy(op.top, input) == 0) { + break; + } + } else { + *selection = input; + break; + } + } + gtk_widget_destroy(GTK_WIDGET(op.dialog)); + free(op.last_module_path); + g_object_unref(op.module_store); +} diff --git a/seaudit/open_policy_window.h b/seaudit/open_policy_window.h new file mode 100644 index 0000000..2b57e9d --- /dev/null +++ b/seaudit/open_policy_window.h @@ -0,0 +1,46 @@ +/** + * @file + * Dialog that allows the user to select either a monolithic policy + * or a base policy + list of modules. The dialog may also actually + * open the policy or it may be used simply as a file chooser. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPEN_POLICY_WINDOW_H +#define OPEN_POLICY_WINDOW_H + +#include "toplevel.h" +#include + +/** + * Display and run a dialog that allows the user open a policy, either + * a monolithic or a modular policy. + * + * @param top Toplevel for the application. + * @param path If not NULL, the default path for the policy. + * @param selection If non-NULL, then allocate and set this reference + * pointer to the path selected; caller must call + * apol_policy_path_destroy() afterwards. Otherwise if NULL then + * actually load the policy and set the path tloplvel_open_policy(). + */ +void open_policy_window_run(toplevel_t * top, const apol_policy_path_t * path, apol_policy_path_t ** selection); + +#endif diff --git a/seaudit/policy_components_view.c b/seaudit/policy_components_view.c new file mode 100644 index 0000000..c2f1d02 --- /dev/null +++ b/seaudit/policy_components_view.c @@ -0,0 +1,373 @@ +/** + * @file + * Run the dialog to select from lists of strings. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "policy_components_view.h" + +#include +#include +#include +#include +#include + +struct polcomp_view +{ + GladeXML *xml; + toplevel_t *top; + apol_vector_t *log_items, *policy_items, *both_items, *included_items; + GtkDialog *dialog; + + GtkRadioButton *log_radio, *policy_radio, *both_radio; + + GtkListStore *master_store; + GtkTreeModel *inc_store, *exc_store; + GtkTreeView *inc_view, *exc_view; + GtkButton *inc_select_button, *inc_unselect_button, *exc_select_button, *exc_unselect_button; + + GtkButton *to_exc_button, *to_inc_button; +}; + +enum polcom_columns +{ + POINTER_COLUMN = 0, NAME_COLUMN, ISLOG_COLUMN, ISPOLICY_COLUMN, ISINC_COLUMN +}; + +static void policy_components_view_init_widgets(struct polcomp_view *pv) +{ + pv->dialog = GTK_DIALOG(glade_xml_get_widget(pv->xml, "PolicyComponentListsWindow")); + assert(pv->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(pv->dialog), toplevel_get_window(pv->top)); + + pv->log_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompLogRadio")); + pv->policy_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompPolicyRadio")); + pv->both_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompBothRadio")); + assert(pv->log_radio != NULL && pv->policy_radio != NULL && pv->both_radio != NULL); + + pv->inc_view = GTK_TREE_VIEW(glade_xml_get_widget(pv->xml, "PolicyCompIncView")); + pv->exc_view = GTK_TREE_VIEW(glade_xml_get_widget(pv->xml, "PolicyCompExcView")); + assert(pv->inc_view != NULL && pv->exc_view != NULL); + + pv->inc_select_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompIncSelectButton")); + pv->inc_unselect_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompIncUnselectButton")); + pv->exc_select_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompExcSelectButton")); + pv->exc_unselect_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompExcUnselectButton")); + assert(pv->inc_select_button != NULL && pv->inc_unselect_button && + pv->exc_select_button != NULL && pv->exc_unselect_button); + + pv->to_exc_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompToExcButton")); + pv->to_inc_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompToIncButton")); + assert(pv->to_exc_button != NULL && pv->to_inc_button); +} + +/******************** functions to build the lists ********************/ + +/** + * Determine if a particular row should be visible based upon the + * current radio button selection. If the current item source is + * 'Log' then return TRUE if the islog attribute is enabled. If the + * source is 'Policy' then return TRUE if ispolicy is enabled. + * Otherwise the item is visible by default. + */ +static gboolean policy_components_view_is_visible_radio(struct polcomp_view *pv, gboolean islog, gboolean ispolicy) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->log_radio))) { + return islog; + } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->policy_radio))) { + return ispolicy; + } + return TRUE; +} + +/** + * Callback invoked to determine if a row should be visible to the + * included view. + */ +static gboolean policy_components_view_is_visible_included(GtkTreeModel * model, GtkTreeIter * iter, gpointer data) +{ + struct polcomp_view *pv = (struct polcomp_view *)data; + gboolean islog, ispolicy, isinc; + gtk_tree_model_get(model, iter, ISLOG_COLUMN, &islog, ISPOLICY_COLUMN, &ispolicy, ISINC_COLUMN, &isinc, -1); + if (!isinc) { + return FALSE; + } + return policy_components_view_is_visible_radio(pv, islog, ispolicy); +} + +/** + * Callback invoked to determine if a row should be visible in the + * included view. + */ +static gboolean policy_components_view_is_visible_excluded(GtkTreeModel * model, GtkTreeIter * iter, gpointer data) +{ + struct polcomp_view *pv = (struct polcomp_view *)data; + gboolean islog, ispolicy, isinc; + gtk_tree_model_get(model, iter, ISLOG_COLUMN, &islog, ISPOLICY_COLUMN, &ispolicy, ISINC_COLUMN, &isinc, -1); + if (isinc) { + return FALSE; + } + return policy_components_view_is_visible_radio(pv, islog, ispolicy); +} + +static void policy_components_view_init_lists(struct polcomp_view *pv) +{ + GtkTreeIter iter; + size_t i, j; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + if ((pv->both_items = apol_vector_create_from_vector(pv->log_items, NULL, NULL, NULL)) == NULL) { + toplevel_ERR(pv->top, "Error generating union list: %s", strerror(errno)); + return; + } + if (pv->policy_items == NULL) { + gtk_widget_set_sensitive(GTK_WIDGET(pv->policy_radio), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(pv->both_radio), FALSE); + } else { + if (apol_vector_cat(pv->both_items, pv->policy_items) < 0) { + toplevel_ERR(pv->top, "Error generating union list: %s", strerror(errno)); + return; + } + apol_vector_sort_uniquify(pv->both_items, apol_str_strcmp, NULL); + } + pv->master_store = + gtk_list_store_new(ISINC_COLUMN + 1, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); + for (i = 0; i < apol_vector_get_size(pv->both_items); i++) { + char *s = apol_vector_get_element(pv->both_items, i); + gboolean is_log = FALSE, is_policy = FALSE, is_included = FALSE; + if (apol_vector_get_index(pv->log_items, s, NULL, NULL, &j) == 0) { + is_log = TRUE; + } + if (pv->policy_items != NULL && apol_vector_get_index(pv->policy_items, s, NULL, NULL, &j) == 0) { + is_policy = TRUE; + } + if (apol_vector_get_index(pv->included_items, s, apol_str_strcmp, NULL, &j) == 0) { + is_included = TRUE; + } + gtk_list_store_append(pv->master_store, &iter); + gtk_list_store_set(pv->master_store, &iter, + POINTER_COLUMN, s, + NAME_COLUMN, s, ISLOG_COLUMN, is_log, ISPOLICY_COLUMN, is_policy, ISINC_COLUMN, is_included, -1); + } + + pv->inc_store = gtk_tree_model_filter_new(GTK_TREE_MODEL(pv->master_store), NULL); + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(pv->inc_store), policy_components_view_is_visible_included, pv, + NULL); + pv->exc_store = gtk_tree_model_filter_new(GTK_TREE_MODEL(pv->master_store), NULL); + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(pv->exc_store), policy_components_view_is_visible_excluded, pv, + NULL); + gtk_tree_view_set_model(pv->inc_view, pv->inc_store); + gtk_tree_view_set_model(pv->exc_view, pv->exc_store); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Item names", renderer, "text", NAME_COLUMN, NULL); + gtk_tree_view_column_set_clickable(column, FALSE); + gtk_tree_view_column_set_resizable(column, FALSE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_visible(column, TRUE); + gtk_tree_view_append_column(pv->inc_view, column); + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("Item names", renderer, "text", NAME_COLUMN, NULL); + gtk_tree_view_column_set_clickable(column, FALSE); + gtk_tree_view_column_set_resizable(column, FALSE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_visible(column, TRUE); + gtk_tree_view_append_column(pv->exc_view, column); + + selection = gtk_tree_view_get_selection(pv->inc_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + selection = gtk_tree_view_get_selection(pv->exc_view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); +} + +/******************** signal handlers ********************/ + +static void policy_components_view_on_source_toggle(GtkToggleButton * widget, gpointer user_data) +{ + struct polcomp_view *pv = (struct polcomp_view *)user_data; + /* clicking on the radio buttons emit two toggle signals, one for + * the original button and one for the new one. thus only need to + * handle half of all signals */ + if (!gtk_toggle_button_get_active(widget)) { + return; + } + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store)); + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store)); +} + +static void policy_components_gtk_tree_path_free(gpointer data, gpointer user_data __attribute__ ((unused))) +{ + gtk_tree_path_free((GtkTreePath *) data); +} + +static void policy_components_gtk_tree_row_reference_free(gpointer data, gpointer user_data __attribute__ ((unused))) +{ + gtk_tree_row_reference_free((GtkTreeRowReference *) data); +} + +static void policy_components_view_on_to_exc_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct polcomp_view *pv = (struct polcomp_view *)user_data; + GtkTreeSelection *selection = gtk_tree_view_get_selection(pv->inc_view); + GList *r, *rows = gtk_tree_selection_get_selected_rows(selection, NULL); + GList *refs = NULL; + + /* use references because the filtered store will be changing + * as master_store's ISINC_COLUMN value changes */ + for (r = rows; r != NULL; r = r->next) { + GtkTreePath *path = (GtkTreePath *) r->data; + GtkTreeRowReference *ref = gtk_tree_row_reference_new(pv->inc_store, path); + refs = g_list_prepend(refs, ref); + } + + for (r = refs; r != NULL; r = r->next) { + GtkTreeRowReference *ref = (GtkTreeRowReference *) r->data; + GtkTreePath *path = gtk_tree_row_reference_get_path(ref); + GtkTreeIter iter, child_iter; + char *name; + size_t i = 0; + int retval; + gtk_tree_model_get_iter(pv->inc_store, &iter, path); + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(pv->inc_store), &child_iter, &iter); + gtk_tree_model_get(GTK_TREE_MODEL(pv->master_store), &child_iter, NAME_COLUMN, &name, -1); + gtk_list_store_set(pv->master_store, &child_iter, ISINC_COLUMN, FALSE, -1); + retval = apol_vector_get_index(pv->included_items, name, apol_str_strcmp, NULL, &i); + assert(retval == 0); + name = apol_vector_get_element(pv->included_items, i); + free(name); + apol_vector_remove(pv->included_items, i); + } + + g_list_foreach(refs, policy_components_gtk_tree_row_reference_free, NULL); + g_list_free(refs); + g_list_foreach(rows, policy_components_gtk_tree_path_free, NULL); + g_list_free(rows); + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store)); + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store)); +} + +static void policy_components_view_on_to_inc_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + struct polcomp_view *pv = (struct polcomp_view *)user_data; + GtkTreeSelection *selection = gtk_tree_view_get_selection(pv->exc_view); + GList *r, *rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + /* use references because the filtered store will be changing + * as master_store's ISINC_COLUMN value changes */ + GList *refs = NULL; + for (r = rows; r != NULL; r = r->next) { + GtkTreePath *path = (GtkTreePath *) r->data; + GtkTreeRowReference *ref = gtk_tree_row_reference_new(pv->exc_store, path); + refs = g_list_prepend(refs, ref); + } + + for (r = refs; r != NULL; r = r->next) { + GtkTreeRowReference *ref = (GtkTreeRowReference *) r->data; + GtkTreePath *path = gtk_tree_row_reference_get_path(ref); + GtkTreeIter iter, child_iter; + char *name; + gtk_tree_model_get_iter(pv->exc_store, &iter, path); + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(pv->exc_store), &child_iter, &iter); + gtk_tree_model_get(GTK_TREE_MODEL(pv->master_store), &child_iter, NAME_COLUMN, &name, -1); + gtk_list_store_set(pv->master_store, &child_iter, ISINC_COLUMN, TRUE, -1); + apol_vector_append(pv->included_items, strdup(name)); + } + apol_vector_sort_uniquify(pv->included_items, apol_str_strcmp, NULL); + g_list_foreach(refs, policy_components_gtk_tree_row_reference_free, NULL); + g_list_free(refs); + g_list_foreach(rows, policy_components_gtk_tree_path_free, NULL); + g_list_free(rows); + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store)); + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store)); +} + +static void policy_components_view_on_select_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + GtkTreeView *tv = GTK_TREE_VIEW(user_data); + GtkTreeSelection *selection = gtk_tree_view_get_selection(tv); + gtk_tree_selection_select_all(selection); +} + +static void policy_components_view_on_unselect_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data) +{ + GtkTreeView *tv = GTK_TREE_VIEW(user_data); + GtkTreeSelection *selection = gtk_tree_view_get_selection(tv); + gtk_tree_selection_unselect_all(selection); +} + +static void policy_components_view_init_signals(struct polcomp_view *pv) +{ + g_signal_connect(pv->log_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv); + g_signal_connect(pv->policy_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv); + g_signal_connect(pv->both_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv); + g_signal_connect(pv->to_exc_button, "clicked", G_CALLBACK(policy_components_view_on_to_exc_click), pv); + g_signal_connect(pv->to_inc_button, "clicked", G_CALLBACK(policy_components_view_on_to_inc_click), pv); + g_signal_connect(pv->inc_select_button, "clicked", G_CALLBACK(policy_components_view_on_select_click), pv->inc_view); + g_signal_connect(pv->inc_unselect_button, "clicked", G_CALLBACK(policy_components_view_on_unselect_click), pv->inc_view); + g_signal_connect(pv->exc_select_button, "clicked", G_CALLBACK(policy_components_view_on_select_click), pv->exc_view); + g_signal_connect(pv->exc_unselect_button, "clicked", G_CALLBACK(policy_components_view_on_unselect_click), pv->exc_view); +} + +apol_vector_t *policy_components_view_run(toplevel_t * top, GtkWindow * parent __attribute__ ((unused)), const char *title, + apol_vector_t * log_items, apol_vector_t * policy_items, apol_vector_t * included) +{ + struct polcomp_view pv; + gint response; + + memset(&pv, 0, sizeof(pv)); + pv.top = top; + pv.xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyComponentListsWindow", NULL); + pv.log_items = log_items; + pv.policy_items = policy_items; + if (included == NULL) { + pv.included_items = apol_vector_create(free); + } else { + pv.included_items = apol_vector_create_from_vector(included, apol_str_strdup, NULL, free); + } + apol_vector_destroy(&included); + if (pv.included_items == NULL) { + toplevel_ERR(pv.top, "Error creating dialog: %s", strerror(errno)); + return NULL; + } + + policy_components_view_init_widgets(&pv); + policy_components_view_init_lists(&pv); + policy_components_view_init_signals(&pv); + gtk_window_set_title(GTK_WINDOW(pv.dialog), title); + do { + response = gtk_dialog_run(pv.dialog); + } while (response != GTK_RESPONSE_CLOSE); + + gtk_widget_destroy(GTK_WIDGET(pv.dialog)); + g_object_unref(pv.master_store); + g_object_unref(pv.inc_store); + g_object_unref(pv.exc_store); + apol_vector_destroy(&pv.both_items); + if (apol_vector_get_size(pv.included_items) == 0) { + apol_vector_destroy(&pv.included_items); + return NULL; + } + return pv.included_items; +} diff --git a/seaudit/policy_components_view.h b/seaudit/policy_components_view.h new file mode 100644 index 0000000..eece365 --- /dev/null +++ b/seaudit/policy_components_view.h @@ -0,0 +1,56 @@ +/** + * @file + * Dialog that shows two columns of strings, an included list and an + * excluded list. The user then moves items between the two lists. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POLICY_COMPONENTS_VIEW_H +#define POLICY_COMPONENTS_VIEW_H + +#include "toplevel.h" +#include +#include + +/** + * Display and run a dialog that allows the user to select items from + * arrays of strings. + * + * @param top Toplevel containing message view. + * @param parent Parent window upon which to center this dialog. + * @param title Title for the dialog window. + * @param log_items Vector of strings that the log has. The function + * will not modify this vector. + * @param policy_items Vector of strings that the policy has. If + * NULL, then no policy is loaded. The function will not modify this + * vector. + * @param included List of strings to be included. The strings are + * assumed to have been strdup()ped from some other source. This + * function takes ownership of the vector and its contents. + * + * @return A vector newly allocated strings corresponding to the items + * to be included, or NULL upon error. The caller must call + * apol_vector_destroy() upon the return value. + */ +apol_vector_t *policy_components_view_run(toplevel_t * top, GtkWindow * parent, const char *title, + apol_vector_t * log_items, apol_vector_t * policy_items, apol_vector_t * included); + +#endif diff --git a/seaudit/policy_view.c b/seaudit/policy_view.c new file mode 100644 index 0000000..5fdc9c0 --- /dev/null +++ b/seaudit/policy_view.c @@ -0,0 +1,573 @@ +/** + * @file + * Implementation of policy viewer. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "policy_view.h" +#include "utilgui.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* these are for mmaping the policy file */ +#include +#include +#include +#include +#include + +struct policy_view +{ + toplevel_t *top; + GladeXML *xml; + GtkWindow *window; + GtkNotebook *notebook; + GtkToggleButton *stype_check, *ttype_check, *class_check; + GtkComboBoxEntry *stype_combo, *ttype_combo, *class_combo; + GtkToggleButton *stype_direct, *ttype_direct; + GtkListStore *type_model, *class_model; + apol_vector_t *type_list, *class_list; + GtkTextBuffer *rules_text, *policy_text; + char *policy_text_mmap; + size_t policy_text_len; +}; + +/** + * Display a vector of rules (either qpol_avrule_t or + * qpol_syn_avrule_t) in the rules text buffer. + */ +static void policy_view_display_avrule_results(policy_view_t * pv, apol_vector_t * results, int is_syn_rules) +{ + apol_policy_t *policy = toplevel_get_policy(pv->top); + qpol_policy_t *qp = apol_policy_get_qpol(policy); + GtkTextIter start, end; + char *string, buf[64]; + size_t i; + + gtk_text_buffer_get_start_iter(pv->rules_text, &start); + gtk_text_buffer_get_end_iter(pv->rules_text, &end); + gtk_text_buffer_delete(pv->rules_text, &start, &end); + + snprintf(buf, 64, "%zd rule(s) match the search criteria.\n\n", apol_vector_get_size(results)); + gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, buf, -1, "summary", NULL); + for (i = 0; i < apol_vector_get_size(results); i++) { + if (!is_syn_rules) { + qpol_avrule_t *rule = apol_vector_get_element(results, i); + string = apol_avrule_render(policy, rule); + } else { + qpol_syn_avrule_t *rule = apol_vector_get_element(results, i); + string = apol_syn_avrule_render(policy, rule); + unsigned long lineno; + if (qpol_policy_has_capability(qp, QPOL_CAP_LINE_NUMBERS)) { + qpol_syn_avrule_get_lineno(qp, rule, &lineno); + sprintf(buf, "%ld", lineno); + gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, "[", -1, "rule", NULL); + gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, buf, -1, "line-number", NULL); + gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, "] ", -1, "rule", NULL); + } + } + if (string == NULL) { + toplevel_ERR(pv->top, "Error displaying rule: %s", strerror(errno)); + return; + } + gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, string, -1, "rule", NULL); + free(string); + gtk_text_buffer_insert(pv->rules_text, &end, "\n", -1); + } +} + +struct find_terules_datum +{ + apol_policy_t *policy; + apol_avrule_query_t *query; + apol_vector_t *results; + int is_syn_rules, retval; + progress_t *progress; +}; + +/** + * Perform the rule query within a thread. + */ +static gpointer policy_view_find_terules_runner(gpointer data) +{ + struct find_terules_datum *run = (struct find_terules_datum *)data; + run->results = NULL; + qpol_policy_t *q = apol_policy_get_qpol(run->policy); + if (!qpol_policy_has_capability(q, QPOL_CAP_SYN_RULES)) { + progress_update(run->progress, "Searching AV rules"); + run->retval = apol_avrule_get_by_query(run->policy, run->query, &run->results); + run->is_syn_rules = 0; + } else { + qpol_policy_build_syn_rule_table(q); + progress_update(run->progress, "Searching syntactic AV rules"); + run->retval = apol_syn_avrule_get_by_query(run->policy, run->query, &run->results); + run->is_syn_rules = 1; + } + if (run->retval == 0) { + progress_done(run->progress); + } else { + progress_abort(run->progress, NULL); + } + return NULL; +} + +/** + * Collect the rule search criteria into an avrule_query_t object. + * Actually execute the query in a progress thread. + */ +static void policy_view_on_find_terules_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + policy_view_t *pv = (policy_view_t *) user_data; + apol_policy_t *policy = toplevel_get_policy(pv->top); + apol_avrule_query_t *query = apol_avrule_query_create(); + apol_avrule_query_set_regex(policy, query, 1); + struct find_terules_datum run; + const char *s; + gboolean only_direct; + apol_avrule_query_set_rules(policy, query, QPOL_RULE_ALLOW); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->stype_check))) { + s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->stype_combo)); + only_direct = gtk_toggle_button_get_active(pv->stype_direct); + if (strcmp(s, "") == 0) { + toplevel_ERR(pv->top, "No source type was selected."); + return; + } + apol_avrule_query_set_source(policy, query, s, only_direct == FALSE); + apol_avrule_query_set_source_component(policy, query, APOL_QUERY_SYMBOL_IS_TYPE); + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->ttype_check))) { + s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->ttype_combo)); + only_direct = gtk_toggle_button_get_active(pv->ttype_direct); + if (strcmp(s, "") == 0) { + toplevel_ERR(pv->top, "No target type was selected."); + return; + } + apol_avrule_query_set_target(policy, query, s, only_direct == FALSE); + apol_avrule_query_set_source_component(policy, query, APOL_QUERY_SYMBOL_IS_TYPE); + } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->class_check))) { + s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->class_combo)); + if (strcmp(s, "") == 0) { + toplevel_ERR(pv->top, "No object class was selected."); + return; + } + apol_avrule_query_append_class(policy, query, s); + } + + util_cursor_wait(GTK_WIDGET(pv->window)); + run.policy = policy; + run.query = query; + run.progress = toplevel_get_progress(pv->top); + progress_show(run.progress, "Finding TE Rules"); + g_thread_create(policy_view_find_terules_runner, &run, FALSE, NULL); + progress_wait(run.progress); + progress_hide(run.progress); + util_cursor_clear(GTK_WIDGET(pv->window)); + apol_avrule_query_destroy(&query); + if (run.retval == 0) { + policy_view_display_avrule_results(pv, run.results, run.is_syn_rules); + } + apol_vector_destroy(&run.results); +} + +static void policy_view_close(GtkButton * button __attribute__ ((unused)), gpointer user_data) +{ + policy_view_t *pv = (policy_view_t *) user_data; + gtk_widget_hide(GTK_WIDGET(pv->window)); +} + +static gboolean policy_view_on_delete_event(GtkWidget * widget, GdkEvent * event __attribute__ ((unused)), gpointer user_data + __attribute__ ((unused))) +{ + gtk_widget_hide(widget); + return TRUE; +} + +static void policy_view_on_stype_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + policy_view_t *pv = (policy_view_t *) user_data; + gtk_widget_set_sensitive(GTK_WIDGET(pv->stype_combo), sens); + gtk_widget_set_sensitive(GTK_WIDGET(pv->stype_direct), sens); +} + +static void policy_view_on_ttype_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + policy_view_t *pv = (policy_view_t *) user_data; + gtk_widget_set_sensitive(GTK_WIDGET(pv->ttype_combo), sens); + gtk_widget_set_sensitive(GTK_WIDGET(pv->ttype_direct), sens); +} + +static void policy_view_on_class_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + policy_view_t *pv = (policy_view_t *) user_data; + gtk_widget_set_sensitive(GTK_WIDGET(pv->class_combo), sens); + +} + +static gboolean policy_view_on_line_event(GtkTextTag * tag + __attribute__ ((unused)), GObject * event_object + __attribute__ ((unused)), GdkEvent * event, const GtkTextIter * iter, gpointer user_data) +{ + policy_view_t *pv = (policy_view_t *) user_data; + GtkTextIter start, end; + int line; + GtkTextView *view; + if (event->type != GDK_BUTTON_PRESS) { + return FALSE; + } + start = *iter; + while (!gtk_text_iter_starts_word(&start)) + gtk_text_iter_backward_char(&start); + end = start; + while (!gtk_text_iter_ends_word(&end)) + gtk_text_iter_forward_char(&end); + /* subtract 1 because text buffers are indexed from 0 */ + line = atoi(gtk_text_iter_get_slice(&start, &end)) - 1; + view = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowPolicyText")); + assert(view != NULL); + gtk_notebook_set_current_page(pv->notebook, 1); + gtk_text_buffer_get_start_iter(pv->policy_text, &start); + gtk_text_iter_set_line(&start, line); + gtk_text_view_scroll_to_iter(view, &start, 0.0001, TRUE, 0.0, 0.5); + gtk_text_buffer_place_cursor(pv->policy_text, &start); + gtk_text_view_set_cursor_visible(view, TRUE); + return TRUE; +} + +static gboolean policy_view_on_rules_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data __attribute__ ((unused))) +{ + GtkTextView *textview = GTK_TEXT_VIEW(widget); + gint x, ex, y, ey; + GtkTextIter iter; + GSList *tags, *tagp; + int hovering = 0; + if (event->is_hint) { + gdk_window_get_pointer(event->window, &ex, &ey, NULL); + } else { + ex = event->x; + ey = event->y; + } + gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y); + gtk_text_view_get_iter_at_location(textview, &iter, x, y); + tags = gtk_text_iter_get_tags(&iter); + for (tagp = tags; tagp != NULL; tagp = tagp->next) { + if (strcmp(GTK_TEXT_TAG(tagp->data)->name, "line-number") == 0) { + hovering = TRUE; + break; + } + } + if (hovering) { + GdkCursor *cursor = gdk_cursor_new(GDK_HAND2); + gdk_window_set_cursor(event->window, cursor); + gdk_cursor_unref(cursor); + gdk_flush(); + } else { + gdk_window_set_cursor(event->window, NULL); + } + g_slist_free(tags); + return FALSE; +} + +/** + * Create a text buffer to hold the results of running the TE rules + * search. Initialize its tags and add event handlers to the + * "line-number" tag, such that clicking on the tag jumps to the + * policy's line and hovering over the tag changes the cursor. + */ +static void policy_view_create_rules_buffer(policy_view_t * pv) +{ + GtkTextView *rules_textview; + GtkTextTagTable *table; + GtkTextTag *tag; + rules_textview = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowTERulesResults")); + assert(rules_textview != NULL); + pv->rules_text = gtk_text_buffer_new(NULL); + gtk_text_view_set_buffer(rules_textview, pv->rules_text); + table = gtk_text_buffer_get_tag_table(pv->rules_text); + gtk_text_buffer_create_tag(pv->rules_text, "summary", "family", "monospace", "weight", "bold", NULL); + tag = gtk_text_buffer_create_tag(pv->rules_text, "line-number", + "family", "monospace", "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(policy_view_on_line_event), pv); + g_signal_connect(rules_textview, "motion-notify-event", G_CALLBACK(policy_view_on_rules_motion), NULL); + gtk_text_buffer_create_tag(pv->rules_text, "rule", "family", "monospace", NULL); +} + +policy_view_t *policy_view_create(toplevel_t * top) +{ + GtkWidget *w; + GtkTextView *policy_textview; + policy_view_t *pv; + if ((pv = calloc(1, sizeof(*pv))) == NULL) { + return NULL; + } + pv->top = top; + pv->xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyWindow", NULL); + pv->window = GTK_WINDOW(glade_xml_get_widget(pv->xml, "PolicyWindow")); + assert(pv->window != NULL); + gtk_window_set_transient_for(pv->window, toplevel_get_window(top)); + pv->notebook = GTK_NOTEBOOK(glade_xml_get_widget(pv->xml, "PolicyWindowNotebook")); + assert(pv->notebook != NULL); + + pv->stype_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeCheck")); + pv->ttype_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeCheck")); + pv->class_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowClassCheck")); + assert(pv->stype_check != NULL && pv->ttype_check != NULL && pv->class_check != NULL); + g_signal_connect(pv->stype_check, "toggled", G_CALLBACK(policy_view_on_stype_toggle), pv); + g_signal_connect(pv->ttype_check, "toggled", G_CALLBACK(policy_view_on_ttype_toggle), pv); + g_signal_connect(pv->class_check, "toggled", G_CALLBACK(policy_view_on_class_toggle), pv); + + pv->stype_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeCombo")); + pv->ttype_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeCombo")); + pv->class_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowClassCombo")); + assert(pv->stype_combo != NULL && pv->ttype_combo != NULL && pv->class_combo != NULL); + pv->type_model = gtk_list_store_new(1, G_TYPE_STRING); + pv->class_model = gtk_list_store_new(1, G_TYPE_STRING); + gtk_combo_box_set_model(GTK_COMBO_BOX(pv->stype_combo), GTK_TREE_MODEL(pv->type_model)); + gtk_combo_box_set_model(GTK_COMBO_BOX(pv->ttype_combo), GTK_TREE_MODEL(pv->type_model)); + gtk_combo_box_set_model(GTK_COMBO_BOX(pv->class_combo), GTK_TREE_MODEL(pv->class_model)); + gtk_combo_box_entry_set_text_column(pv->stype_combo, 0); + gtk_combo_box_entry_set_text_column(pv->ttype_combo, 0); + gtk_combo_box_entry_set_text_column(pv->class_combo, 0); + + pv->stype_direct = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeDirectCheck")); + pv->ttype_direct = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeDirectCheck")); + assert(pv->stype_direct != NULL && pv->ttype_direct != NULL); + + policy_view_create_rules_buffer(pv); + + policy_textview = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowPolicyText")); + assert(policy_textview != NULL); + pv->policy_text = gtk_text_buffer_new(NULL); + gtk_text_view_set_buffer(policy_textview, pv->policy_text); + + /* set up signal handlers for the widgets */ + + w = glade_xml_get_widget(pv->xml, "PolicyWindowFindTERulesButton"); + assert(w != NULL); + g_signal_connect(w, "clicked", G_CALLBACK(policy_view_on_find_terules_click), pv); + + w = glade_xml_get_widget(pv->xml, "PolicyWindowCloseButton"); + assert(w != NULL); + g_signal_connect(w, "clicked", G_CALLBACK(policy_view_close), pv); + g_signal_connect(pv->window, "delete_event", G_CALLBACK(policy_view_on_delete_event), NULL); + + policy_view_update(pv, NULL); + return pv; +} + +void policy_view_destroy(policy_view_t ** pv) +{ + if (pv != NULL && *pv != NULL) { + apol_vector_destroy(&(*pv)->type_list); + apol_vector_destroy(&(*pv)->class_list); + free(*pv); + *pv = NULL; + } +} + +/** + * If the currently loaded policy is a source policy, then load its + * contents into the policy text buffer. Otherwise let the user know + * that the policy is binary. + * + * @param pv Policy view's policy source tab to update. + * @param path Path to the currently loaded policy, or NULL if no + * policy is loaded. + */ +static void policy_view_load_policy_source(policy_view_t * pv, apol_policy_path_t * path) +{ + apol_policy_t *policy = toplevel_get_policy(pv->top); + const char *primary_path; + if (path == NULL) { + gtk_text_buffer_set_text(pv->policy_text, "No policy has been loaded.", -1); + return; + } + primary_path = apol_policy_path_get_primary(path); + if (!qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SOURCE)) { + GString *string = g_string_new(""); + g_string_printf(string, "Policy file %s is not a source policy.", primary_path); + gtk_text_buffer_set_text(pv->policy_text, string->str, -1); + g_string_free(string, TRUE); + } else { + /* load the policy by mmap()ing the file */ + struct stat statbuf; + int fd; + if (pv->policy_text_mmap != NULL) { + munmap(pv->policy_text_mmap, pv->policy_text_len); + } + + pv->policy_text_mmap = NULL; + pv->policy_text_len = 0; + + if ((fd = open(primary_path, O_RDONLY)) < 0) { + toplevel_ERR(pv->top, "Could not open %s for reading.", primary_path); + return; + } + if (fstat(fd, &statbuf) < 0) { + close(fd); + toplevel_ERR(pv->top, "Could not stat %s.", primary_path); + return; + } + + pv->policy_text_len = statbuf.st_size; + if ((pv->policy_text_mmap = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + close(fd); + pv->policy_text_mmap = NULL; + toplevel_ERR(pv->top, "Could not mmap %s.", primary_path); + return; + } + close(fd); + gtk_text_buffer_set_text(pv->policy_text, pv->policy_text_mmap, pv->policy_text_len); + } +} + +/** + * If a policy is currently loaded then set the rule search combo box + * menus to that policy's components. Otherwies clear the combo box + * menus. + * + * @param pv Policy view's rule search boxes to modify. + */ +static void policy_view_populate_combo_boxes(policy_view_t * pv) +{ + apol_policy_t *policy = toplevel_get_policy(pv->top); + gtk_list_store_clear(pv->type_model); + gtk_list_store_clear(pv->class_model); + apol_vector_destroy(&pv->type_list); + apol_vector_destroy(&pv->class_list); + pv->type_list = apol_vector_create(NULL); + pv->class_list = apol_vector_create(NULL); + if (policy != NULL) { + qpol_policy_t *qp = apol_policy_get_qpol(policy); + size_t i; + const qpol_type_t *type; + const qpol_class_t *obj_class; + const char *s; + GtkTreeIter iter; + apol_vector_t *v; + apol_type_get_by_query(policy, NULL, &v); + for (i = 0; i < apol_vector_get_size(v); i++) { + type = apol_vector_get_element(v, i); + qpol_type_get_name(qp, type, &s); + apol_vector_append(pv->type_list, (void *)s); + } + apol_vector_destroy(&v); + apol_vector_sort(pv->type_list, apol_str_strcmp, NULL); + for (i = 0; i < apol_vector_get_size(pv->type_list); i++) { + s = apol_vector_get_element(pv->type_list, i); +#ifdef GTK_2_8 + gtk_list_store_insert_with_values(pv->type_model, &iter, i, 0, s, -1); +#else + gtk_list_store_insert(pv->type_model, &iter, i); + gtk_list_store_set(pv->type_model, &iter, 0, s, -1); +#endif + } + apol_class_get_by_query(policy, NULL, &v); + for (i = 0; i < apol_vector_get_size(v); i++) { + obj_class = apol_vector_get_element(v, i); + qpol_class_get_name(qp, obj_class, &s); + apol_vector_append(pv->class_list, (void *)s); + } + apol_vector_destroy(&v); + apol_vector_sort(pv->class_list, apol_str_strcmp, NULL); + for (i = 0; i < apol_vector_get_size(pv->class_list); i++) { + s = apol_vector_get_element(pv->class_list, i); +#ifdef GTK_2_8 + gtk_list_store_insert_with_values(pv->class_model, &iter, i, 0, s, -1); +#else + gtk_list_store_insert(pv->class_model, &iter, i); + gtk_list_store_set(pv->class_model, &iter, 0, s, -1); +#endif + } + } +} + +void policy_view_update(policy_view_t * pv, apol_policy_path_t * path) +{ + GtkTextIter start, end; + policy_view_load_policy_source(pv, path); + policy_view_populate_combo_boxes(pv); + gtk_text_buffer_get_start_iter(pv->rules_text, &start); + gtk_text_buffer_get_end_iter(pv->rules_text, &end); + gtk_text_buffer_delete(pv->rules_text, &start, &end); + +} + +void policy_view_find_terules(policy_view_t * pv, seaudit_message_t * message) +{ + seaudit_message_type_e type = SEAUDIT_MESSAGE_TYPE_INVALID; + void *data = NULL; + const char *stype = "", *ttype = "", *obj_class = ""; + size_t i; + assert(pv->type_list != NULL); + assert(pv->class_list != NULL); + if (message != NULL) { + data = seaudit_message_get_data(message, &type); + } + if (type == SEAUDIT_MESSAGE_TYPE_AVC) { + seaudit_avc_message_t *avc = data; + if ((stype = seaudit_avc_message_get_source_type(avc)) == NULL) { + stype = ""; + } + if ((ttype = seaudit_avc_message_get_target_type(avc)) == NULL) { + ttype = ""; + } + if ((obj_class = seaudit_avc_message_get_object_class(avc)) == NULL) { + obj_class = ""; + } + } + if (strcmp(stype, "") == 0 || apol_vector_get_index(pv->type_list, stype, apol_str_strcmp, NULL, &i) < 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->stype_combo), -1); + gtk_toggle_button_set_active(pv->stype_check, FALSE); + } else { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->stype_combo), i); + gtk_toggle_button_set_active(pv->stype_check, TRUE); + } + if (strcmp(ttype, "") == 0 || apol_vector_get_index(pv->type_list, ttype, apol_str_strcmp, NULL, &i) < 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->ttype_combo), -1); + gtk_toggle_button_set_active(pv->ttype_check, FALSE); + } else { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->ttype_combo), i); + gtk_toggle_button_set_active(pv->ttype_check, TRUE); + } + if (strcmp(obj_class, "") == 0 || apol_vector_get_index(pv->class_list, obj_class, apol_str_strcmp, NULL, &i) < 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->class_combo), -1); + gtk_toggle_button_set_active(pv->class_check, FALSE); + } else { + gtk_combo_box_set_active(GTK_COMBO_BOX(pv->class_combo), i); + gtk_toggle_button_set_active(pv->class_check, TRUE); + } + gtk_notebook_set_current_page(pv->notebook, 0); + gtk_widget_show(GTK_WIDGET(pv->window)); +} diff --git a/seaudit/policy_view.h b/seaudit/policy_view.h new file mode 100644 index 0000000..17b23ed --- /dev/null +++ b/seaudit/policy_view.h @@ -0,0 +1,77 @@ +/** + * @file + * Declaration of viewer for the currently loaded policy. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POLICY_VIEW_H +#define POLICY_VIEW_H + +#include "toplevel.h" + +#include + +typedef struct policy_view policy_view_t; + +/** + * Create a new policy view object. This is used to display the + * policy's content and to search for TE rules. + * + * @param top Toplevel object that will control the newly opened + * policy view. + * + * @return An initialized policy view object, or NULL upon error. The + * caller must call policy_view_destroy() upon the returned value. + */ +policy_view_t *policy_view_create(toplevel_t * top); + +/** + * Destroy the policy view object. This does nothing if the pointer + * is set to NULL. + * + * @param pv Reference to a policy view object. Afterwards the + * pointer will be set to NULL. + */ +void policy_view_destroy(policy_view_t ** pv); + +/** + * (Re)synchronize the policy displayed in the viewer with the one + * actually loaded. If there is no policy loaded then clear the + * viewer's contents. + * + * @param pv Policy view to update. + * @param path Path to the policy, or NULL if no policy is loaded. + */ +void policy_view_update(policy_view_t * pv, apol_policy_path_t * path); + +/** + * (Re)open the policy view window to allow the user to search for TE + * rules in the currently opened policy. If message is not NULL then + * set the query's initial parameters to the message's source type, + * target type, and object class. + * + * @param pv Policy view object. Note that a policy must already + * exist and policy_view_update() must be first called. + * @param message If non-NULL, the initial parameters for query. + */ +void policy_view_find_terules(policy_view_t * pv, seaudit_message_t * message); + +#endif diff --git a/seaudit/preferences.c b/seaudit/preferences.c new file mode 100644 index 0000000..5e65385 --- /dev/null +++ b/seaudit/preferences.c @@ -0,0 +1,585 @@ +/** + * @file + * Implementation of the storage class preferences_t. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "preferences.h" + +#include +#include +#include +#include +#include + +/** default frequency, in milliseconds, to poll log file for changes */ +#define DEFAULT_LOG_UPDATE_INTERVAL 1000 + +/** maximum number of recent log files and recent policy files to remember */ +#define MAX_RECENT_ENTRIES 5 + +/** name of the user's seaudit personal preferences file */ +#define USER_SEAUDIT_CONF ".seaudit" + +/** name of the system seaudit preference file */ +#define SYSTEM_SEAUDIT_CONF "dot_seaudit" + +struct visible_field +{ + preference_field_e id; + const char *field; + int visible; +}; + +static const struct visible_field default_visible_fields[] = { + {HOST_FIELD, "host_field", 1}, + {MESSAGE_FIELD, "msg_field", 1}, + {DATE_FIELD, "date_field", 1}, + {SUSER_FIELD, "src_usr_field", 0}, + {SROLE_FIELD, "src_role_field", 0}, + {STYPE_FIELD, "src_type_field", 1}, + {SMLS_LVL_FIELD, "src_mls_lvl_field", 0}, + {SMLS_CLR_FIELD, "src_mls_clr_field", 0}, + {TUSER_FIELD, "tgt_usr_field", 0}, + {TROLE_FIELD, "tgt_role_field", 0}, + {TTYPE_FIELD, "tgt_type_field", 1}, + {TMLS_LVL_FIELD, "tgt_mls_lvl_field", 0}, + {TMLS_CLR_FIELD, "tgt_mls_clr_field", 0}, + {OBJCLASS_FIELD, "obj_class_field", 1}, + {PERM_FIELD, "perm_field", 1}, + {EXECUTABLE_FIELD, "exe_field", 1}, + {COMMAND_FIELD, "comm_field", 1}, + {NAME_FIELD, "name_field", 0}, + {PID_FIELD, "pid_field", 0}, + {INODE_FIELD, "inode_field", 0}, + {PATH_FIELD, "path_field", 0}, + {OTHER_FIELD, "other_field", 1} +}; + +static const size_t num_visible_fields = sizeof(default_visible_fields) / sizeof(default_visible_fields[0]); + +struct preferences +{ + /** path to default system log file */ + char *log; + /** path to default policy */ + apol_policy_path_t *policy; + /** default path when writing reports */ + char *report; + /** default path to the stylesheet, used during report writing */ + char *stylesheet; + /** vector of paths (strings) to recently opened log files */ + apol_vector_t *recent_log_files; + /** vector of apol_policy_path_t objects to recently opened + policies */ + apol_vector_t *recent_policy_files; + /** non-zero if seaudit should poll the log file for changes */ + int real_time_log; + /** frequency, in milliesconds, to poll log file */ + int real_time_interval; + struct visible_field *fields; +}; + +void preferences_apol_policy_path_free(void *elem) +{ + apol_policy_path_t *path = elem; + apol_policy_path_destroy(&path); +} + +/** + * Parse the old-style recent policies list (a ':' separated list of + * paths) into the recent_policy_files field. + */ +static int preferences_parse_old_recent_files(preferences_t * prefs, const char *s) +{ + apol_vector_t *v = NULL; + size_t i; + char *base; + apol_policy_path_t *path; + int error = 0; + + if ((v = apol_str_split(s, ":")) == NULL) { + error = errno; + goto cleanup; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + base = apol_vector_get_element(v, i); + if ((path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, base, NULL)) == NULL || + apol_vector_append(prefs->recent_policy_files, path) < 0) { + error = errno; + apol_policy_path_destroy(&path); + goto cleanup; + } + } + + cleanup: + apol_vector_destroy(&v); + if (error != 0) { + errno = error; + return -1; + } + return 0; +} + +/** + * Parse the new recent policy files, which now spans across multiple + * lines. + */ +static int preferences_parse_new_recent_files(preferences_t * prefs, FILE * f, int num_files) +{ + int count; + for (count = 0; count < num_files; count++) { + char *var_name, *value = NULL; + apol_policy_path_t *p = NULL; + if (asprintf(&var_name, "RECENT_POLICY_PATH_%d", count) < 0) { + return -1; + } + value = apol_config_get_var(var_name, f); + free(var_name); + if (value == NULL || + (p = apol_policy_path_create_from_string(value)) == NULL || + apol_vector_append(prefs->recent_policy_files, p) < 0) { + free(value); + apol_policy_path_destroy(&p); + return -1; + } + free(value); + } + return 0; +} + +preferences_t *preferences_create(void) +{ + preferences_t *prefs = NULL; + FILE *file = NULL; + char *path = NULL, *value; + apol_vector_t *v = NULL; + size_t i, j; + int error = 0; + + if ((prefs = calloc(1, sizeof(*prefs))) == NULL || + (prefs->log = strdup("")) == NULL || + (prefs->report = strdup("")) == NULL || + (prefs->stylesheet = strdup("")) == NULL || + (prefs->recent_log_files = apol_vector_create(free)) == NULL || + (prefs->recent_policy_files = apol_vector_create(preferences_apol_policy_path_free)) == NULL || + (prefs->fields = calloc(num_visible_fields, sizeof(struct visible_field))) == NULL) { + error = errno; + goto cleanup; + } + prefs->real_time_interval = DEFAULT_LOG_UPDATE_INTERVAL; + memcpy(prefs->fields, default_visible_fields, num_visible_fields * sizeof(struct visible_field)); + path = apol_file_find_user_config(USER_SEAUDIT_CONF); + if (!path) { + if ((path = apol_file_find_path(SYSTEM_SEAUDIT_CONF)) == NULL) { + return prefs; + } + } + if ((file = fopen(path, "r")) == NULL) { + error = errno; + goto cleanup; + } + if ((value = apol_config_get_var("DEFAULT_LOG_FILE", file)) != NULL) { + free(prefs->log); + prefs->log = value; + } + if ((value = apol_config_get_var("DEFAULT_POLICY_FILE", file)) != NULL) { + apol_policy_path_destroy(&prefs->policy); + if (apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, value, NULL) == NULL) { + error = errno; + free(value); + goto cleanup; + } + free(value); + } + if ((value = apol_config_get_var("DEFAULT_POLICY_PATH", file)) != NULL) { + apol_policy_path_destroy(&prefs->policy); + if ((prefs->policy = apol_policy_path_create_from_string(value)) == NULL) { + error = errno; + free(value); + goto cleanup; + } + free(value); + } + if ((value = apol_config_get_var("DEFAULT_REPORT_CONFIG_FILE", file)) != NULL) { + free(prefs->report); + prefs->report = value; + } + if ((value = apol_config_get_var("DEFAULT_REPORT_CSS_FILE", file)) != NULL) { + free(prefs->stylesheet); + prefs->stylesheet = value; + } + if ((value = apol_config_get_var("RECENT_LOG_FILES", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) { + error = errno; + free(value); + goto cleanup; + } + free(value); + apol_vector_destroy(&prefs->recent_log_files); + prefs->recent_log_files = v; + + /* test if there exists the new recent list that contains + * module filenames */ + if ((value = apol_config_get_var("RECENT_POLICY_PATH_FILES", file)) != NULL) { + if (preferences_parse_new_recent_files(prefs, file, atoi(value)) < 0) { + error = errno; + free(value); + goto cleanup; + } + } else { + /* use older style that could only handle monolithic policies */ + if ((value = apol_config_get_var("RECENT_POLICY_FILES", file)) == NULL + || preferences_parse_old_recent_files(prefs, value) < 0) { + error = errno; + free(value); + goto cleanup; + } + } + free(value); + + if ((value = apol_config_get_var("LOG_COLUMNS_HIDDEN", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) { + error = errno; + goto cleanup; + } + for (j = 0; j < num_visible_fields; j++) { + prefs->fields[j].visible = 1; + } + for (i = 0; i < apol_vector_get_size(v); i++) { + char *s = apol_vector_get_element(v, i); + for (j = 0; j < num_visible_fields; j++) { + if (strcmp(s, prefs->fields[j].field) == 0) { + prefs->fields[j].visible = 0; + break; + } + } + } + free(value); + apol_vector_destroy(&v); + value = apol_config_get_var("REAL_TIME_LOG_MONITORING", file); + if (value != NULL && value[0] != '0') { + prefs->real_time_log = 1; + } + free(value); + value = apol_config_get_var("REAL_TIME_LOG_UPDATE_INTERVAL", file); + if (value != NULL) { + prefs->real_time_interval = atoi(value); + } + free(value); + cleanup: + free(path); + if (file != NULL) { + fclose(file); + } + if (error != 0) { + preferences_destroy(&prefs); + errno = error; + return NULL; + } + return prefs; +} + +void preferences_destroy(preferences_t ** prefs) +{ + if (prefs != NULL && *prefs != NULL) { + free((*prefs)->log); + apol_policy_path_destroy(&(*prefs)->policy); + free((*prefs)->report); + free((*prefs)->stylesheet); + apol_vector_destroy(&(*prefs)->recent_log_files); + apol_vector_destroy(&(*prefs)->recent_policy_files); + free((*prefs)->fields); + free(*prefs); + *prefs = NULL; + } +} + +int preferences_write_to_conf_file(preferences_t * prefs) +{ + FILE *file = NULL; + char *home, *conf_file = NULL, *value; + apol_vector_t *hidden_fields = NULL; + size_t i; + int retval = 0, error = 0; + + /* we need to open ~/.seaudit */ + home = getenv("HOME"); + if (!home) { + error = EBADRQC; + goto cleanup; + } + if (asprintf(&conf_file, "%s/%s", home, USER_SEAUDIT_CONF) < 0) { + error = errno; + goto cleanup; + } + + if ((file = fopen(conf_file, "w")) == NULL) { + error = errno; + goto cleanup; + } + + fprintf(file, "# configuration file for seaudit - an audit log tool for Security Enhanced Linux.\n"); + fprintf(file, "# this file is auto-generated\n\n"); + + if (strcmp(prefs->log, "") != 0) { + fprintf(file, "DEFAULT_LOG_FILE %s\n", prefs->log); + } + if (prefs->policy != NULL) { + value = apol_policy_path_to_string(prefs->policy); + if (value == NULL) { + error = errno; + goto cleanup; + } + fprintf(file, "DEFAULT_POLICY_PATH %s\n", value); + free(value); + } + if (strcmp(prefs->report, "") != 0) { + fprintf(file, "DEFAULT_REPORT_CONFIG_FILE %s\n", prefs->report); + } + if (strcmp(prefs->stylesheet, "") != 0) { + fprintf(file, "DEFAULT_REPORT_CSS_FILE %s\n", prefs->stylesheet); + } + if ((value = apol_str_join(prefs->recent_log_files, ":")) == NULL) { + error = errno; + goto cleanup; + } + fprintf(file, "RECENT_LOG_FILES %s\n", value); + free(value); + + fprintf(file, "RECENT_POLICY_PATH_FILES %zd\n", apol_vector_get_size(prefs->recent_policy_files)); + for (i = 0; i < apol_vector_get_size(prefs->recent_policy_files); i++) { + apol_policy_path_t *p = apol_vector_get_element(prefs->recent_policy_files, i); + if ((value = apol_policy_path_to_string(p)) == NULL) { + error = errno; + goto cleanup; + } + fprintf(file, "RECENT_POLICY_PATH_%zd %s\n", i, value); + free(value); + } + + if ((hidden_fields = apol_vector_create(NULL)) == NULL) { + error = errno; + goto cleanup; + } + for (i = 0; i < num_visible_fields; i++) { + if (!prefs->fields[i].visible && apol_vector_append(hidden_fields, (char *)prefs->fields[i].field) < 0) { + error = errno; + goto cleanup; + } + } + if ((value = apol_str_join(hidden_fields, ":")) == NULL) { + error = errno; + goto cleanup; + } + fprintf(file, "LOG_COLUMNS_HIDDEN %s\n", value); + free(value); + fprintf(file, "REAL_TIME_LOG_MONITORING %d\n", prefs->real_time_log); + fprintf(file, "REAL_TIME_LOG_UPDATE_INTERVAL %d\n", prefs->real_time_interval); + retval = 0; + cleanup: + free(conf_file); + apol_vector_destroy(&hidden_fields); + if (file != NULL) { + fclose(file); + } + errno = error; + return retval; +} + +int preferences_is_column_visible(preferences_t * prefs, preference_field_e id) +{ + size_t i; + for (i = 0; i < num_visible_fields; i++) { + if (prefs->fields[i].id == id) { + return prefs->fields[i].visible; + } + } + assert(0); + return -1; +} + +void preferences_set_column_visible(preferences_t * prefs, preference_field_e id, int visible) +{ + size_t i; + for (i = 0; i < num_visible_fields; i++) { + if (prefs->fields[i].id == id) { + prefs->fields[i].visible = visible; + return; + } + } + assert(0); +} + +int preferences_set_log(preferences_t * prefs, const char *log) +{ + char *s; + if ((s = strdup(log)) == NULL) { + return -1; + } + free(prefs->log); + prefs->log = s; + return 0; +} + +const char *preferences_get_log(preferences_t * prefs) +{ + return prefs->log; +} + +int preferences_set_policy(preferences_t * prefs, const apol_policy_path_t * policy) +{ + apol_policy_path_t *new_policy; + if ((new_policy = apol_policy_path_create_from_policy_path(policy)) == NULL) { + return -1; + } + apol_policy_path_destroy(&prefs->policy); + prefs->policy = new_policy; + return 0; +} + +const apol_policy_path_t *preferences_get_policy(preferences_t * prefs) +{ + return prefs->policy; +} + +int preferences_set_report(preferences_t * prefs, const char *report) +{ + char *s; + if ((s = strdup(report)) == NULL) { + return -1; + } + free(prefs->report); + prefs->report = s; + return 0; +} + +const char *preferences_get_report(preferences_t * prefs) +{ + return prefs->report; +} + +int preferences_set_stylesheet(preferences_t * prefs, const char *stylesheet) +{ + char *s; + if ((s = strdup(stylesheet)) == NULL) { + return -1; + } + free(prefs->stylesheet); + prefs->stylesheet = s; + return 0; +} + +const char *preferences_get_stylesheet(preferences_t * prefs) +{ + return prefs->stylesheet; +} + +void preferences_set_real_time_at_startup(preferences_t * prefs, int startup) +{ + prefs->real_time_log = startup; +} + +int preferences_get_real_time_at_startup(preferences_t * prefs) +{ + return prefs->real_time_log; +} + +void preferences_set_real_time_interval(preferences_t * prefs, int interval) +{ + if (interval <= 0) { + prefs->real_time_interval = 0; + } else { + prefs->real_time_interval = interval; + } +} + +int preferences_get_real_time_interval(preferences_t * prefs) +{ + return prefs->real_time_interval; +} + +/** + * Add an entry to a vector, discarding the oldest entry if the vector + * size is too large. + */ +static int prefs_add_recent_vector(apol_vector_t * v, const char *entry) +{ + size_t i; + char *s; + if (apol_vector_get_index(v, (void *)entry, apol_str_strcmp, NULL, &i) == 0) { + return 0; + } + if ((s = strdup(entry)) == NULL || apol_vector_append(v, s) < 0) { + int error = errno; + free(s); + errno = error; + return -1; + } + if (apol_vector_get_size(v) >= MAX_RECENT_ENTRIES) { + s = apol_vector_get_element(v, 0); + free(s); + return apol_vector_remove(v, 0); + } + return 0; +} + +int preferences_add_recent_log(preferences_t * prefs, const char *log) +{ + return prefs_add_recent_vector(prefs->recent_log_files, log); +} + +apol_vector_t *preferences_get_recent_logs(preferences_t * prefs) +{ + return prefs->recent_log_files; +} + +static int preferences_policy_path_compare(const void *a, const void *b, void *data __attribute__ ((unused))) +{ + return apol_policy_path_compare((const apol_policy_path_t *)a, (const apol_policy_path_t *)b); +} + +int preferences_add_recent_policy(preferences_t * prefs, const apol_policy_path_t * policy) +{ + size_t i; + apol_policy_path_t *p = NULL; + if (apol_vector_get_index(prefs->recent_policy_files, policy, preferences_policy_path_compare, NULL, &i) == 0) { + return 0; + } + if ((p = apol_policy_path_create_from_policy_path(policy)) == NULL || apol_vector_append(prefs->recent_policy_files, p) < 0) { + int error = errno; + apol_policy_path_destroy(&p); + errno = error; + return -1; + } + if (apol_vector_get_size(prefs->recent_policy_files) >= MAX_RECENT_ENTRIES) { + p = apol_vector_get_element(prefs->recent_policy_files, 0); + apol_policy_path_destroy(&p); + return apol_vector_remove(prefs->recent_policy_files, 0); + } + return 0; +} + +apol_vector_t *preferences_get_recent_policies(preferences_t * prefs) +{ + return prefs->recent_policy_files; +} diff --git a/seaudit/preferences.h b/seaudit/preferences.h new file mode 100644 index 0000000..8891943 --- /dev/null +++ b/seaudit/preferences.h @@ -0,0 +1,274 @@ +/** + * @file + * Declaration of the current user's preferences for the seaudit + * application. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PREFERENCES_H +#define PREFERENCES_H + +#include +#include + +typedef struct preferences preferences_t; + +/* n.b.: OTHER_FIELD must be the last entry in this enumeration, for + message_view stops processing after that token */ +typedef enum preference_field +{ + HOST_FIELD, MESSAGE_FIELD, DATE_FIELD, + SUSER_FIELD, SROLE_FIELD, STYPE_FIELD, SMLS_LVL_FIELD, SMLS_CLR_FIELD, + TUSER_FIELD, TROLE_FIELD, TTYPE_FIELD, TMLS_LVL_FIELD, TMLS_CLR_FIELD, + OBJCLASS_FIELD, PERM_FIELD, + EXECUTABLE_FIELD, COMMAND_FIELD, NAME_FIELD, + PID_FIELD, INODE_FIELD, PATH_FIELD, OTHER_FIELD +} preference_field_e; + +/** + * Allocate and return a preferences object. This function will first + * initialize the object using the user's configuration file. If that + * is not readable then the system-wide configuration is attempted. + * It is not an error if both files are not available. + * + * @return An initialized preferences object, or NULL upon error. The + * caller must call preferences_destroy() afterwards. + */ +preferences_t *preferences_create(void); + +/** + * Destroy a preferences object, and all memory associated with it. + * Does nothing if the pointer is already NULL. + * + * @param prefs Reference to a preferences object to destroy. This + * will be set to NULL afterwards. + */ +void preferences_destroy(preferences_t ** prefs); + +/** + * Write the preferences object to the user's configuration file, + * overwriting any existing file. + * + * @param prefs Preference object to write. + * + * @return 0 if successfully written, < 0 upon error. + */ +int preferences_write_to_conf_file(preferences_t * prefs); + +/** + * Return the visibility of the column with the given preference id. + * + * @param prefs Preference object to query. + * @param id Preferences column identifier. + * + * @return Non-zero if the column is set to be visible, zero if not. + */ +int preferences_is_column_visible(preferences_t * prefs, preference_field_e id); + +/** + * Set the visibility of a column with the given preference id. Note + * that this will not update any message_view_t. + * + * @param prefs Preference object to query. + * @param id Preferences column identifier. + * @param visible If non-zero then set column visible, zero to hide. + * + * @see message_view_update_visible_columns needs to be called if + * column visibilities are changed. + */ +void preferences_set_column_visible(preferences_t * prefs, preference_field_e id, int visible); + +/** + * Set the filename for the preferred audit log file. Unless + * overridden by the command line, this log file will be opened when + * seaudit is launched. + * + * @param prefs Preference object to modify. + * @param log Path to the log file. The string will be duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_set_log(preferences_t * prefs, const char *log); + +/** + * Get the filename for the preferred log file from the preferences + * object. + * + * @param prefs Preference object to query. + * + * @return Filename for the log file, or an empty string if none set. + * Do not modify this string. + */ +const char *preferences_get_log(preferences_t * prefs); + +/** + * Set the path for the preferred policy. Unless overridden by the + * command line, this policy will be opened when seaudit is launched. + * + * @param prefs Preference object to modify. + * @param policy Path to the policy file. The policy path object will + * be duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_set_policy(preferences_t * prefs, const apol_policy_path_t * policy); + +/** + * Get the policy path object for the preferred policy from the + * preferences object. + * + * @param prefs Preference object to query. + * + * @return Policy path object for the policy, or NULL if none set. Do + * not modify this object. + */ +const apol_policy_path_t *preferences_get_policy(preferences_t * prefs); + +/** + * Set the default report filename. + * + * @param prefs Preference object to modify. + * @param report Path to the report. The string will be duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_set_report(preferences_t * prefs, const char *report); + +/** + * Get the default report filename. + * + * @param prefs Preference object to query. + * + * @return Filename for the report, or an empty string if none set. + * Do not modify this string. + */ +const char *preferences_get_report(preferences_t * prefs); + +/** + * Set the default stylesheet filename. + * + * @param prefs Preference object to modify. + * @param stylesheet Path to the stylesheet. The string will be + * duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_set_stylesheet(preferences_t * prefs, const char *stylesheet); + +/** + * Get the default stylesheet filename. + * + * @param prefs Preference object to query. + * + * @return Filename for the stylesheet, or an empty string if none + * set. Do not modify this string. + */ +const char *preferences_get_stylesheet(preferences_t * prefs); + +/** + * Set the default real-time setting for opened log files. If startup + * is non-zero, then the real-time monitor will be enabled for new log + * files. + * + * @param prefs Preferences object to modify. + * @param startup If non-zero, then enable real-time by default. + */ +void preferences_set_real_time_at_startup(preferences_t * prefs, int startup); + +/** + * Get the default value for real-time monitoring. + * + * @param prefs Preference object to query. + * + * @return Non-zero if opened logs should be monitored. + */ +int preferences_get_real_time_at_startup(preferences_t * prefs); + +/** + * Set the time interval (in milliseconds) for polling the log file + * during real-time monitoring. + * + * @param prefs Preferences object to modify. + * @param interval Polling interval in milliseconds. + */ +void preferences_set_real_time_interval(preferences_t * prefs, int interval); + +/** + * Get the time interval (in milliseconds) when performing real-time + * monitoring. + * + * @param prefs Preference object to query. + * + * @return Time interval in milliseconds. + */ +int preferences_get_real_time_interval(preferences_t * prefs); + +/** + * Add a filename to the recently opened log files list. If the name + * is already in the list then do nothing. Otherwise append the name + * to the end of the list. If the list grows too large then remove + * the oldest entry. + * + * @param prefs Preference object to modify. + * @param log Path to the most recently opened log. The string will + * be duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_add_recent_log(preferences_t * prefs, const char *log); + +/** + * Return a vector of recently loaded log files (type char *), with + * the oldest file first. Note that the vector may be empty. + * + * @param prefs Preferences object to query. + * + * @return Vector of paths. Treat this vector as const. + */ +apol_vector_t *preferences_get_recent_logs(preferences_t * prefs); + +/** + * Add a policy path to the recently opened policy files list. If the + * name is already in the list then do nothing. Otherwise append the + * name to the end of the list. If the list grows too large then + * remove the oldest entry. + * + * @param prefs Preference object to modify. + * @param policy Path to the most recently opened policy. The path + * will be duplicated. + * + * @return 0 on success, < 0 on error. + */ +int preferences_add_recent_policy(preferences_t * prefs, const apol_policy_path_t * policy); + +/** + * Return a vector of recently loaded policy files (type + * apol_policy_path_t *), with the oldest file first. Note that the + * vector may be empty. + * + * @param prefs Preferences object to query. + * + * @return Vector of paths. Treat this vector as const. + */ +apol_vector_t *preferences_get_recent_policies(preferences_t * prefs); + +#endif diff --git a/seaudit/preferences_view.c b/seaudit/preferences_view.c new file mode 100644 index 0000000..7ac5ed3 --- /dev/null +++ b/seaudit/preferences_view.c @@ -0,0 +1,310 @@ +/** + * @file + * Implementation of preferences editor. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Jeremy Solt jsolt@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "open_policy_window.h" +#include "preferences_view.h" +#include "utilgui.h" +#include +#include +#include + +struct pref_view +{ + GladeXML *xml; + toplevel_t *top; + preferences_t *prefs; + GtkDialog *dialog; + const char *current_log; + const apol_policy_path_t *current_policy; + apol_policy_path_t *policy_path; +}; + +struct pref_entry +{ + const char *entry_name, *browse_name; + const char *(*accessor) (preferences_t *); + int (*modifier) (preferences_t *, const char *); + const char *title; + /* next field is for callbacks to the browse button */ + struct pref_view *pv; +}; + +static struct pref_entry pref_entry_data[] = { + {"PrefsViewLogEntry", "PrefsViewLogBrowseButton", preferences_get_log, preferences_set_log, "Select Default Log"}, + {"PrefsViewConfigEntry", "PrefsViewConfigBrowseButton", preferences_get_report, preferences_set_report, + "Select Report Configuration File"}, + {"PrefsViewStylesheetEntry", "PrefsViewStylesheetBrowseButton", preferences_get_stylesheet, preferences_set_stylesheet, + "Select HTML Report Style File"} +}; +static const size_t num_entries = sizeof(pref_entry_data) / sizeof(pref_entry_data[0]); + +struct pref_toggle +{ + const char *widget_name; + preference_field_e preference_field; +}; + +static const struct pref_toggle pref_toggle_map[] = { + {"HostCheck", HOST_FIELD}, + {"MessageCheck", MESSAGE_FIELD}, + {"DateCheck", DATE_FIELD}, + {"SourceUserCheck", SUSER_FIELD}, + {"SourceRoleCheck", SROLE_FIELD}, + {"SourceTypeCheck", STYPE_FIELD}, + {"SourceMLSLVLCheck", SMLS_LVL_FIELD}, + {"SourceMLSCLRCheck", SMLS_CLR_FIELD}, + {"TargetUserCheck", TUSER_FIELD}, + {"TargetRoleCheck", TROLE_FIELD}, + {"TargetTypeCheck", TTYPE_FIELD}, + {"TargetMLSLVLCheck", TMLS_LVL_FIELD}, + {"TargetMLSCLRCheck", TMLS_CLR_FIELD}, + {"ObjectClassCheck", OBJCLASS_FIELD}, + {"PermissionCheck", PERM_FIELD}, + {"ExecutableCheck", EXECUTABLE_FIELD}, + {"CommandCheck", COMMAND_FIELD}, + {"NameCheck", NAME_FIELD}, + {"PIDCheck", PID_FIELD}, + {"InodeCheck", INODE_FIELD}, + {"PathCheck", PATH_FIELD}, + {"OtherCheck", OTHER_FIELD} +}; +static const size_t num_toggles = sizeof(pref_toggle_map) / sizeof(pref_toggle_map[0]); + +static void preferences_view_on_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + const struct pref_entry *pe = (const struct pref_entry *)user_data; + struct pref_view *pv = pe->pv; + GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, pe->entry_name)); + const char *current_path = gtk_entry_get_text(entry); + GtkWindow *parent = GTK_WINDOW(pv->dialog); + const char *title = pe->title; + apol_vector_t *new_paths = util_open_file(parent, title, current_path, 0); + if (new_paths != NULL) { + gtk_entry_set_text(entry, apol_vector_get_element(new_paths, 0)); + apol_vector_destroy(&new_paths); + } +} + +static void preferences_view_on_log_current_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + struct pref_view *pv = (struct pref_view *)user_data; + GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewLogEntry")); + assert(entry != NULL); + if (pv->current_log == NULL) { + gtk_entry_set_text(entry, ""); + } else { + gtk_entry_set_text(entry, pv->current_log); + } +} + +static void preferences_view_on_policy_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + struct pref_view *pv = (struct pref_view *)user_data; + GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry")); + assert(entry != NULL); + apol_policy_path_t *new_path; + + open_policy_window_run(pv->top, pv->policy_path, &new_path); + if (new_path != NULL) { + apol_policy_path_destroy(&pv->policy_path); + pv->policy_path = new_path; + char *path_string = util_policy_path_to_string(pv->policy_path); + gtk_entry_set_text(entry, path_string); + free(path_string); + } +} + +static void preferences_view_on_policy_current_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + struct pref_view *pv = (struct pref_view *)user_data; + GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry")); + assert(entry != NULL); + apol_policy_path_destroy(&pv->policy_path); + if (pv->current_policy != NULL) { + pv->policy_path = apol_policy_path_create_from_policy_path(pv->current_policy); + char *path_string = util_policy_path_to_string(pv->policy_path); + gtk_entry_set_text(entry, path_string); + free(path_string); + } else { + gtk_entry_set_text(entry, ""); + } +} + +static void preferences_view_init_widgets(struct pref_view *pv) +{ + GtkWidget *w; + size_t i; + + w = glade_xml_get_widget(pv->xml, "PreferencesWindow"); + assert(w != NULL); + pv->dialog = GTK_DIALOG(w); + gtk_window_set_transient_for(GTK_WINDOW(pv->dialog), toplevel_get_window(pv->top)); + + for (i = 0; i < num_entries; i++) { + struct pref_entry *pe = pref_entry_data + i; + w = glade_xml_get_widget(pv->xml, pe->browse_name); + assert(w != NULL); + pe->pv = pv; + g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_browse_click), pe); + } + + w = glade_xml_get_widget(pv->xml, "PrefsViewLogCurrentButton"); + assert(w != NULL); + if (pv->current_log == NULL) { + gtk_widget_set_sensitive(w, FALSE); + } + g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_log_current_click), pv); + + w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyModifyButton"); + assert(w != NULL); + g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_policy_browse_click), pv); + w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyCurrentButton"); + assert(w != NULL); + if (pv->current_policy == NULL) { + gtk_widget_set_sensitive(w, FALSE); + } + g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_policy_current_click), pv); +} + +/** + * Copy values from preferences object to dialog widgets. + */ +static void preferences_view_init_values(struct pref_view *pv) +{ + GtkWidget *w; + const char *current_value; + const apol_policy_path_t *current_path; + char *s; + size_t i; + + for (i = 0; i < num_entries; i++) { + const struct pref_entry *pe = pref_entry_data + i; + w = glade_xml_get_widget(pv->xml, pe->entry_name); + assert(w != NULL); + current_value = pe->accessor(pv->prefs); + gtk_entry_set_text(GTK_ENTRY(w), current_value); + } + if ((current_path = preferences_get_policy(pv->prefs)) != NULL) { + pv->policy_path = apol_policy_path_create_from_policy_path(current_path); + w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry"); + char *path_string = util_policy_path_to_string(pv->policy_path); + assert(w != NULL); + gtk_entry_set_text(GTK_ENTRY(w), path_string); + free(path_string); + } + for (i = 0; i < num_toggles; i++) { + int visible; + w = glade_xml_get_widget(pv->xml, pref_toggle_map[i].widget_name); + assert(w != NULL); + visible = preferences_is_column_visible(pv->prefs, pref_toggle_map[i].preference_field); + if (visible) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE); + } + } + w = glade_xml_get_widget(pv->xml, "PrefsViewIntervalEntry"); + assert(w != NULL); + if (asprintf(&s, "%d", preferences_get_real_time_interval(pv->prefs)) >= 0) { + gtk_entry_set_text(GTK_ENTRY(w), s); + free(s); + } else { + gtk_entry_set_text(GTK_ENTRY(w), ""); + } + w = glade_xml_get_widget(pv->xml, "RealTimeCheck"); + assert(w != NULL); + if (preferences_get_real_time_at_startup(pv->prefs)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE); + } +} + +/** + * Copy values from dialog widget to the preferences object. + */ +static void preferences_view_get_from_dialog(struct pref_view *pv) +{ + GtkWidget *w; + const gchar *entry; + size_t i; + + for (i = 0; i < num_entries; i++) { + const struct pref_entry *pe = pref_entry_data + i; + w = glade_xml_get_widget(pv->xml, pe->entry_name); + entry = gtk_entry_get_text(GTK_ENTRY(w)); + pe->modifier(pv->prefs, entry); + } + preferences_set_policy(pv->prefs, pv->policy_path); + for (i = 0; i < num_toggles; i++) { + gboolean active; + w = glade_xml_get_widget(pv->xml, pref_toggle_map[i].widget_name); + active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + if (active) { + preferences_set_column_visible(pv->prefs, pref_toggle_map[i].preference_field, 1); + } else { + preferences_set_column_visible(pv->prefs, pref_toggle_map[i].preference_field, 0); + } + } + w = glade_xml_get_widget(pv->xml, "PrefsViewIntervalEntry"); + entry = gtk_entry_get_text(GTK_ENTRY(w)); + if (strcmp(entry, "") == 0) { + entry = "0"; + } + preferences_set_real_time_interval(pv->prefs, atoi(entry)); + w = glade_xml_get_widget(pv->xml, "RealTimeCheck"); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) { + preferences_set_real_time_at_startup(pv->prefs, 1); + } else { + preferences_set_real_time_at_startup(pv->prefs, 0); + } +} + +int preferences_view_run(toplevel_t * top, const char *current_log, const apol_policy_path_t * current_policy) +{ + struct pref_view pv; + gint response; + + memset(&pv, 0, sizeof(pv)); + pv.top = top; + pv.xml = glade_xml_new(toplevel_get_glade_xml(top), "PreferencesWindow", NULL); + pv.prefs = toplevel_get_prefs(top); + pv.current_log = current_log; + pv.current_policy = current_policy; + + preferences_view_init_widgets(&pv); + preferences_view_init_values(&pv); + + response = gtk_dialog_run(pv.dialog); + if (response != GTK_RESPONSE_OK) { + gtk_widget_destroy(GTK_WIDGET(pv.dialog)); + return 0; + } + preferences_view_get_from_dialog(&pv); + gtk_widget_destroy(GTK_WIDGET(pv.dialog)); + return 1; +} diff --git a/seaudit/preferences_view.h b/seaudit/preferences_view.h new file mode 100644 index 0000000..d36aa3c --- /dev/null +++ b/seaudit/preferences_view.h @@ -0,0 +1,44 @@ +/** + * @file + * Declaration of preferences editor. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PREFERENCES_VIEW_H +#define PREFERENCES_VIEW_H + +#include "toplevel.h" +#include + +/** + * Display a dialog from which the user may edit his preferences. + * + * @param top Toplevel object containing preferences to modify + * @param current_log Path to the currently loaded log file, or NULL + * if none loaded. + * @param current_policy Path to the currently loaded policy, or NULL + * if none loaded. + * + * @return Non-zero if preferences changed, zero if not. + */ +int preferences_view_run(toplevel_t * top, const char *current_log, const apol_policy_path_t * current_policy); + +#endif diff --git a/seaudit/progress.c b/seaudit/progress.c new file mode 100644 index 0000000..01b01fb --- /dev/null +++ b/seaudit/progress.c @@ -0,0 +1,200 @@ +/** + * @file + * Routines to show a progress dialog, indicating that the + * application is doing something. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "progress.h" +#include "utilgui.h" + +#include +#include +#include + +struct progress +{ + toplevel_t *top; + GtkWidget *progress; + GtkWidget *label1, *label2; + char *s; + int done; + GCond *cond; + GMutex *mutex; +}; + +progress_t *progress_create(toplevel_t * top) +{ + progress_t *p; + GtkWidget *vbox; + + if ((p = calloc(1, sizeof(*p))) == NULL) { + return NULL; + } + p->top = top; + p->progress = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_modal(GTK_WINDOW(p->progress), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(p->progress), toplevel_get_window(top)); + gtk_window_set_default_size(GTK_WINDOW(p->progress), 300, 100); + vbox = gtk_vbox_new(FALSE, 2); + gtk_container_add(GTK_CONTAINER(p->progress), vbox); + p->label1 = gtk_label_new(NULL); + gtk_container_add(GTK_CONTAINER(vbox), p->label1); + p->label2 = gtk_label_new(NULL); + gtk_container_add(GTK_CONTAINER(vbox), p->label2); + gtk_widget_show(p->label1); + gtk_widget_show(p->label2); + gtk_widget_show(vbox); + util_cursor_wait(p->progress); + p->cond = g_cond_new(); + p->mutex = g_mutex_new(); + return p; +} + +void progress_destroy(progress_t ** progress) +{ + if (progress != NULL && *progress != NULL) { + free((*progress)->s); + g_cond_free((*progress)->cond); + g_mutex_free((*progress)->mutex); + free(*progress); + *progress = NULL; + } +} + +void progress_show(progress_t * progress, const char *title) +{ + gtk_label_set_text(GTK_LABEL(progress->label1), title); + gtk_label_set_text(GTK_LABEL(progress->label2), ""); + gtk_widget_show(progress->progress); + gtk_window_deiconify(GTK_WINDOW(progress->progress)); + gtk_window_set_title(GTK_WINDOW(progress->progress), title); + progress->done = 0; +} + +void progress_hide(progress_t * progress) +{ + gtk_widget_hide(progress->progress); +} + +int progress_wait(progress_t * progress) +{ + GTimeVal wait_time = { 0, 50000 }; + g_mutex_lock(progress->mutex); + while (!progress->done) { + g_cond_timed_wait(progress->cond, progress->mutex, &wait_time); + if (progress->s != NULL) { + gtk_label_set_text(GTK_LABEL(progress->label2), progress->s); + free(progress->s); + progress->s = NULL; + } + while (gtk_events_pending()) + gtk_main_iteration(); + } + g_mutex_unlock(progress->mutex); + if (progress->done < 0) { + toplevel_ERR(progress->top, GTK_LABEL(progress->label2)->label); + return progress->done; + } else if (progress->done > 1) { + toplevel_WARN(progress->top, GTK_LABEL(progress->label2)->label); + return progress->done - 1; + } else { + progress->done = 0; + return 0; + } +} + +void progress_done(progress_t * progress) +{ + g_mutex_lock(progress->mutex); + progress->done = 1; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_warn(progress_t * progress, char *reason, ...) +{ + gchar *s; + va_list ap; + g_mutex_lock(progress->mutex); + if (reason != NULL) { + va_start(ap, reason); + g_vasprintf(&s, reason, ap); + free(progress->s); + progress->s = s; + va_end(ap); + } + progress->done = 2; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_abort(progress_t * progress, char *reason, ...) +{ + gchar *s; + va_list ap; + g_mutex_lock(progress->mutex); + if (reason != NULL) { + va_start(ap, reason); + g_vasprintf(&s, reason, ap); + free(progress->s); + progress->s = s; + va_end(ap); + } + progress->done = -1; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +static void progress_update_label(progress_t * progress, const char *fmt, va_list va_args) +{ + gchar *s = NULL; + g_vasprintf(&s, fmt, va_args); + g_mutex_lock(progress->mutex); + free(progress->s); + progress->s = s; + g_cond_signal(progress->cond); + g_mutex_unlock(progress->mutex); +} + +void progress_update(progress_t * progress, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + progress_update_label(progress, fmt, ap); + va_end(ap); +} + +void progress_seaudit_handle_func(void *arg, const seaudit_log_t * log __attribute__ ((unused)), int level + __attribute__ ((unused)), const char *fmt, va_list va_args) +{ + progress_t *progress = arg; + progress_update_label(progress, fmt, va_args); +} + +void progress_apol_handle_func(void *varg, const apol_policy_t * p __attribute__ ((unused)), int level + __attribute__ ((unused)), const char *fmt, va_list argp) +{ + progress_t *progress = varg; + progress_update_label(progress, fmt, argp); +} diff --git a/seaudit/progress.h b/seaudit/progress.h new file mode 100644 index 0000000..5c6646f --- /dev/null +++ b/seaudit/progress.h @@ -0,0 +1,139 @@ +/** + * @file + * Header for showing progress dialogs. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2006-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef PROGRESS_H +#define PROGRESS_H + +typedef struct progress progress_t; + +#include "toplevel.h" + +#include +#include + +/** + * Allocate and return a new progress dialog object. + * + * @param top Toplevel object that will control the progress object. + * + * @return An initialized progress object, or NULL upon error. The + * caller is responsible for calling progress_destroy() afterwards. + */ +progress_t *progress_create(toplevel_t * top); + +/** + * Destroy a progress dialog. Does nothing if the pointer is already + * NULL. + * + * @param prefs Reference to a progress object to destroy. This will + * be set to NULL afterwards. + */ +void progress_destroy(progress_t ** progress); + +/** + * Display a progress dialog. + * + * @param progress Progress dialog to show. + * @param title Title for the progress window. + */ +void progress_show(progress_t * progress, const char *title); + +/** + * Hide the progress dialog. Note that this does not actually destroy + * the object. + * + * @param progress Progress dialog to hide. + */ +void progress_hide(progress_t * progress); + +/* the rest of these are for multi-threaded progress dialog */ + +/** + * Block the current thread until the progress dialog receives a done + * signal via progress_done() or progress_abort(). The dialog will + * periodically awake and update the user interface, based upon + * message received by its handle implementations. + * + * @param progress Progress object to wait against. + * + * @return 0 if the progress object got a progress_done(), < 0 if + * progress_abort(). + */ +int progress_wait(progress_t * progress); + +/** + * Signal to a progress object that this thread is ending + * successfully. This will cause all threads waiting upon the + * progress object to resume. + * + * @param progress Progress object to signal completion. + */ +void progress_done(progress_t * progress); + +/** + * Signal to a progress object that this thread completed with + * warnings. This will cause all threads waiting upon the progress + * object to resume. + * + * @param progress Progress object to signal completion. + * @param reason Explanation for warning, or NULL to use most recently + * written message as the reason. + */ +void progress_warn(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Signal to a progress object that this thread is aborting. This + * will cause all threads waiting upon the progress object to resume. + * + * @param progress Progress object to signal completion. + * @param reason Explanation for abort, or NULL to abort for no + * reason. The most recently written message will be used as the + * reason. + */ +void progress_abort(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Have the progress dialog show a message upon its next refresh. + * + * @param progress Progress object to update. + * @param fmt Format for string to display. + */ +void progress_update(progress_t * progress, char *fmt, ...) __attribute__ ((format(printf, 2, 3))); + +/** + * Implementation of libseaudit's message callback function. This + * will route messages generated by libseaudit to the progress + * dialog's display. To use this, pass the progress_t object as + * seaudit_log_create()'s callback_arg parameter. + */ +void progress_seaudit_handle_func(void *arg, const seaudit_log_t * log, int level, const char *fmt, va_list va_args); + +/** + * Implementation of a libapol message callback function. This will + * route messages generated by libapol to the progress dialog's + * display. To use this, pass the progress_t object as + * apol_policy_open()'s varg parameter. + */ +void progress_apol_handle_func(void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp); + +#endif diff --git a/seaudit/report_window.c b/seaudit/report_window.c new file mode 100644 index 0000000..d7213b5 --- /dev/null +++ b/seaudit/report_window.c @@ -0,0 +1,261 @@ +/** + * @file + * Run the dialog that generates reports. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "report_window.h" +#include "utilgui.h" +#include +#include +#include +#include +#include + +struct report_window +{ + GladeXML *xml; + GtkDialog *dialog; + GtkRadioButton *all_messages_radio, *text_radio; + GtkToggleButton *malformed_toggle, *use_stylesheet_toggle; + GtkWidget *stylesheet_label, *stylesheet_browse, *config_browse; + GtkEntry *stylesheet_entry, *config_entry; + char *filename; + message_view_t *current_view; + seaudit_log_t *log; + int result; + progress_t *progress; +}; + +static void report_window_on_all_messages_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + struct report_window *rw = (struct report_window *)user_data; + gtk_widget_set_sensitive(GTK_WIDGET(rw->malformed_toggle), sens); +} + +static void report_window_on_use_stylesheet_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + struct report_window *rw = (struct report_window *)user_data; + gtk_widget_set_sensitive(rw->stylesheet_label, sens); + gtk_widget_set_sensitive(GTK_WIDGET(rw->stylesheet_entry), sens); + gtk_widget_set_sensitive(rw->stylesheet_browse, sens); +} + +static void report_window_on_output_format_toggle(GtkToggleButton * toggle, gpointer user_data) +{ + gboolean sens = gtk_toggle_button_get_active(toggle); + struct report_window *rw = (struct report_window *)user_data; + gtk_widget_set_sensitive(GTK_WIDGET(rw->use_stylesheet_toggle), !sens); + if (sens == TRUE) { + gtk_widget_set_sensitive(rw->stylesheet_label, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(rw->stylesheet_entry), FALSE); + gtk_widget_set_sensitive(rw->stylesheet_browse, FALSE); + } else { + report_window_on_use_stylesheet_toggle(rw->use_stylesheet_toggle, rw); + } +} + +static void report_window_browse(GtkEntry * entry, GtkWindow * parent, const char *title) +{ + const char *current_path = gtk_entry_get_text(entry); + apol_vector_t *new_paths = util_open_file(parent, title, current_path, 0); + if (new_paths != NULL) { + gtk_entry_set_text(entry, apol_vector_get_element(new_paths, 0)); + apol_vector_destroy(&new_paths); + } +} + +static void report_window_on_stylesheet_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + struct report_window *rw = (struct report_window *)user_data; + report_window_browse(rw->stylesheet_entry, GTK_WINDOW(rw->dialog), "Select Style Sheet"); +} + +static void report_window_on_config_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data) +{ + struct report_window *rw = (struct report_window *)user_data; + report_window_browse(rw->config_entry, GTK_WINDOW(rw->dialog), "Select Report Configuration"); +} + +/** + * Set up report window struct's widget pointers. + */ +static void report_window_init_dialog(struct report_window *rw, toplevel_t * top) +{ + rw->dialog = GTK_DIALOG(glade_xml_get_widget(rw->xml, "ReportWindow")); + assert(rw->dialog != NULL); + gtk_window_set_transient_for(GTK_WINDOW(rw->dialog), toplevel_get_window(top)); + + rw->all_messages_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowAllMessagesRadio")); + rw->malformed_toggle = GTK_TOGGLE_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowMalformedCheck")); + assert(rw->all_messages_radio != NULL && rw->malformed_toggle != NULL); + + rw->text_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowTextRadio")); + rw->use_stylesheet_toggle = GTK_TOGGLE_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowUseStylesheetCheck")); + assert(rw->text_radio != NULL && rw->use_stylesheet_toggle != NULL); + + rw->stylesheet_label = glade_xml_get_widget(rw->xml, "ReportWindowStylesheetLabel"); + rw->stylesheet_entry = GTK_ENTRY(glade_xml_get_widget(rw->xml, "ReportWindowStylesheetEntry")); + rw->stylesheet_browse = glade_xml_get_widget(rw->xml, "ReportWindowStylesheetBrowse"); + assert(rw->stylesheet_label != NULL && rw->stylesheet_entry && rw->stylesheet_browse); + + rw->config_entry = GTK_ENTRY(glade_xml_get_widget(rw->xml, "ReportWindowConfigEntry")); + rw->config_browse = glade_xml_get_widget(rw->xml, "ReportWindowConfigBrowse"); + assert(rw->config_entry != NULL && rw->config_browse != NULL); + + /* set up signal handlers */ + g_signal_connect(rw->all_messages_radio, "toggled", G_CALLBACK(report_window_on_all_messages_toggle), rw); + g_signal_connect(rw->text_radio, "toggled", G_CALLBACK(report_window_on_output_format_toggle), rw); + g_signal_connect(rw->use_stylesheet_toggle, "toggled", G_CALLBACK(report_window_on_use_stylesheet_toggle), rw); + g_signal_connect(rw->stylesheet_browse, "clicked", G_CALLBACK(report_window_on_stylesheet_browse_click), rw); + g_signal_connect(rw->config_browse, "clicked", G_CALLBACK(report_window_on_config_browse_click), rw); + +} + +/** + * The first time the report window is shown, populate its entry boxes + * with values from the user's preferences. On subsequent times + * remember the user's entries. + */ +static void report_window_copy_prefs(struct report_window *rw, toplevel_t * top) +{ + static int report_window_initialized = 0; + if (!report_window_initialized) { + preferences_t *prefs = toplevel_get_prefs(top); + gtk_entry_set_text(rw->stylesheet_entry, preferences_get_stylesheet(prefs)); + gtk_entry_set_text(rw->config_entry, preferences_get_report(prefs)); + } + report_window_initialized = 1; +} + +static gpointer report_window_create_report_runner(gpointer data) +{ + struct report_window *rw = (struct report_window *)data; + seaudit_model_t *model = NULL; + seaudit_report_t *report = NULL; + seaudit_report_format_e format = SEAUDIT_REPORT_FORMAT_TEXT; + int do_malformed = 0, do_stylesheet = 0; + const char *config_name = NULL, *stylesheet_name = NULL; + + rw->result = -1; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rw->all_messages_radio))) { + model = seaudit_model_create("All Messages", rw->log); + if (gtk_toggle_button_get_active(rw->malformed_toggle)) { + do_malformed = 1; + } + } else { + seaudit_model_t *view_model = message_view_get_model(rw->current_view); + model = seaudit_model_create_from_model(view_model); + } + if (model == NULL) { + progress_abort(rw->progress, "%s", strerror(errno)); + goto cleanup; + } + if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rw->text_radio))) { + format = SEAUDIT_REPORT_FORMAT_HTML; + } + if (gtk_toggle_button_get_active(rw->use_stylesheet_toggle)) { + do_stylesheet = 1; + } + stylesheet_name = gtk_entry_get_text(rw->stylesheet_entry); + if (strcmp(stylesheet_name, "") == 0) { + stylesheet_name = NULL; + } + config_name = gtk_entry_get_text(rw->config_entry); + if (strcmp(config_name, "") == 0) { + config_name = NULL; + } + + if ((report = seaudit_report_create(model)) == NULL) { + progress_abort(rw->progress, "%s", strerror(errno)); + goto cleanup; + } + if (seaudit_report_set_format(rw->log, report, format) < 0 || + seaudit_report_set_configuration(rw->log, report, config_name) < 0 || + seaudit_report_set_stylesheet(rw->log, report, stylesheet_name, do_stylesheet) < 0 || + seaudit_report_set_malformed(rw->log, report, do_malformed) < 0) { + goto cleanup; + } + progress_update(rw->progress, "Writing"); + if (seaudit_report_write(rw->log, report, rw->filename) < 0) { + goto cleanup; + } + rw->result = 0; + cleanup: + seaudit_report_destroy(&report); + seaudit_model_destroy(&model); + if (rw->result == 0) { + progress_done(rw->progress); + } else { + progress_abort(rw->progress, NULL); + } + return NULL; +} + +void report_window_run(toplevel_t * top, message_view_t * view) +{ + struct report_window rw; + /** keey track of most recently used report filename */ + static char *filename = NULL; + + memset(&rw, 0, sizeof(rw)); + rw.xml = glade_xml_new(toplevel_get_glade_xml(top), "ReportWindow", NULL); + report_window_init_dialog(&rw, top); + report_window_copy_prefs(&rw, top); + + rw.current_view = view; + rw.log = toplevel_get_log(top); + rw.progress = toplevel_get_progress(top); + rw.filename = filename; + do { + gint response = gtk_dialog_run(rw.dialog); + if (response != GTK_RESPONSE_OK) { + break; + } + if ((filename = util_save_file(GTK_WINDOW(rw.dialog), "Save Report to File", rw.filename)) == NULL) { + continue; + } + g_free(rw.filename); + rw.filename = filename; + util_cursor_wait(GTK_WIDGET(rw.dialog)); + progress_show(rw.progress, "Creating Report"); + g_thread_create(report_window_create_report_runner, &rw, FALSE, NULL); + progress_wait(rw.progress); + progress_hide(rw.progress); + /* Reset the cursor if the save failed. upon success, + * the window will be destroyed anyways, so don't + * bother resetting the cursor. + * + * (Real reason: util_cursor_clear() resets the cursor + * as an idle callback -- but by the time it triggers, + * the window will be destroyed by then.) + */ + if (rw.result == 0) { + break; + } + util_cursor_clear(GTK_WIDGET(rw.dialog)); + } while (1); + gtk_widget_destroy(GTK_WIDGET(rw.dialog)); +} diff --git a/seaudit/report_window.h b/seaudit/report_window.h new file mode 100644 index 0000000..ff9fcb7 --- /dev/null +++ b/seaudit/report_window.h @@ -0,0 +1,41 @@ +/** + * @file + * Dialog that generates reports from all messages or only those in + * the current view. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REPORT_WINDOW_H +#define REPORT_WINDOW_H + +#include "toplevel.h" +#include "message_view.h" + +/** + * Display and run a dialog that allows the user to generate a report. + * + * @param top Toplevel containing preferences and log file for report + * writer. + * @param view Current message view. + */ +void report_window_run(toplevel_t * top, message_view_t * view); + +#endif diff --git a/seaudit/seaudit-report-group.conf b/seaudit/seaudit-report-group.conf new file mode 100644 index 0000000..3b0664c --- /dev/null +++ b/seaudit/seaudit-report-group.conf @@ -0,0 +1,3 @@ +# This is the logfile group configuration file for Logwatch +LogFile = messages + diff --git a/seaudit/seaudit-report-service.conf b/seaudit/seaudit-report-service.conf new file mode 100644 index 0000000..30efcde --- /dev/null +++ b/seaudit/seaudit-report-service.conf @@ -0,0 +1,5 @@ +# This is the service filter configuration file for Logwatch +Title = "seaudit-report" + +# Which logfile group... +LogFile = seaudit-report-group diff --git a/seaudit/seaudit-report-service.in b/seaudit/seaudit-report-service.in new file mode 100644 index 0000000..d923589 --- /dev/null +++ b/seaudit/seaudit-report-service.in @@ -0,0 +1,24 @@ +#!/bin/sh +# shell script to run seaudit-report on STDIN +# + +SEAUDITREPORT=@bindir@ +OPTS="--stdin --malformed" + +echo "Date Range: $LOGWATCH_DATE_RANGE" +echo "Detail Level: $LOGWATCH_DETAIL_LEVEL" +echo "Temp Dir: $LOGWATCH_TEMP_DIR" +echo "Debug Level: $LOGWATCH_DEBUG" + +# execute the program with the specified options +${SEAUDITREPORT} ${OPTS} + +# program failed +if [ $? -ne 0 ]; then + RC=$? + echo >&2 "Failed while executing seaudit-report.\n" + exit $RC +fi + +# All done, exit ok +exit 0 diff --git a/seaudit/seaudit-report.c b/seaudit/seaudit-report.c new file mode 100644 index 0000000..af3c6fb --- /dev/null +++ b/seaudit/seaudit-report.c @@ -0,0 +1,246 @@ +/** + * @file + * Command line tool for processing SELinux audit logs and generating + * a concise report containing standard information as well as + * customized information using seaudit views. Reports are rendered + * in either HTML or plain text. Future support will provide + * rendering into XML. The HTML report can be formatted by providing + * an alternate stylesheet file or by configuring the default + * stylesheet. This tool also provides the option for including + * malformed strings within the report. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2004-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC" + +enum opts +{ + OPT_HTML = 256, OPT_STYLESHEET +}; + +static struct option const longopts[] = { + {"html", no_argument, NULL, OPT_HTML}, + {"malformed", no_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"stylesheet", required_argument, NULL, OPT_STYLESHEET}, + {"stdin", no_argument, NULL, 's'}, + {"config", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +/** + * Vector of seaudit_log_t, corresponding to each of the log files to + * process. + */ +static apol_vector_t *logs = NULL; + +/** + * Error reporting log handler. + */ +static seaudit_log_t *first_log = NULL; + +/** + * Model that incorporates all of the logs within the logs vector. + */ +static seaudit_model_t *model = NULL; + +/** + * Report object for the above model. + */ +static seaudit_report_t *report = NULL; + +/** + * Destination file for the seaudit report, or NULL to write to + * standard output. + */ +static char *outfile = NULL; + +static void seaudit_report_info_usage(const char *program_name, int brief) +{ + printf("Usage: %s [OPTIONS] LOGFILE ...\n\n", program_name); + if (brief) { + printf("\tTry %s --help for more help.\n\n", program_name); + return; + } + printf("Generate a customized SELinux log report.\n\n"); + printf(" -s, --stdin read log data from standard input\n"); + printf(" -m, --malformed include malformed log messages\n"); + printf(" -o FILE, --output=FILE output to FILE\n"); + printf(" --config=FILE read configuration from FILE\n"); + printf(" --html set output format to HTML\n"); + printf(" --stylesheet=FILE HTML style sheet for formatting HTML report\n"); + printf(" (ignored if --html is not given)\n"); + printf(" -h, --help print this help text and exit\n"); + printf(" -V, --version print version information and exit\n"); + printf("\n"); + printf("Default style sheet is at %s.\n", APOL_INSTALL_DIR); +} + +static void parse_command_line_args(int argc, char **argv) +{ + int optc, i; + int do_malformed = 0, do_style = 0, read_stdin = 0; + seaudit_report_format_e format = SEAUDIT_REPORT_FORMAT_TEXT; + char *configfile = NULL, *stylesheet = NULL; + + /* get option arguments */ + while ((optc = getopt_long(argc, argv, "smo:c:hV", longopts, NULL)) != -1) { + switch (optc) { + case 's': /* read LOGFILES from standard input */ + read_stdin = 1; + break; + case 'm': /* include malformed messages */ + do_malformed = 1; + break; + case 'o': /* output file name */ + outfile = optarg; + break; + case 'c': /* Alternate config file path */ + configfile = optarg; + break; + case OPT_HTML: /* Set the output to format to html */ + format = SEAUDIT_REPORT_FORMAT_HTML; + do_style = 1; + break; + case OPT_STYLESHEET: /* HTML stylesheet file path */ + stylesheet = optarg; + do_style = 1; + break; + case 'h': + /* display help */ + seaudit_report_info_usage(argv[0], 0); + exit(0); + case 'V': + /* display version */ + printf("seaudit-report %s\n%s\n", VERSION, COPYRIGHT_INFO); + exit(0); + default: + /* display usage and handle error */ + seaudit_report_info_usage(argv[0], 1); + exit(-1); + } + } + + /* Throw warning if a stylesheet was specified, but the --html + * option was not. */ + if (stylesheet != NULL && format != SEAUDIT_REPORT_FORMAT_HTML) { + fprintf(stderr, "Warning: The --html option was not specified.\n"); + exit(-1); + } + + if (!read_stdin && optind >= argc) { + /* display usage and handle error */ + seaudit_report_info_usage(argv[0], 1); + exit(-1); + } + + if ((model = seaudit_model_create("seaudit-report", NULL)) == NULL) { + exit(-1); + } + if ((first_log = seaudit_log_create(NULL, NULL)) == NULL || seaudit_model_append_log(model, first_log) < 0) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + exit(-1); + } + if ((logs = apol_vector_create(NULL)) == NULL || apol_vector_append(logs, first_log) < 0) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + exit(-1); + } + if (read_stdin) { + /* Ensure that logfiles were not specified in addition + * to the standard-in option */ + if (optind < argc) { + fprintf(stderr, "WARNING: %s\n", "Command line filename(s) will be ignored. Reading from stdin."); + } + if (seaudit_log_parse(first_log, stdin) < 0) { + exit(-1); + } + } else { + /* Parse given filenames */ + FILE *f; + seaudit_log_t *l; + if ((f = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + exit(-1); + } + if (seaudit_log_parse(first_log, f) < 0) { + exit(-1); + } + fclose(f); + for (i = optind + 1; i < argc; i++) { + if ((l = seaudit_log_create(NULL, NULL)) == NULL || seaudit_model_append_log(model, l) < 0) { + exit(-1); + } + if (apol_vector_append(logs, l) < 0) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + exit(-1); + } + if ((f = fopen(argv[i], "r")) == NULL) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + exit(-1); + } + if (seaudit_log_parse(l, f) < 0) { + exit(-1); + } + fclose(f); + } + } + + if ((report = seaudit_report_create(model)) == NULL || + seaudit_report_set_format(first_log, report, format) < 0 || + seaudit_report_set_configuration(first_log, report, configfile) < 0 || + seaudit_report_set_stylesheet(first_log, report, stylesheet, do_style) < 0 || + seaudit_report_set_malformed(first_log, report, do_malformed) < 0) { + exit(-1); + } +} + +int main(int argc, char **argv) +{ + size_t i; + parse_command_line_args(argc, argv); + if (seaudit_report_write(first_log, report, outfile) < 0) { + return -1; + } + seaudit_report_destroy(&report); + seaudit_model_destroy(&model); + for (i = 0; i < apol_vector_get_size(logs); i++) { + seaudit_log_t *l = apol_vector_get_element(logs, i); + seaudit_log_destroy(&l); + } + apol_vector_destroy(&logs); + return 0; +} diff --git a/seaudit/seaudit-report.conf b/seaudit/seaudit-report.conf new file mode 100644 index 0000000..fb36c24 --- /dev/null +++ b/seaudit/seaudit-report.conf @@ -0,0 +1,62 @@ + + + + + + + + + + + diff --git a/seaudit/seaudit-report.css b/seaudit/seaudit-report.css new file mode 100644 index 0000000..4780a2d --- /dev/null +++ b/seaudit/seaudit-report.css @@ -0,0 +1,178 @@ +# seaudit-report.css +# +# This is a default stylesheet template used to format an HTML report +# generated by the seaudit-report tool. +# +# There are two ways of configuring this style sheet: +# +# 1. External Style Sheet: +# Specify an external stylesheet to link to the HTML report using a +# tag, as follows: +# +# The seaudit-report tool will insert the tag you define +# within the section of the generated HTML report. +# +# 2. Internal Style Sheet: +# Specify internal style tags for the HTML report using the +# The seaudit-report tool will insert the