diff options
Diffstat (limited to 'sediff/policy_view.c')
-rw-r--r-- | sediff/policy_view.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/sediff/policy_view.c b/sediff/policy_view.c new file mode 100644 index 0000000..bc733a7 --- /dev/null +++ b/sediff/policy_view.c @@ -0,0 +1,391 @@ +/** + * @file + * Routines are responsible calculating a policy's statistics and + * displaying its source. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * @author Randy Wicks rwicks@tresys.com + * + * Copyright (C) 2005-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 <config.h> + +#include "policy_view.h" +#include "utilgui.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <glade/glade.h> + +struct policy_view +{ + toplevel_t *top; + sediffx_policy_e which; + GladeXML *xml; + GtkTextBuffer *stats, *source; + GtkTextView *stats_view, *source_view; + GtkNotebook *notebook; + GtkLabel *line_number; + void *mmap_start; + size_t mmap_length; +}; + +static const char *policy_view_widget_names[SEDIFFX_POLICY_NUM][3] = { + {"toplevel policy_orig stats text", "toplevel policy_orig source text", + "toplevel policy_orig notebook"}, + {"toplevel policy_mod stats text", "toplevel policy_mod source text", + "toplevel policy_mod notebook"} +}; + +/** + * As the user moves the cursor within the source policy text view + * update the bottom label with the line number. Once that view is + * hidden, by flipping to a different tab, then clear the label. + */ +static void policy_view_notebook_on_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event + __attribute__ ((unused)), gpointer user_data) +{ + policy_view_t *view = (policy_view_t *) user_data; + guint main_pagenum, view_pagenum; + GtkTextMark *mark = NULL; + GtkTextIter iter; + + main_pagenum = toplevel_get_notebook_page(view->top); + view_pagenum = gtk_notebook_get_current_page(view->notebook); + if (main_pagenum == 1 + view->which && view_pagenum == 1) { + mark = gtk_text_buffer_get_insert(view->source); + if (mark != NULL) { + GString *string = g_string_new(""); + gtk_text_buffer_get_iter_at_mark(view->source, &iter, mark); + g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1); + gtk_label_set_text(view->line_number, string->str); + g_string_free(string, TRUE); + } + } else { + gtk_label_set_text(view->line_number, ""); + } +} + +policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which) +{ + policy_view_t *view; + GtkTextTag *mono_tag; + int error = 0; + + if ((view = calloc(1, sizeof(*view))) == NULL) { + error = errno; + goto cleanup; + } + view->top = top; + view->which = which; + + view->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(view->top))); + view->stats = gtk_text_buffer_new(NULL); + view->source = gtk_text_buffer_new(NULL); + mono_tag = gtk_text_buffer_create_tag(view->source, "mono-tag", + "style", PANGO_STYLE_NORMAL, + "weight", PANGO_WEIGHT_NORMAL, "family", "monospace", NULL); + + view->stats_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][0])); + view->source_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][1])); + assert(view->stats_view != NULL && view->source_view != NULL); + gtk_text_view_set_buffer(view->stats_view, view->stats); + gtk_text_view_set_buffer(view->source_view, view->source); + + view->notebook = GTK_NOTEBOOK(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][2])); + view->line_number = GTK_LABEL(glade_xml_get_widget(view->xml, "toplevel line label")); + assert(view->notebook != NULL && view->line_number != NULL); + g_signal_connect_after(G_OBJECT(view->notebook), "event-after", G_CALLBACK(policy_view_notebook_on_event_after), view); + + cleanup: + if (error != 0) { + policy_view_destroy(&view); + errno = error; + return NULL; + } + return view; +} + +void policy_view_destroy(policy_view_t ** view) +{ + if (view != NULL && *view != NULL) { + free(*view); + *view = NULL; + } +} + +/** + * Update the policy stats text buffer (and hence its view) to show + * statistics about the given policy. + * + * @param view View to update. + * @param p New policy whose statistics to get. + * @param path Path to the new policy. + */ +static void policy_view_stats_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path) +{ + GtkTextIter iter; + gchar *contents = NULL; + char *path_desc, *tmp = NULL; + size_t num_classes = 0, + num_commons = 0, + num_perms = 0, + num_types = 0, + num_attribs = 0, + num_allow = 0, num_auditallow = 0, num_dontaudit = 0, + num_type_change = 0, num_type_member, num_type_trans = 0, + num_roles = 0, num_roleallow = 0, num_role_trans = 0, num_users = 0, num_bools = 0; + apol_vector_t *vec = NULL; + qpol_iterator_t *i = NULL; + qpol_policy_t *q = apol_policy_get_qpol(p); + + util_text_buffer_clear(view->stats); + + if ((path_desc = util_policy_path_to_full_string(path)) == NULL) { + toplevel_ERR(view->top, "%s", strerror(errno)); + return; + } + tmp = apol_policy_get_version_type_mls_str(p); + contents = g_strdup_printf("Policy: %s\n" "Policy Version & Type: %s\n", path_desc, tmp); + free(path_desc); + free(tmp); + tmp = NULL; + gtk_text_buffer_get_end_iter(view->stats, &iter); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_class_get_by_query(p, NULL, &vec); + num_classes = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_common_get_by_query(p, NULL, &vec); + num_commons = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_perm_get_by_query(p, NULL, &vec); + num_perms = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Classes and Permissions:\n" + "\tObject Classes: %zd\n" + "\tCommon Classes: %zd\n" "\tPermissions: %zd\n", num_classes, num_commons, num_perms); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_type_get_by_query(p, NULL, &vec); + num_types = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_attr_get_by_query(p, NULL, &vec); + num_attribs = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Types and Attributes:\n" + "\tTypes: %zd\n" "\tAttributes: %zd\n", num_types, num_attribs); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &i); + qpol_iterator_get_size(i, &num_allow); + qpol_iterator_destroy(&i); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &i); + qpol_iterator_get_size(i, &num_auditallow); + qpol_iterator_destroy(&i); + + qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &i); + qpol_iterator_get_size(i, &num_dontaudit); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &i); + qpol_iterator_get_size(i, &num_type_change); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &i); + qpol_iterator_get_size(i, &num_type_member); + qpol_iterator_destroy(&i); + + qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &i); + qpol_iterator_get_size(i, &num_type_trans); + qpol_iterator_destroy(&i); + + contents = g_strdup_printf("\nNumber of Rules:\n" + "\tallow: %zd\n" + "\tauditallow: %zd\n" + "\tdontaudit %zd\n" + "\tneverallow: not calculated\n" + "\ttype_change: %zd\n" + "\ttype_member: %zd\n" + "\ttype_transition: %zd\n", + num_allow, num_auditallow, num_dontaudit, num_type_change, num_type_member, num_type_trans); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_role_get_by_query(p, NULL, &vec); + num_roles = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + qpol_policy_get_role_allow_iter(q, &i); + qpol_iterator_get_size(i, &num_roleallow); + qpol_iterator_destroy(&i); + + qpol_policy_get_role_trans_iter(q, &i); + qpol_iterator_get_size(i, &num_roleallow); + qpol_iterator_destroy(&i); + + contents = g_strdup_printf("\nNumber of Roles: %zd\n" + "\nNumber of RBAC Rules:\n" + "\tallow: %zd\n" "\trole_transition %zd\n", num_roles, num_roleallow, num_role_trans); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); + + apol_user_get_by_query(p, NULL, &vec); + num_users = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + apol_bool_get_by_query(p, NULL, &vec); + num_bools = apol_vector_get_size(vec); + apol_vector_destroy(&vec); + + contents = g_strdup_printf("\nNumber of Users: %zd\n" "\nNumber of Booleans: %zd\n", num_users, num_bools); + gtk_text_buffer_insert(view->stats, &iter, contents, -1); + g_free(contents); +} + +/** + * Attempt to load the primary policy into this view's source policy + * buffer. If the policy is not a source policy then show an + * appropriate message. Otherwise mmap() the policy's contents to the + * buffer. + * + * @param view View whose source buffer to update. + * @param policy Policy to show, or NULL if none loaded. + * @param path Path to the policy. + */ +static void policy_view_source_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path) +{ + const char *primary_path; + util_text_buffer_clear(view->source); + + /* clear out any old data */ + if (view->mmap_start != NULL) { + munmap(view->mmap_start, view->mmap_length); + view->mmap_start = NULL; + view->mmap_length = 0; + } + if (p == NULL) { + gtk_text_buffer_set_text(view->source, "No policy has been loaded.", -1); + return; + } + primary_path = apol_policy_path_get_primary(path); + if (!qpol_policy_has_capability(apol_policy_get_qpol(p), 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(view->source, string->str, -1); + g_string_free(string, TRUE); + } else { + /* load the policy by mmap()ing the file */ + struct stat statbuf; + int fd; + + if ((fd = open(primary_path, O_RDONLY)) < 0) { + toplevel_ERR(view->top, "Could not open %s for reading: %s", primary_path, strerror(errno)); + return; + } + if (fstat(fd, &statbuf) < 0) { + toplevel_ERR(view->top, "Could not stat %s: %s", primary_path, strerror(errno)); + close(fd); + return; + } + + view->mmap_length = statbuf.st_size; + if ((view->mmap_start = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + toplevel_ERR(view->top, "Could not mmap %s: %s", primary_path, strerror(errno)); + close(fd); + view->mmap_start = NULL; + return; + } + close(fd); + gtk_text_buffer_set_text(view->source, view->mmap_start, view->mmap_length); + } +} + +void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path) +{ + policy_view_stats_update(view, policy, path); + policy_view_source_update(view, policy, path); +} + +void policy_view_show_policy_line(policy_view_t * view, unsigned long line) +{ + GtkTextTagTable *table = NULL; + GtkTextIter iter, end_iter; + GtkTextMark *mark = NULL; + GString *string = g_string_new(""); + + gtk_notebook_set_current_page(view->notebook, 1); + + /* when moving the buffer we must use marks to scroll because + * goto_line if called before the line height has been + * calculated can produce undesired results, in our case we + * get no scrolling at all */ + table = gtk_text_buffer_get_tag_table(view->source); + gtk_text_buffer_get_start_iter(view->source, &iter); + gtk_text_iter_set_line(&iter, line); + gtk_text_buffer_get_start_iter(view->source, &end_iter); + gtk_text_iter_set_line(&end_iter, line); + while (!gtk_text_iter_ends_line(&end_iter)) { + gtk_text_iter_forward_char(&end_iter); + } + + mark = gtk_text_buffer_create_mark(view->source, "line-position", &iter, TRUE); + assert(mark); + gtk_text_view_scroll_to_mark(view->source_view, mark, 0.0, TRUE, 0.0, 0.5); + + /* destroying the mark and recreating is faster than doing a + * move on a mark that still exists, so we always destroy it + * once we're done */ + gtk_text_buffer_delete_mark(view->source, mark); + gtk_text_view_set_cursor_visible(view->source_view, TRUE); + gtk_text_buffer_place_cursor(view->source, &iter); + gtk_text_buffer_select_range(view->source, &iter, &end_iter); + + gtk_container_set_focus_child(GTK_CONTAINER(view->notebook), GTK_WIDGET(view->source_view)); + + g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1); + gtk_label_set_text(view->line_number, string->str); + g_string_free(string, TRUE); +} + +GtkTextView *policy_view_get_text_view(policy_view_t * view) +{ + gint pagenum = gtk_notebook_get_current_page(view->notebook); + if (pagenum == 0) { + return view->stats_view; + } else { + return view->source_view; + } +} |