#include #include #include #include #include #include #include #include "ruleset.h" #include "mainwin.h" #include "support.h" #include "format.h" #include "interface.h" #include "advancedwin.h" ruleset_t ruleset = { modified : FALSE, filename : NULL, icmp_reject_code : 13, use_tcp_rst : FALSE, ignore_rules : FALSE, unmatch_verdict : VERDICT_QUERY, rules : NULL }; static pid_t child_pid = (pid_t) -1; static GtkListStore *ruleset_list_store = NULL; enum { COLUMN_ENABLED, COLUMN_DESCRIPTION, COLUMN_VERDICT, COLUMN_RULE, N_COLUMNS }; static int commit_pipe[2] = {-1, -1}; volatile static gboolean commit_running = FALSE; GtkWidget *commit_window = NULL; static void _free_list() { while (ruleset.rules) { rule_free(ruleset.rules->data); ruleset.rules = g_list_remove(ruleset.rules, ruleset.rules->data); } } void _set_data(rule_t *rule) { g_assert(ruleset_list_store && rule); gtk_list_store_set(ruleset_list_store, &rule->iter, COLUMN_ENABLED, rule->enabled, COLUMN_DESCRIPTION, rule->description, COLUMN_VERDICT, format_verdict(rule->verdict, FORMAT_USER), COLUMN_RULE, rule, -1); } void ruleset_reset() { g_free(ruleset.filename); ruleset.filename = NULL; _free_list(); ruleset.modified = FALSE; ruleset.icmp_reject_code = 13; ruleset.use_tcp_rst = FALSE; ruleset.ignore_rules = FALSE; ruleset.unmatch_verdict = VERDICT_QUERY; ruleset.rules = NULL; } void ruleset_fill_ui() { GList *l; GtkWidget *mw = get_main_window(); g_assert(ruleset_list_store); gtk_list_store_clear(ruleset_list_store); for (l = ruleset.rules; l; l = l->next) { rule_t *rule = (rule_t*) l->data; gtk_list_store_append(ruleset_list_store, &rule->iter); _set_data(rule); rule->realized = TRUE; } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(mw, "ruleset_check_button")), !ruleset.ignore_rules); gtk_option_menu_set_history(GTK_OPTION_MENU(lookup_widget(mw, "unmatch_optionmenu")), ruleset.unmatch_verdict); ruleset_update_ui(); } int ruleset_load(gchar *fn) { xmlDocPtr doc = NULL; xmlNodePtr node; ruleset_reset(); if (!(doc = xmlParseFile(fn))) goto finish; if (!(node = xmlDocGetRootElement(doc))) goto finish; if (xmlStrcmp(node->name, "ruleset")) goto finish; for (node = node->xmlChildrenNode; node; node = node->next) { rule_t *rule; if (node->type == XML_TEXT_NODE) continue; if (node->type != XML_ELEMENT_NODE) goto finish; if (!xmlStrcmp(node->name, "use-tcp-rst")) { ruleset.use_tcp_rst = TRUE; continue; } if (!xmlStrcmp(node->name, "ignore-rules")) { ruleset.ignore_rules = TRUE; continue; } if (!xmlStrcmp(node->name, "unmatch-verdict")) { xmlChar *text; text = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (!xmlStrcmp(text, "drop")) ruleset.unmatch_verdict = VERDICT_DROP; else if (!xmlStrcmp(text, "reject")) ruleset.unmatch_verdict = VERDICT_REJECT; else if (!xmlStrcmp(text, "accept")) ruleset.unmatch_verdict = VERDICT_ACCEPT; else if (!xmlStrcmp(text, "query") || !xmlStrcmp(text, "ask")) ruleset.unmatch_verdict = VERDICT_QUERY; else { xmlFree(text); goto finish; } xmlFree(text); continue; } if (!xmlStrcmp(node->name, "icmp-reject-code")) { xmlChar *text; text = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); ruleset.icmp_reject_code = atoi(text); xmlFree(text); continue; } if (xmlStrcmp(node->name, "rule")) goto finish; if (!(rule = rule_new_from_xml(doc, node))) goto finish; ruleset.rules = g_list_append(ruleset.rules, rule); } xmlFreeDoc(doc); ruleset.filename = g_strdup(fn); ruleset.modified = FALSE; ruleset_fill_ui(); return 0; finish: fprintf(stderr, "Broken XML file.\n"); if (doc) xmlFreeDoc(doc); _free_list(); return -1; } int ruleset_save(gchar *fn) { GList *l; xmlDocPtr doc = NULL; xmlNodePtr node; int r = -1; char txt[256]; if (!fn) fn = ruleset.filename; g_assert(fn); if (!(doc = xmlNewDoc("1.0"))) goto finish; if (!(node = xmlNewNode(NULL, "ruleset"))) goto finish; xmlDocSetRootElement(doc, node); if (ruleset.use_tcp_rst) xmlNewTextChild(node, NULL, "use-tcp-rst", NULL); if (ruleset.ignore_rules) xmlNewTextChild(node, NULL, "ignore-rules", NULL); xmlNewTextChild(node, NULL, "unmatch-verdict", format_verdict(ruleset.unmatch_verdict, FORMAT_XML)); snprintf(txt, sizeof(txt), "%u", ruleset.icmp_reject_code); xmlNewTextChild(node, NULL, "icmp-reject-code", txt); for (l = ruleset.rules; l; l = l->next) { if (rule_to_xml((rule_t*) l->data, doc, node) < 0) goto finish; } if (xmlSaveFormatFile(fn, doc, 1) < 0) goto finish; xmlFreeDoc(doc); ruleset.modified = FALSE; ruleset_update_ui(); return 0; finish: fprintf(stderr, "Could not write XML file.\n"); if (doc) xmlFreeDoc(doc); return r; } rule_t* ruleset_get_current_rule() { GtkTreePath *path; GtkTreeIter iter; GtkTreeView *tv = GTK_TREE_VIEW(lookup_widget(get_main_window(), "ruleset_view")); rule_t *rule; gtk_tree_view_get_cursor(tv, &path, NULL); if (!path) return NULL; gtk_tree_model_get_iter(GTK_TREE_MODEL(ruleset_list_store), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(ruleset_list_store), &iter, COLUMN_RULE, &rule, -1); return rule; } int ruleset_install() { char *argv[3]; GError *e; g_assert(ruleset.filename); argv[0] = "install-firewall"; argv[1] = ruleset.filename; argv[2] = NULL; if (!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, NULL, NULL, NULL, &e)) { fprintf(stderr, "Could not run install-firewall: %s\n", e->message); g_error_free(e); return -1; } return 0; } static void _sigchld(int sig) { gchar c = 'X'; write(commit_pipe[1], &c, 1); signal(SIGCHLD, SIG_DFL); } static gboolean _commit_finish(GIOChannel *source, GIOCondition condition, gpointer data) { gchar c; read(commit_pipe[0], &c, 1); gtk_widget_hide(commit_window); commit_running = FALSE; return TRUE; } int ruleset_commit() { g_assert(ruleset.filename); if (commit_running) return 0; commit_running = TRUE; if (!commit_window) commit_window = create_wait_window(); gtk_widget_show_all(commit_window); signal(SIGCHLD, _sigchld); if (ruleset.modified) ruleset_save(ruleset.filename); return ruleset_install(); } void ruleset_update_ui() { GList *l; rule_t *rule = ruleset_get_current_rule(); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "properties_button")), rule && !rule->being_edited); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "remove_button")), rule ? TRUE : FALSE); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "clear_button")), ruleset.rules ? TRUE : FALSE); l = g_list_first(ruleset.rules); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "up_button")), !rule || !l || l->data == rule ? FALSE : TRUE); l = g_list_last(ruleset.rules); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "down_button")), !rule || !l || l->data == rule ? FALSE : TRUE); gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(get_main_window(), "commit_button")), ruleset.modified); if (rule) { gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_enabled_label")), rule->enabled ? "Yes" : "No"); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_name_label")), rule->description); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_match_label")), rule_match_string(rule)); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_verdict_label")), format_verdict(rule->verdict, FORMAT_USER)); } else { gchar *p = "n/a"; gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_enabled_label")), p); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_name_label")), p); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_match_label")), p); gtk_label_set_label(GTK_LABEL(lookup_widget(get_main_window(), "rule_verdict_label")), p); } } int ruleset_append_rule(rule_t *rule) { g_assert(rule); if (g_list_find(ruleset.rules, rule)) return -1; ruleset.rules = g_list_append(ruleset.rules, rule); gtk_list_store_append(ruleset_list_store, &rule->iter); _set_data(rule); rule->realized = TRUE; ruleset.modified = TRUE; ruleset_update_ui(); return 0; } int ruleset_update_rule(rule_t *rule) { g_assert(rule && rule->realized); _set_data(rule); ruleset.modified = TRUE; ruleset_update_ui(); return 0; } int ruleset_remove_rule(rule_t *rule) { g_assert(rule); if (rule->being_edited) return -1; if (!g_list_find(ruleset.rules, rule)) return -1; gtk_list_store_remove(ruleset_list_store, &rule->iter); ruleset.rules = g_list_remove(ruleset.rules, rule); rule_free(rule); ruleset.modified = TRUE; ruleset_update_ui(); return 0; } int ruleset_move_rule(rule_t *rule, int i) { GtkTreeIter *iter = NULL; GList *a = NULL, *l; if (i == 0) return -1; l = g_list_find(ruleset.rules, rule); g_assert(l); if (i > 0) { if (!l->next) return -1; iter = &(((rule_t*) l->next->data)->iter); a = l->next->next; } if (i < 0) { if (!l->prev) return -1; iter = &(((rule_t*) l->prev->data)->iter); a = l->prev; } ruleset.rules = g_list_remove(ruleset.rules, rule); ruleset.rules = g_list_insert_before(ruleset.rules, a, rule); gtk_list_store_swap(ruleset_list_store, &rule->iter, iter); ruleset.modified = TRUE; ruleset_update_ui(); return 0; } void ruleset_clear() { _free_list(); if (ruleset_list_store) gtk_list_store_clear(ruleset_list_store); ruleset_update_ui(); } static void _ruleset_toggled(GtkCellRendererToggle *cell, gchar *path_str, gpointer d) { rule_t *rule; GtkTreeIter iter; GtkTreePath *path = gtk_tree_path_new_from_string(path_str); gtk_tree_model_get_iter(GTK_TREE_MODEL(ruleset_list_store), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(ruleset_list_store), &iter, COLUMN_RULE, &rule, -1); rule->enabled = !rule->enabled; _set_data(rule); gtk_tree_path_free (path); ruleset.modified = TRUE; ruleset_update_ui(); } void ruleset_widget_init() { GtkTreeView *tv = GTK_TREE_VIEW(lookup_widget(get_main_window(), "ruleset_view")); GtkTreeViewColumn *c; GtkCellRenderer *r; gtk_tree_view_set_model(tv, GTK_TREE_MODEL(ruleset_list_store)); r = gtk_cell_renderer_toggle_new(); g_signal_connect(r, "toggled", G_CALLBACK(_ruleset_toggled), NULL); c = gtk_tree_view_column_new_with_attributes("", r, "active", COLUMN_ENABLED, NULL); gtk_tree_view_append_column(tv, c); c = gtk_tree_view_column_new_with_attributes("Verdict", gtk_cell_renderer_text_new(), "text", COLUMN_VERDICT, NULL); gtk_tree_view_column_set_resizable(c, TRUE); gtk_tree_view_append_column(tv, c); c = gtk_tree_view_column_new_with_attributes("Description", gtk_cell_renderer_text_new(), "text", COLUMN_DESCRIPTION, NULL); gtk_tree_view_column_set_resizable(c, TRUE); gtk_tree_view_append_column(tv, c); ruleset_update_ui(); } int ruleset_init() { GIOChannel *c; if (pipe(commit_pipe) < 0) return -1; if (!(c = g_io_channel_unix_new(commit_pipe[0]))) { ruleset_done(); return -1; } g_io_add_watch(c, G_IO_IN, _commit_finish, NULL); g_io_channel_unref(c); g_assert(!ruleset_list_store); ruleset_list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); return 0; } void ruleset_done() { g_free(ruleset.filename); ruleset.filename = NULL; _free_list(); if (commit_pipe[0] >= 0) close(commit_pipe[0]); if (commit_pipe[1] >= 0) close(commit_pipe[1]); commit_pipe[0] = commit_pipe[1] = -1; g_object_unref(ruleset_list_store); } int ruleset_new(gchar *fn) { ruleset_reset(); ruleset.filename = g_strdup(fn); ruleset.modified = TRUE; ruleset_fill_ui(); return 0; } int ruleset_initial_load() { char fn[PATH_MAX]; snprintf(fn, sizeof(fn), "%s/.fieryfilter/", getenv("HOME")); if (mkdir(fn, 0700) && errno != EEXIST) return -1; snprintf(fn, sizeof(fn), "%s/.fieryfilter/default.fwx", getenv("HOME")); if (ruleset_load(fn) < 0) if (ruleset_new(fn) < 0) return -1; return 0; }