#!/usr/bin/perl use strict; use lib qw(/usr/lib/libDrakX); # i18n: IMPORTANT: to get correct namespace (drakx-net instead of libDrakX) BEGIN { unshift @::textdomains, 'drakx-net' } use common; use standalone; use Socket; use mygtk3 qw(gtknew); use ugtk3 qw(:dialogs); use POSIX qw(strftime); use dbus_object; use network::ifw; use Gtk3::SimpleList; use ugtk3 qw(:create :helpers :wrappers); my $loglist = create_attack_list(); $loglist->get_selection->set_mode('single'); my $blacklist = create_attack_list(); $blacklist->get_selection->set_mode('multiple'); my $whitelist = Gtk3::SimpleList->new(addr => 'hidden', N("Allowed addresses") => 'text', ); $whitelist->get_selection->set_mode('multiple'); $whitelist->set_headers_clickable(1); $whitelist->get_column(0)->signal_connect('clicked', \&sort_by_column, $whitelist->get_model); $whitelist->get_column(0)->set_sort_column_id(0); my $i_m_ifw2 = member("--ifw2", @ARGV); my $services_log = create_service_list('status'); my $allowed_services = create_service_list(); my $blocked_services = create_service_list(); my $title = N("Interactive Firewall"); my $icon = "drakfirewall"; $ugtk3::wm_icon = $icon; my $w = ugtk3->new($title); my $ifw; eval { my $bus = dbus_object::system_bus(); dbus_object::set_gtk3_watch_helper($bus); network::ifw::init($bus, sub { my ($_con, $msg) = @_; my $member = $msg->get_member; if ($member eq 'Attack') { handle_log($msg->get_args_list); } elsif ($member eq 'Blacklist') { handle_blacklist($msg->get_args_list); } elsif ($member eq 'Whitelist') { handle_whitelist($msg->get_args_list); } elsif ($member eq 'Clear') { clear_lists(); } elsif ($member eq 'Init') { handle_init(); } elsif ($member eq 'ManageRequest') { $w->{window}->present; } }); $ifw = network::ifw->new($bus); }; if ($@) { err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); $w->exit(1); } init_lists(); gtkadd($w->{window}, gtknew('VBox', spacing => 5, children => [ $::isEmbedded ? () : (0, Gtk3::Banner->new($icon, $title)), 1, gtknew('Notebook', children => [ if_($i_m_ifw2, gtknew('Label', text => N("Log")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $services_log), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Allow"), clicked => sub {}), gtknew('Button', text => N("Block"), clicked => sub {}), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), gtknew('Label', text => N("Allowed services")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $allowed_services), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Remove"), clicked => sub {}), gtknew('Button', text => N("Block"), clicked => sub {}), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), gtknew('Label', text => N("Blocked services")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $blocked_services), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Remove"), clicked => sub {}), gtknew('Button', text => N("Allow"), clicked => sub {}), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), ), gtknew('Label', text => N("Log")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $loglist), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Clear logs"), clicked => \&clear_log), gtknew('Button', text => N("Blacklist"), clicked => sub { blacklist(get_selected_log_seq()) }), gtknew('Button', text => N("Whitelist"), clicked => sub { whitelist(get_selected_log()) }), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), gtknew('Label', text => N("Blacklist")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $blacklist), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Remove from blacklist"), clicked => sub { unblacklist(get_selected_blacklist()) }), gtknew('Button', text => N("Move to whitelist"), clicked => sub { my @addr = get_selected_blacklist(); unblacklist(@addr); whitelist(@addr); }), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), gtknew('Label', text => N("Whitelist")), gtknew('VBox', spacing => 5, children => [ 1, gtknew('ScrolledWindow', width => 600, height => 260, child => $whitelist), 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Remove from whitelist"), clicked => sub { unwhitelist(get_selected_whitelist()) }), gtknew('Button', text => N("Close"), clicked => sub { Gtk3->main_quit }) ]), ]), ]), ]), ); $w->show; Gtk3->main; $w->exit(0); sub sort_by_column { my ($column, $model) = @_; my $col_id = $column->get_sort_column_id; my ($old_id, $old_order) = $model->get_sort_column_id; $model->set_sort_column_id($col_id, $old_id == $col_id && $old_order ne 'descending' ? 'ascending' : 'descending'); } sub handle_init() { $ifw->attach_object; init_lists(); } sub list_remove_addr { my ($list, @addr) = @_; #- workaround buggy Gtk3::SimpleList array abstraction, it destroys references @$list = map { member($_->[0], @addr) ? () : [ @$_ ] } @$list; } #- may throw an exception sub init_blacklist() { my @packets = $ifw->get_blacklist; while (my @blacklist = splice(@packets, 0, 8)) { handle_blacklist(@blacklist); } } sub clear_blacklist() { @{$blacklist->{data}} = (); } sub handle_blacklist { attack_list_add($blacklist, network::ifw::attack_to_hash(\@_)); } sub get_selected_blacklist() { uniq(map { $blacklist->{data}[$_][0] } $blacklist->get_selected_indices); } sub blacklist { my @seq = @_; eval { $ifw->set_blacklist_verdict($_, 1) foreach @seq }; $@ and err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } sub unblacklist { my @addr = @_; eval { $ifw->unblacklist($_) foreach @addr }; if (!$@) { list_remove_addr($blacklist->{data}, @addr); } else { err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } } #- may throw an exception sub init_whitelist() { handle_whitelist($_) foreach $ifw->get_whitelist; } sub clear_whitelist() { @{$whitelist->{data}} = (); } sub handle_whitelist { my ($addr) = @_; push @{$whitelist->{data}}, [ $addr, network::ifw::resolve_address(network::ifw::get_ip_address($addr)) ]; } sub get_selected_whitelist() { uniq(map { $whitelist->{data}[$_][0] } $whitelist->get_selected_indices); } sub whitelist { my @addr = @_; eval { $ifw->whitelist($_) foreach @addr }; $@ and err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } sub unwhitelist { my @addr = @_; eval { $ifw->unwhitelist($_) foreach @addr }; if (!$@) { list_remove_addr($whitelist->{data}, @addr); } else { err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } } sub init_lists() { eval { init_loglist(); init_blacklist(); init_whitelist(); }; $@ and print "$@\n", err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } sub clear_lists() { clear_loglist(); clear_blacklist(); clear_whitelist(); } sub create_attack_list() { my $attacklist = Gtk3::SimpleList->new(addr => 'hidden', seq => 'hidden', timestamp => 'hidden', N("Date") => 'text', N("Remote host") => 'text', N("Type") => 'text', N("Service") => 'text', N("Network interface") => 'text', N("Protocol") => 'text', ); $attacklist->set_headers_clickable(1); foreach (0, 1, 2) { $attacklist->get_column($_)->signal_connect('clicked', \&sort_by_column, $attacklist->get_model); #- sort on timestamp if Date column is clicked #- sort columns include hidden columns while list columns don't $attacklist->get_column($_)->set_sort_column_id($_ == 0 ? 1 : $_ + 2); } $attacklist; } sub attack_list_add { my ($attacklist, $attack) = @_; push @{$attacklist->{data}}, [ $attack->{addr}, $attack->{seq}, $attack->{timestamp}, $attack->{date}, $attack->{hostname}, $attack->{type}, $attack->{service}, $attack->{indev}, $attack->{protocol}, ]; } sub create_service_list { my ($o_status) = @_; my $service_list = Gtk3::SimpleList->new( N("Application") => 'text', N("Service") => 'text', if_($o_status, N("Status") => 'text'), ); N_("Allowed"); N_("Blocked"); $service_list->set_headers_clickable(1); foreach (0, 1, if_($o_status, 2)) { $service_list->get_column($_)->signal_connect('clicked', \&sort_by_column, $service_list->get_model); } $service_list; } #- may throw an exception sub init_loglist() { my @packets = $ifw->get_reports(1); while (my @attack = splice(@packets, 0, 10)) { handle_log(@attack); } } sub clear_loglist() { @{$loglist->{data}} = (); } sub handle_log { attack_list_add($loglist, network::ifw::attack_to_hash(\@_)); } sub get_selected_log_seq() { uniq(map { $loglist->{data}[$_][1] } $loglist->get_selected_indices); } sub get_selected_log() { uniq(map { $loglist->{data}[$_][0] } $loglist->get_selected_indices); } sub clear_log() { eval { $ifw->clear_processed_reports; $ifw->send_alert_ack; }; if (!$@) { clear_loglist(); } else { err_dialog(N("Interactive Firewall"), N("Unable to contact daemon")); } }