diff options
Diffstat (limited to 'client/ruleset.c')
-rw-r--r-- | client/ruleset.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/client/ruleset.c b/client/ruleset.c new file mode 100644 index 0000000..5c1095a --- /dev/null +++ b/client/ruleset.c @@ -0,0 +1,534 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <libxml/tree.h> +#include <sys/stat.h> +#include <sys/types.h> + +#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 = "<i>n/a</i>"; + 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; +} + |