From 039f61cf2431986680c3ca1a75f19f188974882e Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 7 Apr 2010 19:55:56 +0000 Subject: Added patch from Tiago Marques to support application blocking, msec integration, white-black lists and UI improvements (#43335). --- bin/drakguard | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 372 insertions(+), 19 deletions(-) (limited to 'bin') diff --git a/bin/drakguard b/bin/drakguard index 2d02d37..f86dd37 100755 --- a/bin/drakguard +++ b/bin/drakguard @@ -14,10 +14,16 @@ use interactive; use network::shorewall; use network::squid; use services; +use Encode; my $dansguardian_main_file = "/etc/dansguardian/dansguardian.conf"; my $dansguardian_filter_file = "/etc/dansguardian/dansguardianf1.conf"; my $time_control_file = "/etc/shorewall/time_control"; +my $dansguardian_bannedsitelist = "/etc/dansguardian/lists/bannedsitelist"; +my $dansguardian_exceptionsitelist = "/etc/dansguardian/lists/exceptionsitelist"; +my $dansguardian_protected_program_list = "/etc/dansguardian/lists/blacklists/drakguard/protected_list"; +my $msec_conf = "/etc/security/msec/perms.conf"; +my $perms_orig = "/etc/security/msec/perms.orig"; my %dansguardian_levels = ( 160 => N_("Low"), 100 => N_("Normal"), @@ -55,7 +61,7 @@ my %dansguardian_langs = ( my $blacklist_url_file = "/etc/dansguardian/lists/blacklists/drakguard/urls"; my $whitelist_url_file = "/etc/dansguardian/lists/whitelists/drakguard/urls"; -my ($enable, $level, $time_control, $time_start_h, $time_start_m, $time_stop_h, $time_stop_m, $allow_time_change); +my ($enable, $level, $time_control, $time_start_h, $time_start_m, $time_stop_h, $time_stop_m, $allow_time_change, $net_control, $not_net_control, $program_control, $net_control_state, $program_control_state); my $shorewall = network::shorewall::read(); my $proxy_port = 3128; my $proxy_user = 'squid'; @@ -78,6 +84,9 @@ my $allusers_list = Gtk2::SimpleList->new(N("All users") => 'text'); $allusers_list->get_selection->set_mode('multiple'); @{$allusers_list->{data}} = sort(list_users()); +my $allusers_program_list; +my $users_program_list; + my $users_list = Gtk2::SimpleList->new(N("Allowed users") => 'text'); $users_list->get_selection->set_mode('multiple'); @{$users_list->{data}} = difference2($shorewall->{accept_local_users}{http}, [ $proxy_user ]); @@ -96,11 +105,30 @@ my @url_lists = ( remove_text => N("Remove from whitelist"), file => $whitelist_url_file, apply => \&apply_whitelist, + }, + { + tab_title => N("Block programs"), + list_title => N("Programs with blocked execution"), + remove_text => N("Remove from blocked execution list"), + file => $dansguardian_protected_program_list, + apply => \&apply_block_program, } ); sub update_time_change() { gtkval_modify(\$allow_time_change, $enable && $time_control); + gtkval_modify(\$net_control_state, $enable && $net_control); + gtkval_modify(\$not_net_control, $enable && !$net_control); + gtkval_modify(\$program_control_state, $enable && $program_control); +} + +sub update_network_change() { + gtkval_modify(\$not_net_control, $enable && !$net_control); + gtkval_modify(\$net_control_state, $enable && $net_control); +} + +sub update_program_state() { + gtkval_modify(\$program_control_state, $enable && $program_control); } $w->{ok_clicked} = \&save; @@ -112,13 +140,17 @@ gtkadd($w->{window}, 1, gtknew('Notebook', children => [ gtknew('Label', text => N("Configuration")), gtknew('VBox', spacing => 5, border_width => 5, children => [ - 1, gtknew('WrappedLabel', text => N("This tool allows to configure parental control. It can block access to web sites and restrict connection during a specified timeframe.")), - 1, gtknew('Label'), - 0, gtknew('Title2', label => N("Main options")), + 0, gtknew('Label', text => N("This tool allows to configure parental control.\nIt can block access to web sites and restrict connection during a specified timeframe.")), 0, gtknew('CheckButton', text => N("Enable parental control"), active_ref => \$enable, toggled => \&update_time_change), + 1, gtknew('Label'), + 0, gtknew('Title2', label => N("Main options")), 0, gtknew('HBox', children_tight => [ - gtknew('Label_Left', text_markup => N("Control level"), + gtknew('CheckButton', text => N("Block all network traffic"), + sensitive_ref => \$enable, active_ref => \$net_control, toggled => \&update_network_change), + ]), + 0, gtknew('HBox', children_tight => [ + gtknew('Label', sensitive_ref => \$enable, text_markup => N("Obscenity sensibility"), #Sensitivity to bad words alignment => [ 0, 0.5 ]), gtknew('ComboBox', list => [ keys %dansguardian_levels ], @@ -165,27 +197,89 @@ gtkadd($w->{window}, my $url_list = $_; $url_list->{list} = Gtk2::SimpleList->new($url_list->{list_title} => 'text'); $url_list->{list}->get_selection->set_mode('multiple'); - @{$url_list->{list}{data}} = read_url_list($url_list->{file}); + if ($url_list->{tab_title} eq N("Block programs")){@{$url_list->{list}{data}} = read_program_list($url_list->{file}); } + else{@{$url_list->{list}{data}} = read_url_list($url_list->{file});} + + $allusers_program_list = Gtk2::SimpleList->new(N("Blocked users") => 'text'); + $allusers_program_list->get_selection->set_mode('multiple'); + @{$allusers_program_list->{data}} = sort(list_users()); + + $users_program_list = Gtk2::SimpleList->new(N("Allowed users") => 'text'); + $users_program_list->get_selection->set_mode('multiple'); + @{$users_program_list->{data}} = sort (read_program_user_list($url_list->{file})); my $entry; + my $original_entry; ( - gtknew('Label', text => $url_list->{tab_title}), - gtknew('VBox', spacing => 5, children => [ - 0, gtknew('HBox', border_width => 5, spacing => 5, children_loose => [ + gtknew('Label', if_($url_list->{tab_title} eq N("Whitelist"), sensitive_ref => \$net_control_state), + if_($url_list->{tab_title} eq N("Blacklist"), sensitive_ref => \$not_net_control), + if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$enable), text => $url_list->{tab_title}), + gtknew('VBox', if_($url_list->{tab_title} eq N("Whitelist"), sensitive_ref => \$net_control_state), + if_($url_list->{tab_title} eq N("Blacklist"), sensitive_ref => \$not_net_control), spacing => 5, children => [ + 0, gtknew('HBox', children_tight => [ + if_($url_list->{tab_title} eq N("Block programs"),gtknew('CheckButton', text => N("Block defined applications"), + sensitive_ref => \$enable, active_ref => \$program_control, toggled => \&update_program_state)), + ]), + 0, gtknew('HBox', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), border_width => 5, spacing => 5, children_loose => [ $entry = gtknew('Entry'), - gtknew('Button', text => N("Add"), clicked => sub { + if_($url_list->{tab_title} eq N("Block programs"), gtknew('Button', text => N("..."), clicked => sub { + $entry->set_text($in->ask_file(N("Please select the program you want to control"), "/var/lib/caixamagica/programas")); + })), + 0, gtknew('Button', text => N("Add"), clicked => sub { my $text = $entry->get_text; - $text =~ s,^[^:]+://,,g; #- strip protocol - list_add_entry($url_list->{list}, $text); - $entry->set_text(""); + $text =~ s,^[^:]+://,,g; #- strip protocol + if ($url_list->{tab_title} eq N("Block programs")) { + $text =~ s,(\W?)$,,g; #- remove unknown caracters + $original_entry = ""; + $text = resolve_symlinks($text); + if ( (grep { $_ && m/^\[Desktop Entry\]\s*/g} chomp_(cat_($text))) || $text =~ m/\.desktop/g) {# make this only for application block + my @exec_line = grep { $_ && m/^Exec=(.*)\s*/g} chomp_(cat_($text)); + require lang; + my $locale = lang::read(); + $locale = lc($locale->{country}); + my @system_name = grep { $_ && m/^GenericName(\[$locale\])?=(.*)\s*/g} chomp_(cat_($text)); + foreach (@exec_line){ + ($exec, $text, $args) = /(^Exec=)(\w+)\s*(.*)/; + if ($text eq ""){ + ($exec, $text, $args) = /(^Exec=)(.*+)\s*(.*)/;} + } + foreach my $b (@system_name){ + $original_entry = $b; + $original_entry =~ s/^GenericName(\[$locale\])?=//g; + $original_entry = Encode::decode("utf8", $original_entry); + } + } + $text = `which $text 2> /dev/null` if $text ne ""; + $text =~ s/\n//g; + } + if ( $text eq "" && $url_list->{tab_title} eq N("Block programs")) { $in->ask_warn(N("Error"), N("Invalid Binary Name")) and return; } + else { if ($url_list->{tab_title} eq N("Block programs")) { + list_add_entry_program($url_list->{list}, $text. " ( $original_entry )") if $original_entry ne ""; + list_add_entry_program($url_list->{list}, $text) if $original_entry eq ""; + } + else { list_add_entry($url_list->{list}, $text); } + } + $entry->set_text(""); }), ]), - 1, gtknew('ScrolledWindow', width => 500, height => 300, child => $url_list->{list}), - 0, gtknew('HButtonBox', border_width => 5, layout => 'edge', children_loose => [ + 1, gtknew('ScrolledWindow', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), width => 500, height => 150, child => $url_list->{list}), + 0, gtknew('HButtonBox', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), border_width => 5, layout => 'edge', children_loose => [ gtknew('Button', text => $url_list->{remove_text}, clicked => sub { list_remove_selected($url_list->{list}); }), ]), + if_($url_list->{tab_title} eq N("Block programs"), 1, gtknew('Label'), + 0, gtknew('Title2', label => N("Unblock users")), + 0, gtknew('HButtonBox', sensitive_ref => \$program_control_state, spacing => 5, children_tight => [ + gtknew('ScrolledWindow', width => 220, height => 120, child => $allusers_program_list), + gtknew('VBox', spacing => 5, children_tight => [ + gtknew('Button', stock => "gtk-add", clicked => \&add_user_program), + gtknew('Button', stock => "gtk-remove", clicked => \&remove_user_program), + ]), + gtknew('ScrolledWindow', width => 220, height => 120, child => $users_program_list), + ]), + 0, gtknew('HBox'), + ), ]), ); } @url_lists), @@ -209,6 +303,17 @@ sub list_add_entry { } } +sub list_add_entry_program { + my ($list, @addr) = @_; + foreach my $a (@addr) { + $b = $a; + $a =~ s/\s(.*)//g; + @abc = map {$_->[0]} @{$list->{data}}; + push @{$list->{data}}, $b + unless (any { $_->[0] eq $b } @{$list->{data}}) || (grep {$_ && m/$a(.*)/g} @abc); + } +} + sub list_remove_entry { my ($list, @addr) = @_; #- workaround buggy Gtk2::SimpleList array abstraction, it destroys references @@ -238,6 +343,14 @@ sub remove_user() { list_remove_selected($users_list); } +sub add_user_program() { + list_add_entry($users_program_list, list_get_selected($allusers_program_list)); +} + +sub remove_user_program() { + list_remove_selected($users_program_list); +} + sub quit_gui { my ($o_code) = @_; $w->exit($o_code); @@ -246,6 +359,9 @@ sub quit_gui { sub load() { my $guardian = read_dansguardian(); my $levelname = { %dansguardian_levels }->{$guardian->{naughtynesslimit}}; + $net_control = $guardian->{netblock}; + $program_control = $guardian->{programblock}; + gtkval_modify(\$not_net_control, $enable && !$net_control); $level = { reverse %dansguardian_levels }->{$levelname} if $levelname; $level ||= { reverse %dansguardian_levels }->{High}; $enable = services::starts_on_boot('dansguardian'); @@ -314,6 +430,10 @@ sub save() { #- reload shorewall config if it has just been installed $shorewall ||= network::shorewall::read(); } + else { + $program_control = 0; + @url_lists[2]->{apply}(list_get_entries(@url_lists[2]->{list})); + } services::set_status($_, $enable) foreach qw(squid dansguardian); if ($shorewall) { @@ -323,10 +443,78 @@ sub save() { network::shorewall::set_redirected_ports($shorewall, 'tcp', $guardian_port, if_($enable, 'http', $proxy_port)); network::shorewall::write($shorewall, $in); } - + quit_gui(); } +sub resolve_symlinks { + + # Check if a given file (either the pure filename or in a SANE device + # string as ":") is a symlink, if so expand the link. + # If the new file name is a link, expand again, until finding the + # physical file. + my ($file) = @_; + my $prefix = ""; + if ($file =~ m!^([^/]*)(/.*)$!) { + $prefix = $1; + $file = $2; + } else { + return $file; + } + while (1) { + #my $ls = `ls -l $file 2> /dev/null`; + my $ls = readlink $file; + if ($ls =~ m!\s($file)\s*\->\s*(\S+)\s*$!) { + my $target = $2; + if ($target !~ m!^/! && $file =~ m!^(.*)/[^/]+$!) { + $target = "$1/$target"; + } + $file = $target; + } else { + last; + } + } + return $prefix . $file; +} + +sub remove_acl { + my ($file, $binary) = @_; + `setfacl -b $binary`; + clean_config_line($file, "$binary"); +} + +sub set_permissions { + my ($line, $line_info) = @_; + #`protect $line --add`; + chmod 0700, $line; + chown "root","root", $line; + #remove msec lines + #clean_config_line($msec_conf,"$line"); + subst_config_line($dansguardian_protected_program_list,"$line_info \n"); #adds the file to dansguardian_protected_program_list + subst_config_line($msec_conf,"$line\troot.root\t700\tforce\t\n"); #adds the file to msec + #`protect $line $_` foreach list_get_entries($users_program_list); + #add msec line or lines... + foreach my $user_msec (list_get_entries($users_program_list)) { + my @msec_line = grep { $_ && m/$line/g} chomp_(cat_($msec_conf)); + my @program_list_line = grep { $_ && m/^$line/g} chomp_(cat_($dansguardian_protected_program_list)); + `setfacl -m u:$user_msec:r-x $line`; + subst_config_line($dansguardian_protected_program_list,"$_$user_msec,\n") foreach @program_list_line; #adds the users to the programs blocked in msec + subst_config_line($msec_conf,"$_$user_msec:r-x,\n") foreach @msec_line; #adds the users to the programs blocked in msec + } +} + +sub restore_permissions { + my ($line) = @_; + foreach ($line){ + ($fich, $usrname, $grpname, $mode) = /^(.*)\t(\w+)\.(\w+)\t(\d+)\s*/ or next; + } + $mode = sprintf "%04d", $mode;#make number with 4 digits + $guid = getgrnam($grpname); + $uid = getpwnam($usrname); + chown $uid, $guid, $fich; + chmod oct($mode), $fich; +} + sub subst_config_line { my ($file, $line) = @_; my $key = first(split(' ', $line)); @@ -337,6 +525,31 @@ sub subst_config_line { } $file; } +sub clean_config_line { + my ($file, $line) = @_; + my $key = first(split(' ', $line)); + my $done; + substInFile { + $done = 1 if s|^\s*$key.*\n||; + } $file; +} + +sub block_internet_dansguardian { + my ($file, $line) = @_; + my $key = "#"; + substInFile { + s|^$key\*\*\s|**\n|; + } $file; +} + +sub unblock_internet_dansguardian { + my ($file) = @_; + my $key = "#"; + substInFile { + s|^\*\*\s|$key**\n|; + } $file; +} + sub enable_transparent_proxy { my ($port) = @_; #- FIXME: use network::squid once it is rewritten to be more gentle with the config file @@ -364,15 +577,29 @@ sub write_dansguardian() { my $dansguardian_lang = $lang_to_dansguardian{$locale_lang} || $lang_to_dansguardian{$locale->{lang}}; subst_config_line($dansguardian_main_file, "language = '$dansguardian_lang'\n") if $dansguardian_lang; - subst_config_line($dansguardian_filter_file, "naughtynesslimit = $level\n"); + subst_config_line($dansguardian_filter_file, "naughtynesslimit = '$level'\n"); + subst_config_line($dansguardian_filter_file, "netblock = '$net_control'\n"); + subst_config_line($dansguardian_filter_file, "programblock = '$program_control'\n"); + if ($net_control) { + + block_internet_dansguardian($dansguardian_bannedsitelist); + + } + else { + + unblock_internet_dansguardian($dansguardian_bannedsitelist); + } } sub include_guardian_file { my ($guardian_file, $external_file) = @_; my $to_add = ".Include<$external_file>\n"; + clean_config_line($guardian_file, "$to_add" ); my @all = cat_($guardian_file); - if (!member($to_add, @all)) { - output_p($guardian_file, @all, $to_add); + if ((($dansguardian_bannedsitelist eq $guardian_file) && !$net_control) || (($dansguardian_exceptionsitelist eq $guardian_file) && $net_control)) { + if (!member($to_add, @all)) { + output_p($guardian_file, @all, $to_add); + } } } @@ -381,6 +608,18 @@ sub read_url_list { grep { $_ && !/^\s*#/ } chomp_(cat_($file)); } +sub read_program_list { + my ($file) = @_; + grep { $_ && s/(\s*(\w+),)*//g } chomp_(cat_($file)); +} + +sub read_program_user_list { + my ($file) = @_; + my @user_list = grep { $_ && s/(.*?)\s//g } chomp_(cat_($file)); + uniq(map{ my $user = $_; + $user =~ m/(\w+),/g;} @user_list); +} + sub apply_blacklist { my @addr = @_; my $blacklist_top = "/etc/dansguardian/lists/bannedsitelist"; @@ -396,3 +635,117 @@ sub apply_whitelist { output_p($whitelist_url_file, map { $_ . "\n" } @addr); include_guardian_file($whitelist_top, $whitelist_url_file); } + +sub apply_block_program { + #my @addr = @_; + #output_p($acl_file, map { $_ . "\n" } @addr); + if ($program_control) { + my @original_list = read_program_list(@url_lists[2]->{file}); + my @orig_list = read_program_list(@url_lists[2]->{file}); + for (@original_list){ + s/\s(.*)//; + } + my @actual_list = list_get_entries(@url_lists[2]->{list}); + my @act_list = list_get_entries(@url_lists[2]->{list}); + for (@actual_list){ + s/\s(.*)//; + } + #code to make diff bettween vectors + @union = @intersection = @difference = (); + %count = (); + foreach $element (@original_list, @actual_list) { $count{$element}++ } + foreach $element (keys %count) { + # push @union, $element; + push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; + } + if (scalar(@difference)) { + #keep the permissons save, make the way to do that + foreach $diff (@difference) { + my @orig_file = cat_($perms_orig); + my @progs_orig_file = grep { $_ && m/$diff/g } chomp_(cat_($perms_orig)); + #test if program is in msec + my @msec_program = grep { $_ && m/$diff/g } chomp_(cat_($msec_conf)); + if (scalar(@progs_orig_file)) {#test if is to remove or to add acl + #clean config file of the removed programs + foreach $element (@progs_orig_file){ + my $val = $element; + substInFile { + s|$element.*\n||; + } $perms_orig; + $val =~ s/\t(.*)\s*//; + $element =~ s/^(\d+)\t//; + if ($val) { + #write back in the msec file and set original file permissions + #`unprotect $diff --remove`; + remove_acl($dansguardian_protected_program_list, $diff); + subst_config_line($msec_conf,"$element\n"); + restore_permissions($element); + } + else { + #only make the permissions wright + #`unprotect $diff --remove`; + remove_acl($dansguardian_protected_program_list, $diff); + clean_config_line($msec_conf,"$diff"); + restore_permissions($element); + } + } + } + else { + if (scalar(@msec_program)){ + output_p($perms_orig, @orig_file, "1\t".$_."\n") foreach @msec_program; + #add file to msec and set acls + } + else { + my $mode = (stat($diff))[2] & 07777; + #"%04o" important to make 0755 to chmod ou chown + $mode = sprintf "%lo", $mode; + my $uid = (stat($diff))[4]; + my $gid = (stat($diff))[5]; + my $usrname = getpwuid($uid); + my $gname = getgrgid($gid); + my $to_add = "0\t$diff\t$usrname.$gname\t$mode\n"; + #keep the permissons safe make the way to do that + output_p($perms_orig, @orig_file, $to_add); + } + set_permissions($diff, grep {$_ && m/$diff/g} @act_list); + } + } + } + #this is just the case to reactivate the parental control + #`unprotect $_ --remove` foreach @intersection; + remove_acl($dansguardian_protected_program_list, $_) foreach @intersection; + foreach $intersec (@intersection){ + set_permissions($intersec, grep {$_ && m/$intersec/g} @orig_list); + } + } + else { + my @original_list = list_get_entries(@url_lists[2]->{list}); + for (@original_list){ + s/\s(.*)//; + } + #restore the original file permissions + `setfacl -b $_` foreach @original_list; + foreach $list (@original_list) { + my @progs_orig_file = grep { $_ && m/$list/g } chomp_(cat_($perms_orig)); + #test if program is in msec + if (scalar(@progs_orig_file)) { + #clean config file of the removed programs + foreach $element (@progs_orig_file){ + my $val = $element; + $val =~ s/\t(.*)\s*//; + $element =~ s/^(\d+)\t//; + if ($val) { + #write back in the msec file and set original file permissions + subst_config_line($msec_conf,"$element\n"); + restore_permissions($element); + } + else { + #only make the permissions wright + clean_config_line($msec_conf,"$list"); + restore_permissions($element); + } + } + } + } + } +} -- cgit v1.2.1