summaryrefslogtreecommitdiffstats
path: root/client/ruleset.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/ruleset.c')
-rw-r--r--client/ruleset.c534
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;
+}
+