/** * @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_policies_dialog.h" #include "utilgui.h" #include #include #include #include #include struct open_policy_pane { struct open_policy *op; 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; char *last_module_path; }; struct open_policy { GladeXML *xml; toplevel_t *top; GtkDialog *dialog; GtkButton *ok_button, *rundiff_button; struct open_policy_pane pane[SEDIFFX_POLICY_NUM]; }; 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_pane(struct open_policy *op, sediffx_policy_e which, const char *suffix) { struct open_policy_pane *pane = op->pane + which; GString *s = g_string_new(NULL); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn *column; GtkTreeSelection *selection; g_string_printf(s, "monolithic radio%s", suffix); pane->monolithic_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "modular radio%s", suffix); pane->modular_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "main filename label%s", suffix); pane->main_label = GTK_LABEL(glade_xml_get_widget(op->xml, s->str)); assert(pane->monolithic_radio != NULL && pane->modular_radio != NULL && pane->main_label != NULL); g_string_printf(s, "base entry%s", suffix); pane->base_entry = GTK_ENTRY(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "base browse%s", suffix); pane->base_browse_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); assert(pane->base_entry != NULL && pane->base_browse_button != NULL); g_string_printf(s, "hbox.3%s", suffix); pane->bottom_hbox = GTK_HBOX(glade_xml_get_widget(op->xml, s->str)); assert(pane->bottom_hbox != NULL); g_string_printf(s, "module view%s", suffix); pane->module_view = GTK_TREE_VIEW(glade_xml_get_widget(op->xml, s->str)); assert(pane->module_view != NULL); pane->module_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model(pane->module_view, GTK_TREE_MODEL(pane->module_store)); g_string_printf(s, "module add button%s", suffix); pane->add_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "module remove button%s", suffix); pane->remove_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "module list import button%s", suffix); pane->import_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); g_string_printf(s, "module list export button%s", suffix); pane->export_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str)); assert(pane->add_button != NULL && pane->remove_button != NULL && pane->import_button != NULL && pane->export_button != NULL); g_string_free(s, TRUE); selection = gtk_tree_view_get_selection(pane->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(pane->module_view, column); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->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(pane->module_view, column); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->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(pane->module_view, column); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), PATH_COLUMN, open_policy_sort, (gpointer) PATH_COLUMN, NULL); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pane->module_store), NAME_COLUMN, GTK_SORT_ASCENDING); } static void open_policy_init_widgets(struct open_policy *op) { op->dialog = GTK_DIALOG(glade_xml_get_widget(op->xml, "PoliciesOpenWindow")); assert(op->dialog != NULL); gtk_window_set_transient_for(GTK_WINDOW(op->dialog), toplevel_get_window(op->top)); op->ok_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "ok button")); op->rundiff_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "rundiff button")); assert(op->ok_button != NULL && op->rundiff_button != NULL); open_policy_init_pane(op, SEDIFFX_POLICY_ORIG, ""); open_policy_init_pane(op, SEDIFFX_POLICY_MOD, " 1"); } static void open_policy_on_policy_type_toggle(GtkToggleButton * widget, gpointer user_data) { struct open_policy_pane *pane = (struct open_policy_pane *)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; } char *prefix; GString *s = g_string_new(NULL); if (pane == &(pane->op->pane[SEDIFFX_POLICY_ORIG])) { prefix = "Original"; } else { prefix = "Modified"; } if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio))) { gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), FALSE); g_string_printf(s, "%s Policy Filename:", prefix); gtk_label_set_markup(pane->main_label, s->str); } else { gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), TRUE); g_string_printf(s, "%s Base Filename:", prefix); gtk_label_set_markup(pane->main_label, s->str); } g_string_free(s, TRUE); } 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_orig = FALSE, sens_mod = FALSE; if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_ORIG].base_entry), "") != 0) { sens_orig = TRUE; } if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_MOD].base_entry), "") != 0) { sens_mod = TRUE; } gboolean sens = sens_orig && sens_mod; gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_ORIG].export_button), sens_orig); gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_MOD].export_button), sens_mod); gtk_widget_set_sensitive(GTK_WIDGET(op->ok_button), sens); gtk_widget_set_sensitive(GTK_WIDGET(op->rundiff_button), sens); } static void open_policy_on_base_browse_click(GtkButton * button __attribute__ ((unused)), gpointer user_data) { struct open_policy_pane *pane = (struct open_policy_pane *)user_data; const char *current_path = gtk_entry_get_text(pane->base_entry); char *title; apol_vector_t *paths; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->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(pane->op->dialog), title, current_path, 0)) == NULL) { return; } gtk_entry_set_text(pane->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, struct open_policy_pane *pane, 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(pane->module_store), &iter); while (iter_valid) { char *s; gtk_tree_model_get(GTK_TREE_MODEL(pane->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(pane->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(pane->module_store, &iter); gtk_list_store_set(pane->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_pane *pane = (struct open_policy_pane *)user_data; apol_vector_t *paths; const char *path = NULL, *prev_path; size_t i; if ((prev_path = pane->last_module_path) == NULL) { prev_path = gtk_entry_get_text(pane->base_entry); if (strcmp(prev_path, "") == 0) { prev_path = NULL; } } paths = util_open_file(GTK_WINDOW(pane->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(pane->op, pane, path) < 0) { all_succeed = 0; } } if (all_succeed) { assert(path != NULL); free(pane->last_module_path); pane->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_pane *pane = (struct open_policy_pane *)user_data; GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view); GtkTreeIter iter; char *path; if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { return; } gtk_tree_model_get(GTK_TREE_MODEL(pane->module_store), &iter, 0, &path, -1); gtk_list_store_remove(pane->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_pane *pane = (struct open_policy_pane *)user_data; gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL); gtk_widget_set_sensitive(GTK_WIDGET(pane->remove_button), sens); } static void open_policy_init_signals(struct open_policy *op) { sediffx_policy_e i; for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) { struct open_policy_pane *pane = op->pane + i; GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view); g_signal_connect(pane->monolithic_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane); g_signal_connect(pane->modular_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane); g_signal_connect(pane->base_entry, "event-after", G_CALLBACK(open_policy_on_entry_event_after), op); g_signal_connect(pane->base_browse_button, "clicked", G_CALLBACK(open_policy_on_base_browse_click), pane); g_signal_connect(selection, "changed", G_CALLBACK(open_policy_on_selection_change), pane); g_signal_connect(pane->add_button, "clicked", G_CALLBACK(open_policy_on_add_click), pane); g_signal_connect(pane->remove_button, "clicked", G_CALLBACK(open_policy_on_remove_click), pane); g_signal_connect(pane->import_button, "clicked", G_CALLBACK(open_policy_on_import_click), pane); g_signal_connect(pane->export_button, "clicked", G_CALLBACK(open_policy_on_export_click), pane); } } static void open_policy_init_value(struct open_policy *op, const apol_policy_path_t * path, struct open_policy_pane *pane) { 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(pane->base_entry, primary_path); gtk_list_store_clear(pane->module_store); if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pane->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(pane->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, pane, module_path) < 0) { break; } } } else { /* should never get here */ toplevel_ERR(pane->op->top, "Unknown policy path type %d.", path_type); } } static void open_policy_init_values(struct open_policy *op, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path) { if (orig_path != NULL) { open_policy_init_value(op, orig_path, op->pane + SEDIFFX_POLICY_ORIG); } if (mod_path != NULL) { open_policy_init_value(op, mod_path, op->pane + SEDIFFX_POLICY_MOD); } } /** * 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_pane *pane) { const char *primary_path = gtk_entry_get_text(pane->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(pane->modular_radio))) { path_type = APOL_POLICY_PATH_TYPE_MODULAR; GtkTreeIter iter; if ((modules = apol_vector_create(free)) == NULL) { toplevel_ERR(pane->op->top, "%s", strerror(errno)); return NULL; } if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pane->module_store), &iter)) { do { GValue value = { 0 }; char *module_path; gtk_tree_model_get_value(GTK_TREE_MODEL(pane->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(pane->op->top, "%s", strerror(errno)); free(module_path); apol_vector_destroy(&modules); return NULL; } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pane->module_store), &iter)); } } path = apol_policy_path_create(path_type, primary_path, modules); apol_vector_destroy(&modules); if (path == NULL) { toplevel_ERR(pane->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_pane *pane = (struct open_policy_pane *)user_data; apol_vector_t *paths = NULL; apol_policy_path_t *ppath = NULL; const char *path = NULL, *prev_path; if ((prev_path = pane->last_module_path) == NULL) { prev_path = gtk_entry_get_text(pane->base_entry); if (strcmp(prev_path, "") == 0) { prev_path = NULL; } } paths = util_open_file(GTK_WINDOW(pane->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(pane->op->top, "Error importing policy list %s: %s", path, strerror(errno)); goto cleanup; } open_policy_init_value(pane->op, ppath, pane); free(pane->last_module_path); pane->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_pane *pane = (struct open_policy_pane *)user_data; char *path = util_save_file(GTK_WINDOW(pane->op->dialog), "Export Policy List", NULL); apol_policy_path_t *ppath = NULL; if (path == NULL) { return; } ppath = open_policy_build_path(pane); if (ppath == NULL) { goto cleanup; } if (apol_policy_path_to_file(ppath, path) < 0) { toplevel_ERR(pane->op->top, "Error exporting policy list %s: %s", path, strerror(errno)); } cleanup: g_free(path); apol_policy_path_destroy(&ppath); } void open_policies_dialog_run(toplevel_t * top, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path) { struct open_policy op; gint response; apol_policy_path_t *input[SEDIFFX_POLICY_NUM]; memset(&op, 0, sizeof(op)); op.top = top; op.xml = glade_xml_new(toplevel_get_glade_xml(top), "PoliciesOpenWindow", NULL); op.pane[SEDIFFX_POLICY_ORIG].op = &op; op.pane[SEDIFFX_POLICY_MOD].op = &op; open_policy_init_widgets(&op); open_policy_init_signals(&op); open_policy_init_values(&op, orig_path, mod_path); open_policy_on_entry_event_after(NULL, NULL, &op); while (1) { response = gtk_dialog_run(op.dialog); if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) { break; } if ((input[SEDIFFX_POLICY_ORIG] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_ORIG])) == NULL || (input[SEDIFFX_POLICY_MOD] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_MOD])) == NULL) { continue; } if (toplevel_open_policies(op.top, input[SEDIFFX_POLICY_ORIG], input[SEDIFFX_POLICY_MOD]) == 0) { break; } } gtk_widget_destroy(GTK_WIDGET(op.dialog)); free(op.pane[SEDIFFX_POLICY_ORIG].last_module_path); free(op.pane[SEDIFFX_POLICY_MOD].last_module_path); g_object_unref(op.pane[SEDIFFX_POLICY_ORIG].module_store); g_object_unref(op.pane[SEDIFFX_POLICY_MOD].module_store); if (response == 0) { /* Run Diff button was clicked */ toplevel_run_diff(op.top); } }