From d8ae032328e45ae2873b1e16f948d88d38c5af74 Mon Sep 17 00:00:00 2001 From: Jesse Michael Date: Tue, 20 Mar 2007 21:58:38 +0000 Subject: [PATCH] used perltidy to clean up the formatting for the perl scripts in the utils package and manually fixed some places where perltidy's reformatting made it harder to read. the options used were-- -i=4 # 4-space indentation -l=0 # unlimited line length (for now) -pt=2 # slightly tightened parens -ce # cuddled elses -nolq # don't outdent long quotes -nsfs # don't add spaces in front of semi-colons in for ( ) statements -isbc # only indent block comments that have whitespace in front of them -otr # don't place a break between a comma and an opening brace the code will be refactored to make it possible to switch to using 80-column line-breaks without resorting to really nasty formatting constructs. --- utils/Reports.pm | 2343 +++++++++++---------- utils/Severity.pm | 289 +-- utils/SubDomain.pm | 5017 +++++++++++++++++++++++--------------------- utils/aa-eventd | 1505 ++++++------- utils/audit | 107 +- utils/autodep | 112 +- utils/complain | 107 +- utils/enforce | 105 +- utils/genprof | 167 +- utils/logprof | 34 +- utils/unconfined | 103 +- 11 files changed, 5115 insertions(+), 4774 deletions(-) diff --git a/utils/Reports.pm b/utils/Reports.pm index f7ecd7f7c..2734e1664 100755 --- a/utils/Reports.pm +++ b/utils/Reports.pm @@ -40,14 +40,25 @@ use ycp; setlocale(LC_MESSAGES, ""); textdomain("Reports"); -my $eventDb = '/var/log/apparmor/events.db'; +my $eventDb = '/var/log/apparmor/events.db'; my $numEvents = 1000; sub month2Num { - my $lexMon = shift; - my $months = { "Jan" =>'01', "Feb"=>'02', "Mar"=>'03', "Apr"=>'04', "May"=>'05', "Jun"=>'06', - "Jul"=>'07', "Aug"=>'08', "Sep"=>'09', "Oct"=>'10', "Nov"=>'11', "Dec"=>'12' }; + my $months = { + "Jan" => '01', + "Feb" => '02', + "Mar" => '03', + "Apr" => '04', + "May" => '05', + "Jun" => '06', + "Jul" => '07', + "Aug" => '08', + "Sep" => '09', + "Oct" => '10', + "Nov" => '11', + "Dec" => '12' + }; my $numMonth = $months->{$lexMon}; @@ -55,29 +66,27 @@ sub month2Num { } sub num2Month { - my $monthNum = shift; my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); - my $lexMonth = $months[($monthNum -1)]; + my $lexMonth = $months[ ($monthNum - 1) ]; return $lexMonth; } # Converts Epoch Time to Formatted Date String sub getDate { - my $epTime = shift; my $date = localtime($epTime); my ($day, $mon, $mondate, $time, $year) = split(/\s+/, $date); - my ($hour,$min,$sec) = split(/:/, $time); + my ($hour, $min, $sec) = split(/:/, $time); $mon = month2Num($mon); # we want 2 digits for easier reading - $mon = sprintf("%02d", $mon); + $mon = sprintf("%02d", $mon); $mondate = sprintf("%02d", $mondate); my $newDate = "$year-$mon-$mondate $time"; @@ -90,14 +99,12 @@ sub round { return ("$num"); } - # round up sub pageRound { - - my $num = shift; + my $num = shift; my $pnum = int($num); - if ( $pnum < $num ) { + if ($pnum < $num) { $pnum++; } @@ -105,29 +112,26 @@ sub pageRound { } sub checkFileExists { + my $file = shift; - my $file = shift; - - if ( $file && -e $file ) { - return 1; - } else { - return 0; - } - + if ($file && -e $file) { + return 1; + } else { + return 0; + } } # Translate mode & sdmode for parsing sub rewriteModes { - - my $filts = shift; + my $filts = shift; # Mode wrangling - Rewrite for better matches if ($filts->{'mode'} && $filts->{'mode'} ne "All") { - my @mode = (); + my @mode = (); my $tmpMode = undef; - @mode = split (//, $filts->{'mode'}); + @mode = split(//, $filts->{'mode'}); if (@mode > 0) { $tmpMode = join("|", @mode); @@ -138,15 +142,14 @@ sub rewriteModes { if ($tmpMode) { $filts->{'mode'} = $tmpMode; } - } - # Rewrite sdmode for more flexible matches + # Rewrite sdmode for more flexible matches if ($filts->{'sdmode'} && $filts->{'sdmode'} ne "All") { my @tmpMode = (); - if ( $filts->{'sdmode'} =~ /[pP]/ ) { push(@tmpMode,'PERMIT'); } - if ( $filts->{'sdmode'} =~ /[rR]/ ) { push(@tmpMode,'REJECT'); } - if ( $filts->{'sdmode'} =~ /[aA]/ ) { push(@tmpMode,'AUDIT'); } + if ($filts->{'sdmode'} =~ /[pP]/) { push(@tmpMode, 'PERMIT'); } + if ($filts->{'sdmode'} =~ /[rR]/) { push(@tmpMode, 'REJECT'); } + if ($filts->{'sdmode'} =~ /[aA]/) { push(@tmpMode, 'AUDIT'); } if (@tmpMode > 0) { $filts->{'sdmode'} = join('|', @tmpMode); } else { @@ -154,151 +157,151 @@ sub rewriteModes { } } - return $filts; + return $filts; } sub enableEventD { - # make sure the eventd is enabled before we do any reports - my $need_enable = 0; - if(open(SDCONF, "/etc/apparmor/subdomain.conf")) { - while() { - if(/^\s*APPARMOR_ENABLE_AAEVENTD\s*=\s*(\S+)\s*$/) { - my $flag = lc($1); - # strip quotes from the value if present - $flag = $1 if $flag =~ /^"(\S+)"$/; - $need_enable = 1 if $flag ne "yes"; - } - } - close(SDCONF); - } - # if the eventd isn't enabled, we'll turn it on the first time they - # run a report and start it up - if something fails for some reason, - # we should just fall through and the db check should correctly tell - # the caller that the db isn't initialized correctly - if($need_enable) { - my $old = "/etc/apparmor/subdomain.conf"; - my $new = "/etc/apparmor/subdomain.conf.$$"; - if(open(SDCONF, $old)) { - if(open(SDCONFNEW, ">$new")) { - my $foundit = 0; + # make sure the eventd is enabled before we do any reports + my $need_enable = 0; + if (open(SDCONF, "/etc/apparmor/subdomain.conf")) { + while () { + if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=\s*(\S+)\s*$/) { + my $flag = lc($1); - while() { - if(/^\s*APPARMOR_ENABLE_AAEVENTD\s*=/) { - print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n"; + # strip quotes from the value if present + $flag = $1 if $flag =~ /^"(\S+)"$/; + $need_enable = 1 if $flag ne "yes"; + } + } + close(SDCONF); + } - $foundit = 1; - } else { - print SDCONFNEW; - } - } + # if the eventd isn't enabled, we'll turn it on the first time they + # run a report and start it up - if something fails for some reason, + # we should just fall through and the db check should correctly tell + # the caller that the db isn't initialized correctly + if ($need_enable) { + my $old = "/etc/apparmor/subdomain.conf"; + my $new = "/etc/apparmor/subdomain.conf.$$"; + if (open(SDCONF, $old)) { + if (open(SDCONFNEW, ">$new")) { + my $foundit = 0; - unless($foundit) { - print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n"; - } + while () { + if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=/) { + print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n"; - close(SDCONFNEW); + $foundit = 1; + } else { + print SDCONFNEW; + } + } - # if we were able to overwrite the old config - # config file with the new stuff, we'll kick - # the init script to start up aa-eventd - if(rename($new, $old)) { - if(-e "/sbin/rcaaeventd") { - system("/sbin/rcaaeventd restart >/dev/null 2>&1"); - } else { - system("/sbin/rcapparmor restart >/dev/null 2>&1"); - } - } - } - close(SDCONF); - } - - } + unless ($foundit) { + print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n"; + } - return $need_enable; + close(SDCONFNEW); + + # if we were able to overwrite the old config + # config file with the new stuff, we'll kick + # the init script to start up aa-eventd + if (rename($new, $old)) { + if (-e "/sbin/rcaaeventd") { + system("/sbin/rcaaeventd restart >/dev/null 2>&1"); + } else { + system("/sbin/rcapparmor restart >/dev/null 2>&1"); + } + } + } + close(SDCONF); + } + + } + + return $need_enable; } # Check that events db exists and is populated # - Returns 1 for good db, 0 for bad db sub checkEventDb { + my $count = undef; + my $eventDb = '/var/log/apparmor/events.db'; - my $count = undef; - my $eventDb = '/var/log/apparmor/events.db'; + # make sure the event daemon is enabled + if (enableEventD()) { - # make sure the event daemon is enabled - if(enableEventD()) { + my $now = time; - my $now = time; - - # wait until the event db appears or we hit 1 min - while (! -e $eventDb) { - sleep 2; - return 0 if ((time - $now) >= 60); - } - - # wait until it stops changing or we hit 1 min - the event - # daemon flushes events to the db every five seconds. - my $last_modified = 0; - my $modified = (stat($eventDb))[9]; - while($last_modified != $modified) { - sleep 10; - last if ((time - $now) >= 60); - $last_modified = $modified; - $modified = (stat($eventDb))[9]; - } - } - - my $query = "SELECT count(*) FROM events "; - - # Pull stuff from db - my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1}); - - eval { - my $sth = $dbh->prepare($query); - $sth->execute; - $count = $sth->fetchrow_array(); - - $sth->finish; - }; - - if ( $@ ) { - ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr)); - return; + # wait until the event db appears or we hit 1 min + while (!-e $eventDb) { + sleep 2; + return 0 if ((time - $now) >= 60); } - $dbh->disconnect(); + # wait until it stops changing or we hit 1 min - the event + # daemon flushes events to the db every five seconds. + my $last_modified = 0; + my $modified = (stat($eventDb))[9]; + while ($last_modified != $modified) { + sleep 10; + last if ((time - $now) >= 60); + $last_modified = $modified; + $modified = (stat($eventDb))[9]; + } + } + my $query = "SELECT count(*) FROM events "; - if ( $count && $count > 0 ) { - return 1; - } else { - return 0; - } + # Pull stuff from db + my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 }); + + eval { + my $sth = $dbh->prepare($query); + $sth->execute; + $count = $sth->fetchrow_array(); + + $sth->finish; + }; + + if ($@) { + ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr)); + return; + } + + $dbh->disconnect(); + + if ($count && $count > 0) { + return 1; + } else { + return 0; + } } # Called from ag_reports_parse sub getNumPages { + my $args = shift; - my $args = shift; - my $db = (); + my $db = (); my $numPages = 0; - my $count = 0; - my $type = undef; + my $count = 0; + my $type = undef; my $eventRep = "/var/log/apparmor/reports/events.rpt"; - # Figure out whether we want db count or file parse - if ( $args->{'type'} ) { - if ( $args->{'type'} eq 'sir' || $args->{'type'} eq 'ess-multi' ) { - $type = 'db'; - } elsif ( $args->{'type'} eq 'ess') { - return 1; # ess reports have one page by definition - } else { - $type = 'arch'; # archived or file - } - } + # Figure out whether we want db count or file parse + if ($args->{'type'}) { + if ($args->{'type'} eq 'sir' || $args->{'type'} eq 'ess-multi') { + $type = 'db'; + } elsif ($args->{'type'} eq 'ess') { + return 1; # ess reports have one page by definition + } else { + $type = 'arch'; # archived or file + } + } # Parse sdmode & mode labels - if ( $args->{'sdmode'} ) { + if ($args->{'sdmode'}) { $args->{'sdmode'} =~ s/\&//g; $args->{'sdmode'} =~ s/\://g; $args->{'sdmode'} =~ s/\s//g; @@ -306,170 +309,169 @@ sub getNumPages { if ($args->{'sdmode'} eq "All") { delete($args->{'sdmode'}); - } + } } - if ( $args->{'mode'} ) { + if ($args->{'mode'}) { $args->{'mode'} =~ s/\&//g; $args->{'mode'} =~ s/Mode\://g; $args->{'mode'} =~ s/\s//g; if ($args->{'mode'} eq "All") { delete($args->{'mode'}); - } + } } - ######################################## + ######################################## - $args = rewriteModes($args); + $args = rewriteModes($args); - if ( $type && $type eq 'db' ) { + if ($type && $type eq 'db') { - my $start = undef; my $end = undef; + my $start = undef; + my $end = undef; - if ( $args->{'startTime'} && $args->{'startTime'} > 0 ) { - $start = $args->{'startTime'}; - } + if ($args->{'startTime'} && $args->{'startTime'} > 0) { + $start = $args->{'startTime'}; + } - if ( $args->{'endTime'} && $args->{'endTime'} > 0 ) { - $end = $args->{'endTime'}; - } + if ($args->{'endTime'} && $args->{'endTime'} > 0) { + $end = $args->{'endTime'}; + } - my $query = "SELECT count(*) FROM events "; + my $query = "SELECT count(*) FROM events "; - # We need filter information for getting a correct count + # We need filter information for getting a correct count #my $filts = getSirFilters($args); # these should be sent from YaST - my $filts = undef; + my $filts = undef; - if ( $args->{'prog'} ) { $filts->{'prog'} = $args->{'prog'}; } - if ( $args->{'profile'} ) { $filts->{'profile'} = $args->{'profile'}; } - if ( $args->{'pid'} ) { $filts->{'pid'} = $args->{'pid'}; } - if ( $args->{'resource'} ) { $filts->{'resource'} = $args->{'resource'}; } - if ( $args->{'severity'} ) { $filts->{'severity'} = $args->{'severity'}; } - if ( $args->{'sdmode'} ) { $filts->{'sdmode'} = $args->{'sdmode'}; } - if ( $args->{'mode'} ) { $filts->{'mode'} = $args->{'mode'}; } + if ($args->{'prog'}) { $filts->{'prog'} = $args->{'prog'}; } + if ($args->{'profile'}) { $filts->{'profile'} = $args->{'profile'}; } + if ($args->{'pid'}) { $filts->{'pid'} = $args->{'pid'}; } + if ($args->{'resource'}) { $filts->{'resource'} = $args->{'resource'}; } + if ($args->{'severity'}) { $filts->{'severity'} = $args->{'severity'}; } + if ($args->{'sdmode'}) { $filts->{'sdmode'} = $args->{'sdmode'}; } + if ($args->{'mode'}) { $filts->{'mode'} = $args->{'mode'}; } - for(sort(keys(%$filts))) { - if ( $filts->{$_} eq '-' || $filts->{$_} eq 'All' ) { - delete( $filts->{$_}); - } - } + for (sort(keys(%$filts))) { + if ($filts->{$_} eq '-' || $filts->{$_} eq 'All') { + delete($filts->{$_}); + } + } - my $midQuery = getQueryFilters($filts,$start,$end); + my $midQuery = getQueryFilters($filts, $start, $end); - $query .= "$midQuery"; + $query .= "$midQuery"; - # Pull stuff from db - my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1}); + # Pull stuff from db + my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 }); - eval { - my $sth = $dbh->prepare($query); - $sth->execute; - $count = $sth->fetchrow_array(); + eval { + my $sth = $dbh->prepare($query); + $sth->execute; + $count = $sth->fetchrow_array(); - $sth->finish; - }; + $sth->finish; + }; - if ( $@ ) { + if ($@) { ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr)); - return; - } + return; + } - $dbh->disconnect(); + $dbh->disconnect(); - #ycp::y2milestone("Numpages Query: $query"); # debug + #ycp::y2milestone("Numpages Query: $query"); # debug - $numPages = pageRound($count/$numEvents); - if ( $numPages < 1 ) { $numPages = 1; } + $numPages = pageRound($count / $numEvents); + if ($numPages < 1) { $numPages = 1; } - } elsif ( $type && $type eq 'arch' ) { + } elsif ($type && $type eq 'arch') { - if ( open(REP, "<$eventRep") ) { + if (open(REP, "<$eventRep")) { - while() { - if (/^Page/) { - $numPages++; - } else { - $count++; - } - } + while () { + if (/^Page/) { + $numPages++; + } else { + $count++; + } + } - close REP; + close REP; - } else { + } else { ycp::y2error(sprintf(gettext("Couldn't open file: %s."), $eventRep)); - } + } - } else { + } else { ycp::y2error(gettext("No type value passed. Unable to determine page count.")); - return("1"); - } + return ("1"); + } - if ( $numPages < 1 ) { $numPages = 1; } + if ($numPages < 1) { $numPages = 1; } - my $numCheck = int($count/$numEvents); + my $numCheck = int($count / $numEvents); - if ($numPages < $numCheck) { - $numPages = $numCheck; - } + if ($numPages < $numCheck) { + $numPages = $numCheck; + } - return($numPages); + return ($numPages); } sub getEpochFromNum { - my $date = shift; - my $place = shift || undef; # Used to set default $sec if undef + my $place = shift || undef; # Used to set default $sec if undef - my ($numMonth,$numDay,$time,$year) = split(/\s+/, $date); - my ($hour,$min,$sec) = '0'; - my $junk = undef; + my ($numMonth, $numDay, $time, $year) = split(/\s+/, $date); + my ($hour, $min, $sec) = '0'; + my $junk = undef; - if ($time =~ /:/) { - ($hour,$min,$sec,$junk) = split(/\:/, $time); - if (! $hour || $hour eq "" ) { $hour = '0'; } - if (! $min || $min eq "" ) { $min = '0'; } - if (! $sec || $sec eq "" ) { - if ($place eq 'end') { - $sec = '59'; - } else { - $sec = '0'; - } - } - } + if ($time =~ /:/) { + ($hour, $min, $sec, $junk) = split(/\:/, $time); + if (!$hour || $hour eq "") { $hour = '0'; } + if (!$min || $min eq "") { $min = '0'; } + if (!$sec || $sec eq "") { + if ($place eq 'end') { + $sec = '59'; + } else { + $sec = '0'; + } + } + } $numMonth--; # Months start from 0 for epoch translation - if (! $year) { $year = (split(/\s+/, localtime))[4]; } - my $epochDate = timelocal($sec,$min,$hour,$numDay,$numMonth,$year); + if (!$year) { $year = (split(/\s+/, localtime))[4]; } + my $epochDate = timelocal($sec, $min, $hour, $numDay, $numMonth, $year); return $epochDate; } sub getEpochFromStr { - my $lexDate = shift; my ($lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate); - #my ($lexDay, $lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate); - my ($hour,$min,$sec) = split(/\:/, $fullTime); - if (! $year) { $year = (split(/\s+/, localtime))[4]; } + #my ($lexDay, $lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate); + my ($hour, $min, $sec) = split(/\:/, $fullTime); + + if (!$year) { $year = (split(/\s+/, localtime))[4]; } my $numMonth = month2Num($lexMonth); - my $epochDate = timelocal($sec,$min,$hour,$dateDay,$numMonth,$year); + my $epochDate = timelocal($sec, $min, $hour, $dateDay, $numMonth, $year); return $epochDate; } # Replaces old files with new files sub updateFiles { + my ($oldFile, $newFile) = @_; - my ( $oldFile, $newFile ) = @_; - - if ( unlink("$oldFile") ) { - if ( ! rename ("$newFile", "$oldFile") ) { - if ( ! system('/bin/mv', "$newFile","$oldFile") ) { + if (unlink("$oldFile")) { + if (!rename("$newFile", "$oldFile")) { + if (!system('/bin/mv', "$newFile", "$oldFile")) { ycp::y2error(sprintf(gettext("Failed copying %s."), $oldFile)); return 1; } @@ -485,186 +487,194 @@ sub updateFiles { # This is a holder, that was originally part of exportLog() # Used by /usr/bin/reportgen.pl sub exportFormattedText { + my ($repName, $logFile, $db) = @_; - my ($repName,$logFile,$db) = @_; + my $date = localtime; + open(LOG, ">$logFile") || die "Couldn't open $logFile"; - my $date = localtime; - open (LOG, ">$logFile") || die "Couldn't open $logFile"; + # Date Profile PID Mesg + print LOG "$repName: Log generated by Novell AppArmor, $date\n\n"; + printf LOG "%-21s%-32s%-8s%-51s", "Host", "Date", "Program", "Profile", "PID", "Severity", "Mode", "Detail", "Access Type"; + print LOG "\n"; - # Date Profile PID Mesg - print LOG "$repName: Log generated by Novell AppArmor, $date\n\n"; - printf LOG "%-21s%-32s%-8s%-51s", "Host","Date","Program","Profile","PID","Severity","Mode","Detail","Access Type"; - print LOG "\n"; + for (sort (@$db)) { + print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},"; + print LOG "$_->{'pid'},$_->{'severity'},$->{'mode'},$_->{'resource'},$_->{'sdmode'}\n"; + } - for (sort (@$db) ) { - print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},"; - print LOG "$_->{'pid'},$_->{'severity'},$->{'mode'},$_->{'resource'},$_->{'sdmode'}\n"; - } - - - close LOG; + close LOG; } sub exportLog { + my ($exportLog, $db, $header) = @_; - my ($exportLog,$db,$header) = @_; + if (open(LOG, ">$exportLog")) { - if ( open (LOG, ">$exportLog") ) { + my $date = localtime(); - my $date = localtime(); + if ($exportLog =~ /csv/) { - if ($exportLog =~ /csv/ ) { + # $header comes from reportgen.pl (scheduled reports) + if ($header) { print LOG "$header\n\n"; } - # $header comes from reportgen.pl (scheduled reports) - if ($header) { print LOG "$header\n\n"; } + for (@$db) { + no strict; - for (@$db) { - no strict; +# host time prog profile pid severity resource sdmode mode +#print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},$_->{'pid'},"; + print LOG "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},"; + print LOG "$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n"; - # host time prog profile pid severity resource sdmode mode - #print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},$_->{'pid'},"; - print LOG "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},"; - print LOG "$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n"; + } - } + } elsif ($exportLog =~ /html/) { - } elsif ( $exportLog =~ /html/ ) { + print LOG "\n\n"; + print LOG "\n"; - print LOG "\n\n"; - print LOG "\n"; + # $header comes from reportgen.pl (scheduled reports) + if ($header) { + print LOG "$header\n\n"; + } else { + print LOG "

$exportLog


\n

Log generated by Novell AppArmor, $date

\n\n"; + } - # $header comes from reportgen.pl (scheduled reports) - if ($header) { - print LOG "$header\n\n"; - } else { - print LOG "

$exportLog


\n

Log generated by Novell AppArmor, $date

\n\n"; - } + print LOG "

\n"; - print LOG "

\n"; - #print LOG "\n"; - print LOG "" . - "\n"; +#print LOG "\n"; + print LOG "" + . "\n"; - my $idx = 1; + my $idx = 1; - for (@$db) { - no strict; - $idx++; - if ( $idx%2 == 0 ) { + for (@$db) { + no strict; + $idx++; + if ($idx % 2 == 0) { - #"" . - print LOG "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "\n"; + #"" . + print LOG "" + . "" + . "" + . "" + . "" + . "" + . "" + . "" + . "\n"; - } else { - # Shade every other row - print LOG "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "\n"; + } else { - } - } + # Shade every other row + print LOG "" + . "" + . "" + . "" + . "" + . "" + . "" + . "" + . "\n"; - print LOG "
DateProfilePIDMessage
HostDateProgramProfilePIDSeverityModeDetailAccess Type
DateProfilePIDMessage
HostDateProgramProfilePIDSeverityModeDetailAccess Type
 $_->{'time'} 
 $_->{'host'}  $_->{'date'}  $_->{'prog'}  $_->{'profile'}  $_->{'pid'}  $_->{'severity'}  $_->{'mode'}  $_->{'resource'}  $_->{'sdmode'} 
 $_->{'time'} 
 $_->{'host'}  $_->{'date'}  $_->{'prog'}  $_->{'profile'}  $_->{'pid'}  $_->{'severity'}  $_->{'mode'}  $_->{'resource'}  $_->{'sdmode'} 
 $_->{'host'}  $_->{'date'}  $_->{'prog'}  $_->{'profile'}  $_->{'pid'}  $_->{'severity'}  $_->{'mode'}  $_->{'resource'}  $_->{'sdmode'} 
 $_->{'host'}  $_->{'date'}  $_->{'prog'}  $_->{'profile'}  $_->{'pid'}  $_->{'severity'}  $_->{'mode'}  $_->{'resource'}  $_->{'sdmode'} 
\n\n"; - } + } + } + + print LOG "
\n\n"; + } close LOG; } else { ycp::y2error(sprintf(gettext("Export Log Error: Couldn't open %s"), $exportLog)); } - # return($error); + + # return($error); } # Pulls info on single report from apparmor xml file sub getXmlReport { + my ($repName, $repConf) = @_; - my ($repName,$repConf) = @_; - my $repFlag = 0; + my $repFlag = 0; + my %rep = (); - my %rep = (); + if (defined($repName) && ref($repName)) { - if ( defined($repName) && ref($repName) ) { - - if ( $repName->{'base'} ) { + if ($repName->{'base'}) { $repName = $repName->{'base'}; - } elsif ( $repName->{'name'} ) { + } elsif ($repName->{'name'}) { $repName = $repName->{'name'}; } - } - if ( ! $repName ) { + if (!$repName) { ycp::y2error(gettext("Fatal error. No report name given. Exiting.")); } - if ( ! $repConf || ! -e $repConf ) { - $repConf = '/etc/apparmor/reports.conf'; - if ( ! -e $repConf ) { - ycp::y2error(sprintf(gettext("Unable to get configuration info for %s. - Unable to find %s."), $repName, $repConf)); - exit 1; - } - } + if (!$repConf || !-e $repConf) { + $repConf = '/etc/apparmor/reports.conf'; + if (!-e $repConf) { + ycp::y2error( + sprintf( + gettext( + "Unable to get configuration info for %s. + Unable to find %s." + ), + $repName, + $repConf + ) + ); + exit 1; + } + } - if ( open(XML, "<$repConf") ) { + if (open(XML, "<$repConf")) { - while() { + while () { - chomp; + chomp; - if ( /\/ ) { - #my $name = (split(/\"/, $_))[1]; - /\(.+)\<\/name\>/; - my $name = $1; - if ( $name eq $repName ) { - $rep{'name'} = $name; - $repFlag = 1; - } + if (/\/) { - } elsif ( /\<\/report\>/ ) { + #my $name = (split(/\"/, $_))[1]; + /\(.+)\<\/name\>/; + my $name = $1; + if ($name eq $repName) { + $rep{'name'} = $name; + $repFlag = 1; + } - $repFlag = 0; + } elsif (/\<\/report\>/) { + + $repFlag = 0; + + } elsif ($repFlag == 1) { + if (/\s*\<\w+\s+(.*)\/\>.*$/) { + my $attrs = $1; + chomp($attrs); + my @attrlist = split(/\s+/, $attrs); + for (@attrlist) { - } elsif ( $repFlag == 1 ) { - if ( /\s*\<\w+\s+(.*)\/\>.*$/ ) { - my $attrs = $1; - chomp($attrs); - my @attrlist = split(/\s+/, $attrs); - for ( @attrlist ) { #Match attributes - if ( /\s*(\S+)=\"(\S+)\"/ ) { - $rep{$1} = $2 unless $2 eq '-'; - } - } - } elsif ( /\<(\w+)\>([\w+|\/].*)\<\// ) { + if (/\s*(\S+)=\"(\S+)\"/) { + $rep{$1} = $2 unless $2 eq '-'; + } + } + } elsif (/\<(\w+)\>([\w+|\/].*)\<\//) { - if ($1) { - $rep{"$1"}= $2 unless $2 eq '-'; - } else { - ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_)); - } - } - } + if ($1) { + $rep{"$1"} = $2 unless $2 eq '-'; + } else { + ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_)); + } + } + } } close XML; } else { - ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s."), $repConf)); - exit 1; + ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s."), $repConf)); + exit 1; } return \%rep; @@ -673,19 +683,19 @@ sub getXmlReport { # Returns info on currently confined processes sub getCfInfo { - my $ref = (); + my $ref = (); my @cfDb = (); my $cfApp = '/usr/sbin/unconfined'; - if ( open (CF, "$cfApp |") ) { + if (open(CF, "$cfApp |")) { my $host = `hostname`; chomp($host); my $date = localtime; - while() { + while () { my $ref = (); my $all = undef; @@ -700,13 +710,13 @@ sub getCfInfo { $ref->{'state'} =~ s/not\s+/not-/g; ($ref->{'prof'}, $ref->{'type'}) = split(/\s+/, $_); - if ( $ref->{'prog'} eq "") { $ref->{'prog'} = "-"; } - if ( $ref->{'prof'} eq "") { $ref->{'prof'} = "-"; } - if ( $ref->{'pid'} eq "") { $ref->{'pid'} = "-"; } - if ( $ref->{'state'} eq "") { $ref->{'state'} = "-"; } - if ( $ref->{'type'} eq "") { $ref->{'type'} = "-"; } + if ($ref->{'prog'} eq "") { $ref->{'prog'} = "-"; } + if ($ref->{'prof'} eq "") { $ref->{'prof'} = "-"; } + if ($ref->{'pid'} eq "") { $ref->{'pid'} = "-"; } + if ($ref->{'state'} eq "") { $ref->{'state'} = "-"; } + if ($ref->{'type'} eq "") { $ref->{'type'} = "-"; } - push (@cfDb, $ref); + push(@cfDb, $ref); } close CF; @@ -721,37 +731,36 @@ sub getCfInfo { # generate stats for ESS reports sub getEssStats { - - my $args = shift; + my $args = shift; #my ($host, $targetDir, $startdate, $enddate) = @_; - my @hostDb = (); - my @hostList = (); - my $targetDir = undef; - my $host = undef; - my $startdate = undef; - my $enddate = undef; + my @hostDb = (); + my @hostList = (); + my $targetDir = undef; + my $host = undef; + my $startdate = undef; + my $enddate = undef; - if ( ! $args->{'targetDir'} ) { + if (!$args->{'targetDir'}) { $targetDir = '/var/log/apparmor/'; } - if ( $args->{'host'} ) { $host = $args->{'host'}; } + if ($args->{'host'}) { $host = $args->{'host'}; } - if ( $args->{'startdate'} ) { - $startdate = $args->{'startdate'}; - } else { - $startdate = '1104566401'; # Jan 1, 2005 - } + if ($args->{'startdate'}) { + $startdate = $args->{'startdate'}; + } else { + $startdate = '1104566401'; # Jan 1, 2005 + } - if ( $args->{'enddate'} ) { - $enddate = $args->{'enddate'}; - } else { - $enddate = time; - } + if ($args->{'enddate'}) { + $enddate = $args->{'enddate'}; + } else { + $enddate = time; + } - if ( ! -e $targetDir ) { + if (!-e $targetDir) { ycp::y2error(sprintf(gettext("Fatal Error. No directory, %s, found. Exiting."), $targetDir)); return; } @@ -759,13 +768,14 @@ sub getEssStats { # Max Sev, Ave. Sev, Num. Rejects, Start Time, End Time my $ctQuery = "SELECT count(*) FROM events WHERE time >= $startdate AND time <= $enddate"; - my $query = "SELECT MAX(severity), AVG(severity), COUNT(id), MIN(time), " . - "MAX(time) FROM events WHERE sdmode='REJECTING' AND " . - "time >= $startdate AND time <= $enddate"; + my $query = "SELECT MAX(severity), AVG(severity), COUNT(id), MIN(time), " + . "MAX(time) FROM events WHERE sdmode='REJECTING' AND " + . "time >= $startdate AND time <= $enddate"; + # "MAX(time) FROM events join info WHERE sdmode='REJECTING' AND " . # Get list of hosts to scan - if ( opendir (TDIR, $targetDir) ) { + if (opendir(TDIR, $targetDir)) { @hostList = grep(/\.db/, readdir(TDIR)); close TDIR; @@ -780,25 +790,26 @@ sub getEssStats { $eventDb = "$targetDir/$eventDb"; - my $ess = undef; - my $ret = undef; + my $ess = undef; + my $ret = undef; my $count = undef; + #my $eventDb = '/var/log/apparmor/events.db'; - my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1}); + my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 }); # get hostname - my $host = undef; + my $host = undef; my $hostQuery = "SELECT * FROM info WHERE name='host'"; eval { my $sth = $dbh->prepare($hostQuery); $sth->execute; - $host = $sth->fetchrow_array(); + $host = $sth->fetchrow_array(); $sth->finish; }; - if ( $@ ) { + if ($@) { ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr)); return; } @@ -807,21 +818,19 @@ sub getEssStats { eval { my $sth = $dbh->prepare($ctQuery); $sth->execute; - $count = $sth->fetchrow_array(); + $count = $sth->fetchrow_array(); $sth->finish; }; - if ( $@ ) { + if ($@) { ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr)); return; } - # Get rest of stats - eval { - $ret = $dbh->selectall_arrayref("$query"); - }; + # Get rest of stats + eval { $ret = $dbh->selectall_arrayref("$query"); }; - if ( $@ ) { + if ($@) { ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr)); return; } @@ -829,328 +838,347 @@ sub getEssStats { $dbh->disconnect(); # hostIp, startDate, endDate, sevHi, sevMean, numRejects - if ( $host ) { - $ess->{'host'} = $host; + if ($host) { + $ess->{'host'} = $host; } else { $ess->{'host'} = ''; } - $ess->{'sevHi'} = $$ret[0]->[0]; + $ess->{'sevHi'} = $$ret[0]->[0]; - if ( ! $ess->{'sevHi'} ) { + if (!$ess->{'sevHi'}) { $ess->{'sevHi'} = 0; } - $ess->{'sevMean'} = $$ret[0]->[1]; + $ess->{'sevMean'} = $$ret[0]->[1]; - if ( ! $ess->{'sevMean'} || $ess->{'sevHi'} == 0) { + if (!$ess->{'sevMean'} || $ess->{'sevHi'} == 0) { $ess->{'sevMean'} = 0; } else { - $ess->{'sevMean'} = round("$ess->{'sevMean'}"); + $ess->{'sevMean'} = round("$ess->{'sevMean'}"); } - $ess->{'numRejects'} = $$ret[0]->[2]; - $ess->{'startdate'} = $$ret[0]->[3]; - $ess->{'enddate'} = $$ret[0]->[4]; - $ess->{'numEvents'} = $count; + $ess->{'numRejects'} = $$ret[0]->[2]; + $ess->{'startdate'} = $$ret[0]->[3]; + $ess->{'enddate'} = $$ret[0]->[4]; + $ess->{'numEvents'} = $count; # Convert dates - if ( $ess->{'startdate'} && $ess->{'startdate'} !~ /:/) { + if ($ess->{'startdate'} && $ess->{'startdate'} !~ /:/) { $ess->{'startdate'} = Immunix::Reports::getDate($ess->{'startdate'}); } - if ( $ess->{'enddate'} && $ess->{'enddate'} !~ /:/) { + if ($ess->{'enddate'} && $ess->{'enddate'} !~ /:/) { $ess->{'enddate'} = Immunix::Reports::getDate($ess->{'enddate'}); } - push (@hostDb, $ess); + push(@hostDb, $ess); } return \@hostDb; } - -# get ESS stats for archived reports (warning -- this can be slow for large files +# get ESS stats for archived reports (warning -- this can be slow for large files # debug -- not fully functional yet sub getArchEssStats { + my $args = shift; - my $args = shift; + my $prevTime = '0'; + my $prevDate = '0'; + my $startDate = '1104566401'; # Jan 1, 2005 + my $endDate = time; - my $prevTime = '0'; - my $prevDate = '0'; - my $startDate = '1104566401'; # Jan 1, 2005 - my $endDate = time; + if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; } + if ($args->{'enddate'}) { $endDate = $args->{'enddate'}; } - if ($args->{'startdate'} ) { $startDate = $args->{'startdate'}; } - if ($args->{'enddate'} ) { $endDate = $args->{'enddate'}; } + # hostIp, startDate, endDate, sevHi, sevMean, numRejects + my @eventDb = getEvents("$startDate", "$endDate"); - # hostIp, startDate, endDate, sevHi, sevMean, numRejects - my @eventDb = getEvents("$startDate","$endDate"); + my @hostIdx = (); # Simple index to all hosts for quick host matching + my @hostDb = (); # Host-keyed Data for doing REJECT stats - my @hostIdx = (); # Simple index to all hosts for quick host matching - my @hostDb = (); # Host-keyed Data for doing REJECT stats + # Outer Loop for Raw Event db + for (@eventDb) { - # Outer Loop for Raw Event db - for (@eventDb) { + if ($_->{'host'}) { - if ( $_->{'host'} ) { + my $ev = $_; # current event record - my $ev = $_; # current event record + # Create new host entry, or add to existing + if (grep(/$ev->{'host'}/, @hostIdx) == 1) { - # Create new host entry, or add to existing - if ( grep(/$ev->{'host'}/, @hostIdx) == 1 ) { + # Inner loop, but the number of hosts should be small + for (@hostDb) { - # Inner loop, but the number of hosts should be small - for (@hostDb) { + if ($_->{'host'} eq $ev->{'host'}) { - if ($_->{'host'} eq $ev->{'host'}) { + # Find earliest start date + if ($_->{'startdate'} > $ev->{'date'}) { + $_->{'startdate'} = $ev->{'date'}; + } - if ( $_->{'startdate'} > $ev->{'date'} ) { - $_->{'startdate'} = $ev->{'date'}; # Find earliest start date - } + # tally all events reported for host + $_->{'numEvents'}++; - $_->{'numEvents'}++; # tally all events reported for host + if ($ev->{'sdmode'}) { + if ($ev->{'sdmode'} =~ /PERMIT/) { + $_->{'numPermits'}++; + } + if ($ev->{'sdmode'} =~ /REJECT/) { + $_->{'numRejects'}++; + } + if ($ev->{'sdmode'} =~ /AUDIT/) { + $_->{'numAudits'}++; + } + } - if ( $ev->{'sdmode'} ) { - if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $_->{'numPermits'}++; } - if ( $ev->{'sdmode'} =~ /REJECT/ ) { $_->{'numRejects'}++; } - if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $_->{'numAudits'}++; } - } + # Add stats to host entry + #if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {} + if ($ev->{'severity'} && $ev->{'severity'} != -1) { - # Add stats to host entry - #if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {} - if ( $ev->{'severity'} && $ev->{'severity'} != -1 ) { + $_->{'sevNum'}++; + $_->{'sevTotal'} = $_->{'sevTotal'} + $ev->{'severity'}; - $_->{'sevNum'}++; - $_->{'sevTotal'} = $_->{'sevTotal'} + $ev->{'severity'}; + if ($ev->{'severity'} > $_->{'sevHi'}) { + $_->{'sevHi'} = $ev->{'severity'}; + } + } else { + $_->{'unknown'}++; + } + } + } - if ($ev->{'severity'} > $_->{'sevHi'} ) { - $_->{'sevHi'} = $ev->{'severity'}; - } - } else { - $_->{'unknown'}++; - } - } - } + } else { - } else { + # New host + my $rec = undef; + push(@hostIdx, $ev->{'host'}); # Add host entry to index - # New host - my $rec = undef; - push(@hostIdx,$ev->{'host'}); # Add host entry to index + $rec->{'host'} = $ev->{'host'}; + $rec->{'startdate'} = $startDate; - $rec->{'host'} = $ev->{'host'}; - $rec->{'startdate'} = $startDate; - #$rec->{'startdate'} = $ev->{'date'}; + #$rec->{'startdate'} = $ev->{'date'}; - if ( $endDate ) { - $rec->{'enddate'} = $endDate; - } else { - $rec->{'enddate'} = time; - } + if ($endDate) { + $rec->{'enddate'} = $endDate; + } else { + $rec->{'enddate'} = time; + } - # Add stats to host entry - if ( $ev->{'sev'} && $ev->{'sev'} ne "U" ) { - - $rec->{'sevHi'} = $ev->{'sev'}; - $rec->{'sevTotal'} = $ev->{'sev'}; - $rec->{'sevNum'} = 1; - $rec->{'unknown'} = 0; + # Add stats to host entry + if ($ev->{'sev'} && $ev->{'sev'} ne "U") { - } else { - $rec->{'sevHi'} = 0; - $rec->{'sevTotal'} = 0; - $rec->{'sevNum'} = 0; - $rec->{'unknown'} = 1; - } + $rec->{'sevHi'} = $ev->{'sev'}; + $rec->{'sevTotal'} = $ev->{'sev'}; + $rec->{'sevNum'} = 1; + $rec->{'unknown'} = 0; - # Start sdmode stats - $rec->{'numPermits'} = 0; - $rec->{'numRejects'} = 0; - $rec->{'numAudits'} = 0; - $rec->{'numEvents'} = 1; # tally all events reported for host + } else { - if ( $ev->{'sdmode'} ) { - if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $rec->{'numPermits'}++; } - if ( $ev->{'sdmode'} =~ /REJECT/ ) { $rec->{'numRejects'}++; } - if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $rec->{'numAudits'}++; } - } + $rec->{'sevHi'} = 0; + $rec->{'sevTotal'} = 0; + $rec->{'sevNum'} = 0; + $rec->{'unknown'} = 1; - push (@hostDb,$rec); # Add new records to host data list - } + } - } else { - next; # Missing host info -- big problem - } - } # END @eventDb loop + # Start sdmode stats + $rec->{'numPermits'} = 0; + $rec->{'numRejects'} = 0; + $rec->{'numAudits'} = 0; + $rec->{'numEvents'} = 1; # tally all events reported for host - # Process simple REJECT-related stats (for Executive Security Summaries) - for ( @hostDb ) { + if ($ev->{'sdmode'}) { + if ($ev->{'sdmode'} =~ /PERMIT/) { $rec->{'numPermits'}++; } + if ($ev->{'sdmode'} =~ /REJECT/) { $rec->{'numRejects'}++; } + if ($ev->{'sdmode'} =~ /AUDIT/) { $rec->{'numAudits'}++; } + } - # In the end, we want this info: - # - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity + push(@hostDb, $rec); # Add new records to host data list + } - if ( $_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0 ) { - $_->{'sevMean'} = round($_->{'sevTotal'}/$_->{'sevNum'}); - } else { - $_->{'sevMean'} = 0; - } + } else { + next; # Missing host info -- big problem + } + } # END @eventDb loop - # Convert dates - if ($_->{'startdate'} !~ /:/) {$_->{'startdate'} = getDate($startDate); } - if ($_->{'enddate'} !~ /:/) { $_->{'enddate'} = getDate($_->{'enddate'}); } + # Process simple REJECT-related stats (for Executive Security Summaries) + for (@hostDb) { - # Delete stuff that we may use in later versions (YaST is a silly, silly data handler) - delete($_->{'sevTotal'}); - delete($_->{'sevNum'}); - delete($_->{'numPermits'}); - delete($_->{'numAudits'}); - delete($_->{'unknown'}); +# In the end, we want this info: +# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity - } + if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) { + $_->{'sevMean'} = round($_->{'sevTotal'} / $_->{'sevNum'}); + } else { + $_->{'sevMean'} = 0; + } - return(\@hostDb); + # Convert dates + if ($_->{'startdate'} !~ /:/) { + $_->{'startdate'} = getDate($startDate); + } + if ($_->{'enddate'} !~ /:/) { + $_->{'enddate'} = getDate($_->{'enddate'}); + } + + # Delete stuff that we may use in later versions (YaST is a silly, + # silly data handler) + delete($_->{'sevTotal'}); + delete($_->{'sevNum'}); + delete($_->{'numPermits'}); + delete($_->{'numAudits'}); + delete($_->{'unknown'}); + + } + + return (\@hostDb); } # special version of getEvents() for /usr/bin/reportgen.pl sub grabEvents { + my ($rep, $start, $end) = @_; - my ($rep,$start,$end) = @_; - my $db = undef; - my $prevDate = "0"; - my $prevTime = "0"; + my $db = undef; + my $prevDate = "0"; + my $prevTime = "0"; - my $query = "SELECT * FROM events "; + my $query = "SELECT * FROM events "; # Clear unnecessary filters - if ( $rep->{'prog'} ) { $rep->{'prog'} =~ s/\s+//g; } - if ( $rep->{'prof'} ) { $rep->{'prof'} =~ s/\s+//g; } - if ( $rep->{'mode'} ) { $rep->{'mode'} =~ s/\s+//g; } - if ( $rep->{'sdmode'} ) { $rep->{'sdmode'} =~ s/\s+//g; } - if ( $rep->{'sev'} ) { $rep->{'sev'} =~ s/\s+//g; } - if ( $rep->{'res'} ) { $rep->{'res'} =~ s/\s+//g; } + if ($rep->{'prog'}) { $rep->{'prog'} =~ s/\s+//g; } + if ($rep->{'prof'}) { $rep->{'prof'} =~ s/\s+//g; } + if ($rep->{'mode'}) { $rep->{'mode'} =~ s/\s+//g; } + if ($rep->{'sdmode'}) { $rep->{'sdmode'} =~ s/\s+//g; } + if ($rep->{'sev'}) { $rep->{'sev'} =~ s/\s+//g; } + if ($rep->{'res'}) { $rep->{'res'} =~ s/\s+//g; } - if ($rep->{'prog'} && ($rep->{'prog'} eq "-" || $rep->{'prog'} eq "All") ) { + if ($rep->{'prog'} && ($rep->{'prog'} eq "-" || $rep->{'prog'} eq "All")) { delete($rep->{'prog'}); } if ($rep->{'prof'} && $rep->{'prof'} eq "-") { delete($rep->{'prof'}); } - if ($rep->{'pid'} && $rep->{'pid'} eq "-") { delete($rep->{'pid'}); } - if ( $rep->{'sev'} && ( $rep->{'sev'} eq "-" || $rep->{'sev'} eq "All" ) ) { - delete($rep->{'sev'}); - } - if ($rep->{'resource'} && $rep->{'resource'} eq "-") { delete($rep->{'resource'}); } + if ($rep->{'pid'} && $rep->{'pid'} eq "-") { delete($rep->{'pid'}); } + if ($rep->{'sev'} && ($rep->{'sev'} eq "-" || $rep->{'sev'} eq "All")) { + delete($rep->{'sev'}); + } + if ($rep->{'resource'} && $rep->{'resource'} eq "-") { + delete($rep->{'resource'}); + } - if ($rep->{'mode'} && ( $rep->{'mode'} eq "-" || $rep->{'mode'} eq "All" ) ) { + if ($rep->{'mode'} && ($rep->{'mode'} eq "-" || $rep->{'mode'} eq "All")) { delete($rep->{'mode'}); } - if ($rep->{'sdmode'} && ($rep->{'sdmode'} eq "-" || $rep->{'sdmode'} eq "All") ) { + if ($rep->{'sdmode'} + && ($rep->{'sdmode'} eq "-" || $rep->{'sdmode'} eq "All")) + { delete($rep->{'sdmode'}); } - $rep = rewriteModes($rep); + $rep = rewriteModes($rep); - # Set Dates far enough apart to get all entries (ie. no date filter) - my $startDate = '1104566401'; # Jan 1, 2005 - my $endDate = time; + # Set Dates far enough apart to get all entries (ie. no date filter) + my $startDate = '1104566401'; # Jan 1, 2005 + my $endDate = time; - if ( $start && $start > 0 ) { $startDate = $start; } + if ($start && $start > 0) { $startDate = $start; } - if ( ref($rep) ) { - my $midQuery = getQueryFilters($rep,$startDate,$endDate); + if (ref($rep)) { + my $midQuery = getQueryFilters($rep, $startDate, $endDate); $query .= "$midQuery"; } - $db = getEvents($query,"$startDate","$endDate"); + $db = getEvents($query, "$startDate", "$endDate"); - return($db); + return ($db); } sub getQueryFilters { + my ($filts, $start, $end) = @_; - my ($filts,$start,$end) = @_; + my $query = undef; + my $wFlag = 0; - my $query = undef; - my $wFlag = 0; - - if ( $filts ) { + if ($filts) { # Match any requested filters or drop record ############################################################ - if ( $filts->{'prog'} ) { - $query .= "WHERE events.prog = \'$filts->{'prog'}\' "; - $wFlag = 1; - } + if ($filts->{'prog'}) { + $query .= "WHERE events.prog = \'$filts->{'prog'}\' "; + $wFlag = 1; + } - if ( $filts->{'profile'} && $_->{'profile'} ) { - if ( $wFlag == 1 ) { - $query .= "AND events.profile = \'$filts->{'profile'}\' "; - } else { - $query .= "WHERE events.profile = \'$filts->{'profile'}\' "; - } - $wFlag = 1; - } + if ($filts->{'profile'} && $_->{'profile'}) { + if ($wFlag == 1) { + $query .= "AND events.profile = \'$filts->{'profile'}\' "; + } else { + $query .= "WHERE events.profile = \'$filts->{'profile'}\' "; + } + $wFlag = 1; + } - if ( $filts->{'pid'} ) { - if ( $wFlag == 1 ) { - $query .= "AND events.pid = \'$filts->{'pid'}\' "; - } else { - $query .= "WHERE events.pid = \'$filts->{'pid'}\' "; - } - $wFlag = 1; - } + if ($filts->{'pid'}) { + if ($wFlag == 1) { + $query .= "AND events.pid = \'$filts->{'pid'}\' "; + } else { + $query .= "WHERE events.pid = \'$filts->{'pid'}\' "; + } + $wFlag = 1; + } if ($filts->{'severity'}) { - if ($filts->{'severity'} eq "-" || $filts->{'severity'} eq "All" ) { + if ($filts->{'severity'} eq "-" || $filts->{'severity'} eq "All") { delete($filts->{'severity'}); - } elsif ( $filts->{'severity'} eq "-1" || $filts->{'severity'} eq "U" ) { - if ( $wFlag == 1 ) { - $query .= "AND events.severity = '-1' "; - } else { - $query .= "WHERE events.severity = '-1' "; - } - $wFlag = 1; + } elsif ($filts->{'severity'} eq "-1" + || $filts->{'severity'} eq "U") + { + if ($wFlag == 1) { + $query .= "AND events.severity = '-1' "; + } else { + $query .= "WHERE events.severity = '-1' "; + } + $wFlag = 1; } else { - if ( $wFlag == 1 ) { - $query .= "AND events.severity >= \'$filts->{'severity'}\' "; - } else { - $query .= "WHERE events.severity >= \'$filts->{'severity'}\' "; - } - $wFlag = 1; - } + if ($wFlag == 1) { + $query .= "AND events.severity >= \'$filts->{'severity'}\' "; + } else { + $query .= "WHERE events.severity >= \'$filts->{'severity'}\' "; + } + $wFlag = 1; + } } - if ( $filts->{'resource'} ) { - if ( $wFlag == 1 ) { - $query .= "AND events.resource LIKE '%$filts->{'resource'}%' "; - } else { - $query .= "WHERE events.resource LIKE '%$filts->{'resource'}%' "; - } - $wFlag = 1; + if ($filts->{'resource'}) { + if ($wFlag == 1) { + $query .= "AND events.resource LIKE '%$filts->{'resource'}%' "; + } else { + $query .= "WHERE events.resource LIKE '%$filts->{'resource'}%' "; + } + $wFlag = 1; } - if ( $filts->{'mode'} ) { - if ( $wFlag == 1 ) { - $query .= "AND events.mode LIKE '%$filts->{'mode'}%' "; - } else { - $query .= "WHERE events.mode LIKE '%$filts->{'mode'}%' "; - } - $wFlag = 1; + if ($filts->{'mode'}) { + if ($wFlag == 1) { + $query .= "AND events.mode LIKE '%$filts->{'mode'}%' "; + } else { + $query .= "WHERE events.mode LIKE '%$filts->{'mode'}%' "; + } + $wFlag = 1; } - if ( $filts->{'sdmode'} ) { + if ($filts->{'sdmode'}) { - if ( $filts->{'sdmode'} =~ /\|/ ) { + if ($filts->{'sdmode'} =~ /\|/) { my @sdmunge = split(/\|/, $filts->{'sdmode'}); for (@sdmunge) { $_ = "\'\%" . "$_" . "\%\'"; } - $filts->{'sdmode'} = join( " OR events.sdmode LIKE ", @sdmunge); + $filts->{'sdmode'} = join(" OR events.sdmode LIKE ", @sdmunge); } else { $filts->{'sdmode'} = "\'\%" . "$filts->{'sdmode'}" . "\%\'"; - } + } - if ( $wFlag == 1 ) { + if ($wFlag == 1) { $query .= "AND events.sdmode LIKE $filts->{'sdmode'} "; } else { $query .= "WHERE events.sdmode LIKE $filts->{'sdmode'} "; @@ -1158,78 +1186,78 @@ sub getQueryFilters { $wFlag = 1; } - } + } - if ( $start && $start =~ /\d+/ && $start > 0 ) { - if ( $wFlag == 1 ) { - $query .= "AND events.time >= $start "; - } else { - $query .= "WHERE events.time >= $start "; - } - $wFlag = 1; - } + if ($start && $start =~ /\d+/ && $start > 0) { + if ($wFlag == 1) { + $query .= "AND events.time >= $start "; + } else { + $query .= "WHERE events.time >= $start "; + } + $wFlag = 1; + } - if ( $end && $end =~ /\d+/ && $end > $start ) { - if ( $wFlag == 1 ) { - $query .= "AND events.time <= $end "; - } else { - $query .= "WHERE events.time <= $end "; - } - } + if ($end && $end =~ /\d+/ && $end > $start) { + if ($wFlag == 1) { + $query .= "AND events.time <= $end "; + } else { + $query .= "WHERE events.time <= $end "; + } + } - return $query; + return $query; } - sub getQuery { + my ($filts, $page, $sortKey, $numEvents) = @_; - my ($filts,$page,$sortKey,$numEvents) = @_; + if (!$page || $page < 1 || $page !~ /\d+/) { $page = 1; } + if (!$sortKey) { $sortKey = 'time'; } + if (!$numEvents) { $numEvents = '1000'; } - if ( ! $page || $page < 1 || $page !~ /\d+/ ) { $page = 1; } - if ( ! $sortKey ) { $sortKey = 'time'; } - if ( ! $numEvents ) { $numEvents = '1000'; } - - my $limit = ( ($page * $numEvents) - $numEvents ); + my $limit = (($page * $numEvents) - $numEvents); my $query = "SELECT * FROM events "; - if ( $filts ) { - my $midQuery = getQueryFilters($filts); - $query .= "$midQuery"; - } + if ($filts) { + my $midQuery = getQueryFilters($filts); + $query .= "$midQuery"; + } # Finish query $query .= "Order by $sortKey LIMIT $limit,$numEvents"; - return $query; + return $query; } -# - This should exec AFTER the initial select (should limit the number of records +# - This should exec AFTER the initial select (should limit the number of records # that we'll be mangling # - There may be a way to do this with a creative query statement generator sub queryPostProcess { + my $db = shift; - my $db = shift; - my @newDb = (); - my $prevTime = 0; - my $prevDate = 0; + my @newDb = (); + my $prevTime = 0; + my $prevDate = 0; - for (@$db) { + for (@$db) { - # Shuffle special events into appropriate column variables - ############################################################ - if ( $_->{'attrch'} ) { $_->{'sdmode'} .= " $_->{'attrch'}"; } + # Shuffle special events into appropriate column variables + ############################################################ + if ($_->{'attrch'}) { $_->{'sdmode'} .= " $_->{'attrch'}"; } - if ( $_->{'type'} ) { + if ($_->{'type'}) { + + if ($_->{'type'} eq 'control_variable') { - if ( $_->{'type'} eq 'control_variable' ) { # OWLSM gets special treatment - if ( $_->{'variable'} eq 'owlsm' ) { + if ($_->{'variable'} eq 'owlsm') { + #if ( $_->{'value'} ) {} - if ( $_->{'value'} == '0' ) { + if ($_->{'value'} == '0') { $_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM DISABLED"; - } elsif ( $_->{'value'} == '1' ) { + } elsif ($_->{'value'} == '1') { $_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM ENABLED"; } else { $_->{'resource'} = "Unrecognized OWLSM activity."; @@ -1237,81 +1265,84 @@ sub queryPostProcess { } else { $_->{'resource'} = "$_->{'variable'}"; } - } elsif ( $_->{'type'} eq 'capability' ) { + } elsif ($_->{'type'} eq 'capability') { $_->{'resource'} .= " $_->{'capability'}"; - } elsif ( $_->{'type'} eq 'attribute_change' ) { + } elsif ($_->{'type'} eq 'attribute_change') { $_->{'sdmode'} .= " $_->{'attribute'} change"; - } elsif ( $_->{'type'} eq 'subdomain_insmod' ) { + } elsif ($_->{'type'} eq 'subdomain_insmod') { $_->{'resource'} = "AppArmor Started"; - } elsif ( $_->{'type'} eq 'subdomain_rmmod' ) { + } elsif ($_->{'type'} eq 'subdomain_rmmod') { $_->{'resource'} = "AppArmor Stopped"; - # DROP logprof-hints - } elsif ( $_->{'type'} eq 'unknown_hat' ) { + + # DROP logprof-hints + } elsif ($_->{'type'} eq 'unknown_hat') { next; - # DROP logprof-hints - } elsif ( $_->{'type'} eq 'changing_profile' ) { + + # DROP logprof-hints + } elsif ($_->{'type'} eq 'changing_profile') { next; - # DROP logprof-hints - } elsif ( $_->{'type'} eq 'fork' ) { + + # DROP logprof-hints + } elsif ($_->{'type'} eq 'fork') { next; - } elsif ( $_->{'type'} ne 'path' ) { + } elsif ($_->{'type'} ne 'path') { $_->{'resource'} .= " $_->{'type'}"; } } # Convert Epoch Time to Date - if ( $_->{'time'} && $_->{'time'} == $prevTime ) { + if ($_->{'time'} && $_->{'time'} == $prevTime) { $_->{'date'} = $prevDate; } elsif ($_->{'time'}) { my $newDate = getDate("$_->{'time'}"); $_->{'date'} = $newDate; - $prevDate = $newDate; - $prevTime = $_->{'time'}; + $prevDate = $newDate; + $prevTime = $_->{'time'}; } else { $_->{'date'} = "0000-00-00 00:00:00"; } - # $_->{'time'} = undef; # Don't need 'time', only 'date' - if (! $_->{'host'}) { $_->{'host'} = "-"; } - if (! $_->{'date'}) { $_->{'date'} = "-"; } - if (! $_->{'prog'}) { $_->{'prog'} = "-"; } - if (! $_->{'profile'}) { $_->{'profile'} = "-"; } - if (! $_->{'pid'}) { $_->{'pid'} = "-"; } - if (! $_->{'mode'}) { $_->{'mode'} = "-"; } - if (! $_->{'resource'}) { $_->{'resource'} = "-"; } - if (! $_->{'sdmode'}) { $_->{'sdmode'} = "-"; } - if (! $_->{'severity'}) { + # $_->{'time'} = undef; # Don't need 'time', only 'date' + if (!$_->{'host'}) { $_->{'host'} = "-"; } + if (!$_->{'date'}) { $_->{'date'} = "-"; } + if (!$_->{'prog'}) { $_->{'prog'} = "-"; } + if (!$_->{'profile'}) { $_->{'profile'} = "-"; } + if (!$_->{'pid'}) { $_->{'pid'} = "-"; } + if (!$_->{'mode'}) { $_->{'mode'} = "-"; } + if (!$_->{'resource'}) { $_->{'resource'} = "-"; } + if (!$_->{'sdmode'}) { $_->{'sdmode'} = "-"; } + + if (!$_->{'severity'}) { $_->{'severity'} = "-"; } elsif ($_->{'severity'} eq "-1") { $_->{'severity'} = "U"; - }# else { - # $_->{'severity'} = sprintf("%02d", $_->{'severity'}); - #} + } # else { + # $_->{'severity'} = sprintf("%02d", $_->{'severity'}); + #} - push(@newDb, $_); # Don't quote the $_ (breaks hash) + push(@newDb, $_); # Don't quote the $_ (breaks hash) - } - - return \@newDb; + } + return \@newDb; } -# Creates single hashref for the various filters +# Creates single hashref for the various filters sub setFormFilters { + my $args = shift; - my $args = shift; my $filts = undef; - if ( $args ) { + if ($args) { - if ( $args->{'prog'} ) { $filts->{'prog'} = $args->{'prog'}; } - if ( $args->{'profile'} ) { $filts->{'profile'} = $args->{'profile'}; } - if ( $args->{'pid'} ) { $filts->{'pid'} = $args->{'pid'}; } - if ( $args->{'resource'} ) { $filts->{'resource'} = $args->{'resource'}; } - if ( $args->{'severity'} ) { $filts->{'severity'} = $args->{'severity'}; } - if ( $args->{'sdmode'} ) { $filts->{'sdmode'} = $args->{'sdmode'}; } - if ( $args->{'mode'} ) { $filts->{'mode'} = $args->{'mode'}; } + if ($args->{'prog'}) { $filts->{'prog'} = $args->{'prog'}; } + if ($args->{'profile'}) { $filts->{'profile'} = $args->{'profile'}; } + if ($args->{'pid'}) { $filts->{'pid'} = $args->{'pid'}; } + if ($args->{'resource'}) { $filts->{'resource'} = $args->{'resource'}; } + if ($args->{'severity'}) { $filts->{'severity'} = $args->{'severity'}; } + if ($args->{'sdmode'}) { $filts->{'sdmode'} = $args->{'sdmode'}; } + if ($args->{'mode'}) { $filts->{'mode'} = $args->{'mode'}; } } @@ -1319,117 +1350,133 @@ sub setFormFilters { } # helper for getSirFilters() -# Makes gui-centric filters querying-friendly +# Makes gui-centric filters querying-friendly sub rewriteFilters { - - my $filts = shift; + my $filts = shift; # Clear unnecessary filters - for (keys(%$filts)) { if ( $filts->{$_} eq "All" ) { delete($filts->{$_}); } } + for (keys(%$filts)) { + if ($filts->{$_} eq "All") { delete($filts->{$_}); } + } - if ($filts->{'prog'} && ($filts->{'prog'} eq "-" || $filts->{'prog'} eq "All") ) { + if ($filts->{'prog'} + && ($filts->{'prog'} eq "-" || $filts->{'prog'} eq "All")) + { delete($filts->{'prog'}); } - if ($filts->{'profile'} && ($filts->{'profile'} eq "-") ) { delete($filts->{'profile'}); } - if ($filts->{'pid'} && ($filts->{'pid'} eq "-") ) { delete($filts->{'pid'}); } - if ($filts->{'severity'} && ($filts->{'severity'} eq "-") ) { delete($filts->{'severity'}); } - if ($filts->{'resource'} && ($filts->{'resource'} eq "-") ) { delete($filts->{'resource'}); } + if ($filts->{'profile'} && ($filts->{'profile'} eq "-")) { + delete($filts->{'profile'}); + } + if ($filts->{'pid'} && ($filts->{'pid'} eq "-")) { + delete($filts->{'pid'}); + } + if ($filts->{'severity'} && ($filts->{'severity'} eq "-")) { + delete($filts->{'severity'}); + } + if ($filts->{'resource'} && ($filts->{'resource'} eq "-")) { + delete($filts->{'resource'}); + } - if ($filts->{'mode'} && ($filts->{'mode'} eq "-" || $filts->{'mode'} eq "All") ) { + if ($filts->{'mode'} + && ($filts->{'mode'} eq "-" || $filts->{'mode'} eq "All")) + { delete($filts->{'mode'}); } - if ($filts->{'sdmode'} && ($filts->{'sdmode'} eq "-" || $filts->{'sdmode'} eq "All") ) { + if ($filts->{'sdmode'} + && ($filts->{'sdmode'} eq "-" || $filts->{'sdmode'} eq "All")) + { delete($filts->{'sdmode'}); } ############################################################ - $filts = rewriteModes($filts); + $filts = rewriteModes($filts); - return $filts; + return $filts; } # returns ref to active filters for the specific SIR report sub getSirFilters { + my $args = shift; - my $args = shift; my $repName = undef; - if ( $args && $args->{'name'} ) { + if ($args && $args->{'name'}) { $repName = $args->{'name'}; } else { - $repName = "Security.Incident.Report"; - } + $repName = "Security.Incident.Report"; + } my $repConf = '/etc/apparmor/reports.conf'; - my $rec = undef; + my $rec = undef; - my $filts = getXmlReport($repName); + my $filts = getXmlReport($repName); # Clean hash of useless refs - for (sort keys(%$filts) ) { + for (sort keys(%$filts)) { if ($filts->{$_} eq "-") { delete($filts->{$_}); } } - # remove non-filter info - if ( $filts->{'name'} ) { delete( $filts->{'name'}); } - if ( $filts->{'exportpath'} ) { delete( $filts->{'exportpath'}); } - if ( $filts->{'exporttype'} ) { delete( $filts->{'exporttype'}); } - if ( $filts->{'addr1'} ) { delete( $filts->{'addr1'}); } - if ( $filts->{'addr2'} ) { delete( $filts->{'addr2'}); } - if ( $filts->{'addr3'} ) { delete( $filts->{'addr3'}); } - if ( $filts->{'time'} ) { delete( $filts->{'time'}); } + # remove non-filter info + if ($filts->{'name'}) { delete($filts->{'name'}); } + if ($filts->{'exportpath'}) { delete($filts->{'exportpath'}); } + if ($filts->{'exporttype'}) { delete($filts->{'exporttype'}); } + if ($filts->{'addr1'}) { delete($filts->{'addr1'}); } + if ($filts->{'addr2'}) { delete($filts->{'addr2'}); } + if ($filts->{'addr3'}) { delete($filts->{'addr3'}); } + if ($filts->{'time'}) { delete($filts->{'time'}); } - if ( ! $args->{'gui'} || $args->{'gui'} ne "1" ) { - $filts = rewriteModes($filts); + if (!$args->{'gui'} || $args->{'gui'} ne "1") { + $filts = rewriteModes($filts); $filts = rewriteFilters($filts); } - return $filts; + return $filts; } # deprecated (pre-xml) sub OldgetSirFilters { + my $args = shift; - my $args = shift; - my $repName = undef; + my $repName = undef; - if ( $args && $args->{'name'} ) { - $repName = $args->{'name'}; - } + if ($args && $args->{'name'}) { + $repName = $args->{'name'}; + } my $repConf = '/etc/apparmor/reports.conf'; - my $rec = undef; + my $rec = undef; - if (! $repName) { + if (!$repName) { $repName = "\"Security.Incident.Report\""; } else { $repName = "\"$repName\""; } - if ( open(CF, "<$repConf") ) { + if (open(CF, "<$repConf")) { while () { next if /^#/; chomp; my ($cfRptName) = (split(/:/, $_))[0]; - $cfRptName =~ s/\s+$//; # remove trailing spaces + $cfRptName =~ s/\s+$//; # remove trailing spaces next unless ($cfRptName eq "$repName"); - # Name : csv.html : prog, prof, pid, res, sev, sdmode, mode : (up to 3) email addresses : last run time - my ($name,$info) = split(/:/, $_, 2); +# Name : csv.html : prog, prof, pid, res, sev, sdmode, mode : (up to 3) email addresses : last run time + my ($name, $info) = split(/:/, $_, 2); $info =~ s/\s+//g; $name =~ s/^\s+//; $name =~ s/\s+$//; - my ($mailtype, $filters, $email, $lastRun) = split(/\s*:\s*/, $info, 4); + my ($mailtype, $filters, $email, $lastRun) = + split(/\s*:\s*/, $info, 4); $rec->{'name'} = $name; $rec->{'name'} =~ s/\"//g; - ($rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'resource'}, - $rec->{'severity'}, $rec->{'sdmode'}, $rec->{'mode'}) = split(/\,/, $filters, 7); + ($rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'resource'}, $rec->{'severity'}, $rec->{'sdmode'}, $rec->{'mode'}) = + split(/\,/, $filters, 7); } @@ -1440,87 +1487,86 @@ sub OldgetSirFilters { } # Clean hash of useless refs - for (sort keys(%$rec) ) { + for (sort keys(%$rec)) { if ($rec->{$_} eq "-") { delete($rec->{$_}); } } - $rec = rewriteModes($rec); + $rec = rewriteModes($rec); - if ( ! $args->{'gui'} || $args->{'gui'} ne "1" ) { - $rec = rewriteFilters($rec); - } + if (!$args->{'gui'} || $args->{'gui'} ne "1") { + $rec = rewriteFilters($rec); + } return $rec; } # Main SIR report generator sub getEvents { + my ($query, $start, $end, $dbFile) = @_; - my ($query, $start, $end, $dbFile) = @_; - my @events = (); - my $prevTime = 0; - my $prevDate = '0'; + my @events = (); + my $prevTime = 0; + my $prevDate = '0'; - if ( ! $query || $query !~ /^SELECT/ ) { $query = "SELECT * FROM events"; } - if ( $dbFile && -f $dbFile ) { $eventDb = $dbFile; } + if (!$query || $query !~ /^SELECT/) { $query = "SELECT * FROM events"; } + if ($dbFile && -f $dbFile) { $eventDb = $dbFile; } - my $hostName = `/bin/hostname` || 'unknown'; - chomp $hostName unless $hostName eq 'unknown'; + my $hostName = `/bin/hostname` || 'unknown'; + chomp $hostName unless $hostName eq 'unknown'; - if ( ! $start) { $start = '1104566401'; } # Give default start of 1/1/2005 - if ( ! $end) { $end = time; } + if (!$start) { $start = '1104566401'; } # Give default start of 1/1/2005 + if (!$end) { $end = time; } - # make sure they don't give us a bad range - ($start, $end) = ($end, $start) if $start > $end; + # make sure they don't give us a bad range + ($start, $end) = ($end, $start) if $start > $end; - # Events Schema - # - (id,time,counter,pid,sdmode,type,mode,resource,target,profile,prog,severity); +# Events Schema +# - (id,time,counter,pid,sdmode,type,mode,resource,target,profile,prog,severity); - # Pull stuff from db - my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1}); - my $all = undef; - eval { - $all = $dbh->selectall_arrayref("$query"); - }; + # Pull stuff from db + my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 }); + my $all = undef; + eval { $all = $dbh->selectall_arrayref("$query"); }; - if ( $@ ) { + if ($@) { ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr)); - return; - } + return; + } - $dbh->disconnect(); + $dbh->disconnect(); for my $row (@$all) { - my $rec = undef; - ($rec->{'id'}, $rec->{'time'}, $rec->{'counter'}, $rec->{'pid'}, $rec->{'sdmode'}, $rec->{'type'}, - $rec->{'mode'}, $rec->{'resource'}, $rec->{'target'}, $rec->{'profile'}, $rec->{'prog'}, $rec->{'severity'} ) = @$row; + my $rec = undef; + ($rec->{'id'}, $rec->{'time'}, $rec->{'counter'}, $rec->{'pid'}, $rec->{'sdmode'}, $rec->{'type'}, $rec->{'mode'}, $rec->{'resource'}, $rec->{'target'}, $rec->{'profile'}, $rec->{'prog'}, $rec->{'severity'}) = @$row; - # Give empty record values a default value - if ( ! $rec->{'host'} ) { $rec->{'host'} = $hostName; } - for (keys(%$rec)) { if ( !$rec->{$_} ) { $rec->{$_} = '-'; } } + # Give empty record values a default value + if (!$rec->{'host'}) { $rec->{'host'} = $hostName; } + for (keys(%$rec)) { + if (!$rec->{$_}) { $rec->{$_} = '-'; } + } - # Change 'time' to date - if ( $rec->{'time'} && $rec->{'time'} == $prevTime ) { + # Change 'time' to date + if ($rec->{'time'} && $rec->{'time'} == $prevTime) { $rec->{'date'} = $prevDate; - } elsif ( $rec->{'time'} ) { + } elsif ($rec->{'time'}) { my $newDate = getDate("$rec->{'time'}"); $rec->{'date'} = $newDate; - $prevDate = $newDate; - $prevTime = $rec->{'time'}; + $prevDate = $newDate; + $prevTime = $rec->{'time'}; } else { $rec->{'date'} = "0000-00-00-00:00:00"; } - if ( $rec->{'severity'} && $rec->{'severity'} eq '-1' ) { + if ($rec->{'severity'} && $rec->{'severity'} eq '-1') { $rec->{'severity'} = 'U'; } - delete($rec->{'time'}); - delete($rec->{'counter'}); + delete($rec->{'time'}); + delete($rec->{'counter'}); - push(@events, $rec); + push(@events, $rec); } return \@events; @@ -1529,93 +1575,93 @@ sub getEvents { # Archived Reports Stuff -- Some of this would go away in an ideal world ################################################################################ sub getArchReport { + my $args = shift; - my $args = shift; - my @rec = (); + my @rec = (); my $eventRep = "/var/log/apparmor/reports/events.rpt"; - #if ( $args->{'type'} && $args->{'type'} eq 'archRep' ) {} - if ( $args->{'logFile'} ) { - $eventRep = $args->{'logFile'}; - } + #if ( $args->{'type'} && $args->{'type'} eq 'archRep' ) {} + if ($args->{'logFile'}) { + $eventRep = $args->{'logFile'}; + } - if ( open(REP, "<$eventRep") ) { + if (open(REP, "<$eventRep")) { - my $page = 1; + my $page = 1; - if ( $args->{'page'} ) { $page = $args->{'page'}; } + if ($args->{'page'}) { $page = $args->{'page'}; } - my $id = 1; - my $slurp = 0; - #my $numPages = 0; + my $id = 1; + my $slurp = 0; - my $prevTime = undef; - my $prevDate = undef; + #my $numPages = 0; - while () { + my $prevTime = undef; + my $prevDate = undef; - my $db = (); + while () { - # Why not get rid of page and just do divide by $i later? - if (/Page/) { - # $numPages++; + my $db = (); + + # Why not get rid of page and just do divide by $i later? + if (/Page/) { + + # $numPages++; chomp; if ($_ eq "Page $page") { - $slurp = 1; - } else { - $slurp = 0; - } - } elsif ( $slurp == 1 ) { + $slurp = 1; + } else { + $slurp = 0; + } + } elsif ($slurp == 1) { - chomp; + chomp; - ($db->{'host'}, $db->{'time'}, $db->{'prog'}, $db->{'profile'}, $db->{'pid'}, $db->{'severity'}, - $db->{'mode'}, $db->{'denyRes'}, $db->{'sdmode'} ) = split(/\,/, $_); + ($db->{'host'}, $db->{'time'}, $db->{'prog'}, $db->{'profile'}, $db->{'pid'}, $db->{'severity'}, $db->{'mode'}, $db->{'denyRes'}, $db->{'sdmode'}) = split(/\,/, $_); - # Convert epoch time to date - if ($db->{'time'} == $prevTime) { - $db->{'date'} = $prevDate; - } else { - $prevTime = $db->{'time'}; - $prevDate = getDate("$db->{'time'}"); - $db->{'date'} = $prevDate; - } + # Convert epoch time to date + if ($db->{'time'} == $prevTime) { + $db->{'date'} = $prevDate; + } else { + $prevTime = $db->{'time'}; + $prevDate = getDate("$db->{'time'}"); + $db->{'date'} = $prevDate; + } - $id++; - $db->{'date'} = $db->{'time'}; - delete $db->{'time'}; - push(@rec, $db); - } - } + $id++; + $db->{'date'} = $db->{'time'}; + delete $db->{'time'}; + push(@rec, $db); + } + } + close REP; - close REP; - - } else { + } else { ycp::y2error(sprintf(gettext("Fatal Error. getArchReport() couldn't open %s"), $eventRep)); - return("Couldn't open $eventRep"); - } + return ("Couldn't open $eventRep"); + } - return(\@rec); + return (\@rec); } sub writeEventReport { + my ($db, $args) = @_; # Filters for date, && regexp +# my $type = shift || undef; - my ( $db, $args) = @_; # Filters for date, && regexp - #my $type = shift || undef; my $eventRep = "/var/log/apparmor/reports/events.rpt"; # Not sure if this is needed anymore, but it messes up archived SIR reports # if ( $args->{'logFile'} ) { $eventRep = $args->{'logFile'}; } - if ( open(REP, ">$eventRep") ) { + if (open(REP, ">$eventRep")) { - my $i = 1; + my $i = 1; my $page = 1; my $skip = 0; # Title for scheduled reports - if ( $args->{'title'} ) { print REP "$args->{'title'}"; } + if ($args->{'title'}) { print REP "$args->{'title'}"; } print REP "Page $page\n"; $page++; @@ -1624,7 +1670,7 @@ sub writeEventReport { print REP "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n"; - if ( ($i % $numEvents) == 0 && $skip == 0) { + if (($i % $numEvents) == 0 && $skip == 0) { print REP "Page $page\n"; $page++; $skip = 1; @@ -1638,63 +1684,63 @@ sub writeEventReport { close REP; } else { - return("Couldn't open $eventRep"); + return ("Couldn't open $eventRep"); } return 0; } sub prepSingleLog { - my $args = shift; - my $dir = '/var/log/apparmor/reports-archived'; - my $error = "0"; - my @errors = (); # For non-fatal errors - my @repList = (); - my $readFile = ""; - my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; # write summary to this file - changed 04-14-2005 - #my $eventRep = "/tmp/events.rpt"; # write summary to this file + my $dir = '/var/log/apparmor/reports-archived'; + my $error = "0"; + my @errors = (); # For non-fatal errors + my @repList = (); + my $readFile = ""; + my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; # write summary to this file - changed 04-14-2005 + #my $eventRep = "/tmp/events.rpt"; # write summary to this file - if ( $args->{'logFile'} ) { $readFile = $args->{'logFile'}; } - if ( $args->{'repPath'} ) { $dir = $args->{'repPath'}; } + if ($args->{'logFile'}) { $readFile = $args->{'logFile'}; } + if ($args->{'repPath'}) { $dir = $args->{'repPath'}; } - my @rawDb = (); - my $numPages = 1; + my @rawDb = (); + my $numPages = 1; my $numRecords = 1; - my $skip = 0; + my $skip = 0; # Open record compilation file - if ( open(RREP, "<$dir/$readFile") ) { + if (open(RREP, "<$dir/$readFile")) { + + if (open(WREP, ">$eventRep")) { - if ( open(WREP, ">$eventRep") ) { # print WREP "Page $numPages\n"; - $numPages++; + $numPages++; - while() { + while () { - next if (/Page/); - next if /^#/; + next if (/Page/); + next if /^#/; print WREP "$_"; - if ( ($numRecords % $numEvents) == 0 && $skip == 0) { + if (($numRecords % $numEvents) == 0 && $skip == 0) { print WREP "Page $numPages\n"; $numPages++; $skip = 1; } else { $numRecords++; $skip = 0; - } + } - } - close WREP; - } else { - $error = "Problem in prepSingleLog() - couldn't open $eventRep."; - return $error; - } + } + close WREP; + } else { + $error = "Problem in prepSingleLog() - couldn't open $eventRep."; + return $error; + } - close RREP; + close RREP; } else { $error = "Problem in prepSingleLog() - couldn't open -$dir/$readFile-."; @@ -1706,390 +1752,419 @@ sub prepSingleLog { # Cats files in specified directory for easier parsing sub prepArchivedLogs { + my $args = shift; - my $args = shift; + my $dir = '/var/log/apparmor/reports-archived'; + my $error = "0"; + my @errors = (); # For non-fatal errors + my @repList = (); + my @db = (); + my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; - my $dir = '/var/log/apparmor/reports-archived'; - my $error = "0"; - my @errors = (); # For non-fatal errors - my @repList = (); - my @db = (); - my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; + my $useFilters = 0; - my $useFilters = 0; + if ($args->{'logFile'}) { + $eventRep = $args->{'logFile'}; + } - if ( $args->{'logFile'} ) { - $eventRep = $args->{'logFile'}; - } + if ($args->{'repPath'}) { + $dir = $args->{'repPath'}; + } - if ( $args->{'repPath'} ) { - $dir = $args->{'repPath'}; - } - - # Check to see if we need to use filters - if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/) ) { + # Check to see if we need to use filters + if ($args->{'mode'} + && ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/)) + { delete($args->{'mode'}); } - if ( $args->{'sdmode'} && ( $args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/) ) { + if ($args->{'sdmode'} + && ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/)) + { delete($args->{'sdmode'}); } - if ( $args->{'resource'} && ( $args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/) ) { + if ($args->{'resource'} + && ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/)) + { delete($args->{'resource'}); } - if ( $args->{'sevLevel'} && ( $args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/) ) { + if ($args->{'sevLevel'} + && ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/)) + { delete($args->{'sevLevel'}); } - if ( $args->{'prog'} || $args->{'profile'} || $args->{'pid'} || $args->{'denyRes'} || - $args->{'mode'} || $args->{'sdmode'} || ($args->{'startdate'} && $args->{'enddate'} ) ) { + if ( $args->{'prog'} + || $args->{'profile'} + || $args->{'pid'} + || $args->{'denyRes'} + || $args->{'mode'} + || $args->{'sdmode'} + || ($args->{'startdate'} && $args->{'enddate'})) + { - $useFilters = 1; - } - ############################################################ + $useFilters = 1; + } + ############################################################ - - # Get list of files in archived report directory - if ( opendir (RDIR, $dir) ) { + # Get list of files in archived report directory + if (opendir(RDIR, $dir)) { my @firstPass = grep(/csv/, readdir(RDIR)); - @repList = grep(!/Applications.Audit|Executive.Security.Summary/, @firstPass); - close RDIR; + @repList = + grep(!/Applications.Audit|Executive.Security.Summary/, @firstPass); + close RDIR; - } else { - $error = "Failure in prepArchivedLogs() - couldn't open $dir."; - return($error); # debug - exit instead? - } + } else { + $error = "Failure in prepArchivedLogs() - couldn't open $dir."; + return ($error); # debug - exit instead? + } - my @rawDb = (); - my $numPages = 1; + my @rawDb = (); + my $numPages = 1; my $numRecords = 1; - # Open record compilation file - if ( open(AREP, ">$eventRep") ) { + # Open record compilation file + if (open(AREP, ">$eventRep")) { - for (@repList) { + for (@repList) { - my $file = $_; + my $file = $_; - # Cycle through each $file in $dir - if (open (RPT, "<$dir/$file") ) { - push(@rawDb, ); - close RPT; - } else { - $error = "Problem in prepArchivedLogs() - couldn't open $dir/$file."; - push(@errors, $error); - } - } + # Cycle through each $file in $dir + if (open(RPT, "<$dir/$file")) { + push(@rawDb, ); + close RPT; + } else { + $error = "Problem in prepArchivedLogs() - couldn't open $dir/$file."; + push(@errors, $error); + } + } - # sort & store cat'd files - if (@rawDb > 0 ) { + # sort & store cat'd files + if (@rawDb > 0) { - # Run Filters - if ( $useFilters == 1 ) { + # Run Filters + if ($useFilters == 1) { - my @tmpDb = parseMultiDb($args,@rawDb); - @db = sort(@tmpDb); + my @tmpDb = parseMultiDb($args, @rawDb); + @db = sort(@tmpDb); - } else { - @db = sort(@rawDb); - } + } else { + @db = sort(@rawDb); + } - my $skip = 0; - print AREP "Page $numPages\n"; - $numPages++; + my $skip = 0; + print AREP "Page $numPages\n"; + $numPages++; - for (@db) { + for (@db) { - next if /^Page/; - next if /^#/; + next if /^Page/; + next if /^#/; - print AREP "$_"; + print AREP "$_"; - if ( ($numRecords % $numEvents) == 0 && $skip == 0) { - print AREP "Page $numPages\n"; - $numPages++; - $skip = 1; - } else { - $numRecords++; - $skip = 0; - } - } + if (($numRecords % $numEvents) == 0 && $skip == 0) { + print AREP "Page $numPages\n"; + $numPages++; + $skip = 1; + } else { + $numRecords++; + $skip = 0; + } + } - } else { - $error = "DB created from $dir is empty."; - } + } else { + $error = "DB created from $dir is empty."; + } - close AREP; + close AREP; - } else { - $error = "Problem in prepArchivedLogs() - couldn't open $eventRep."; - push(@errors, $error); - } + } else { + $error = "Problem in prepArchivedLogs() - couldn't open $eventRep."; + push(@errors, $error); + } - return $error; + return $error; } # Similar to parseLog(), but expects @db to be passed sub parseMultiDb { + my ($args, @db) = @_; - my ($args,@db) = @_; - my @newDb = (); + my @newDb = (); - my $error = undef; + my $error = undef; my $startDate = undef; - my $endDate = undef; + my $endDate = undef; # deref dates for speed - if ($args->{'startdate'} && $args->{'enddate'} ) { - $startDate = getEpochFromNum("$args->{'startdate'}",'start'); - $endDate = getEpochFromNum("$args->{'enddate'}",'end'); + if ($args->{'startdate'} && $args->{'enddate'}) { + $startDate = getEpochFromNum("$args->{'startdate'}", 'start'); + $endDate = getEpochFromNum("$args->{'enddate'}", 'end'); } - $args = rewriteModes($args); + $args = rewriteModes($args); - for (@db) { + for (@db) { - my $rec = undef; - my $line = $_; + my $rec = undef; + my $line = $_; - next if /true|false/; # avoid horrible yast bug + next if /true|false/; # avoid horrible yast bug next if /^Page/; next if /^#/; chomp; - next if (! $_ || $_ eq ""); + next if (!$_ || $_ eq ""); # Lazy filters -- maybe these should be with the rest below - if ( $args->{'prog'} ) { next unless /$args->{'prog'}/; } - if ( $args->{'profile'} ) { next unless /$args->{'profile'}/; } + if ($args->{'prog'}) { next unless /$args->{'prog'}/; } + if ($args->{'profile'}) { next unless /$args->{'profile'}/; } # Need (epoch) 'time' element here, do we want to store 'date' instead? - ($rec->{'host'},$rec->{'time'},$rec->{'prog'},$rec->{'profile'}, - $rec->{'pid'},$rec->{'sevLevel'},$rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) - = split(/\,/, $_); + ($rec->{'host'}, $rec->{'time'}, $rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'sevLevel'}, $rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) = split(/\,/, $_); - # Make sure we get the time/date ref. name right. If it's $args->"time", - # the arg will be converted to a human-friendly "date" ref in writeEventReport(). - if ($rec->{'time'} =~ /\:|\-/ ) { +# Make sure we get the time/date ref. name right. If it's $args->"time", +# the arg will be converted to a human-friendly "date" ref in writeEventReport(). + if ($rec->{'time'} =~ /\:|\-/) { $rec->{'date'} = $rec->{'time'}; delete $rec->{'time'}; } # Check filters - if ( $args->{'pid'} && $args->{'pid'} ne '-' ) { + if ($args->{'pid'} && $args->{'pid'} ne '-') { next unless ($args->{'pid'} eq $rec->{'pid'}); } - if ( $args->{'sevLevel'} && $args->{'sevLevel'} ne "00" && $args->{'sevLevel'} ne '-' ) { - if ( $args->{'sevLevel'} eq "U" ) { $args->{'sevLevel'} = '-1'; } + if ( $args->{'sevLevel'} + && $args->{'sevLevel'} ne "00" + && $args->{'sevLevel'} ne '-') + { + if ($args->{'sevLevel'} eq "U") { $args->{'sevLevel'} = '-1'; } next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'}); } - if ( $args->{'mode'} && $args->{'mode'} ne '-' ) { + if ($args->{'mode'} && $args->{'mode'} ne '-') { next unless ($args->{'mode'} eq $rec->{'mode'}); } - if ( $args->{'denyRes'} && $args->{'denyRes'} ne '-' ) { + if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') { next unless ($args->{'denyRes'} eq $rec->{'denyRes'}); } - if ( $args->{'sdmode'} && $args->{'sdmode'} ne '-' ) { - # Needs reversal of comparison for sdmode - next unless ( $rec->{'sdmode'} =~ /$args->{'sdmode'}/ ); + if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') { + + # Needs reversal of comparison for sdmode + next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/); } push(@newDb, $line); } - return @newDb; + return @newDb; } -# Grab & filter events from archived reports (.csv files) +# Grab & filter events from archived reports (.csv files) sub parseLog { - my $args = shift; - my @db = (); + my @db = (); my $eventRep = "/var/log/apparmor/reports/events.rpt"; - if ( $args->{'logFile'} ) { - $eventRep = $args->{'logFile'}; - } - - #my $id = keys(%$db); - #my $rec = undef; - my $error = undef; - my $startDate = undef; - my $endDate = undef; - - # deref dates for speed - if ($args->{'startdate'} && $args->{'enddate'} ) { - $startDate = getEpochFromNum("$args->{'startdate'}",'start'); - $endDate = getEpochFromNum("$args->{'enddate'}",'end'); + if ($args->{'logFile'}) { + $eventRep = $args->{'logFile'}; } - #if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /\s*\-\s*/) ) {} - if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/) ) { - delete($args->{'mode'}); - } - if ( $args->{'sdmode'} && ( $args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/) ) { - delete($args->{'sdmode'}); - } - if ( $args->{'resource'} && ( $args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/) ) { - delete($args->{'resource'}); - } - if ( $args->{'sevLevel'} && ( $args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/) ) { - delete($args->{'sevLevel'}); - } + #my $id = keys(%$db); + #my $rec = undef; + my $error = undef; + my $startDate = undef; + my $endDate = undef; - $args = rewriteModes($args); + # deref dates for speed + if ($args->{'startdate'} && $args->{'enddate'}) { + $startDate = getEpochFromNum("$args->{'startdate'}", 'start'); + $endDate = getEpochFromNum("$args->{'enddate'}", 'end'); + } - if ( open (LOG, "<$eventRep") ) { +#if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /\s*\-\s*/) ) {} + if ($args->{'mode'} + && ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/)) + { + delete($args->{'mode'}); + } + if ($args->{'sdmode'} + && ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/)) + { + delete($args->{'sdmode'}); + } + if ($args->{'resource'} + && ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/)) + { + delete($args->{'resource'}); + } + if ($args->{'sevLevel'} + && ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/)) + { + delete($args->{'sevLevel'}); + } - # Log Parsing - while () { + $args = rewriteModes($args); - my $rec = undef; + if (open(LOG, "<$eventRep")) { - next if /true|false/; # avoid horrible yast bug - next if /Page/; - next if /^#/; - chomp; - next if (! $_ || $_ eq ""); + # Log Parsing + while () { - # Lazy filters -- maybe these should be with the rest below - if ( $args->{'prog'} ) { next unless /$args->{'prog'}/; } - if ( $args->{'profile'} ) { next unless /$args->{'profile'}/; } + my $rec = undef; - # Need (epoch) 'time' element here, do we want to store 'date' instead? - ($rec->{'host'},$rec->{'time'},$rec->{'prog'},$rec->{'profile'}, - $rec->{'pid'},$rec->{'sevLevel'},$rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) - = split(/\,/, $_); + next if /true|false/; # avoid horrible yast bug + next if /Page/; + next if /^#/; + chomp; + next if (!$_ || $_ eq ""); - # Make sure we get the time/date ref. name right. If it's $args->"time", - # the arg will be converted to a human-friendly "date" ref in writeEventReport(). - if ($rec->{'time'} =~ /\:|\-/ ) { - $rec->{'date'} = $rec->{'time'}; - delete $rec->{'time'}; + # Lazy filters -- maybe these should be with the rest below + if ($args->{'prog'}) { next unless /$args->{'prog'}/; } + if ($args->{'profile'}) { next unless /$args->{'profile'}/; } + + # Need (epoch) 'time' element here, do we want to store 'date' instead? + ($rec->{'host'}, $rec->{'time'}, $rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'sevLevel'}, $rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) = split(/\,/, $_); + +# Make sure we get the time/date ref. name right. If it's $args->"time", +# the arg will be converted to a human-friendly "date" ref in writeEventReport(). + if ($rec->{'time'} =~ /\:|\-/) { + $rec->{'date'} = $rec->{'time'}; + delete $rec->{'time'}; } - # Check filters - if ( $args->{'pid'} && $args->{'pid'} ne '-' ) { - next unless ($args->{'pid'} eq $rec->{'pid'}); - } - if ( $args->{'sevLevel'} && $args->{'sevLevel'} ne "00" && $args->{'sevLevel'} ne '-' ) { - next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'}); - } - if ( $args->{'mode'} && $args->{'mode'} ne '-' ) { - next unless ($args->{'mode'} eq $rec->{'mode'}); - } - if ( $args->{'denyRes'} && $args->{'denyRes'} ne '-' ) { - next unless ($args->{'denyRes'} eq $rec->{'denyRes'}); - } - if ( $args->{'sdmode'} && $args->{'sdmode'} ne '-' ) { - # Needs reversal of comparison for sdmode - next unless ( $rec->{'sdmode'} =~ /$args->{'sdmode'}/ ); - } + # Check filters + if ($args->{'pid'} && $args->{'pid'} ne '-') { + next unless ($args->{'pid'} eq $rec->{'pid'}); + } + if ( $args->{'sevLevel'} + && $args->{'sevLevel'} ne "00" + && $args->{'sevLevel'} ne '-') + { + next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'}); + } + if ($args->{'mode'} && $args->{'mode'} ne '-') { + next unless ($args->{'mode'} eq $rec->{'mode'}); + } + if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') { + next unless ($args->{'denyRes'} eq $rec->{'denyRes'}); + } + if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') { - push(@db, $rec); + # Needs reversal of comparison for sdmode + next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/); + } - } + push(@db, $rec); - close LOG; + } - # Export results to file if requested - if ( $args->{'exporttext'} || $args->{'exporthtml'} ) { + close LOG; - my $rawLog = undef; - my $expLog = undef; + # Export results to file if requested + if ($args->{'exporttext'} || $args->{'exporthtml'}) { - if ( $args->{'exportPath'} ) { - $rawLog = $args->{'exportPath'} . '/export-log'; - } else { - $rawLog = '/var/log/apparmor/reports-exported/export-log'; - } + my $rawLog = undef; + my $expLog = undef; - if ( $args->{'exporttext'} && $args->{'exporttext'} eq 'true') { - $expLog = "$rawLog.csv"; - exportLog($expLog,\@db); # redo w/ @$db instead of %db? - } + if ($args->{'exportPath'}) { + $rawLog = $args->{'exportPath'} . '/export-log'; + } else { + $rawLog = '/var/log/apparmor/reports-exported/export-log'; + } - if ( $args->{'exporthtml'} && $args->{'exporthtml'} eq 'true') { - $expLog = "$rawLog.html"; - exportLog($expLog,\@db); # redo w/ @$db instead of %db? - } - } + if ($args->{'exporttext'} && $args->{'exporttext'} eq 'true') { + $expLog = "$rawLog.csv"; + exportLog($expLog, \@db); # redo w/ @$db instead of %db? + } - # write out files to single sorted file (for state, and to speed up yast) - #if (! $args->{'single'} ) { - # $error = writeEventReport(\@db, $args); - #} + if ($args->{'exporthtml'} && $args->{'exporthtml'} eq 'true') { + $expLog = "$rawLog.html"; + exportLog($expLog, \@db); # redo w/ @$db instead of %db? + } + } - # changed 04-13-05 - should probably do this, regardless - $error = writeEventReport(\@db, $args); + # write out files to single sorted file (for state, and to speed up yast) + #if (! $args->{'single'} ) { + # $error = writeEventReport(\@db, $args); + #} - } else { - $error = "Couldn't open $eventRep."; - } + # changed 04-13-05 - should probably do this, regardless + $error = writeEventReport(\@db, $args); - return $error; + } else { + $error = "Couldn't open $eventRep."; + } + + return $error; } # OLD STUFF -- delete # deprecated -- replaced by better SQL queries sub OLDgetEssStats { - my $args = shift; - my $prevTime = '0'; - my $prevDate = '0'; - my $startDate = '1104566401'; # Jan 1, 2005 - my $endDate = time; + my $prevTime = '0'; + my $prevDate = '0'; + my $startDate = '1104566401'; # Jan 1, 2005 + my $endDate = time; - if ($args->{'startdate'} ) { $startDate = $args->{'startdate'}; } - if ($args->{'enddate'} ) { $endDate = $args->{'enddate'}; } + if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; } + if ($args->{'enddate'}) { $endDate = $args->{'enddate'}; } - my $query = "SELECT * FROM events"; + my $query = "SELECT * FROM events"; # hostIp, startDate, endDate, sevHi, sevMean, numRejects - my $eventDb = getEvents($query,"","$startDate","$endDate"); + my $eventDb = getEvents($query, "", "$startDate", "$endDate"); - my @hostIdx = (); # Simple index to all hosts for quick host matching - my @hostDb = (); # Host-keyed data for doing REJECT stats + my @hostIdx = (); # Simple index to all hosts for quick host matching + my @hostDb = (); # Host-keyed data for doing REJECT stats # Outer Loop for Raw Event db for (@$eventDb) { - my $ev = $_; # current event record + my $ev = $_; # current event record - if ( $ev->{'host'} ) { + if ($ev->{'host'}) { # Create new host entry, or add to existing - if ( grep(/$ev->{'host'}/, @hostIdx) == 1 ) { + if (grep(/$ev->{'host'}/, @hostIdx) == 1) { # Inner loop, but the number of hosts should be small for my $hdb (@hostDb) { if ($hdb->{'host'} eq $ev->{'host'}) { - if ( $hdb->{'startdate'} gt $ev->{'date'} ) { - $hdb->{'startdate'} = $ev->{'date'}; # Find earliest start date + if ($hdb->{'startdate'} gt $ev->{'date'}) { + $hdb->{'startdate'} = $ev->{'date'}; # Find earliest start date } - $hdb->{'numEvents'}++; # tally all events reported for host + $hdb->{'numEvents'}++; # tally all events reported for host - if ( $ev->{'sdmode'} ) { - if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $hdb->{'numPermits'}++; } - if ( $ev->{'sdmode'} =~ /REJECT/ ) { $hdb->{'numRejects'}++; } - if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $hdb->{'numAudits'}++; } + if ($ev->{'sdmode'}) { + if ($ev->{'sdmode'} =~ /PERMIT/) { + $hdb->{'numPermits'}++; + } + if ($ev->{'sdmode'} =~ /REJECT/) { + $hdb->{'numRejects'}++; + } + if ($ev->{'sdmode'} =~ /AUDIT/) { + $hdb->{'numAudits'}++; + } } # Add stats to host entry #if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {} - if ( $ev->{'severity'} && $ev->{'severity'} != -1 ) { + if ($ev->{'severity'} && $ev->{'severity'} != -1) { $hdb->{'sevNum'}++; - $hdb->{'sevTotal'} = $hdb->{'sevTotal'} + $ev->{'severity'}; + $hdb->{'sevTotal'} = $hdb->{'sevTotal'} + $ev->{'severity'}; - if ($ev->{'severity'} > $hdb->{'sevHi'} ) { + if ($ev->{'severity'} > $hdb->{'sevHi'}) { $hdb->{'sevHi'} = $ev->{'severity'}; } } else { @@ -2102,70 +2177,75 @@ sub OLDgetEssStats { # New host my $rec = undef; - push(@hostIdx,$ev->{'host'}); # Add host entry to index + push(@hostIdx, $ev->{'host'}); # Add host entry to index - $rec->{'host'} = $ev->{'host'}; + $rec->{'host'} = $ev->{'host'}; $rec->{'startdate'} = $startDate; + #$rec->{'startdate'} = $ev->{'date'}; - if ( $endDate ) { + if ($endDate) { $rec->{'enddate'} = $endDate; } else { $rec->{'enddate'} = time; } # Add stats to host entry - if ( $ev->{'sev'} && $ev->{'sev'} ne "U" ) { + if ($ev->{'sev'} && $ev->{'sev'} ne "U") { - $rec->{'sevHi'} = $ev->{'sev'}; + $rec->{'sevHi'} = $ev->{'sev'}; $rec->{'sevTotal'} = $ev->{'sev'}; - $rec->{'sevNum'} = 1; - $rec->{'unknown'} = 0; + $rec->{'sevNum'} = 1; + $rec->{'unknown'} = 0; } else { - $rec->{'sevHi'} = 0; + $rec->{'sevHi'} = 0; $rec->{'sevTotal'} = 0; - $rec->{'sevNum'} = 0; - $rec->{'unknown'} = 1; + $rec->{'sevNum'} = 0; + $rec->{'unknown'} = 1; } # Start sdmode stats $rec->{'numPermits'} = 0; $rec->{'numRejects'} = 0; $rec->{'numAudits'} = 0; - $rec->{'numEvents'} = 1; # tally all events reported for host + $rec->{'numEvents'} = 1; # tally all events reported for host - if ( $ev->{'sdmode'} ) { - if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $rec->{'numPermits'}++; } - if ( $ev->{'sdmode'} =~ /REJECT/ ) { $rec->{'numRejects'}++; } - if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $rec->{'numAudits'}++; } + if ($ev->{'sdmode'}) { + if ($ev->{'sdmode'} =~ /PERMIT/) { $rec->{'numPermits'}++; } + if ($ev->{'sdmode'} =~ /REJECT/) { $rec->{'numRejects'}++; } + if ($ev->{'sdmode'} =~ /AUDIT/) { $rec->{'numAudits'}++; } } - push (@hostDb,$rec); # Add new records to host data list + push(@hostDb, $rec); # Add new records to host data list } } else { - next; # Missing host info -- big problem + next; # Missing host info -- big problem } - } # END @eventDb loop + } # END @eventDb loop # Process simple REJECT-related stats (for Executive Security Summaries) - for ( @hostDb ) { + for (@hostDb) { - # In the end, we want this info: - # - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity +# In the end, we want this info: +# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity - if ( $_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0 ) { - $_->{'sevMean'} = Immunix::Reports::round($_->{'sevTotal'}/$_->{'sevNum'}); + if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) { + $_->{'sevMean'} = Immunix::Reports::round($_->{'sevTotal'} / $_->{'sevNum'}); } else { $_->{'sevMean'} = 0; } # Convert dates - if ($_->{'startdate'} !~ /:/) {$_->{'startdate'} = Immunix::Reports::getDate($startDate); } - if ($_->{'enddate'} !~ /:/) { $_->{'enddate'} = Immunix::Reports::getDate($_->{'enddate'}); } + if ($_->{'startdate'} !~ /:/) { + $_->{'startdate'} = Immunix::Reports::getDate($startDate); + } + if ($_->{'enddate'} !~ /:/) { + $_->{'enddate'} = Immunix::Reports::getDate($_->{'enddate'}); + } - # Delete stuff that we may use in later versions (YaST is a silly, silly data handler) +# Delete stuff that we may use in later versions (YaST is a silly, silly data handler) delete($_->{'sevTotal'}); delete($_->{'sevNum'}); delete($_->{'numPermits'}); @@ -2174,9 +2254,8 @@ sub OLDgetEssStats { } - return(\@hostDb); + return (\@hostDb); } - 1; diff --git a/utils/Severity.pm b/utils/Severity.pm index 1e43d28a9..af0035ab2 100644 --- a/utils/Severity.pm +++ b/utils/Severity.pm @@ -9,7 +9,6 @@ # # ------------------------------------------------------------------ - package Immunix::Severity; use strict; use Data::Dumper; @@ -17,67 +16,77 @@ use Data::Dumper; my ($debug) = 0; sub debug { - print @_ if $debug; + print @_ if $debug; } sub new { - my $self = {}; - $self->{DATABASENAME} = undef; - $self->{CAPABILITIES} = {}; - $self->{FILES} = {}; - $self->{REGEXPS} = {}; - $self->{DEFAULT_RANK} = 10; - bless($self); - shift; - $self->init(@_) if @_; - return $self; + my $self = {}; + $self->{DATABASENAME} = undef; + $self->{CAPABILITIES} = {}; + $self->{FILES} = {}; + $self->{REGEXPS} = {}; + $self->{DEFAULT_RANK} = 10; + bless($self); + shift; + $self->init(@_) if @_; + return $self; } sub init ($;$) { - my ($self, $resource, $read, $write, $execute, $severity); - $self = shift; - $self->{DATABASENAME} = shift; - $self->{DEFAULT_RANK} = shift if defined $_[0]; - open(DATABASE, $self->{DATABASENAME}) or die "Could not open severity db $self->{DATABASENAME}: $!\n"; - while () { - chomp(); - next if m/^\s*#/; - next if m/^\s*$/; - # leading whitespace is fine; maybe it shouldn't be? - if(/^\s*\/(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) { - my ($path, $read, $write, $execute) = ($1, $2, $3, $4); - - if(index($path, "*") == -1) { - - $self->{FILES}{$path} = { r => $read, w => $write, x => $execute }; - - } else { - - my $ptr = $self->{REGEXPS}; - my @pieces = split(/\//, $path); - - while(my $piece = shift @pieces) { - if(index($piece, "*") != -1) { - my $path = join("/", $piece, @pieces); - my $regexp = convert_regexp($path); - $ptr->{$regexp}{SD_RANK} = { r => $read, w => $write, x => $execute }; - last; - } else { - $ptr->{$piece} = { } unless exists $ptr->{$piece}; - $ptr = $ptr->{$piece}; - } + my ($self, $resource, $read, $write, $execute, $severity); + $self = shift; + $self->{DATABASENAME} = shift; + $self->{DEFAULT_RANK} = shift if defined $_[0]; + open(DATABASE, $self->{DATABASENAME}) + or die "Could not open severity db $self->{DATABASENAME}: $!\n"; + while () { + chomp(); + next if m/^\s*#/; + next if m/^\s*$/; + + # leading whitespace is fine; maybe it shouldn't be? + if (/^\s*\/(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) { + my ($path, $read, $write, $execute) = ($1, $2, $3, $4); + + if (index($path, "*") == -1) { + + $self->{FILES}{$path} = { + r => $read, + w => $write, + x => $execute + }; + + } else { + + my $ptr = $self->{REGEXPS}; + my @pieces = split(/\//, $path); + + while (my $piece = shift @pieces) { + if (index($piece, "*") != -1) { + my $path = join("/", $piece, @pieces); + my $regexp = convert_regexp($path); + $ptr->{$regexp}{SD_RANK} = { + r => $read, + w => $write, + x => $execute + }; + last; + } else { + $ptr->{$piece} = {} unless exists $ptr->{$piece}; + $ptr = $ptr->{$piece}; + } + } + } + } elsif (m|^\s*CAP|) { + ($resource, $severity) = split; + $self->{CAPABILITIES}{$resource} = $severity; + } else { + print "unexpected database line: $_\n"; } - } - } elsif (m|^\s*CAP|) { - ($resource, $severity) = split; - $self->{CAPABILITIES}{$resource}=$severity; - } else { - print "unexpected database line: $_\n"; } - } - close(DATABASE); - debug Dumper($self); - return $self; + close(DATABASE); + debug Dumper($self); + return $self; } #rank: @@ -87,7 +96,7 @@ sub init ($;$) { # handle capability # if the name is in the database, return it # otherwise, send a diagnostic message to stderr and return the default -# +# # handle file # initialize the current return value to 0 # loop over each entry in the database; @@ -96,110 +105,118 @@ sub init ($;$) { # otherwise, return the maximum from the database sub handle_capability ($) { - my ($self, $resource) = @_; + my ($self, $resource) = @_; - my $ret = $self->{CAPABILITIES}{$resource}; - if (!defined($ret)) { - return "unexpected capability rank input: $resource\n"; - } - return $ret; + my $ret = $self->{CAPABILITIES}{$resource}; + if (!defined($ret)) { + return "unexpected capability rank input: $resource\n"; + } + return $ret; } sub check_subtree { - my ($tree, $mode, $sev, $first, @rest) = @_; + my ($tree, $mode, $sev, $first, @rest) = @_; - # reassemble the remaining path from this directory level - my $path = join("/", $first, @rest); + # reassemble the remaining path from this directory level + my $path = join("/", $first, @rest); - # first check if we have a literal directory match to descend into - if($tree->{$first}) { - $sev = check_subtree($tree->{$first}, $mode, $sev, @rest); - } - - # if we didn't get a severity already, check for matching globs - unless($sev) { - - # check each glob at this directory level - for my $chunk (grep { index($_, "*") != -1 } keys %{$tree}) { - - # does it match the rest of our path? - if($path =~ /^$chunk$/) { - - # if we've got a ranking, check if it's higher than current one, if any - if($tree->{$chunk}->{SD_RANK}) { - for my $m (split(//, $mode)) { - if((! defined $sev) || $tree->{$chunk}->{SD_RANK}->{$m} > $sev) { - $sev = $tree->{$chunk}->{SD_RANK}->{$m}; - } - } - } - } + # first check if we have a literal directory match to descend into + if ($tree->{$first}) { + $sev = check_subtree($tree->{$first}, $mode, $sev, @rest); } - } - return $sev; + # if we didn't get a severity already, check for matching globs + unless ($sev) { + + # check each glob at this directory level + for my $chunk (grep { index($_, "*") != -1 } keys %{$tree}) { + + # does it match the rest of our path? + if ($path =~ /^$chunk$/) { + + # if we've got a ranking, check if it's higher than + # current one, if any + if ($tree->{$chunk}->{SD_RANK}) { + for my $m (split(//, $mode)) { + if ((!defined $sev) + || $tree->{$chunk}->{SD_RANK}->{$m} > $sev) + { + $sev = $tree->{$chunk}->{SD_RANK}->{$m}; + } + } + } + } + } + } + + return $sev; } - sub handle_file ($$) { - my ($self, $resource, $mode) = @_; + my ($self, $resource, $mode) = @_; - # strip off the initial / from the path we're checking - $resource = substr($resource, 1); + # strip off the initial / from the path we're checking + $resource = substr($resource, 1); - # break the path into directory-level chunks - my @pieces = split(/\//, $resource); + # break the path into directory-level chunks + my @pieces = split(/\//, $resource); - my $sev; + my $sev; - # if there's a exact match for this path in the db, use that instead of - # checking the globs - if($self->{FILES}{$resource}) { + # if there's a exact match for this path in the db, use that instead of + # checking the globs + if ($self->{FILES}{$resource}) { + + # check each piece of the passed mode against the db entry + for my $m (split(//, $mode)) { + if ((!defined $sev) || $self->{FILES}{$resource}{$m} > $sev) { + $sev = $self->{FILES}{$resource}{$m}; + } + } + + } else { + + # descend into the regexp tree looking for matches + $sev = check_subtree($self->{REGEXPS}, $mode, $sev, @pieces); - # check each piece of the passed mode against the db entry - for my $m (split(//, $mode)) { - if((! defined $sev) || $self->{FILES}{$resource}{$m} > $sev) { - $sev = $self->{FILES}{$resource}{$m}; - } } - } else { - - # descend into the regexp tree looking for matches - $sev = check_subtree($self->{REGEXPS}, $mode, $sev, @pieces); - - } - - return (defined $sev) ? $sev : $self->{DEFAULT_RANK}; + return (defined $sev) ? $sev : $self->{DEFAULT_RANK}; } - sub rank ($;$) { - my ($self, $resource, $mode) = @_; - if (substr($resource,0,1) eq "/") { - return $self->handle_file($resource, $mode); - } elsif (substr($resource,0,3) eq "CAP") { - return $self->handle_capability($resource); - } else { - return "unexpected rank input: $resource\n"; - } + my ($self, $resource, $mode) = @_; + + if (substr($resource, 0, 1) eq "/") { + return $self->handle_file($resource, $mode); + } elsif (substr($resource, 0, 3) eq "CAP") { + return $self->handle_capability($resource); + } else { + return "unexpected rank input: $resource\n"; + } } sub convert_regexp ($) { - my ($input) = shift; - # we need to convert subdomain regexps to perl regexps - my $regexp = $input; - # escape + . [ and ] characters - $regexp =~ s/(\+|\.|\[|\])/\\$1/g; - # convert ** globs to match anything - $regexp =~ s/\*\*/.SDPROF_INTERNAL_GLOB/g; - # convert * globs to match anything at current path level - $regexp =~ s/\*/[^\/]SDPROF_INTERNAL_GLOB/g; - # convert {foo,baz} to (foo|baz) - $regexp =~ y/\{\}\,/\(\)\|/ if $regexp =~ /\{.*\,.*\}/; - # twiddle the escaped * chars back - $regexp =~ s/SDPROF_INTERNAL_GLOB/\*/g; - return $regexp; + my ($input) = shift; + + # we need to convert subdomain regexps to perl regexps + my $regexp = $input; + + # escape + . [ and ] characters + $regexp =~ s/(\+|\.|\[|\])/\\$1/g; + + # convert ** globs to match anything + $regexp =~ s/\*\*/.SDPROF_INTERNAL_GLOB/g; + + # convert * globs to match anything at current path level + $regexp =~ s/\*/[^\/]SDPROF_INTERNAL_GLOB/g; + + # convert {foo,baz} to (foo|baz) + $regexp =~ y/\{\}\,/\(\)\|/ if $regexp =~ /\{.*\,.*\}/; + + # twiddle the escaped * chars back + $regexp =~ s/SDPROF_INTERNAL_GLOB/\*/g; + return $regexp; } -1; # so the require or use succeeds +1; # so the require or use succeeds diff --git a/utils/SubDomain.pm b/utils/SubDomain.pm index 33ee5a4c7..21b358961 100755 --- a/utils/SubDomain.pm +++ b/utils/SubDomain.pm @@ -36,14 +36,65 @@ use Immunix::Severity; require Exporter; our @ISA = qw(Exporter); -our @EXPORT = qw(%sd $filename $profiledir $parser %qualifiers %include %helpers $UI_Mode which getprofilefilename getprofileflags setprofileflags complain enforce autodep reload UI_GetString UI_GetFile UI_YesNo UI_Important UI_Info getkey do_logprof_pass readconfig loadincludes check_for_subdomain UI_PromptUser $running_under_genprof GetDataFromYast SendDataToYast setup_yast shutdown_yast readprofile readprofiles writeprofile get_full_path fatal_error checkProfileSyntax checkIncludeSyntax); +our @EXPORT = qw( + %sd + %qualifiers + %include + %helpers + + $filename + $profiledir + $parser + $UI_Mode + $running_under_genprof + + which + getprofilefilename + get_full_path + fatal_error + + getprofileflags + setprofileflags + complain + enforce + + autodep + reload + + UI_GetString + UI_GetFile + UI_YesNo + UI_Important + UI_Info + UI_PromptUser + + getkey + + do_logprof_pass + + readconfig + loadincludes + readprofile + readprofiles + writeprofile + + check_for_subdomain + + setup_yast + shutdown_yast + GetDataFromYast + SendDataToYast + + checkProfileSyntax + checkIncludeSyntax +); no warnings 'all'; our $confdir = "/etc/apparmor"; our $running_under_genprof = 0; -our $finishing = 0; +our $finishing = 0; our $DEBUGGING; @@ -57,24 +108,23 @@ our $sevdb; # initialize Term::ReadLine if it's available our $term; eval { - require Term::ReadLine; - import Term::ReadLine; - $term = new Term::ReadLine 'AppArmor'; + require Term::ReadLine; + import Term::ReadLine; + $term = new Term::ReadLine 'AppArmor'; }; - # initialize the local poo setlocale(LC_MESSAGES, ""); textdomain("apparmor-utils"); # where do we get our log messages from? our $filename; -if(-f "/var/log/audit/audit.log") { - $filename = "/var/log/audit/audit.log"; -} elsif(-f "/etc/slackware-version") { - $filename = "/var/log/syslog"; +if (-f "/var/log/audit/audit.log") { + $filename = "/var/log/audit/audit.log"; +} elsif (-f "/etc/slackware-version") { + $filename = "/var/log/syslog"; } else { - $filename = "/var/log/messages"; + $filename = "/var/log/messages"; } our $profiledir = "/etc/apparmor.d"; @@ -104,7 +154,7 @@ our @userglobs; ### THESE VARIABLES ARE USED WITHIN LOGPROF our %t; our %transitions; -our %sd; # we keep track of the original profiles in %sd +our %sd; # we keep track of the original profiles in %sd my %seen; my %profilechanges; @@ -112,774 +162,795 @@ my %prelog; my %log; my %changed; my %skip; -our %helpers; # we want to preserve this one between passes +our %helpers; # we want to preserve this one between passes -my %variables; # variables in config files +my %variables; # variables in config files ### THESE VARIABLES ARE USED WITHIN LOGPROF sub debug ($) { - my $message = shift; + my $message = shift; - print DEBUG "$message\n" if $DEBUGGING; + print DEBUG "$message\n" if $DEBUGGING; } BEGIN { - use POSIX qw(:termios_h); + use POSIX qw(:termios_h); - my ($term, $oterm, $echo, $noecho, $fd_stdin); + my ($term, $oterm, $echo, $noecho, $fd_stdin); - $fd_stdin = fileno(STDIN); + $fd_stdin = fileno(STDIN); - $term = POSIX::Termios->new(); - $term->getattr($fd_stdin); - $oterm = $term->getlflag(); + $term = POSIX::Termios->new(); + $term->getattr($fd_stdin); + $oterm = $term->getlflag(); - $echo = ECHO | ECHOK | ICANON; - $noecho = $oterm & ~$echo; + $echo = ECHO | ECHOK | ICANON; + $noecho = $oterm & ~$echo; - sub cbreak { - $term->setlflag($noecho); - $term->setcc(VTIME, 1); - $term->setattr($fd_stdin, TCSANOW); - } + sub cbreak { + $term->setlflag($noecho); + $term->setcc(VTIME, 1); + $term->setattr($fd_stdin, TCSANOW); + } - sub cooked { - $term->setlflag($oterm); - $term->setcc(VTIME, 0); - $term->setattr($fd_stdin, TCSANOW); - } + sub cooked { + $term->setlflag($oterm); + $term->setcc(VTIME, 0); + $term->setattr($fd_stdin, TCSANOW); + } - sub getkey { - my $key = ''; - cbreak(); - sysread(STDIN, $key, 1); - cooked(); - return $key; - } + sub getkey { + my $key = ''; + cbreak(); + sysread(STDIN, $key, 1); + cooked(); + return $key; + } - # set things up to log extra info if they want... - if($ENV{LOGPROF_DEBUG}) { - $DEBUGGING = 1; - open(DEBUG, ">/tmp/logprof_debug_$$.log"); - my $oldfd = select(DEBUG); - $| = 1; - select($oldfd); - } else { - $DEBUGGING = 0; - } + # set things up to log extra info if they want... + if ($ENV{LOGPROF_DEBUG}) { + $DEBUGGING = 1; + open(DEBUG, ">/tmp/logprof_debug_$$.log"); + my $oldfd = select(DEBUG); + $| = 1; + select($oldfd); + } else { + $DEBUGGING = 0; + } } END { - # reset the terminal state - cooked(); + # reset the terminal state + cooked(); - $DEBUGGING && debug "Exiting..."; + $DEBUGGING && debug "Exiting..."; - # close the debug log if necessary - close(DEBUG) if $DEBUGGING; + # close the debug log if necessary + close(DEBUG) if $DEBUGGING; } # returns true if the specified program contains references to LD_PRELOAD or # LD_LIBRARY_PATH to give the PX/UX code better suggestions sub check_for_LD_XXX ($) { - my $file = shift; + my $file = shift; - return undef unless -f $file; + return undef unless -f $file; - # limit our checking to programs/scripts under 10k to speed things up a bit - my $size = -s $file; - return undef unless ($size && $size < 10000); + # limit our checking to programs/scripts under 10k to speed things up a bit + my $size = -s $file; + return undef unless ($size && $size < 10000); - my $found = undef; - if(open(F, $file)) { - while() { - $found = 1 if /LD_(PRELOAD|LIBRARY_PATH)/; + my $found = undef; + if (open(F, $file)) { + while () { + $found = 1 if /LD_(PRELOAD|LIBRARY_PATH)/; + } + close(F); } - close(F); - } - return $found; + return $found; } sub fatal_error ($) { - my $message = shift; + my $message = shift; - my $details = "$message\n"; + my $details = "$message\n"; - if($DEBUGGING) { - # we'll include the stack backtrace if we're debugging... - $details = Carp::longmess($message); + if ($DEBUGGING) { - # write the error to the log - print DEBUG $details; - } + # we'll include the stack backtrace if we're debugging... + $details = Carp::longmess($message); - # we'll just shoot ourselves in the head if it was one of the yast - # interface functions that ran into an error. it gets really ugly if - # the yast frontend goes away and we try to notify the user of that - # problem by trying to send the yast frontend a pretty dialog box - my $caller = (caller(1))[3]; - exit 1 if $caller =~ /::(Send|Get)Data(To|From)Yast$/; + # write the error to the log + print DEBUG $details; + } - # tell the user what the hell happened - UI_Important($details); + # we'll just shoot ourselves in the head if it was one of the yast + # interface functions that ran into an error. it gets really ugly if + # the yast frontend goes away and we try to notify the user of that + # problem by trying to send the yast frontend a pretty dialog box + my $caller = (caller(1))[3]; + exit 1 if $caller =~ /::(Send|Get)Data(To|From)Yast$/; - # make sure the frontend exits cleanly... - shutdown_yast(); + # tell the user what the hell happened + UI_Important($details); - # die a horrible flaming death - exit 1; + # make sure the frontend exits cleanly... + shutdown_yast(); + + # die a horrible flaming death + exit 1; } sub setup_yast { - # set up the yast connection if we're running under yast... - if($ENV{YAST_IS_RUNNING}) { - # load the yast module if available. - eval { require ycp; }; - unless($@) { - import ycp; + # set up the yast connection if we're running under yast... + if ($ENV{YAST_IS_RUNNING}) { - no warnings 'all'; + # load the yast module if available. + eval { require ycp; }; + unless ($@) { + import ycp; - $UI_Mode = "yast"; + no warnings 'all'; - # let the frontend know that we're starting - SendDataToYast( { type => "initial_handshake", status => "backend_starting" } ); + $UI_Mode = "yast"; - # see if the frontend is just starting up also... - my ($ypath, $yarg) = GetDataFromYast(); - unless($yarg && - (ref($yarg) eq "HASH") && - ($yarg->{type} eq "initial_handshake") && - ($yarg->{status} eq "frontend_starting")) { + # let the frontend know that we're starting + SendDataToYast({ + type => "initial_handshake", + status => "backend_starting" + }); - # something's broken, die a horrible, painful death - fatal_error "Yast frontend is out of sync from backend agent."; - } + # see if the frontend is just starting up also... + my ($ypath, $yarg) = GetDataFromYast(); + unless ($yarg + && (ref($yarg) eq "HASH") + && ($yarg->{type} eq "initial_handshake") + && ($yarg->{status} eq "frontend_starting")) + { + + # something's broken, die a horrible, painful death + fatal_error "Yast frontend is out of sync from backend agent."; + } + + # the yast connection seems to be working okay + return 1; + } - # the yast connection seems to be working okay - return 1; } - } - - # couldn't init yast - return 0; + # couldn't init yast + return 0; } sub shutdown_yast { - if($UI_Mode eq "yast") { - SendDataToYast( { type => "final_shutdown" } ); - my ($ypath, $yarg) = GetDataFromYast(); - } + if ($UI_Mode eq "yast") { + SendDataToYast({ type => "final_shutdown" }); + my ($ypath, $yarg) = GetDataFromYast(); + } } sub check_for_subdomain () { - my ($support_subdomainfs, $support_securityfs); - if(open(MOUNTS, "/proc/filesystems")) { - while() { - $support_subdomainfs = 1 if m/subdomainfs/; - $support_securityfs = 1 if m/securityfs/; - } - close(MOUNTS); - } - - my $sd_mountpoint; - if(open(MOUNTS, "/proc/mounts")) { - while() { - if($support_subdomainfs) { - $sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/; - } elsif($support_securityfs) { - if ( m/^\S+\s+(\S+)\s+securityfs\s/ ) { - if ( -e "$1/apparmor" ) { - $sd_mountpoint = "$1/apparmor"; - } elsif ( -e "$1/subdomain" ) { - $sd_mountpoint = "$1/subdomain"; - } + my ($support_subdomainfs, $support_securityfs); + if (open(MOUNTS, "/proc/filesystems")) { + while () { + $support_subdomainfs = 1 if m/subdomainfs/; + $support_securityfs = 1 if m/securityfs/; } - } + close(MOUNTS); } - close(MOUNTS); - } - # make sure that subdomain is actually mounted there - $sd_mountpoint = undef unless -f "$sd_mountpoint/profiles"; + my $sd_mountpoint; + if (open(MOUNTS, "/proc/mounts")) { + while () { + if ($support_subdomainfs) { + $sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/; + } elsif ($support_securityfs) { + if (m/^\S+\s+(\S+)\s+securityfs\s/) { + if (-e "$1/apparmor") { + $sd_mountpoint = "$1/apparmor"; + } elsif (-e "$1/subdomain") { + $sd_mountpoint = "$1/subdomain"; + } + } + } + } + close(MOUNTS); + } - return $sd_mountpoint; + # make sure that subdomain is actually mounted there + $sd_mountpoint = undef unless -f "$sd_mountpoint/profiles"; + + return $sd_mountpoint; } sub which ($) { - my $file = shift; + my $file = shift; - foreach my $dir (split(/:/, $ENV{PATH})) { - return "$dir/$file" if -x "$dir/$file"; - } + foreach my $dir (split(/:/, $ENV{PATH})) { + return "$dir/$file" if -x "$dir/$file"; + } - return undef; + return undef; } # we need to convert subdomain regexps to perl regexps sub convert_regexp ($) { - my $regexp = shift; + my $regexp = shift; - # escape regexp-special characters we don't support - $regexp =~ s/(? 64) { - fatal_error "Followed too many symlinks resolving $originalpath"; + if ($linkcount++ > 64) { + fatal_error "Followed too many symlinks resolving $originalpath"; + } + + # split out the directory/file components + if ($path =~ m/^(.*)\/(.+)$/) { + my ($dir, $file) = ($1, $2); + + # figure out where the link is pointing... + my $link = readlink($path); + if ($link =~ /^\//) { + # if it's an absolute link, just replace it + $path = $link; + } else { + # if it's relative, let abs_path handle it + $path = $dir . "/$link"; + } + } } - # split out the directory/file components - if($path =~ m/^(.*)\/(.+)$/) { - my ($dir, $file) = ($1, $2); - - # figure out where the link is pointing... - my $link = readlink($path); - if($link =~ /^\//) { - $path = $link; # if it's an absolute link, just replace it - } else { - $path = $dir . "/$link"; # if it's relative, let abs_path handle it - } + if (-f $path) { + my ($dir, $file) = $path =~ m/^(.*)\/(.+)$/; + $path = realpath($dir) . "/$file"; + } else { + $path = realpath($path); } - } - if(-f $path) { - my ($dir, $file) = $path =~ m/^(.*)\/(.+)$/; - $path = realpath($dir) . "/$file"; - } else { - $path = realpath($path); - } - - return $path; + return $path; } sub findexecutable ($) { - my $bin = shift; + my $bin = shift; - my $fqdbin; - if(-e $bin) { - $fqdbin = get_full_path($bin); - chomp($fqdbin); - } else { - if($bin !~ /\//) { - my $which = which($bin); - if($which) { - $fqdbin = get_full_path($which); - } + my $fqdbin; + if (-e $bin) { + $fqdbin = get_full_path($bin); + chomp($fqdbin); + } else { + if ($bin !~ /\//) { + my $which = which($bin); + if ($which) { + $fqdbin = get_full_path($which); + } + } } - } - unless($fqdbin && -e $fqdbin) { - return undef; - } + unless ($fqdbin && -e $fqdbin) { + return undef; + } - return $fqdbin; + return $fqdbin; } sub complain ($) { - my $bin = shift; - my $fqdbin = findexecutable($bin) or fatal_error(sprintf(gettext('Can\'t find %s.'), $bin)); + my $bin = shift; + my $fqdbin = findexecutable($bin) + or fatal_error(sprintf(gettext('Can\'t find %s.'), $bin)); - # skip directories - return unless -f $fqdbin; + # skip directories + return unless -f $fqdbin; - UI_Info(sprintf(gettext('Setting %s to complain mode.'), $fqdbin)); + UI_Info(sprintf(gettext('Setting %s to complain mode.'), $fqdbin)); - my $filename = getprofilefilename($fqdbin); - setprofileflags($filename, "complain"); + my $filename = getprofilefilename($fqdbin); + setprofileflags($filename, "complain"); } sub enforce ($) { - my $bin = shift; + my $bin = shift; - my $fqdbin = findexecutable($bin) or fatal_error(sprintf(gettext('Can\'t find %s.'), $bin)); + my $fqdbin = findexecutable($bin) + or fatal_error(sprintf(gettext('Can\'t find %s.'), $bin)); - # skip directories - return unless -f $fqdbin; + # skip directories + return unless -f $fqdbin; - UI_Info(sprintf(gettext('Setting %s to enforce mode.'), $fqdbin)); + UI_Info(sprintf(gettext('Setting %s to enforce mode.'), $fqdbin)); - my $filename = getprofilefilename($fqdbin); - setprofileflags($filename, ""); + my $filename = getprofilefilename($fqdbin); + setprofileflags($filename, ""); } sub head ($) { - my $file = shift; + my $file = shift; - my $first = ""; - if(open(FILE, $file)) { - $first = ; - close(FILE); - } + my $first = ""; + if (open(FILE, $file)) { + $first = ; + close(FILE); + } - return $first; + return $first; } sub get_output (@) { - my ($program, @args) = @_; + my ($program, @args) = @_; - my $ret = -1; + my $ret = -1; - my $pid; - my @output; + my $pid; + my @output; - if(-x $program) { - $pid = open(KID_TO_READ, "-|"); - unless (defined $pid) { - fatal_error "can't fork: $!"; + if (-x $program) { + $pid = open(KID_TO_READ, "-|"); + unless (defined $pid) { + fatal_error "can't fork: $!"; + } + + if ($pid) { + while () { + chomp; + push @output, $_; + } + close(KID_TO_READ); + $ret = $?; + } else { + ($>, $)) = ($<, $(); + open(STDERR, ">&STDOUT") + || fatal_error "can't dup stdout to stderr"; + exec($program, @args) || fatal_error "can't exec program: $!"; + + # NOTREACHED + } } - if ($pid) { - while () { - chomp; - push @output, $_; - } - close(KID_TO_READ); - $ret = $?; - } else { - ($>, $)) = ($<, $(); - open(STDERR, ">&STDOUT") || fatal_error "can't dup stdout to stderr"; - exec($program, @args) || fatal_error "can't exec program: $!"; - # NOTREACHED - } - } - - return ($ret, @output); + return ($ret, @output); } sub get_reqs ($) { - my $file = shift; + my $file = shift; - my @reqs; - my ($ret, @ldd) = get_output($ldd, $file); + my @reqs; + my ($ret, @ldd) = get_output($ldd, $file); - if($ret == 0) { - for my $line (@ldd) { - last if $line =~ /not a dynamic executable/; - last if $line =~ /cannot read header/; - last if $line =~ /statically linked/; + if ($ret == 0) { + for my $line (@ldd) { + last if $line =~ /not a dynamic executable/; + last if $line =~ /cannot read header/; + last if $line =~ /statically linked/; - next if $line =~ /linux-(gate|vdso(32|64)).so/; # avoid new kernel 2.6 poo + # avoid new kernel 2.6 poo + next if $line =~ /linux-(gate|vdso(32|64)).so/; - if($line =~ /^\s*\S+ => (\/\S+)/) { - push @reqs, $1; - } elsif ($line =~ /^\s*(\/\S+)/) { - push @reqs, $1; - } + if ($line =~ /^\s*\S+ => (\/\S+)/) { + push @reqs, $1; + } elsif ($line =~ /^\s*(\/\S+)/) { + push @reqs, $1; + } + } } - } - return @reqs; + return @reqs; } sub handle_binfmt ($$) { - my ($profile, $fqdbin) = @_; + my ($profile, $fqdbin) = @_; - my %reqs; - my @reqs = get_reqs($fqdbin); + my %reqs; + my @reqs = get_reqs($fqdbin); - while (my $library = shift @reqs) { + while (my $library = shift @reqs) { - $library = get_full_path($library); + $library = get_full_path($library); - push @reqs, get_reqs($library) unless $reqs{$library}++; + push @reqs, get_reqs($library) unless $reqs{$library}++; - # does path match anything pulled in by includes in original profile? - my $combinedmode = matchincludes($profile, $library); + # does path match anything pulled in by includes in original profile? + my $combinedmode = matchincludes($profile, $library); - # if we found any matching entries, do the modes match? - next if $combinedmode; + # if we found any matching entries, do the modes match? + next if $combinedmode; - $library = globcommon($library); - chomp $library; - next unless $library; + $library = globcommon($library); + chomp $library; + next unless $library; - $profile->{path}->{$library} = "mr"; - } + $profile->{path}->{$library} = "mr"; + } - return $profile; + return $profile; } sub autodep ($) { - my $bin = shift; + my $bin = shift; - # findexecutable() might fail if we're running on a different system - # than the logs were collected on. ugly. we'll just hope for the best. - my $fqdbin = findexecutable($bin) || $bin; + # findexecutable() might fail if we're running on a different system + # than the logs were collected on. ugly. we'll just hope for the best. + my $fqdbin = findexecutable($bin) || $bin; - # try to make sure we have a full path in case findexecutable failed - return unless $fqdbin =~ /^\//; + # try to make sure we have a full path in case findexecutable failed + return unless $fqdbin =~ /^\//; - # ignore directories - return if -d $fqdbin; + # ignore directories + return if -d $fqdbin; - my $profile = { flags => "complain", - include => { "abstractions/base" => 1 }, - path => { $fqdbin => "mr" } }; + my $profile = { + flags => "complain", + include => { "abstractions/base" => 1 }, + path => { $fqdbin => "mr" } + }; - # if the executable exists on this system, pull in extra dependencies - if(-f $fqdbin) { - my $hashbang = head($fqdbin); - if($hashbang =~ /^#!\s*(\S+)/) { - my $interpreter = get_full_path($1); - $profile->{path}->{$interpreter} = "ix"; - if($interpreter =~ /perl/) { - $profile->{include}->{"abstractions/perl"} = 1; - } elsif ($interpreter =~ m/\/bin\/(bash|sh)/) { - $profile->{include}->{"abstractions/bash"} = 1; - } - $profile = handle_binfmt($profile, $interpreter); - } else { - $profile = handle_binfmt($profile, $fqdbin); + # if the executable exists on this system, pull in extra dependencies + if (-f $fqdbin) { + my $hashbang = head($fqdbin); + if ($hashbang =~ /^#!\s*(\S+)/) { + my $interpreter = get_full_path($1); + $profile->{path}->{$interpreter} = "ix"; + if ($interpreter =~ /perl/) { + $profile->{include}->{"abstractions/perl"} = 1; + } elsif ($interpreter =~ m/\/bin\/(bash|sh)/) { + $profile->{include}->{"abstractions/bash"} = 1; + } + $profile = handle_binfmt($profile, $interpreter); + } else { + $profile = handle_binfmt($profile, $fqdbin); + } } - } - # stick the profile into our data structure. - $sd{$fqdbin}{$fqdbin} = $profile; + # stick the profile into our data structure. + $sd{$fqdbin}{$fqdbin} = $profile; - # instantiate the required infrastructure hats for this changehat application - for my $hatglob (keys %required_hats) { - if($fqdbin =~ /$hatglob/) { - for my $hat (split(/\s+/, $required_hats{$hatglob})) { - $sd{$fqdbin}{$hat} = { flags => "complain" }; - } + # instantiate the required infrastructure hats for this changehat app + for my $hatglob (keys %required_hats) { + if ($fqdbin =~ /$hatglob/) { + for my $hat (split(/\s+/, $required_hats{$hatglob})) { + $sd{$fqdbin}{$hat} = { flags => "complain" }; + } + } } - } - if (-f "$profiledir/tunables/global") { - my $file = getprofilefilename($fqdbin); - - unless (exists $variables{$file}) { - $variables{$file} = { }; - } - $variables{$file}{"#tunables/global"} = 1; # sorry - } + if (-f "$profiledir/tunables/global") { + my $file = getprofilefilename($fqdbin); - # write out the profile... - writeprofile($fqdbin); + unless (exists $variables{$file}) { + $variables{$file} = {}; + } + $variables{$file}{"#tunables/global"} = 1; # sorry + } + + # write out the profile... + writeprofile($fqdbin); } sub getprofilefilename ($) { - my $profile = shift; + my $profile = shift; - my $filename = $profile; - $filename =~ s/\///; # strip leading / - $filename =~ s/\//./g; # convert /'s to .'s + my $filename = $profile; + $filename =~ s/\///; # strip leading / + $filename =~ s/\//./g; # convert /'s to .'s - return "$profiledir/$filename"; + return "$profiledir/$filename"; } sub setprofileflags ($$) { - my $filename = shift; - my $newflags = shift; + my $filename = shift; + my $newflags = shift; - if(open(PROFILE, "$filename")) { - if(open(NEWPROFILE, ">$filename.new")) { - while() { - if(m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) { - my ($binary, $flags) = ($1, $2); + if (open(PROFILE, "$filename")) { + if (open(NEWPROFILE, ">$filename.new")) { + while () { + if (m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) { + my ($binary, $flags) = ($1, $2); - if($newflags) { - $_ = "$binary flags=($newflags) {\n"; - } else { - $_ = "$binary {\n"; - } - } elsif(m/^(\s*\^\S+)\s+(flags=\(.+\)\s+)*\{\s*$/) { - my ($hat, $flags) = ($1, $2); + if ($newflags) { + $_ = "$binary flags=($newflags) {\n"; + } else { + $_ = "$binary {\n"; + } + } elsif (m/^(\s*\^\S+)\s+(flags=\(.+\)\s+)*\{\s*$/) { + my ($hat, $flags) = ($1, $2); - if($newflags) { - $_ = "$hat flags=($newflags) {\n"; - } else { - $_ = "$hat {\n"; - } + if ($newflags) { + $_ = "$hat flags=($newflags) {\n"; + } else { + $_ = "$hat {\n"; + } + } + print NEWPROFILE; + } + close(NEWPROFILE); + rename("$filename.new", "$filename"); } - print NEWPROFILE; - } - close(NEWPROFILE); - rename("$filename.new", "$filename"); + close(PROFILE); } - close(PROFILE); - } } sub profile_exists($) { - my $program = shift || return 0; + my $program = shift || return 0; - # if it's already in the cache, return true - return 1 if $existing_profiles{$program}; + # if it's already in the cache, return true + return 1 if $existing_profiles{$program}; - # if the profile exists, mark it in the cache and return true - my $profile = getprofilefilename($program); - if(-e $profile) { - $existing_profiles{$program} = 1; - return 1 - } + # if the profile exists, mark it in the cache and return true + my $profile = getprofilefilename($program); + if (-e $profile) { + $existing_profiles{$program} = 1; + return 1; + } - # couldn't find a profile, so we'll return false - return 0; + # couldn't find a profile, so we'll return false + return 0; } ########################################################################## # Here are the console/yast interface functions sub UI_Info ($) { - my $text = shift; + my $text = shift; - $DEBUGGING && debug "UI_Info: $UI_Mode: $text"; + $DEBUGGING && debug "UI_Info: $UI_Mode: $text"; - if($UI_Mode eq "text") { - print "$text\n"; - } else { - ycp::y2milestone($text); - } + if ($UI_Mode eq "text") { + print "$text\n"; + } else { + ycp::y2milestone($text); + } } sub UI_Important ($) { - my $text = shift; + my $text = shift; - $DEBUGGING && debug "UI_Important: $UI_Mode: $text"; + $DEBUGGING && debug "UI_Important: $UI_Mode: $text"; - if($UI_Mode eq "text") { - print "\n$text\n"; - } else { - SendDataToYast( { type => "dialog-error", message => $text } ); - my ($path, $yarg) = GetDataFromYast(); - } + if ($UI_Mode eq "text") { + print "\n$text\n"; + } else { + SendDataToYast({ type => "dialog-error", message => $text }); + my ($path, $yarg) = GetDataFromYast(); + } } sub UI_YesNo ($$) { - my $text = shift; - my $default = shift; + my $text = shift; + my $default = shift; - $DEBUGGING && debug "UI_YesNo: $UI_Mode: $text $default"; + $DEBUGGING && debug "UI_YesNo: $UI_Mode: $text $default"; - my $ans; - if($UI_Mode eq "text") { + my $ans; + if ($UI_Mode eq "text") { - my $yes = gettext("(Y)es"); - my $no = gettext("(N)o"); + my $yes = gettext("(Y)es"); + my $no = gettext("(N)o"); - # figure out our localized hotkeys - my $usrmsg = "PromptUser: " . gettext("Invalid hotkey for"); - $yes =~ /\((\S)\)/ or fatal_error "$usrmsg '$yes'"; - my $yeskey = lc($1); - $no =~ /\((\S)\)/ or fatal_error "$usrmsg '$no'"; - my $nokey = lc($1); + # figure out our localized hotkeys + my $usrmsg = "PromptUser: " . gettext("Invalid hotkey for"); + $yes =~ /\((\S)\)/ or fatal_error "$usrmsg '$yes'"; + my $yeskey = lc($1); + $no =~ /\((\S)\)/ or fatal_error "$usrmsg '$no'"; + my $nokey = lc($1); - print "\n$text\n"; - if($default eq "y") { - print "\n[$yes] / $no\n"; + print "\n$text\n"; + if ($default eq "y") { + print "\n[$yes] / $no\n"; + } else { + print "\n$yes / [$no]\n"; + } + $ans = getkey() || (($default eq "y") ? $yeskey : $nokey); + + # convert back from a localized answer to english y or n + $ans = (lc($ans) eq $yeskey) ? "y" : "n"; } else { - print "\n$yes / [$no]\n"; + + SendDataToYast({ type => "dialog-yesno", question => $text }); + my ($ypath, $yarg) = GetDataFromYast(); + $ans = $yarg->{answer} || $default; + } - $ans = getkey() || (($default eq "y") ? $yeskey : $nokey); - # convert back from a localized answer to english y or n - $ans = (lc($ans) eq $yeskey) ? "y" : "n"; - } else { - - SendDataToYast( { type => "dialog-yesno", question => $text } ); - my ($ypath, $yarg) = GetDataFromYast(); - $ans = $yarg->{answer} || $default; - - } - - return $ans; + return $ans; } sub UI_YesNoCancel ($$) { - my $text = shift; - my $default = shift; + my $text = shift; + my $default = shift; - $DEBUGGING && debug "UI_YesNoCancel: $UI_Mode: $text $default"; + $DEBUGGING && debug "UI_YesNoCancel: $UI_Mode: $text $default"; - my $ans; - if($UI_Mode eq "text") { + my $ans; + if ($UI_Mode eq "text") { - my $yes = gettext("(Y)es"); - my $no = gettext("(N)o"); - my $cancel = gettext("(C)ancel"); + my $yes = gettext("(Y)es"); + my $no = gettext("(N)o"); + my $cancel = gettext("(C)ancel"); - # figure out our localized hotkeys - my $usrmsg = "PromptUser: " . gettext("Invalid hotkey for"); - $yes =~ /\((\S)\)/ or fatal_error "$usrmsg '$yes'"; - my $yeskey = lc($1); - $no =~ /\((\S)\)/ or fatal_error "$usrmsg '$no'"; - my $nokey = lc($1); - $cancel =~ /\((\S)\)/ or fatal_error "$usrmsg '$cancel'"; - my $cancelkey = lc($1); + # figure out our localized hotkeys + my $usrmsg = "PromptUser: " . gettext("Invalid hotkey for"); + $yes =~ /\((\S)\)/ or fatal_error "$usrmsg '$yes'"; + my $yeskey = lc($1); + $no =~ /\((\S)\)/ or fatal_error "$usrmsg '$no'"; + my $nokey = lc($1); + $cancel =~ /\((\S)\)/ or fatal_error "$usrmsg '$cancel'"; + my $cancelkey = lc($1); - $ans = "XXXINVALIDXXX"; - while($ans !~ /^(y|n|c)$/) { - print "\n$text\n"; - if($default eq "y") { - print "\n[$yes] / $no / $cancel\n"; - } elsif($default eq "n") { - print "\n$yes / [$no] / $cancel\n"; - } else { - print "\n$yes / $no / [$cancel]\n"; - } - $ans = getkey(); - if($ans) { - # convert back from a localized answer to english y or n - $ans = lc($ans); - if($ans eq $yeskey) { - $ans = "y"; - } elsif($ans eq $nokey) { - $ans = "n"; - } elsif($ans eq $cancelkey) { - $ans = "c"; + $ans = "XXXINVALIDXXX"; + while ($ans !~ /^(y|n|c)$/) { + print "\n$text\n"; + if ($default eq "y") { + print "\n[$yes] / $no / $cancel\n"; + } elsif ($default eq "n") { + print "\n$yes / [$no] / $cancel\n"; + } else { + print "\n$yes / $no / [$cancel]\n"; + } + + $ans = getkey(); + + if ($ans) { + # convert back from a localized answer to english y or n + $ans = lc($ans); + if ($ans eq $yeskey) { + $ans = "y"; + } elsif ($ans eq $nokey) { + $ans = "n"; + } elsif ($ans eq $cancelkey) { + $ans = "c"; + } + } else { + $ans = $default; + } } - } else { - $ans = $default; - } + } else { + + SendDataToYast({ type => "dialog-yesnocancel", question => $text }); + my ($ypath, $yarg) = GetDataFromYast(); + $ans = $yarg->{answer} || $default; + } - } else { - SendDataToYast( { type => "dialog-yesnocancel", question => $text } ); - my ($ypath, $yarg) = GetDataFromYast(); - $ans = $yarg->{answer} || $default; - - } - - return $ans; + return $ans; } sub UI_GetString ($$) { - my $text = shift; - my $default = shift; + my $text = shift; + my $default = shift; - $DEBUGGING && debug "UI_GetString: $UI_Mode: $text $default"; + $DEBUGGING && debug "UI_GetString: $UI_Mode: $text $default"; - my $string; - if($UI_Mode eq "text") { + my $string; + if ($UI_Mode eq "text") { + + if ($term) { + $string = $term->readline($text, $default); + } else { + local $| = 1; + print "$text"; + $string = ; + chomp($string); + } - if($term) { - $string = $term->readline($text, $default); } else { - local $| = 1; - print "$text"; - $string = ; - chomp($string); + + SendDataToYast({ + type => "dialog-getstring", + label => $text, + default => $default + }); + my ($ypath, $yarg) = GetDataFromYast(); + $string = $yarg->{string}; + } - - } else { - - SendDataToYast( { type => "dialog-getstring", label => $text, default => $default } ); - my ($ypath, $yarg) = GetDataFromYast(); - $string = $yarg->{string}; - - } - return $string; + return $string; } sub UI_GetFile ($) { - my $f = shift; + my $f = shift; - $DEBUGGING && debug "UI_GetFile: $UI_Mode"; + $DEBUGGING && debug "UI_GetFile: $UI_Mode"; - my $filename; - if($UI_Mode eq "text") { + my $filename; + if ($UI_Mode eq "text") { - local $| = 1; - print "$f->{description}\n"; - $filename = ; - chomp($filename); + local $| = 1; + print "$f->{description}\n"; + $filename = ; + chomp($filename); - } else { + } else { - $f->{type} = "dialog-getfile"; + $f->{type} = "dialog-getfile"; - SendDataToYast( $f ); - my ($ypath, $yarg) = GetDataFromYast(); - if($yarg->{answer} eq "okay") { - $filename = $yarg->{filename}; + SendDataToYast($f); + my ($ypath, $yarg) = GetDataFromYast(); + if ($yarg->{answer} eq "okay") { + $filename = $yarg->{filename}; + } } - } - return $filename; + return $filename; } my %CMDS = ( - CMD_ALLOW => "(A)llow", - CMD_DENY => "(D)eny", - CMD_ABORT => "Abo(r)t", - CMD_FINISHED => "(F)inish", - CMD_INHERIT => "(I)nherit", - CMD_PROFILE => "(P)rofile", - CMD_PROFILE_CLEAN => "(P)rofile Clean Exec", - CMD_UNCONFINED => "(U)nconfined", - CMD_UNCONFINED_CLEAN => "(U)nconfined Clean Exec", - CMD_NEW => "(N)ew", - CMD_GLOB => "(G)lob", - CMD_GLOBEXT => "Glob w/(E)xt", - CMD_ADDHAT => "(A)dd Requested Hat", - CMD_USEDEFAULT => "(U)se Default Hat", - CMD_SCAN => "(S)can system log for SubDomain events", - CMD_HELP => "(H)elp", + CMD_ALLOW => "(A)llow", + CMD_DENY => "(D)eny", + CMD_ABORT => "Abo(r)t", + CMD_FINISHED => "(F)inish", + CMD_INHERIT => "(I)nherit", + CMD_PROFILE => "(P)rofile", + CMD_PROFILE_CLEAN => "(P)rofile Clean Exec", + CMD_UNCONFINED => "(U)nconfined", + CMD_UNCONFINED_CLEAN => "(U)nconfined Clean Exec", + CMD_NEW => "(N)ew", + CMD_GLOB => "(G)lob", + CMD_GLOBEXT => "Glob w/(E)xt", + CMD_ADDHAT => "(A)dd Requested Hat", + CMD_USEDEFAULT => "(U)se Default Hat", + CMD_SCAN => "(S)can system log for SubDomain events", + CMD_HELP => "(H)elp", ); sub UI_PromptUser ($) { - my $q = shift; + my $q = shift; - my ($cmd, $arg); - if($UI_Mode eq "text") { + my ($cmd, $arg); + if ($UI_Mode eq "text") { - ($cmd, $arg) = Text_PromptUser($q); + ($cmd, $arg) = Text_PromptUser($q); - } else { + } else { - $q->{type} = "wizard"; + $q->{type} = "wizard"; - SendDataToYast($q); - my ($ypath, $yarg) = GetDataFromYast(); + SendDataToYast($q); + my ($ypath, $yarg) = GetDataFromYast(); - $cmd = $yarg->{selection} || "CMD_ABORT"; - $arg = $yarg->{selected}; - } + $cmd = $yarg->{selection} || "CMD_ABORT"; + $arg = $yarg->{selected}; + } - return ($cmd, $arg); + return ($cmd, $arg); } ########################################################################## @@ -890,33 +961,33 @@ sub UI_PromptUser ($) { # back to the ycp front end. sub SendDataToYast { - my $data = shift; + my $data = shift; - $DEBUGGING && debug "SendDataToYast: Waiting for YCP command"; + $DEBUGGING && debug "SendDataToYast: Waiting for YCP command"; - while() { - $DEBUGGING && debug "SendDataToYast: YCP: $_"; - my ($ycommand, $ypath, $yargument) = ycp::ParseCommand($_); + while () { + $DEBUGGING && debug "SendDataToYast: YCP: $_"; + my ($ycommand, $ypath, $yargument) = ycp::ParseCommand($_); - if($ycommand && $ycommand eq "Read") { + if ($ycommand && $ycommand eq "Read") { - if($DEBUGGING) { - my $debugmsg = Data::Dumper->Dump([$data], [qw(*data)]); - debug "SendDataToYast: Sending--\n$debugmsg"; - } + if ($DEBUGGING) { + my $debugmsg = Data::Dumper->Dump([$data], [qw(*data)]); + debug "SendDataToYast: Sending--\n$debugmsg"; + } - ycp::Return($data); - return 1; + ycp::Return($data); + return 1; - } else { + } else { - $DEBUGGING && debug "SendDataToYast: Expected 'Read' but got-- $_"; + $DEBUGGING && debug "SendDataToYast: Expected 'Read' but got-- $_"; + } } - } - # if we ever break out here, something's horribly wrong. - fatal_error "SendDataToYast: didn't receive YCP command before connection died"; + # if we ever break out here, something's horribly wrong. + fatal_error "SendDataToYast: didn't receive YCP command before connection died"; } # this is super ugly, but waits for the next ycp Write command and grabs @@ -924,29 +995,29 @@ sub SendDataToYast { sub GetDataFromYast { - $DEBUGGING && debug "GetDataFromYast: Waiting for YCP command"; + $DEBUGGING && debug "GetDataFromYast: Waiting for YCP command"; - while() { - $DEBUGGING && debug "GetDataFromYast: YCP: $_"; - my ($ycmd, $ypath, $yarg) = ycp::ParseCommand($_); + while () { + $DEBUGGING && debug "GetDataFromYast: YCP: $_"; + my ($ycmd, $ypath, $yarg) = ycp::ParseCommand($_); - if($DEBUGGING) { - my $debugmsg = Data::Dumper->Dump([$yarg], [qw(*data)]); - debug "GetDataFromYast: Received--\n$debugmsg"; + if ($DEBUGGING) { + my $debugmsg = Data::Dumper->Dump([$yarg], [qw(*data)]); + debug "GetDataFromYast: Received--\n$debugmsg"; + } + + if ($ycmd && $ycmd eq "Write") { + + ycp::Return("true"); + return ($ypath, $yarg); + + } else { + $DEBUGGING && debug "GetDataFromYast: Expected 'Write' but got-- $_"; + } } - if($ycmd && $ycmd eq "Write") { - - ycp::Return("true"); - return ($ypath, $yarg); - - } else { - $DEBUGGING && debug "GetDataFromYast: Expected 'Write' but got-- $_"; - } - } - - # if we ever break out here, something's horribly wrong. - fatal_error "GetDataFromYast: didn't receive YCP command before connection died"; + # if we ever break out here, something's horribly wrong. + fatal_error "GetDataFromYast: didn't receive YCP command before connection died"; } ########################################################################## @@ -954,1982 +1025,2104 @@ sub GetDataFromYast { # trees that we've generated by parsing the logfile sub handlechildren { - my $profile = shift; - my $hat = shift; - my $root = shift; + my $profile = shift; + my $hat = shift; + my $root = shift; - my @entries = @$root; - for my $entry (@entries) { - fatal_error "$entry is not a ref" if not ref($entry); + my @entries = @$root; + for my $entry (@entries) { + fatal_error "$entry is not a ref" if not ref($entry); - if(ref($entry->[0])) { - handlechildren($profile, $hat, $entry); - } else { + if (ref($entry->[0])) { + handlechildren($profile, $hat, $entry); + } else { - my @entry = @$entry; - my $type = shift @entry; + my @entry = @$entry; + my $type = shift @entry; - if($type eq "fork") { - my ($pid, $p, $h) = @entry; + if ($type eq "fork") { + my ($pid, $p, $h) = @entry; - if(($p !~ /null(-complain)*-profile/) && - ($h !~ /null(-complain)*-profile/)) { - $profile = $p; - $hat = $h; - } - - $profilechanges{$pid} = $profile; - - } elsif($type eq "unknown_hat") { - my ($pid, $p, $h, $sdmode, $uhat) = @entry; - - if($p !~ /null(-complain)*-profile/) { - $profile = $p; - } - - if($sd{$profile}{$uhat}) { - $hat = $uhat; - next; - } - - # figure out what our default hat for this application is. - my $defaulthat; - for my $hatglob (keys %defaulthat) { - $defaulthat = $defaulthat{$hatglob} if $profile =~ /$hatglob/; - } - - # keep track of previous answers for this run... - my $context = $profile; - $context .= " -> ^$uhat"; - my $ans = $transitions{$context} || ""; - - unless($ans) { - my $q = { }; - $q->{headers} = [ ]; - push @{$q->{headers}}, gettext("Profile"), $profile; - if($defaulthat) { - push @{$q->{headers}}, gettext("Default Hat"), $defaulthat; - } - push @{$q->{headers}}, gettext("Requested Hat"), $uhat; - - $q->{functions} = [ ]; - push @{$q->{functions}}, "CMD_ADDHAT"; - push @{$q->{functions}}, "CMD_USEDEFAULT" if $defaulthat; - push @{$q->{functions}}, "CMD_DENY"; - push @{$q->{functions}}, "CMD_ABORT"; - push @{$q->{functions}}, "CMD_FINISHED"; - - $q->{default} = ($sdmode eq "PERMITTING") ? "CMD_ADDHAT" : "CMD_DENY"; - - $seenevents++; - - my $arg; - ($ans, $arg) = UI_PromptUser($q); - - $transitions{$context} = $ans; - } - - # ugh, there's a bug here. if they pick "abort" or "finish" and then - # say "well, no, I didn't really mean that", we need to ask the - # question again, but we currently go on to the next one. oops. - if($ans eq "CMD_ADDHAT") { - $hat = $uhat; - $sd{$profile}{$hat}{flags} = $sd{$profile}{$profile}{flags}; - } elsif($ans eq "CMD_USEDEFAULT") { - $hat = $defaulthat; - } elsif($ans eq "CMD_DENY") { - return; - } elsif($ans eq "CMD_ABORT") { - my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Abandoning all changes.")); - shutdown_yast(); - exit 0; - } - } elsif($ans eq "CMD_FINISHED") { - my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Saving all changes.")); - $finishing = 1; - # XXX - BUGBUG - this is REALLY nasty, but i'm in a hurry... - goto SAVE_PROFILES; - } - } - - } elsif($type eq "capability") { - my ($pid, $p, $h, $prog, $sdmode, $capability) = @entry; - - if(($p !~ /null(-complain)*-profile/) && - ($h !~ /null(-complain)*-profile/)) { - $profile = $p; - $hat = $h; - } - - # print "$pid $profile $hat $prog $sdmode capability $capability\n"; - - next unless $profile && $hat; - - $prelog{$sdmode}{$profile}{$hat}{capability}{$capability} = 1; - } elsif(($type eq "path") || ($type eq "exec")) { - my ($pid, $p, $h, $prog, $sdmode, $mode, $detail) = @entry; - - if(($p !~ /null(-complain)*-profile/) && - ($h !~ /null(-complain)*-profile/)) { - $profile = $p; - $hat = $h; - } - - next unless $profile && $hat; - - my $domainchange = ($type eq "exec" ) ? "change" : "nochange"; - - # escape special characters that show up in literal paths - $detail =~ s/(\[|\]|\+|\*|\{|\})/\\$1/g; - - # we need to give the Execute dialog if they're requesting x access - # for something that's not a directory - we'll force a "ix" Path - # dialog for directories - my $do_execute = 0; - my $exec_target = $detail; - if($mode =~ s/x//g) { - if(-d $exec_target) { - $mode .= "ix"; - } else { - $do_execute = 1; - } - } - - if($mode eq "link") { - $mode = "l"; - if($detail =~ m/^from (.+) to (.+)$/) { - my ($path, $target) = ($1, $2); - - my $frommode = "lr"; - if(defined $prelog{$sdmode}{$profile}{$hat}{path}{$path}) { - $frommode .= $prelog{$sdmode}{$profile}{$hat}{path}{$path}; - } - $frommode = collapsemode($frommode); - $prelog{$sdmode}{$profile}{$hat}{path}{$path} = $frommode; - - my $tomode = "lr"; - if(defined $prelog{$sdmode}{$profile}{$hat}{path}{$target}) { - $tomode .= $prelog{$sdmode}{$profile}{$hat}{path}{$target}; - } - $tomode = collapsemode($tomode); - $prelog{$sdmode}{$profile}{$hat}{path}{$target} = $tomode; - -# print "$pid $profile $hat $prog $sdmode $path:$frommode -> $target:$tomode\n"; - } else { - next; - } - } elsif ($mode) { - my $path = $detail; - - if(defined $prelog{$sdmode}{$profile}{$hat}{path}{$path}) { - $mode .= $prelog{$sdmode}{$profile}{$hat}{path}{$path}; - $mode = collapsemode($mode); - } - - $prelog{$sdmode}{$profile}{$hat}{path}{$path} = $mode; -# print "$pid $profile $hat $prog $sdmode $mode $path\n"; - } - - if($do_execute) { - - my $context = $profile; - $context .= "^$hat" if $profile ne $hat; - $context .= " -> $exec_target"; - my $ans = $transitions{$context} || ""; - - my ($combinedmode, $cm, @m); - - # does path match any regexps in original profile? - ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $exec_target); - $combinedmode .= $cm if $cm; - - # does path match anything pulled in by includes in original profile? - ($cm, @m) = matchincludes($sd{$profile}{$hat}, $exec_target); - $combinedmode .= $cm if $cm; - - my $exec_mode; - if(contains($combinedmode, "ix")) { - $ans = "CMD_INHERIT"; - $exec_mode = "ixr"; - } elsif(contains($combinedmode, "px")) { - $ans = "CMD_PROFILE"; - $exec_mode = "px"; - } elsif(contains($combinedmode, "ux")) { - $ans = "CMD_UNCONFINED"; - $exec_mode = "ux"; - } elsif(contains($combinedmode, "Px")) { - $ans = "CMD_PROFILE_CLEAN"; - $exec_mode = "Px"; - } elsif(contains($combinedmode, "Ux")) { - $ans = "CMD_UNCONFINED_CLEAN"; - $exec_mode = "Ux"; - } else { - my $options = $qualifiers{$exec_target} || "ipu"; - - # force "ix" as the only option when the profiled program - # executes itself - $options = "i" if $exec_target eq $profile; - - # we always need deny... - $options .= "d"; - - # figure out what our default option should be... - my $default; - if($options =~ /p/ && -e getprofilefilename($exec_target)) { - $default = "CMD_PROFILE"; - } elsif($options =~ /i/) { - $default = "CMD_INHERIT"; - } else { - $default = "CMD_DENY"; - } - - # ugh, this doesn't work if someone does an ix before calling - # this particular child process. at least it's only a hint - # instead of mandatory to get this right. - my $parent_uses_ld_xxx = check_for_LD_XXX($profile); - - my $severity = $sevdb->rank($exec_target, "x"); - - # build up the prompt... - my $q = { }; - $q->{headers} = [ ]; - push @{$q->{headers}}, gettext("Profile"), combine_name($profile, $hat); - if($prog && $prog ne "HINT") { - push @{$q->{headers}}, gettext("Program"), $prog; - } - push @{$q->{headers}}, gettext("Execute"), $exec_target; - push @{$q->{headers}}, gettext("Severity"), $severity; - - $q->{functions} = [ ]; - - my $prompt = "\n$context\n"; - push @{$q->{functions}}, "CMD_INHERIT" if $options =~ /i/; - push @{$q->{functions}}, "CMD_PROFILE" if $options =~ /p/; - push @{$q->{functions}}, "CMD_UNCONFINED" if $options =~ /u/; - push @{$q->{functions}}, "CMD_DENY"; - push @{$q->{functions}}, "CMD_ABORT"; - push @{$q->{functions}}, "CMD_FINISHED"; - - $q->{default} = $default; - - $options = join("|", split(//, $options)); - - $seenevents++; - - my $arg; - while($ans !~ m/^CMD_(INHERIT|PROFILE|PROFILE_CLEAN|UNCONFINED|UNCONFINED_CLEAN|DENY)$/) { - ($ans, $arg) = UI_PromptUser($q); - - # check for Abort or Finish - if($ans eq "CMD_ABORT") { - my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); - $DEBUGGING && debug "back from abort yesno"; - if($ans eq "y") { - UI_Info(gettext("Abandoning all changes.")); - shutdown_yast(); - exit 0; + if ( ($p !~ /null(-complain)*-profile/) + && ($h !~ /null(-complain)*-profile/)) + { + $profile = $p; + $hat = $h; } - } elsif($ans eq "CMD_FINISHED") { - my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Saving all changes.")); - $finishing = 1; - # XXX - BUGBUG - this is REALLY nasty, but i'm in a hurry... - goto SAVE_PROFILES; - } - } elsif($ans eq "CMD_PROFILE") { - my $px_default = "n"; - my $px_mesg = gettext("Should AppArmor sanitize the environment when\nswitching profiles?\n\nSanitizing the environment is more secure,\nbut some applications depend on the presence\nof LD_PRELOAD or LD_LIBRARY_PATH."); - if($parent_uses_ld_xxx) { - $px_mesg = gettext("Should AppArmor sanitize the environment when\nswitching profiles?\n\nSanitizing the environment is more secure,\nbut this application appears to use LD_PRELOAD\nor LD_LIBRARY_PATH and clearing these could\ncause functionality problems."); - } - my $ynans = UI_YesNo($px_mesg, $px_default); - if($ynans eq "y") { - $ans = "CMD_PROFILE_CLEAN"; - } - } elsif($ans eq "CMD_UNCONFINED") { - my $ynans = UI_YesNo(sprintf(gettext("Launching processes in an unconfined state is a very\ndangerous operation and can cause serious security holes.\n\nAre you absolutely certain you wish to remove all\nAppArmor protection when executing \%s?"), $exec_target), "n"); - if($ynans eq "y") { - my $ynans = UI_YesNo(gettext("Should AppArmor sanitize the environment when\nrunning this program unconfined?\n\nNot sanitizing the environment when unconfining\na program opens up significant security holes\nand should be avoided if at all possible."), "y"); - if($ynans eq "y") { - $ans = "CMD_UNCONFINED_CLEAN"; - } - } else { - $ans = "INVALID"; - } - } - } - $transitions{$context} = $ans; - - # if we're inheriting, things'll bitch unless we have r - if($ans eq "CMD_INHERIT") { - $exec_mode = "ixr"; - } elsif($ans eq "CMD_PROFILE") { - $exec_mode = "px"; - } elsif($ans eq "CMD_UNCONFINED") { - $exec_mode = "ux"; - } elsif($ans eq "CMD_PROFILE_CLEAN") { - $exec_mode = "Px"; - } elsif($ans eq "CMD_UNCONFINED_CLEAN") { - $exec_mode = "Ux"; - } else { - # skip all remaining events if they say to deny the exec - return if $domainchange eq "change"; - } - - unless($ans eq "CMD_DENY") { - if(defined $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target}) { - $exec_mode .= $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target}; - $exec_mode = collapsemode($exec_mode); - } - $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target} = $exec_mode; - $log{PERMITTING}{$profile} = { }; - $sd{$profile}{$hat}{path}{$exec_target} = $exec_mode; - $changed{$profile} = 1; # mark this profile as changed - if($ans eq "CMD_INHERIT") { - if($exec_target =~ /perl/) { - $sd{$profile}{$hat}{include}{"abstractions/perl"} = 1; - } elsif ($detail =~ m/\/bin\/(bash|sh)/) { - $sd{$profile}{$hat}{include}{"abstractions/bash"} = 1; - } - my $hashbang = head($exec_target); - if($hashbang =~ /^#!\s*(\S+)/) { - my $interpreter = get_full_path($1); - $sd{$profile}{$hat}{path}->{$interpreter} = "ix"; - if($interpreter =~ /perl/) { - $sd{$profile}{$hat}{include}{"abstractions/perl"} = 1; - } elsif ($interpreter =~ m/\/bin\/(bash|sh)/) { - $sd{$profile}{$hat}{include}{"abstractions/bash"} = 1; - } - } - } elsif($ans =~ /^CMD_PROFILE/) { - # if they want to use px, make sure a profile exists for the target. - unless(-e getprofilefilename($exec_target)) { - $helpers{$exec_target} = "enforce"; - autodep($exec_target); - reload($exec_target); - } - } - } - } - - #print "$pid $profile $hat EXEC $exec_target $ans $exec_mode\n"; - - # update our tracking info based on what kind of change this is... - if($ans eq "CMD_INHERIT") { - $profilechanges{$pid} = $profile; - } elsif($ans =~ /^CMD_PROFILE/) { - if($sdmode eq "PERMITTING") { - if($domainchange eq "change") { - $profile = $exec_target; - $hat = $exec_target; $profilechanges{$pid} = $profile; - } - } - } elsif ($ans =~ /^CMD_UNCONFINED/) { - $profilechanges{$pid} = "unconstrained"; - return if $domainchange eq "change"; - } + } elsif ($type eq "unknown_hat") { + my ($pid, $p, $h, $sdmode, $uhat) = @entry; + + if ($p !~ /null(-complain)*-profile/) { + $profile = $p; + } + + if ($sd{$profile}{$uhat}) { + $hat = $uhat; + next; + } + + # figure out what our default hat for this application is. + my $defaulthat; + for my $hatglob (keys %defaulthat) { + $defaulthat = $defaulthat{$hatglob} + if $profile =~ /$hatglob/; + } + + # keep track of previous answers for this run... + my $context = $profile; + $context .= " -> ^$uhat"; + my $ans = $transitions{$context} || ""; + + unless ($ans) { + my $q = {}; + $q->{headers} = []; + push @{ $q->{headers} }, gettext("Profile"), $profile; + if ($defaulthat) { + push @{ $q->{headers} }, gettext("Default Hat"), $defaulthat; + } + push @{ $q->{headers} }, gettext("Requested Hat"), $uhat; + + $q->{functions} = []; + push @{ $q->{functions} }, "CMD_ADDHAT"; + push @{ $q->{functions} }, "CMD_USEDEFAULT" if $defaulthat; + push @{ $q->{functions} }, "CMD_DENY"; + push @{ $q->{functions} }, "CMD_ABORT"; + push @{ $q->{functions} }, "CMD_FINISHED"; + + $q->{default} = ($sdmode eq "PERMITTING") ? "CMD_ADDHAT" : "CMD_DENY"; + + $seenevents++; + + my $arg; + ($ans, $arg) = UI_PromptUser($q); + + $transitions{$context} = $ans; + } + + # ugh, there's a bug here. if they pick "abort" or "finish" + # and then say "well, no, I didn't really mean that", we need + # to ask the question again, but we currently go on to the + # next one. oops. + if ($ans eq "CMD_ADDHAT") { + $hat = $uhat; + $sd{$profile}{$hat}{flags} = $sd{$profile}{$profile}{flags}; + } elsif ($ans eq "CMD_USEDEFAULT") { + $hat = $defaulthat; + } elsif ($ans eq "CMD_DENY") { + return; + } elsif ($ans eq "CMD_ABORT") { + my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Abandoning all changes.")); + shutdown_yast(); + exit 0; + } + } elsif ($ans eq "CMD_FINISHED") { + my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Saving all changes.")); + $finishing = 1; + + # XXX - BUGBUG - this is REALLY nasty, but i'm in + # a hurry... + goto SAVE_PROFILES; + } + } + + } elsif ($type eq "capability") { + my ($pid, $p, $h, $prog, $sdmode, $capability) = @entry; + + if ( ($p !~ /null(-complain)*-profile/) + && ($h !~ /null(-complain)*-profile/)) + { + $profile = $p; + $hat = $h; + } + + # print "$pid $profile $hat $prog $sdmode capability $capability\n"; + + next unless $profile && $hat; + + $prelog{$sdmode}{$profile}{$hat}{capability}{$capability} = 1; + } elsif (($type eq "path") || ($type eq "exec")) { + my ($pid, $p, $h, $prog, $sdmode, $mode, $detail) = @entry; + + if ( ($p !~ /null(-complain)*-profile/) + && ($h !~ /null(-complain)*-profile/)) + { + $profile = $p; + $hat = $h; + } + + next unless $profile && $hat; + + my $domainchange = ($type eq "exec") ? "change" : "nochange"; + + # escape special characters that show up in literal paths + $detail =~ s/(\[|\]|\+|\*|\{|\})/\\$1/g; + + # we need to give the Execute dialog if they're requesting x + # access for something that's not a directory - we'll force + # a "ix" Path dialog for directories + my $do_execute = 0; + my $exec_target = $detail; + if ($mode =~ s/x//g) { + if (-d $exec_target) { + $mode .= "ix"; + } else { + $do_execute = 1; + } + } + + if ($mode eq "link") { + $mode = "l"; + if ($detail =~ m/^from (.+) to (.+)$/) { + my ($path, $target) = ($1, $2); + + my $frommode = "lr"; + if (defined $prelog{$sdmode}{$profile}{$hat}{path}{$path}) { + $frommode .= $prelog{$sdmode}{$profile}{$hat}{path}{$path}; + } + $frommode = collapsemode($frommode); + $prelog{$sdmode}{$profile}{$hat}{path}{$path} = $frommode; + + my $tomode = "lr"; + if (defined $prelog{$sdmode}{$profile}{$hat}{path}{$target}) { + $tomode .= $prelog{$sdmode}{$profile}{$hat}{path}{$target}; + } + $tomode = collapsemode($tomode); + $prelog{$sdmode}{$profile}{$hat}{path}{$target} = $tomode; + + # print "$pid $profile $hat $prog $sdmode $path:$frommode -> $target:$tomode\n"; + } else { + next; + } + } elsif ($mode) { + my $path = $detail; + + if (defined $prelog{$sdmode}{$profile}{$hat}{path}{$path}) { + $mode .= $prelog{$sdmode}{$profile}{$hat}{path}{$path}; + $mode = collapsemode($mode); + } + + $prelog{$sdmode}{$profile}{$hat}{path}{$path} = $mode; + + # print "$pid $profile $hat $prog $sdmode $mode $path\n"; + } + + if ($do_execute) { + + my $context = $profile; + $context .= "^$hat" if $profile ne $hat; + $context .= " -> $exec_target"; + my $ans = $transitions{$context} || ""; + + my ($combinedmode, $cm, @m); + + # does path match any regexps in original profile? + ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $exec_target); + $combinedmode .= $cm if $cm; + + # does path match anything pulled in by includes in + # original profile? + ($cm, @m) = matchincludes($sd{$profile}{$hat}, $exec_target); + $combinedmode .= $cm if $cm; + + my $exec_mode; + if (contains($combinedmode, "ix")) { + $ans = "CMD_INHERIT"; + $exec_mode = "ixr"; + } elsif (contains($combinedmode, "px")) { + $ans = "CMD_PROFILE"; + $exec_mode = "px"; + } elsif (contains($combinedmode, "ux")) { + $ans = "CMD_UNCONFINED"; + $exec_mode = "ux"; + } elsif (contains($combinedmode, "Px")) { + $ans = "CMD_PROFILE_CLEAN"; + $exec_mode = "Px"; + } elsif (contains($combinedmode, "Ux")) { + $ans = "CMD_UNCONFINED_CLEAN"; + $exec_mode = "Ux"; + } else { + my $options = $qualifiers{$exec_target} || "ipu"; + + # force "ix" as the only option when the profiled + # program executes itself + $options = "i" if $exec_target eq $profile; + + # we always need deny... + $options .= "d"; + + # figure out what our default option should be... + my $default; + if ($options =~ /p/ + && -e getprofilefilename($exec_target)) + { + $default = "CMD_PROFILE"; + } elsif ($options =~ /i/) { + $default = "CMD_INHERIT"; + } else { + $default = "CMD_DENY"; + } + + # ugh, this doesn't work if someone does an ix before + # calling this particular child process. at least + # it's only a hint instead of mandatory to get this + # right. + my $parent_uses_ld_xxx = check_for_LD_XXX($profile); + + my $severity = $sevdb->rank($exec_target, "x"); + + # build up the prompt... + my $q = {}; + $q->{headers} = []; + push @{ $q->{headers} }, gettext("Profile"), combine_name($profile, $hat); + if ($prog && $prog ne "HINT") { + push @{ $q->{headers} }, gettext("Program"), $prog; + } + push @{ $q->{headers} }, gettext("Execute"), $exec_target; + push @{ $q->{headers} }, gettext("Severity"), $severity; + + $q->{functions} = []; + + my $prompt = "\n$context\n"; + push @{ $q->{functions} }, "CMD_INHERIT" + if $options =~ /i/; + push @{ $q->{functions} }, "CMD_PROFILE" + if $options =~ /p/; + push @{ $q->{functions} }, "CMD_UNCONFINED" + if $options =~ /u/; + push @{ $q->{functions} }, "CMD_DENY"; + push @{ $q->{functions} }, "CMD_ABORT"; + push @{ $q->{functions} }, "CMD_FINISHED"; + + $q->{default} = $default; + + $options = join("|", split(//, $options)); + + $seenevents++; + + my $arg; + while ($ans !~ m/^CMD_(INHERIT|PROFILE|PROFILE_CLEAN|UNCONFINED|UNCONFINED_CLEAN|DENY)$/) { + ($ans, $arg) = UI_PromptUser($q); + + # check for Abort or Finish + if ($ans eq "CMD_ABORT") { + my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); + $DEBUGGING && debug "back from abort yesno"; + if ($ans eq "y") { + UI_Info(gettext("Abandoning all changes.")); + shutdown_yast(); + exit 0; + } + } elsif ($ans eq "CMD_FINISHED") { + my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Saving all changes.")); + $finishing = 1; + + # XXX - BUGBUG - this is REALLY nasty, + # but i'm in a hurry... + goto SAVE_PROFILES; + } + } elsif ($ans eq "CMD_PROFILE") { + my $px_default = "n"; + my $px_mesg = gettext("Should AppArmor sanitize the environment when\nswitching profiles?\n\nSanitizing the environment is more secure,\nbut some applications depend on the presence\nof LD_PRELOAD or LD_LIBRARY_PATH."); + if ($parent_uses_ld_xxx) { + $px_mesg = gettext("Should AppArmor sanitize the environment when\nswitching profiles?\n\nSanitizing the environment is more secure,\nbut this application appears to use LD_PRELOAD\nor LD_LIBRARY_PATH and clearing these could\ncause functionality problems."); + } + my $ynans = UI_YesNo($px_mesg, $px_default); + if ($ynans eq "y") { + $ans = "CMD_PROFILE_CLEAN"; + } + } elsif ($ans eq "CMD_UNCONFINED") { + my $ynans = UI_YesNo(sprintf(gettext("Launching processes in an unconfined state is a very\ndangerous operation and can cause serious security holes.\n\nAre you absolutely certain you wish to remove all\nAppArmor protection when executing \%s?"), $exec_target), "n"); + if ($ynans eq "y") { + my $ynans = UI_YesNo(gettext("Should AppArmor sanitize the environment when\nrunning this program unconfined?\n\nNot sanitizing the environment when unconfining\na program opens up significant security holes\nand should be avoided if at all possible."), "y"); + if ($ynans eq "y") { + $ans = "CMD_UNCONFINED_CLEAN"; + } + } else { + $ans = "INVALID"; + } + } + } + $transitions{$context} = $ans; + + # if we're inheriting, things'll bitch unless we have r + if ($ans eq "CMD_INHERIT") { + $exec_mode = "ixr"; + } elsif ($ans eq "CMD_PROFILE") { + $exec_mode = "px"; + } elsif ($ans eq "CMD_UNCONFINED") { + $exec_mode = "ux"; + } elsif ($ans eq "CMD_PROFILE_CLEAN") { + $exec_mode = "Px"; + } elsif ($ans eq "CMD_UNCONFINED_CLEAN") { + $exec_mode = "Ux"; + } else { + + # skip all remaining events if they say to deny + # the exec + return if $domainchange eq "change"; + } + + unless ($ans eq "CMD_DENY") { + if (defined $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target}) { + $exec_mode .= $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target}; + $exec_mode = collapsemode($exec_mode); + } + $prelog{PERMITTING}{$profile}{$hat}{path}{$exec_target} = $exec_mode; + $log{PERMITTING}{$profile} = {}; + $sd{$profile}{$hat}{path}{$exec_target} = $exec_mode; + + # mark this profile as changed + $changed{$profile} = 1; + + if ($ans eq "CMD_INHERIT") { + if ($exec_target =~ /perl/) { + $sd{$profile}{$hat}{include}{"abstractions/perl"} = 1; + } elsif ($detail =~ m/\/bin\/(bash|sh)/) { + $sd{$profile}{$hat}{include}{"abstractions/bash"} = 1; + } + my $hashbang = head($exec_target); + if ($hashbang =~ /^#!\s*(\S+)/) { + my $interpreter = get_full_path($1); + $sd{$profile}{$hat}{path}->{$interpreter} = "ix"; + if ($interpreter =~ /perl/) { + $sd{$profile}{$hat}{include}{"abstractions/perl"} = 1; + } elsif ($interpreter =~ m/\/bin\/(bash|sh)/) { + $sd{$profile}{$hat}{include}{"abstractions/bash"} = 1; + } + } + } elsif ($ans =~ /^CMD_PROFILE/) { + + # if they want to use px, make sure a profile + # exists for the target. + unless (-e getprofilefilename($exec_target)) { + $helpers{$exec_target} = "enforce"; + autodep($exec_target); + reload($exec_target); + } + } + } + } + + # print "$pid $profile $hat EXEC $exec_target $ans $exec_mode\n"; + + # update our tracking info based on what kind of change + # this is... + if ($ans eq "CMD_INHERIT") { + $profilechanges{$pid} = $profile; + } elsif ($ans =~ /^CMD_PROFILE/) { + if ($sdmode eq "PERMITTING") { + if ($domainchange eq "change") { + $profile = $exec_target; + $hat = $exec_target; + $profilechanges{$pid} = $profile; + } + } + } elsif ($ans =~ /^CMD_UNCONFINED/) { + $profilechanges{$pid} = "unconstrained"; + return if $domainchange eq "change"; + } + } + } } - } } - } } sub do_logprof_pass { - my $logmark = shift || ""; + my $logmark = shift || ""; - # zero out the state variables for this pass... - %t = ( ); - %transitions = ( ); - %seen = ( ); - %sd = ( ); - %profilechanges = ( ); - %prelog = ( ); - %log = ( ); - %changed = ( ); - %skip = ( ); - %variables = ( ); + # zero out the state variables for this pass... + %t = (); + %transitions = (); + %seen = (); + %sd = (); + %profilechanges = (); + %prelog = (); + %log = (); + %changed = (); + %skip = (); + %variables = (); - UI_Info(sprintf(gettext('Reading log entries from %s.'), $filename)); - UI_Info(sprintf(gettext('Updating AppArmor profiles in %s.'), $profiledir)); + UI_Info(sprintf(gettext('Reading log entries from %s.'), $filename)); + UI_Info(sprintf(gettext('Updating AppArmor profiles in %s.'), $profiledir)); - readprofiles(); + readprofiles(); - my $seenmark = $logmark ? 0 : 1; + my $seenmark = $logmark ? 0 : 1; - $sevdb = new Immunix::Severity("$confdir/severity.db", gettext("unknown")); + $sevdb = new Immunix::Severity("$confdir/severity.db", gettext("unknown")); - my @log; - my %pid; + my @log; + my %pid; - sub add_to_tree ($@) { - my ($pid, $type, @event) = @_; + sub add_to_tree ($@) { + my ($pid, $type, @event) = @_; - unless(exists $pid{$pid}) { - my $arrayref = [ ]; - push @log, $arrayref; - $pid{$pid} = $arrayref; - } - - push @{$pid{$pid}}, [ $type, $pid, @event ]; - } - - my $stuffed = undef; - my $last; - # okay, done loading the previous profiles, get on to the good stuff... - open(LOG, $filename) or fatal_error "Can't read AppArmor logfile $filename: $!"; - while(($_ = $stuffed) || ($_ = )) { - chomp; - - $stuffed = undef; - - $seenmark = 1 if /$logmark/; - - next unless $seenmark; - - # all we care about is subdomain messages - next unless (/^.* audit\(/ || /type=(APPARMOR|UNKNOWN\[1500\]) msg=audit\([\d\.\:]+\):/ || /SubDomain/); - # workaround for syslog uglyness. - if(s/(PERMITTING|REJECTING)-SYSLOGFIX/$1/) { - s/%%/%/g; - } - - if(m/LOGPROF-HINT unknown_hat (\S+) pid=(\d+) profile=(.+) active=(.+)/) { - my ($uhat, $pid, $profile, $hat) = ($1, $2, $3, $4); - - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - add_to_tree($pid, "unknown_hat", $profile, $hat, "PERMITTING", $uhat); - } elsif(m/LOGPROF-HINT (unknown_profile|missing_mandatory_profile) image=(.+) pid=(\d+) profile=(.+) active=(.+)/) { - my ($image, $pid, $profile, $hat) = ($2, $3, $4, $5); - - next if $last =~ /PERMITTING x access to $image/; - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - add_to_tree($pid, "exec", $profile, $hat, "HINT", "PERMITTING", "x", $image); - - } elsif(m/(PERMITTING|REJECTING) (\S+) access (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { - my ($sdmode, $mode, $detail, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - my $domainchange = "nochange"; - if($mode =~ /x/) { - # we need to try to check if we're doing a domain transition this time - if($sdmode eq "PERMITTING") { - do { - $stuffed = ; - } until $stuffed =~ /AppArmor|audit/; - if($stuffed =~ m/changing_profile/) { - $domainchange = "change"; - $stuffed = undef; - } + unless (exists $pid{$pid}) { + my $arrayref = []; + push @log, $arrayref; + $pid{$pid} = $arrayref; } - } else { - # we want to ignore duplicates for things other than executes... - next if $seen{$&}; - $seen{$&} = 1; - } - $last = $&; + push @{ $pid{$pid} }, [ $type, $pid, @event ]; + } + + my $stuffed = undef; + my $last; + + # okay, done loading the previous profiles, get on to the good stuff... + open(LOG, $filename) + or fatal_error "Can't read AppArmor logfile $filename: $!"; + while (($_ = $stuffed) || ($_ = )) { + chomp; - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - if(($profile ne 'null-complain-profile') && (! profile_exists($profile))) { $stuffed = undef; - next; - } - # currently no way to stick pipe mediation in a profile, ignore any messages like this - next if $detail =~ /to pipe:/; + $seenmark = 1 if /$logmark/; - # strip out extra extended attribute info since we don't currently - # have a way to specify it in the profile and instead just need to - # provide the access to the base filename - $detail =~ s/\s+extended attribute \S+//; + next unless $seenmark; - # kerberos code checks to see if the krb5.conf file is world writable - # in a stupid way so we'll ignore any w accesses to krb5.conf - next if (($detail eq "to /etc/krb5.conf") && contains($mode, "w")); + # all we care about is subdomain messages + next + unless (/^.* audit\(/ + || /type=(APPARMOR|UNKNOWN\[1500\]) msg=audit\([\d\.\:]+\):/ + || /SubDomain/); - # strip off the (deleted) tag that gets added if it's a deleted file - $detail =~ s/\s+\(deleted\)$//; - -# next if (($detail =~ /to \/lib\/ld-/) && ($mode =~ /x/)); - - $detail =~ s/^to\s+//; - - if($domainchange eq "change") { - add_to_tree($pid, "exec", $profile, $hat, $prog, $sdmode, $mode, $detail); - } else { - add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, $mode, $detail); - } - - } elsif(m/(PERMITTING|REJECTING) (?:mk|rm)dir on (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { - my ($sdmode, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); - - # we want to ignore duplicates for things other than executes... - next if $seen{$&}++; - - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, "w", $path); - - } elsif(m/(PERMITTING|REJECTING) xattr (\S+) on (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { - my ($sdmode, $xattr_op, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - # we want to ignore duplicates for things other than executes... - next if $seen{$&}++; - - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - my $xattrmode; - if($xattr_op eq "get" || $xattr_op eq "list") { - $xattrmode = "r"; - } elsif($xattr_op eq "set" || $xattr_op eq "remove") { - $xattrmode = "w"; - } - - if($xattrmode) { - add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, $xattrmode, $path); - } - - } elsif(m/(PERMITTING|REJECTING) attribute \((.*?)\) change to (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { - my ($sdmode, $change, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - # we want to ignore duplicates for things other than executes... - next if $seen{$&}; - $seen{$&} = 1; - - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - # kerberos code checks to see if the krb5.conf file is world writable - # in a stupid way so we'll ignore any w accesses to krb5.conf - next if $path eq "/etc/krb5.conf"; - - add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, "w", $path); - - } elsif(m/(PERMITTING|REJECTING) access to capability '(\S+)' \((.+)\((\d+)\) profile (.+) active (.+)\)/) { - my ($sdmode, $capability, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); - - next if $seen{$&}; - - $seen{$&} = 1; - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - add_to_tree($pid, "capability", $profile, $hat, $prog, $sdmode, $capability); - - } elsif(m/Fork parent (\d+) child (\d+) profile (.+) active (.+)/ || - m/LOGPROF-HINT fork pid=(\d+) child=(\d+) profile=(.+) active=(.+)/ || - m/LOGPROF-HINT fork pid=(\d+) child=(\d+)/) { - my ($parent, $child, $profile, $hat) = ($1, $2, $3, $4); - - $profile ||= "null-complain-profile"; - $hat ||= "null-complain-profile"; - - $last = $&; - - # we want to ignore entries for profiles that don't exist - they're - # most likely broken entries or old entries for deleted profiles - next if (($profile ne 'null-complain-profile') && (! profile_exists($profile))); - - my $arrayref = [ ]; - if(exists $pid{$parent}) { - push @{$pid{$parent}}, $arrayref; - } else { - push @log, $arrayref; - } - $pid{$child} = $arrayref; - push @{$arrayref}, [ "fork", $child. $profile, $hat ]; - } else { - $DEBUGGING && debug "UNHANDLED: $_"; - } - } - close(LOG); - - for my $root (@log) { - handlechildren(undef, undef, $root); - } - - for my $pid (sort { $a <=> $b } keys %profilechanges) { - setprocess($pid, $profilechanges{$pid}); - } - - collapselog(); - - my $found; - - # do the magic foo-foo - for my $sdmode (sort keys %log) { - - # let them know what sort of changes we're about to list... - if($sdmode eq "PERMITTING") { - UI_Info(gettext("Complain-mode changes:")); - } elsif($sdmode eq "REJECTING") { - UI_Info(gettext("Enforce-mode changes:")); - } else { - # if we're not permitting and not rejecting, something's broken. - # most likely the code we're using to build the hash tree of log - # entries - this should never ever happen - fatal_error(sprintf(gettext('Invalid mode found: %s'), $sdmode)); - } - - for my $profile (sort keys %{$log{$sdmode}}) { - - $found++; - - # this sorts the list of hats, but makes sure that the containing - # profile shows up in the list first to keep the question order - # rational - my @hats = grep { $_ ne $profile } keys %{$log{$sdmode}{$profile}}; - unshift @hats, $profile if defined $log{$sdmode}{$profile}{$profile}; - - for my $hat (@hats) { - - # step through all the capabilities first... - for my $capability (sort keys %{$log{$sdmode}{$profile}{$hat}{capability}}) { - - - # we don't care about it if we've already added it to the profile - next if $sd{$profile}{$hat}{capability}{$capability}; - - my $severity = $sevdb->rank(uc("cap_$capability")); - - my $q = { }; - $q->{headers} = [ ]; - push @{$q->{headers}}, gettext("Profile"), combine_name($profile, $hat); - push @{$q->{headers}}, gettext("Capability"), $capability; - push @{$q->{headers}}, gettext("Severity"), $severity; - - $q->{functions} = [ "CMD_ALLOW", "CMD_DENY", "CMD_ABORT", "CMD_FINISHED" ]; - - # complain-mode events default to allow - enforce defaults to deny - $q->{default} = ($sdmode eq "PERMITTING") ? "CMD_ALLOW" : "CMD_DENY"; - - $seenevents++; - - # what did the grand exalted master tell us to do? - my ($ans, $arg) = UI_PromptUser($q); - - if($ans eq "CMD_ALLOW") { - # they picked (a)llow, so... - - # stick the capability into the profile - $sd{$profile}{$hat}{capability}{$capability} = 1; - - # mark this profile as changed - $changed{$profile} = 1; - - # give a little feedback to the user - UI_Info(sprintf(gettext('Adding capability %s to profile.'), $capability)); - } elsif($ans eq "CMD_DENY") { - UI_Info(sprintf(gettext('Denying capability %s to profile.'), $capability)); - } elsif($ans eq "CMD_ABORT") { - # if we're in yast, they've already been asked for confirmation - if($UI_Mode eq "yast") { - UI_Info(gettext("Abandoning all changes.")); - shutdown_yast(); - exit 0; - } - my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Abandoning all changes.")); - shutdown_yast(); - exit 0; - } else { - redo; - } - } elsif($ans eq "CMD_FINISHED") { - # if we're in yast, they've already been asked for confirmation - if($UI_Mode eq "yast") { - UI_Info(gettext("Saving all changes.")); - $finishing = 1; - # XXX - BUGBUG - this is REALLY nasty, but i'm in a hurry... - goto SAVE_PROFILES; - } - my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Saving all changes.")); - $finishing = 1; - # XXX - BUGBUG - this is REALLY nasty, but i'm in a hurry... - goto SAVE_PROFILES; - } else { - redo; - } - } + # workaround for syslog uglyness. + if (s/(PERMITTING|REJECTING)-SYSLOGFIX/$1/) { + s/%%/%/g; } - # and then step through all of the path entries... - for my $path (sort keys %{$log{$sdmode}{$profile}{$hat}{path}}) { + if (m/LOGPROF-HINT unknown_hat (\S+) pid=(\d+) profile=(.+) active=(.+)/) { + my ($uhat, $pid, $profile, $hat) = ($1, $2, $3, $4); - my $mode = $log{$sdmode}{$profile}{$hat}{path}{$path}; + $last = $&; - # if we had an access(X_OK) request or some other kind of event - # that generates a "PERMITTING x" syslog entry, first check if it - # was already dealt with by a i/p/x question due to a exec(). if - # not, ask about adding ix permission. - if($mode =~ /X/) { + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); - # get rid of the access() markers. - $mode =~ s/X//g; + add_to_tree($pid, "unknown_hat", $profile, $hat, "PERMITTING", $uhat); + } elsif (m/LOGPROF-HINT (unknown_profile|missing_mandatory_profile) image=(.+) pid=(\d+) profile=(.+) active=(.+)/) { + my ($image, $pid, $profile, $hat) = ($2, $3, $4, $5); - my $combinedmode = ""; + next if $last =~ /PERMITTING x access to $image/; + $last = $&; - my ($cm, @m); + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); - # does path match any regexps in original profile? - ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); - $combinedmode .= $cm if $cm; + add_to_tree($pid, "exec", $profile, $hat, "HINT", "PERMITTING", "x", $image); - # does path match anything pulled in by includes in original profile? - ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); - $combinedmode .= $cm if $cm; + } elsif (m/(PERMITTING|REJECTING) (\S+) access (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { + my ($sdmode, $mode, $detail, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - if($combinedmode) { - if(contains($combinedmode, "ix") || - contains($combinedmode, "px") || - contains($combinedmode, "ux") || - contains($combinedmode, "Px") || - contains($combinedmode, "Ux")) { - } else { - $mode .= "ix"; - } + my $domainchange = "nochange"; + if ($mode =~ /x/) { + + # we need to try to check if we're doing a domain transition + if ($sdmode eq "PERMITTING") { + do { + $stuffed = ; + } until $stuffed =~ /AppArmor|audit/; + if ($stuffed =~ m/changing_profile/) { + $domainchange = "change"; + $stuffed = undef; + } + } } else { - $mode .= "ix"; - } - } - # if we had an mmap(PROT_EXEC) request, first check if we already - # have added an ix rule to the profile - if($mode =~ /m/) { - my $combinedmode = ""; - my ($cm, @m); - - # does path match any regexps in original profile? - ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); - $combinedmode .= $cm if $cm; - - # does path match anything pulled in by includes in original profile? - ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); - $combinedmode .= $cm if $cm; - - # ix implies m. don't ask if they want to add an "m" rule when - # we already have a matching ix rule. - if($combinedmode && contains($combinedmode, "ix")) { - $mode =~ s/m//g; - } - } - - next unless $mode; - - my $combinedmode = ""; - my @matches; - - my ($cm, @m); - - # does path match any regexps in original profile? - ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); - if($cm) { - $combinedmode .= $cm; - push @matches, @m; - } - - # does path match anything pulled in by includes in original profile? - ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); - if($cm) { - $combinedmode .= $cm; - push @matches, @m; - } - - unless($combinedmode && contains($combinedmode, $mode)) { - - my $defaultoption = 1; - my @options = ( ); - - # check the path against the available set of include files - my @newincludes; - my $includevalid; - for my $incname (keys %include) { - $includevalid = 0; - - # don't suggest it if we're already including it, that's dumb - next if $sd{$profile}{$hat}{$incname}; - - # only match includes that can be suggested to the user - for my $incmatch( @custom_includes ) { - $includevalid = 1 if $incname =~ /$incmatch/; - } - $includevalid = 1 if $incname =~ /abstractions/; - next if ( $includevalid == 0 ); - - ($cm, @m) = matchinclude($incname, $path); - if($cm && contains($cm, $mode)) { - unless(grep { $_ eq "/**" } @m) { - push @newincludes, $incname; - } - } + # we want to ignore duplicates for things other than executes... + next if $seen{$&}; + $seen{$&} = 1; } - # did any match? add them to the option list... - if(@newincludes) { - push @options, map { "#include <$_>" } sort(uniq(@newincludes)); + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))) + { + $stuffed = undef; + next; } - # include the literal path in the option list... - push @options, $path; + # currently no way to stick pipe mediation in a profile, ignore + # any messages like this + next if $detail =~ /to pipe:/; - # match the current path against the globbing list in logprof.conf - my @globs = globcommon($path); - if(@globs) { - push @matches, @globs; + # strip out extra extended attribute info since we don't currently + # have a way to specify it in the profile and instead just need to + # provide the access to the base filename + $detail =~ s/\s+extended attribute \S+//; + + # kerberos code checks to see if the krb5.conf file is world + # writable in a stupid way so we'll ignore any w accesses to + # krb5.conf + next if (($detail eq "to /etc/krb5.conf") && contains($mode, "w")); + + # strip off the (deleted) tag that gets added if it's a deleted file + $detail =~ s/\s+\(deleted\)$//; + +# next if (($detail =~ /to \/lib\/ld-/) && ($mode =~ /x/)); + + $detail =~ s/^to\s+//; + + if ($domainchange eq "change") { + add_to_tree($pid, "exec", $profile, $hat, $prog, $sdmode, $mode, $detail); + } else { + add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, $mode, $detail); } - # suggest any matching globs the user manually entered - for my $userglob (@userglobs) { - push @matches, $userglob if matchliteral($userglob, $path); + } elsif (m/(PERMITTING|REJECTING) (?:mk|rm)dir on (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { + my ($sdmode, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); + + # we want to ignore duplicates for things other than executes... + next if $seen{$&}++; + + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); + + add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, "w", $path); + + } elsif (m/(PERMITTING|REJECTING) xattr (\S+) on (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { + my ($sdmode, $xattr_op, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + # we want to ignore duplicates for things other than executes... + next if $seen{$&}++; + + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); + + my $xattrmode; + if ($xattr_op eq "get" || $xattr_op eq "list") { + $xattrmode = "r"; + } elsif ($xattr_op eq "set" || $xattr_op eq "remove") { + $xattrmode = "w"; } - # we'll take the cheesy way and order the suggested globbing - # list by length, which is usually right, but not always always - push @options, sort { length($b) <=> length($a) } - grep { $_ ne $path } - uniq(@matches); - $defaultoption = $#options + 1; - - my $severity = $sevdb->rank($path, $mode); - - my $done = 0; - while(not $done) { - - my $q = { }; - $q->{headers} = [ ]; - push @{$q->{headers}}, gettext("Profile"), combine_name($profile, $hat); - push @{$q->{headers}}, gettext("Path"), $path; - - # merge in any previous modes from this run - if($combinedmode) { - $combinedmode = collapsemode($combinedmode); - push @{$q->{headers}}, gettext("Old Mode"), $combinedmode; - $mode = collapsemode("$mode$combinedmode"); - push @{$q->{headers}}, gettext("New Mode"), $mode; - } else { - push @{$q->{headers}}, gettext("Mode"), $mode; - } - push @{$q->{headers}}, gettext("Severity"), $severity; - - $q->{options} = [ @options ]; - $q->{selected} = $defaultoption - 1; - - $q->{functions} = [ "CMD_ALLOW", "CMD_DENY", "CMD_GLOB", "CMD_GLOBEXT", "CMD_NEW", "CMD_ABORT", "CMD_FINISHED" ]; - - $q->{default} = ($sdmode eq "PERMITTING") ? "CMD_ALLOW" : "CMD_DENY"; - - $seenevents++; - - # if they just hit return, use the default answer - my ($ans, $selected) = UI_PromptUser($q); - - if($ans eq "CMD_ALLOW") { - $path = $selected; - $done = 1; - if($path =~ m/^#include <(.+)>$/) { - my $inc = $1; - - my $deleted = 0; - for my $entry (keys %{$sd{$profile}{$hat}{path}}) { - - next if $path eq $entry; - - my $cm = matchinclude($inc, $entry); - if($cm && contains($cm, $sd{$profile}{$hat}{path}{$entry})) { - delete $sd{$profile}{$hat}{path}{$entry}; - $deleted++; - } - } - - # record the new entry - $sd{$profile}{$hat}{include}{$inc} = 1; - - $changed{$profile} = 1; - UI_Info(sprintf(gettext('Adding #include <%s> to profile.'), $inc)); - UI_Info(sprintf(gettext('Deleted %s previous matching profile entries.'), $deleted)) if $deleted; - } else { - if($sd{$profile}{$hat}{path}{$path}) { - $mode = collapsemode($mode . $sd{$profile}{$hat}{path}{$path}); - } - - my $deleted = 0; - for my $entry (keys %{$sd{$profile}{$hat}{path}}) { - - next if $path eq $entry; - - if(matchregexp($path, $entry)) { - # regexp matches, add it's mode to the list to check against - if(contains($mode, $sd{$profile}{$hat}{path}{$entry})) { - delete $sd{$profile}{$hat}{path}{$entry}; - $deleted++; - } - } - } - - # record the new entry - $sd{$profile}{$hat}{path}{$path} = $mode; - - $changed{$profile} = 1; - UI_Info(sprintf(gettext('Adding %s %s to profile.'), $path, $mode)); - UI_Info(sprintf(gettext('Deleted %s previous matching profile entries.'), $deleted)) if $deleted; - } - } elsif($ans eq "CMD_DENY") { - # go on to the next entry without saving this one - $done = 1; - } elsif($ans eq "CMD_NEW") { - if($selected !~ /^#include/) { - $ans = UI_GetString(gettext("Enter new path: "), $selected); - if($ans) { - unless(matchliteral($ans, $path)) { - my $ynprompt = gettext("The specified path does not match this log entry:") . "\n\n"; - $ynprompt .= " " . gettext("Log Entry") . ": $path\n"; - $ynprompt .= " " . gettext("Entered Path") . ": $ans\n\n"; - $ynprompt .= gettext("Do you really want to use this path?"). "\n"; - - # we default to no if they just hit return... - my $key = UI_YesNo($ynprompt, "n"); - - next if $key eq "n"; - } - - # save this one for later - push @userglobs, $ans; - - push @options, $ans; - $defaultoption = $#options + 1; - } - } - } elsif($ans eq "CMD_GLOB") { - # do globbing if they don't have an include selected - unless($selected =~ /^#include/) { - my $newpath = $selected; - # do we collapse to /* or /**? - if($newpath =~ m/\/\*{1,2}$/) { - $newpath =~ s/\/[^\/]+\/\*{1,2}$/\/\*\*/; - } else { - $newpath =~ s/\/[^\/]+$/\/\*/; - } - if($newpath ne $selected) { - push @options, $newpath; - $defaultoption = $#options + 1; - } - } - } elsif($ans eq "CMD_GLOBEXT") { - # do globbing if they don't have an include selected - unless($selected =~ /^#include/) { - my $newpath = $selected; - # do we collapse to /*.ext or /**.ext? - if($newpath =~ m/\/\*{1,2}\.[^\/]+$/) { - $newpath =~ s/\/[^\/]+\/\*{1,2}(\.[^\/]+)$/\/\*\*$1/; - } else { - $newpath =~ s/\/[^\/]+(\.[^\/]+)$/\/\*$1/; - } - if($newpath ne $selected) { - push @options, $newpath; - $defaultoption = $#options + 1; - } - } - } elsif($ans =~ /\d/) { - $defaultoption = $ans; - } elsif($ans eq "CMD_ABORT") { - $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Abandoning all changes.")); - shutdown_yast(); - exit 0; - } - } elsif($ans eq "CMD_FINISHED") { - $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); - if($ans eq "y") { - UI_Info(gettext("Saving all changes.")); - $finishing = 1; - # XXX - BUGBUG - this is REALLY nasty, but i'm in a hurry... - goto SAVE_PROFILES; - } - } + if ($xattrmode) { + add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, $xattrmode, $path); } - } + + } elsif (m/(PERMITTING|REJECTING) attribute \((.*?)\) change to (.+) \((.+)\((\d+)\) profile (.+) active (.+)\)/) { + my ($sdmode, $change, $path, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + # we want to ignore duplicates for things other than executes... + next if $seen{$&}; + $seen{$&} = 1; + + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); + + # kerberos code checks to see if the krb5.conf file is world + # writable in a stupid way so we'll ignore any w accesses to + # krb5.conf + next if $path eq "/etc/krb5.conf"; + + add_to_tree($pid, "path", $profile, $hat, $prog, $sdmode, "w", $path); + + } elsif (m/(PERMITTING|REJECTING) access to capability '(\S+)' \((.+)\((\d+)\) profile (.+) active (.+)\)/) { + my ($sdmode, $capability, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); + + next if $seen{$&}; + + $seen{$&} = 1; + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); + + add_to_tree($pid, "capability", $profile, $hat, $prog, $sdmode, $capability); + + } elsif (m/Fork parent (\d+) child (\d+) profile (.+) active (.+)/ + || m/LOGPROF-HINT fork pid=(\d+) child=(\d+) profile=(.+) active=(.+)/ + || m/LOGPROF-HINT fork pid=(\d+) child=(\d+)/) + { + my ($parent, $child, $profile, $hat) = ($1, $2, $3, $4); + + $profile ||= "null-complain-profile"; + $hat ||= "null-complain-profile"; + + $last = $&; + + # we want to ignore entries for profiles that don't exist - they're + # most likely broken entries or old entries for deleted profiles + next + if ( ($profile ne 'null-complain-profile') + && (!profile_exists($profile))); + + my $arrayref = []; + if (exists $pid{$parent}) { + push @{ $pid{$parent} }, $arrayref; + } else { + push @log, $arrayref; + } + $pid{$child} = $arrayref; + push @{$arrayref}, [ "fork", $child . $profile, $hat ]; + } else { + $DEBUGGING && debug "UNHANDLED: $_"; } - } } - } + close(LOG); - if($UI_Mode eq "yast") { - if(not $running_under_genprof) { - if($seenevents) { - my $w = { type => "wizard" }; - $w->{explanation} = gettext("The profile analyzer has completed processing the log files.\nAll updated profiles will be reloaded"); - $w->{functions} = [ "CMD_ABORT", "CMD_FINISHED" ]; - SendDataToYast($w); - my $foo = GetDataFromYast(); - } else { - my $w = { type => "wizard" }; - $w->{explanation} = gettext("No unhandled AppArmor events were found in the system log."); - $w->{functions} = [ "CMD_ABORT", "CMD_FINISHED" ]; - SendDataToYast($w); - my $foo = GetDataFromYast(); - } + for my $root (@log) { + handlechildren(undef, undef, $root); } - } -SAVE_PROFILES: - # make sure the profile changes we've made are saved to disk... - for my $profile (sort keys %changed) { - writeprofile($profile); - reload($profile); - } + for my $pid (sort { $a <=> $b } keys %profilechanges) { + setprocess($pid, $profilechanges{$pid}); + } - # if they hit "Finish" we need to tell the caller that so we can exit - # all the way instead of just going back to the genprof prompt - return $finishing ? "FINISHED" : "NORMAL"; + collapselog(); + + my $found; + + # do the magic foo-foo + for my $sdmode (sort keys %log) { + + # let them know what sort of changes we're about to list... + if ($sdmode eq "PERMITTING") { + UI_Info(gettext("Complain-mode changes:")); + } elsif ($sdmode eq "REJECTING") { + UI_Info(gettext("Enforce-mode changes:")); + } else { + + # if we're not permitting and not rejecting, something's broken. + # most likely the code we're using to build the hash tree of log + # entries - this should never ever happen + fatal_error(sprintf(gettext('Invalid mode found: %s'), $sdmode)); + } + + for my $profile (sort keys %{ $log{$sdmode} }) { + + $found++; + + # this sorts the list of hats, but makes sure that the containing + # profile shows up in the list first to keep the question order + # rational + my @hats = + grep { $_ ne $profile } keys %{ $log{$sdmode}{$profile} }; + unshift @hats, $profile + if defined $log{$sdmode}{$profile}{$profile}; + + for my $hat (@hats) { + + # step through all the capabilities first... + for my $capability (sort keys %{ $log{$sdmode}{$profile}{$hat}{capability} }) { + + # we don't care about it if we've already added it to the + # profile + next if $sd{$profile}{$hat}{capability}{$capability}; + + my $severity = $sevdb->rank(uc("cap_$capability")); + + my $q = {}; + $q->{headers} = []; + push @{ $q->{headers} }, gettext("Profile"), combine_name($profile, $hat); + push @{ $q->{headers} }, gettext("Capability"), $capability; + push @{ $q->{headers} }, gettext("Severity"), $severity; + + $q->{functions} = [ "CMD_ALLOW", "CMD_DENY", "CMD_ABORT", "CMD_FINISHED" ]; + + # complain-mode events default to allow - enforce defaults + # to deny + $q->{default} = ($sdmode eq "PERMITTING") ? "CMD_ALLOW" : "CMD_DENY"; + + $seenevents++; + + # what did the grand exalted master tell us to do? + my ($ans, $arg) = UI_PromptUser($q); + + if ($ans eq "CMD_ALLOW") { + + # they picked (a)llow, so... + + # stick the capability into the profile + $sd{$profile}{$hat}{capability}{$capability} = 1; + + # mark this profile as changed + $changed{$profile} = 1; + + # give a little feedback to the user + UI_Info(sprintf(gettext('Adding capability %s to profile.'), $capability)); + } elsif ($ans eq "CMD_DENY") { + UI_Info(sprintf(gettext('Denying capability %s to profile.'), $capability)); + } elsif ($ans eq "CMD_ABORT") { + + # if we're in yast, they've already been asked for + # confirmation + if ($UI_Mode eq "yast") { + UI_Info(gettext("Abandoning all changes.")); + shutdown_yast(); + exit 0; + } + my $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Abandoning all changes.")); + shutdown_yast(); + exit 0; + } else { + redo; + } + } elsif ($ans eq "CMD_FINISHED") { + + # if we're in yast, they've already been asked for + # confirmation + if ($UI_Mode eq "yast") { + UI_Info(gettext("Saving all changes.")); + $finishing = 1; + + # XXX - BUGBUG - this is REALLY nasty, but i'm in + # a hurry... + goto SAVE_PROFILES; + } + my $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Saving all changes.")); + $finishing = 1; + + # XXX - BUGBUG - this is REALLY nasty, but i'm in + # a hurry... + goto SAVE_PROFILES; + } else { + redo; + } + } + } + + # and then step through all of the path entries... + for my $path (sort keys %{ $log{$sdmode}{$profile}{$hat}{path} }) { + + my $mode = $log{$sdmode}{$profile}{$hat}{path}{$path}; + + # if we had an access(X_OK) request or some other kind of + # event that generates a "PERMITTING x" syslog entry, + # first check if it was already dealt with by a i/p/x + # question due to a exec(). if not, ask about adding ix + # permission. + if ($mode =~ /X/) { + + # get rid of the access() markers. + $mode =~ s/X//g; + + my $combinedmode = ""; + + my ($cm, @m); + + # does path match any regexps in original profile? + ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); + $combinedmode .= $cm if $cm; + + # does path match anything pulled in by includes in + # original profile? + ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); + $combinedmode .= $cm if $cm; + + if ($combinedmode) { + if ( contains($combinedmode, "ix") + || contains($combinedmode, "px") + || contains($combinedmode, "ux") + || contains($combinedmode, "Px") + || contains($combinedmode, "Ux")) + { + } else { + $mode .= "ix"; + } + } else { + $mode .= "ix"; + } + } + + # if we had an mmap(PROT_EXEC) request, first check if we + # already have added an ix rule to the profile + if ($mode =~ /m/) { + my $combinedmode = ""; + my ($cm, @m); + + # does path match any regexps in original profile? + ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); + $combinedmode .= $cm if $cm; + + # does path match anything pulled in by includes in + # original profile? + ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); + $combinedmode .= $cm if $cm; + + # ix implies m. don't ask if they want to add an "m" + # rule when we already have a matching ix rule. + if ($combinedmode && contains($combinedmode, "ix")) { + $mode =~ s/m//g; + } + } + + next unless $mode; + + my $combinedmode = ""; + my @matches; + + my ($cm, @m); + + # does path match any regexps in original profile? + ($cm, @m) = rematchfrag($sd{$profile}{$hat}, $path); + if ($cm) { + $combinedmode .= $cm; + push @matches, @m; + } + + # does path match anything pulled in by includes in + # original profile? + ($cm, @m) = matchincludes($sd{$profile}{$hat}, $path); + if ($cm) { + $combinedmode .= $cm; + push @matches, @m; + } + + unless ($combinedmode && contains($combinedmode, $mode)) { + + my $defaultoption = 1; + my @options = (); + + # check the path against the available set of include + # files + my @newincludes; + my $includevalid; + for my $incname (keys %include) { + $includevalid = 0; + + # don't suggest it if we're already including it, + # that's dumb + next if $sd{$profile}{$hat}{$incname}; + + # only match includes that can be suggested to + # the user + for my $incmatch (@custom_includes) { + $includevalid = 1 if $incname =~ /$incmatch/; + } + $includevalid = 1 if $incname =~ /abstractions/; + next if ($includevalid == 0); + + ($cm, @m) = matchinclude($incname, $path); + if ($cm && contains($cm, $mode)) { + unless (grep { $_ eq "/**" } @m) { + push @newincludes, $incname; + } + } + } + + # did any match? add them to the option list... + if (@newincludes) { + push @options, + map { "#include <$_>" } + sort(uniq(@newincludes)); + } + + # include the literal path in the option list... + push @options, $path; + + # match the current path against the globbing list in + # logprof.conf + my @globs = globcommon($path); + if (@globs) { + push @matches, @globs; + } + + # suggest any matching globs the user manually entered + for my $userglob (@userglobs) { + push @matches, $userglob + if matchliteral($userglob, $path); + } + + # we'll take the cheesy way and order the suggested + # globbing list by length, which is usually right, + # but not always always + push @options, + sort { length($b) <=> length($a) } + grep { $_ ne $path } + uniq(@matches); + $defaultoption = $#options + 1; + + my $severity = $sevdb->rank($path, $mode); + + my $done = 0; + while (not $done) { + + my $q = {}; + $q->{headers} = []; + push @{ $q->{headers} }, gettext("Profile"), combine_name($profile, $hat); + push @{ $q->{headers} }, gettext("Path"), $path; + + # merge in any previous modes from this run + if ($combinedmode) { + $combinedmode = collapsemode($combinedmode); + push @{ $q->{headers} }, gettext("Old Mode"), $combinedmode; + $mode = collapsemode("$mode$combinedmode"); + push @{ $q->{headers} }, gettext("New Mode"), $mode; + } else { + push @{ $q->{headers} }, gettext("Mode"), $mode; + } + push @{ $q->{headers} }, gettext("Severity"), $severity; + + $q->{options} = [@options]; + $q->{selected} = $defaultoption - 1; + + $q->{functions} = [ "CMD_ALLOW", "CMD_DENY", "CMD_GLOB", "CMD_GLOBEXT", "CMD_NEW", "CMD_ABORT", "CMD_FINISHED" ]; + + $q->{default} = + ($sdmode eq "PERMITTING") + ? "CMD_ALLOW" + : "CMD_DENY"; + + $seenevents++; + + # if they just hit return, use the default answer + my ($ans, $selected) = UI_PromptUser($q); + + if ($ans eq "CMD_ALLOW") { + $path = $selected; + $done = 1; + if ($path =~ m/^#include <(.+)>$/) { + my $inc = $1; + + my $deleted = 0; + for my $entry (keys %{ $sd{$profile}{$hat}{path} }) { + + next if $path eq $entry; + + my $cm = matchinclude($inc, $entry); + if ($cm + && contains($cm, $sd{$profile}{$hat}{path}{$entry})) + { + delete $sd{$profile}{$hat}{path}{$entry}; + $deleted++; + } + } + + # record the new entry + $sd{$profile}{$hat}{include}{$inc} = 1; + + $changed{$profile} = 1; + UI_Info(sprintf(gettext('Adding #include <%s> to profile.'), $inc)); + UI_Info(sprintf(gettext('Deleted %s previous matching profile entries.'), $deleted)) if $deleted; + } else { + if ($sd{$profile}{$hat}{path}{$path}) { + $mode = collapsemode($mode . $sd{$profile}{$hat}{path}{$path}); + } + + my $deleted = 0; + for my $entry (keys %{ $sd{$profile}{$hat}{path} }) { + + next if $path eq $entry; + + if (matchregexp($path, $entry)) { + + # regexp matches, add it's mode to + # the list to check against + if (contains($mode, $sd{$profile}{$hat}{path}{$entry})) { + delete $sd{$profile}{$hat}{path}{$entry}; + $deleted++; + } + } + } + + # record the new entry + $sd{$profile}{$hat}{path}{$path} = $mode; + + $changed{$profile} = 1; + UI_Info(sprintf(gettext('Adding %s %s to profile.'), $path, $mode)); + UI_Info(sprintf(gettext('Deleted %s previous matching profile entries.'), $deleted)) if $deleted; + } + } elsif ($ans eq "CMD_DENY") { + + # go on to the next entry without saving this + # one + $done = 1; + } elsif ($ans eq "CMD_NEW") { + if ($selected !~ /^#include/) { + $ans = UI_GetString(gettext("Enter new path: "), $selected); + if ($ans) { + unless (matchliteral($ans, $path)) { + my $ynprompt = gettext("The specified path does not match this log entry:") . "\n\n"; + $ynprompt .= " " . gettext("Log Entry") . ": $path\n"; + $ynprompt .= " " . gettext("Entered Path") . ": $ans\n\n"; + $ynprompt .= gettext("Do you really want to use this path?") . "\n"; + + # we default to no if they just hit return... + my $key = UI_YesNo($ynprompt, "n"); + + next if $key eq "n"; + } + + # save this one for later + push @userglobs, $ans; + + push @options, $ans; + $defaultoption = $#options + 1; + } + } + } elsif ($ans eq "CMD_GLOB") { + + # do globbing if they don't have an include + # selected + unless ($selected =~ /^#include/) { + my $newpath = $selected; + + # do we collapse to /* or /**? + if ($newpath =~ m/\/\*{1,2}$/) { + $newpath =~ s/\/[^\/]+\/\*{1,2}$/\/\*\*/; + } else { + $newpath =~ s/\/[^\/]+$/\/\*/; + } + if ($newpath ne $selected) { + push @options, $newpath; + $defaultoption = $#options + 1; + } + } + } elsif ($ans eq "CMD_GLOBEXT") { + + # do globbing if they don't have an include + # selected + unless ($selected =~ /^#include/) { + my $newpath = $selected; + + # do we collapse to /*.ext or /**.ext? + if ($newpath =~ m/\/\*{1,2}\.[^\/]+$/) { + $newpath =~ s/\/[^\/]+\/\*{1,2}(\.[^\/]+)$/\/\*\*$1/; + } else { + $newpath =~ s/\/[^\/]+(\.[^\/]+)$/\/\*$1/; + } + if ($newpath ne $selected) { + push @options, $newpath; + $defaultoption = $#options + 1; + } + } + } elsif ($ans =~ /\d/) { + $defaultoption = $ans; + } elsif ($ans eq "CMD_ABORT") { + $ans = UI_YesNo(gettext("Are you sure you want to abandon this set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Abandoning all changes.")); + shutdown_yast(); + exit 0; + } + } elsif ($ans eq "CMD_FINISHED") { + $ans = UI_YesNo(gettext("Are you sure you want to save the current set of profile changes and exit?"), "n"); + if ($ans eq "y") { + UI_Info(gettext("Saving all changes.")); + $finishing = 1; + + # XXX - BUGBUG - this is REALLY nasty, but + # i'm in a hurry... + goto SAVE_PROFILES; + } + } + } + } + } + } + } + } + + if ($UI_Mode eq "yast") { + if (not $running_under_genprof) { + if ($seenevents) { + my $w = { type => "wizard" }; + $w->{explanation} = gettext("The profile analyzer has completed processing the log files.\nAll updated profiles will be reloaded"); + $w->{functions} = [ "CMD_ABORT", "CMD_FINISHED" ]; + SendDataToYast($w); + my $foo = GetDataFromYast(); + } else { + my $w = { type => "wizard" }; + $w->{explanation} = gettext("No unhandled AppArmor events were found in the system log."); + $w->{functions} = [ "CMD_ABORT", "CMD_FINISHED" ]; + SendDataToYast($w); + my $foo = GetDataFromYast(); + } + } + } + + SAVE_PROFILES: + + # make sure the profile changes we've made are saved to disk... + for my $profile (sort keys %changed) { + writeprofile($profile); + reload($profile); + } + + # if they hit "Finish" we need to tell the caller that so we can exit + # all the way instead of just going back to the genprof prompt + return $finishing ? "FINISHED" : "NORMAL"; } sub setprocess ($$) { - my ($pid, $profile) = @_; + my ($pid, $profile) = @_; - # don't do anything if the process exited already... - return unless -e "/proc/$pid/attr/current"; + # don't do anything if the process exited already... + return unless -e "/proc/$pid/attr/current"; - return unless open(CURR, "/proc/$pid/attr/current"); - my $current = ; - chomp $current; - close(CURR); + return unless open(CURR, "/proc/$pid/attr/current"); + my $current = ; + chomp $current; + close(CURR); - # only change null profiles - return unless $current =~ /null(-complain)*-profile/; + # only change null profiles + return unless $current =~ /null(-complain)*-profile/; - return unless open(STAT, "/proc/$pid/stat"); - my $stat = ; - chomp $stat; - close(STAT); + return unless open(STAT, "/proc/$pid/stat"); + my $stat = ; + chomp $stat; + close(STAT); - return unless $stat =~ /^\d+ \((\S+)\) /; - my $currprog = $1; + return unless $stat =~ /^\d+ \((\S+)\) /; + my $currprog = $1; - open(CURR, ">/proc/$pid/attr/current") or return; - print CURR "setprofile $profile"; - close(CURR); + open(CURR, ">/proc/$pid/attr/current") or return; + print CURR "setprofile $profile"; + close(CURR); } sub collapselog () { - for my $sdmode (keys %prelog) { - for my $profile (keys %{$prelog{$sdmode}}) { - for my $hat (keys %{$prelog{$sdmode}{$profile}}) { - for my $path (keys %{$prelog{$sdmode}{$profile}{$hat}{path}}) { + for my $sdmode (keys %prelog) { + for my $profile (keys %{ $prelog{$sdmode} }) { + for my $hat (keys %{ $prelog{$sdmode}{$profile} }) { + for my $path (keys %{ $prelog{$sdmode}{$profile}{$hat}{path} }) { - my $mode = $prelog{$sdmode}{$profile}{$hat}{path}{$path}; + my $mode = $prelog{$sdmode}{$profile}{$hat}{path}{$path}; - # we want to ignore anything from the log that's already in the profile - my $combinedmode = ""; + # we want to ignore anything from the log that's already + # in the profile + my $combinedmode = ""; - # is it in the original profile? - if($sd{$profile}{$hat}{path}{$path}) { - $combinedmode .= $sd{$profile}{$hat}{path}{$path}; - } + # is it in the original profile? + if ($sd{$profile}{$hat}{path}{$path}) { + $combinedmode .= $sd{$profile}{$hat}{path}{$path}; + } - # does path match any regexps in original profile? - $combinedmode .= rematchfrag($sd{$profile}{$hat}, $path); + # does path match any regexps in original profile? + $combinedmode .= rematchfrag($sd{$profile}{$hat}, $path); - # does path match anything pulled in by includes in original profile? - $combinedmode .= matchincludes($sd{$profile}{$hat}, $path); + # does path match anything pulled in by includes in + # original profile? + $combinedmode .= matchincludes($sd{$profile}{$hat}, $path); - # if we found any matching entries, do the modes match? - unless($combinedmode && contains($combinedmode, $mode)) { + # if we found any matching entries, do the modes match? + unless ($combinedmode && contains($combinedmode, $mode)) { - # merge in any previous modes from this run - if($log{$sdmode}{$profile}{$hat}{path}{$path}) { - $mode = collapsemode($mode . $log{$sdmode}{$profile}{$hat}{path}{$path}); + # merge in any previous modes from this run + if ($log{$sdmode}{$profile}{$hat}{path}{$path}) { + $mode = collapsemode($mode . $log{$sdmode}{$profile}{$hat}{path}{$path}); + } + + # record the new entry + $log{$sdmode}{$profile}{$hat}{path}{$path} = collapsemode($mode); + } + } + + for my $capability (keys %{ $prelog{$sdmode}{$profile}{$hat}{capability} }) { + + # if we don't already have this capability in the profile, + # add it + unless ($sd{$profile}{$hat}{capability}{$capability}) { + $log{$sdmode}{$profile}{$hat}{capability}{$capability} = 1; + } + } } - - # record the new entry - $log{$sdmode}{$profile}{$hat}{path}{$path} = collapsemode($mode); - } } - - for my $capability (keys %{$prelog{$sdmode}{$profile}{$hat}{capability}}) { - - # if we don't already have this capability in the profile, add it - unless($sd{$profile}{$hat}{capability}{$capability}) { - $log{$sdmode}{$profile}{$hat}{capability}{$capability} = 1; - } - } - } } - } } sub profilemode ($) { - my $mode = shift; + my $mode = shift; - my $modifier = ($mode =~ m/[iupUP]/)[0]; - if($modifier) { - $mode =~ s/[iupUPx]//g; - $mode .= $modifier . "x"; - } + my $modifier = ($mode =~ m/[iupUP]/)[0]; + if ($modifier) { + $mode =~ s/[iupUPx]//g; + $mode .= $modifier . "x"; + } - return $mode; + return $mode; } # kinky. -sub commonprefix (@) { (join( "\0", @_) =~ m/^([^\0]*)[^\0]*(\0\1[^\0]*)*$/)[0] } -sub commonsuffix (@) { reverse(((reverse join( "\0", @_)) =~ m/^([^\0]*)[^\0]*(\0\1[^\0]*)*$/)[0]) } +sub commonprefix (@) { (join("\0", @_) =~ m/^([^\0]*)[^\0]*(\0\1[^\0]*)*$/)[0] } +sub commonsuffix (@) { reverse(((reverse join("\0", @_)) =~ m/^([^\0]*)[^\0]*(\0\1[^\0]*)*$/)[0]); } sub uniq (@) { - my %seen; - my @result = sort grep { ! $seen{$_}++ } @_; - return @result; + my %seen; + my @result = sort grep { !$seen{$_}++ } @_; + return @result; } sub collapsemode ($) { - my $old = shift; + my $old = shift; - my %seen; - my $new = join "", - sort - grep { ! $seen{$_}++ } - $old =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g; - return $new; + my %seen; + my $new = join "", sort + grep { !$seen{$_}++ } $old =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g; + return $new; } sub contains ($$) { - my ($glob, $single) = @_; + my ($glob, $single) = @_; - $glob = "" unless defined $glob; + $glob = "" unless defined $glob; - my %h; - $h{$_}++ for ($glob =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g); + my %h; + $h{$_}++ for ($glob =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g); - for my $mode ($single =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g) { - return 0 unless $h{$mode}; - } + for my $mode ($single =~ m/\G(r|w|l|m|ix|px|ux|Px|Ux)/g) { + return 0 unless $h{$mode}; + } - return 1; + return 1; } - sub checkIncludeSyntax($) { - my $errors = shift; + my $errors = shift; - - if(opendir(SDDIR, $profiledir )) { - my @incdirs = grep { (! /^\./) && (-d "$profiledir/$_") } readdir(SDDIR); - close(SDDIR); - while(my $id = shift @incdirs) { - if(opendir(SDDIR, "$profiledir/$id" )) { - for my $path (grep { ! /^\./ } readdir(SDDIR)) { - chomp($path); - next if $path =~ /\.rpm(save|new)$/; - if(-f "$profiledir/$id/$path") { - my $file = "$id/$path"; - $file =~ s/$profiledir\///; - my $err = loadinclude($file, \&printMessageErrorHandler); - if ( $err ne 0 ) { - push @$errors, $err; - } - } elsif(-d "$id/$path") { - push @incdirs, "$id/$path"; - } - } - closedir(SDDIR); - } - } - } - return $errors; + if (opendir(SDDIR, $profiledir)) { + my @incdirs = grep { (!/^\./) && (-d "$profiledir/$_") } readdir(SDDIR); + close(SDDIR); + while (my $id = shift @incdirs) { + if (opendir(SDDIR, "$profiledir/$id")) { + for my $path (grep { !/^\./ } readdir(SDDIR)) { + chomp($path); + next if $path =~ /\.rpm(save|new)$/; + if (-f "$profiledir/$id/$path") { + my $file = "$id/$path"; + $file =~ s/$profiledir\///; + my $err = loadinclude($file, \&printMessageErrorHandler); + if ($err ne 0) { + push @$errors, $err; + } + } elsif (-d "$id/$path") { + push @incdirs, "$id/$path"; + } + } + closedir(SDDIR); + } + } + } + return $errors; } sub checkProfileSyntax ($) { - my $errors = shift; - # Check the syntax of profiles + my $errors = shift; - opendir(SDDIR, $profiledir) or fatal_error "Can't read AppArmor profiles in $profiledir."; - for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) { - next if $file =~ /\.rpm(save|new)$/; - my $err = readprofile( "$profiledir/$file", \&printMessageErrorHandler); - if ( defined $err and $err ne 1) { - push @$errors, $err; + # Check the syntax of profiles + + opendir(SDDIR, $profiledir) + or fatal_error "Can't read AppArmor profiles in $profiledir."; + for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) { + next if $file =~ /\.rpm(save|new)$/; + my $err = readprofile("$profiledir/$file", \&printMessageErrorHandler); + if (defined $err and $err ne 1) { + push @$errors, $err; + } } - } - closedir(SDDIR); - return $errors; + closedir(SDDIR); + return $errors; } sub printMessageErrorHandler ($) { - my $message = shift; - return $message + my $message = shift; + return $message; } sub readprofiles () { - opendir(SDDIR, $profiledir) or fatal_error "Can't read AppArmor profiles in $profiledir."; - for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) { - next if $file =~ /\.rpm(save|new)$/; - readprofile("$profiledir/$file", \&fatal_error); - } - closedir(SDDIR); + opendir(SDDIR, $profiledir) + or fatal_error "Can't read AppArmor profiles in $profiledir."; + for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) { + next if $file =~ /\.rpm(save|new)$/; + readprofile("$profiledir/$file", \&fatal_error); + } + closedir(SDDIR); } sub readprofile ($$) { - my $file = shift; - my $error_handler = shift; - if(open(SDPROF, "$file")) { - my ($profile, $hat, $in_contained_hat); - my $initial_comment = ""; - while() { - chomp; + my $file = shift; + my $error_handler = shift; + if (open(SDPROF, "$file")) { + my ($profile, $hat, $in_contained_hat); + my $initial_comment = ""; + while () { + chomp; - # we don't care about blank lines - next if /^\s*$/; + # we don't care about blank lines + next if /^\s*$/; - # start of a profile... - if(m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) { + # start of a profile... + if (m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) { - # if we run into the start of a profile while we're already in a - # profile, something's wrong... - if($profile) { - return &$error_handler( "$profile profile in $file contains syntax errors."); - } - - # we hit the start of a profile, keep track of it... - $profile = $1; - my $flags = $2; - $in_contained_hat = 0; - - # hat is same as profile name if we're not in a hat - ($profile, $hat) = split /\^/, $profile; - - # deal with whitespace in profile and hat names. - $profile = $1 if $profile =~ /^"(.+)"$/; - $hat = $1 if $hat =~ /^"(.+)"$/; - - # if we run into old-style hat declarations mark the profile as - # changed so we'll write it out as new-style - if($hat && $hat ne $profile) { - $changed{$profile} = 1; - } - - $hat ||= $profile; - - # keep track of profile flags - if($flags && $flags =~ /^flags=\((.+)\)\s*$/) { - $flags = $1; - $sd{$profile}{$hat}{flags} = $flags; - } - - $sd{$profile}{$hat}{netdomain} = []; - - # store off initial comment if they have one - $sd{$profile}{$hat}{initial_comment} = $initial_comment if $initial_comment; - $initial_comment = ""; - - } elsif(m/^\s*\}\s*$/) { # end of a profile... - - # if we hit the end of a profile when we're not in one, something's - # wrong... - if(not $profile) { - return &$error_handler( sprintf(gettext('%s contains syntax errors.'), $file)); - } - - if($in_contained_hat) { - $hat = $profile; - $in_contained_hat = 0; - } else { - - # if we're finishing a profile, make sure that any required - # infrastructure hats for this changehat application exist - for my $hatglob (keys %required_hats) { - if($profile =~ /$hatglob/) { - for my $hat (split(/\s+/, $required_hats{$hatglob})) { - unless($sd{$profile}{$hat}) { - $sd{$profile}{$hat} = { }; - # if we had to auto-instantiate a hat, we want to write out - # an updated version of the profile - $changed{$profile} = 1; + # if we run into the start of a profile while we're already in a + # profile, something's wrong... + if ($profile) { + return &$error_handler("$profile profile in $file contains syntax errors."); } - } + + # we hit the start of a profile, keep track of it... + $profile = $1; + my $flags = $2; + $in_contained_hat = 0; + + # hat is same as profile name if we're not in a hat + ($profile, $hat) = split /\^/, $profile; + + # deal with whitespace in profile and hat names. + $profile = $1 if $profile =~ /^"(.+)"$/; + $hat = $1 if $hat =~ /^"(.+)"$/; + + # if we run into old-style hat declarations mark the profile as + # changed so we'll write it out as new-style + if ($hat && $hat ne $profile) { + $changed{$profile} = 1; + } + + $hat ||= $profile; + + # keep track of profile flags + if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) { + $flags = $1; + $sd{$profile}{$hat}{flags} = $flags; + } + + $sd{$profile}{$hat}{netdomain} = []; + + # store off initial comment if they have one + $sd{$profile}{$hat}{initial_comment} = $initial_comment + if $initial_comment; + $initial_comment = ""; + + } elsif (m/^\s*\}\s*$/) { # end of a profile... + + # if we hit the end of a profile when we're not in one, + # something's wrong... + if (not $profile) { + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + } + + if ($in_contained_hat) { + $hat = $profile; + $in_contained_hat = 0; + } else { + + # if we're finishing a profile, make sure that any required + # infrastructure hats for this changehat application exist + for my $hatglob (keys %required_hats) { + if ($profile =~ /$hatglob/) { + for my $hat (split(/\s+/, $required_hats{$hatglob})) { + unless ($sd{$profile}{$hat}) { + $sd{$profile}{$hat} = {}; + + # if we had to auto-instantiate a hat, we + # want to write out an updated version of + # the profile + $changed{$profile} = 1; + } + } + } + } + + # mark that we're outside of a profile now... + $profile = undef; + $initial_comment = ""; + } + + } elsif (m/^\s*capability\s+(\S+)\s*,\s*$/) { # capability entry + if (not $profile) { + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + } + + my $capability = $1; + $sd{$profile}{$hat}{capability}{$capability} = 1; + + } elsif (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { # boolean definition + } elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { # variable additions + } elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { # variable definitions + } elsif (m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { # conditional -- boolean + } elsif (m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- variable defined + } elsif (m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- boolean defined + } elsif (m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { # path entry + if (not $profile) { + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + } + + my ($path, $mode) = ($1, $2); + + # strip off any trailing spaces. + $path =~ s/\s+$//; + + $path = $1 if $path =~ /^"(.+)"$/; + + # make sure they don't have broken regexps in the profile + my $p_re = convert_regexp($path); + eval { "foo" =~ m/^$p_re$/; }; + if ($@) { + return &$error_handler(sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path)); + } + + $sd{$profile}{$hat}{path}{$path} = $mode; + + } elsif (m/^\s*#include <(.+)>\s*$/) { # include stuff + my $include = $1; + + if ($profile) { + $sd{$profile}{$hat}{include}{$include} = 1; + } else { + unless (exists $variables{$file}) { + $variables{$file} = {}; + } + $variables{$file}{ "#" . $include } = 1; # sorry + } + my $ret = loadinclude($include, $error_handler); + return $ret if ($ret != 0); + + } elsif (/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) { + if (not $profile) { + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + } + + # XXX - BUGBUGBUG - don't strip netdomain entries + + unless ($sd{$profile}{$hat}{netdomain}) { + $sd{$profile}{$hat}{netdomain} = []; + } + + # strip leading spaces and trailing comma + s/^\s+//; + s/,\s*$//; + + # keep track of netdomain entries... + push @{ $sd{$profile}{$hat}{netdomain} }, $_; + + } elsif (m/^\s*\^(\"?.+?)\s+(flags=\(.+\)\s+)*\{\s*$/) { + # start of a hat + + # if we hit the start of a contained hat when we're not + # in a profile something is wrong... + if (not $profile) { + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + } + + $in_contained_hat = 1; + + # we hit the start of a hat inside the current profile + $hat = $1; + my $flags = $2; + + # deal with whitespace in hat names. + $hat = $1 if $hat =~ /^"(.+)"$/; + + # keep track of profile flags + if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) { + $flags = $1; + $sd{$profile}{$hat}{flags} = $flags; + } + + $sd{$profile}{$hat}{path} = {}; + $sd{$profile}{$hat}{netdomain} = []; + + # store off initial comment if they have one + $sd{$profile}{$hat}{initial_comment} = $initial_comment + if $initial_comment; + $initial_comment = ""; + + } elsif (/^\s*\#/) { + + # we only currently handle initial comments + if (not $profile) { + + # ignore vim syntax highlighting lines + next if /^\s*\# vim:syntax/; + + # ignore Last Modified: lines + next if /^\s*\# Last Modified:/; + $initial_comment .= "$_\n"; + } + } else { + + # we hit something we don't understand in a profile... + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); } - } - - # mark that we're outside of a profile now... - $profile = undef; - $initial_comment = ""; } - } elsif(m/^\s*capability\s+(\S+)\s*,\s*$/) { # capability entry - if(not $profile) { - return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); + # if we're still in a profile when we hit the end of the file, it's bad + if ($profile) { + return &$error_handler("Reached the end of $file while we were still inside the $profile profile."); } - my $capability = $1; - $sd{$profile}{$hat}{capability}{$capability} = 1; - - } elsif(/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { # boolean definition - } elsif(/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { # variable additions - } elsif(/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { # variable definitions - } elsif(m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { # conditional -- boolean - } elsif(m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- variable defined - } elsif(m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- boolean defined - } elsif(m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { # path entry - if(not $profile) { - return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); - } - - my ($path, $mode) = ($1, $2); - - # strip off any trailing spaces. - $path =~ s/\s+$//; - - $path = $1 if $path =~ /^"(.+)"$/; - - # make sure they don't have broken regexps in the profile - my $p_re = convert_regexp($path); - eval { "foo" =~ m/^$p_re$/; }; - if($@) { - return &$error_handler(sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path)); - } - - $sd{$profile}{$hat}{path}{$path} = $mode; - - } elsif(m/^\s*#include <(.+)>\s*$/) { # include stuff - my $include = $1; - - if ($profile) { - $sd{$profile}{$hat}{include}{$include} = 1; - } else { - unless (exists $variables{$file}) { - $variables{$file} = { }; - } - $variables{$file}{"#" . $include} = 1; # sorry - } - my $ret = loadinclude($include, $error_handler); - return $ret if ( $ret != 0 ); - - } elsif(/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) { - if(not $profile) { - return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); - } - - # XXX - BUGBUGBUG - don't strip netdomain entries - - unless($sd{$profile}{$hat}{netdomain}) { - $sd{$profile}{$hat}{netdomain} = [ ]; - } - - # strip leading spaces and trailing comma - s/^\s+//; - s/,\s*$//; - - # keep track of netdomain entries... - push @{$sd{$profile}{$hat}{netdomain}}, $_; - - } elsif(m/^\s*\^(\"?.+?)\s+(flags=\(.+\)\s+)*\{\s*$/) { # start of a hat - # if we hit the start of a contained hat when we're not in a profile - # something is wrong... - if(not $profile) { - return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); - } - - $in_contained_hat = 1; - - # we hit the start of a hat inside the current profile - $hat = $1; - my $flags = $2; - - # deal with whitespace in hat names. - $hat = $1 if $hat =~ /^"(.+)"$/; - - # keep track of profile flags - if($flags && $flags =~ /^flags=\((.+)\)\s*$/) { - $flags = $1; - $sd{$profile}{$hat}{flags} = $flags; - } - - $sd{$profile}{$hat}{path} = { }; - $sd{$profile}{$hat}{netdomain} = []; - - # store off initial comment if they have one - $sd{$profile}{$hat}{initial_comment} = $initial_comment if $initial_comment; - $initial_comment = ""; - - } elsif(/^\s*\#/) { - # we only currently handle initial comments - if(not $profile) { - # ignore vim syntax highlighting lines - next if /^\s*\# vim:syntax/; - # ignore Last Modified: lines - next if /^\s*\# Last Modified:/; - $initial_comment .= "$_\n"; - } - } else { - - # we hit something we don't understand in a profile... - return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); - } + close(SDPROF); + } else { + $DEBUGGING && debug "readprofile: can't read $file - skipping"; } - - # if we're still in a profile when we hit the end of the file, it's bad - if($profile) { - return &$error_handler("Reached the end of $file while we were still inside the $profile profile."); - } - - close(SDPROF); - } else { - $DEBUGGING && debug "readprofile: can't read $file - skipping"; - } } -sub escape($) { - my $dangerous = shift; - if ($dangerous =~ m/^"(.+)"$/) { - $dangerous = $1; - } - $dangerous =~ s/((?\n"; + # dump out the includes + if (exists $sd{$profile}{$hat}{include}) { + for my $include (sort keys %{ $sd{$profile}{$hat}{include} }) { + print $fh "$indent #include <$include>\n"; + } + print $fh "\n" if keys %{ $sd{$profile}{$hat}{include} }; } - print $fh "\n" if keys %{$sd{$profile}{$hat}{include}}; - } } sub writecapabilities ($$$$) { - my ($fh, $profile, $hat, $indent) = @_; + my ($fh, $profile, $hat, $indent) = @_; - # dump out the capability entries... - if(exists $sd{$profile}{$hat}{capability}) { - for my $capability (sort keys %{$sd{$profile}{$hat}{capability}}) { - print $fh "$indent capability $capability,\n"; + # dump out the capability entries... + if (exists $sd{$profile}{$hat}{capability}) { + for my $capability (sort keys %{ $sd{$profile}{$hat}{capability} }) { + print $fh "$indent capability $capability,\n"; + } + print $fh "\n" if keys %{ $sd{$profile}{$hat}{capability} }; } - print $fh "\n" if keys %{$sd{$profile}{$hat}{capability}}; - } } sub writenetdomain ($$$$) { - my ($fh, $profile, $hat, $indent) = @_; + my ($fh, $profile, $hat, $indent) = @_; - # dump out the netdomain entries... - if(exists $sd{$profile}{$hat}{netdomain}) { - for my $nd (sort @{$sd{$profile}{$hat}{netdomain}}) { - print $fh "$indent $nd,\n"; + # dump out the netdomain entries... + if (exists $sd{$profile}{$hat}{netdomain}) { + for my $nd (sort @{ $sd{$profile}{$hat}{netdomain} }) { + print $fh "$indent $nd,\n"; + } + print $fh "\n" if @{ $sd{$profile}{$hat}{netdomain} }; } - print $fh "\n" if @{$sd{$profile}{$hat}{netdomain}}; - } } sub writepaths ($$$$) { - my ($fh, $profile, $hat, $indent) = @_; + my ($fh, $profile, $hat, $indent) = @_; - if(exists $sd{$profile}{$hat}{path}) { - for my $path (sort keys %{$sd{$profile}{$hat}{path}}) { - my $mode = $sd{$profile}{$hat}{path}{$path}; + if (exists $sd{$profile}{$hat}{path}) { + for my $path (sort keys %{ $sd{$profile}{$hat}{path} }) { + my $mode = $sd{$profile}{$hat}{path}{$path}; - # strip out any fake access() modes that might have slipped through - $mode =~ s/X//g; + # strip out any fake access() modes that might have slipped through + $mode =~ s/X//g; - # deal with whitespace in path names - if($path =~ /\s/) { - print $fh "$indent \"$path\" $mode,\n"; - } else { - print $fh "$indent $path $mode,\n"; - } + # deal with whitespace in path names + if ($path =~ /\s/) { + print $fh "$indent \"$path\" $mode,\n"; + } else { + print $fh "$indent $path $mode,\n"; + } + } } - } } sub writepiece ($$) { - my ($sdprof, $profile) = @_; + my ($sdprof, $profile) = @_; - writeheader ($sdprof, $profile, $profile, ""); - writeincludes ($sdprof, $profile, $profile, ""); - writecapabilities ($sdprof, $profile, $profile, ""); - writenetdomain ($sdprof, $profile, $profile, ""); - writepaths ($sdprof, $profile, $profile, ""); + writeheader($sdprof, $profile, $profile, ""); + writeincludes($sdprof, $profile, $profile, ""); + writecapabilities($sdprof, $profile, $profile, ""); + writenetdomain($sdprof, $profile, $profile, ""); + writepaths($sdprof, $profile, $profile, ""); - for my $hat (grep { $_ ne $profile } sort keys %{$sd{$profile}}) { - # deal with whitespace in profile names... - my $h = $hat; - $h = "\"$h\"" if $h =~ /\s/; + for my $hat (grep { $_ ne $profile } sort keys %{ $sd{$profile} }) { - if($sd{$profile}{$hat}{flags}) { - print $sdprof "\n ^$h flags=($sd{$profile}{$hat}{flags}) {\n"; - } else { - print $sdprof "\n ^$h {\n"; + # deal with whitespace in profile names... + my $h = $hat; + $h = "\"$h\"" if $h =~ /\s/; + + if ($sd{$profile}{$hat}{flags}) { + print $sdprof "\n ^$h flags=($sd{$profile}{$hat}{flags}) {\n"; + } else { + print $sdprof "\n ^$h {\n"; + } + + writeincludes($sdprof, $profile, $hat, " "); + writecapabilities($sdprof, $profile, $hat, " "); + writenetdomain($sdprof, $profile, $hat, " "); + writepaths($sdprof, $profile, $hat, " "); + + print $sdprof " }\n"; } - writeincludes ($sdprof, $profile, $hat, " "); - writecapabilities ($sdprof, $profile, $hat, " "); - writenetdomain ($sdprof, $profile, $hat, " "); - writepaths ($sdprof, $profile, $hat, " "); - - print $sdprof " }\n"; - } - - print $sdprof "}\n"; + print $sdprof "}\n"; } sub writeprofile ($) { - my $profile = shift; + my $profile = shift; - UI_Info(sprintf(gettext('Writing updated profile for %s.'), $profile)); + UI_Info(sprintf(gettext('Writing updated profile for %s.'), $profile)); - my $filename = getprofilefilename($profile); + my $filename = getprofilefilename($profile); - open(SDPROF, ">$filename") or fatal_error "Can't write new AppArmor profile $filename: $!"; + open(SDPROF, ">$filename") + or fatal_error "Can't write new AppArmor profile $filename: $!"; - # stick in a vim mode line to turn on AppArmor syntax highlighting - print SDPROF "# vim:syntax=apparmor\n"; + # stick in a vim mode line to turn on AppArmor syntax highlighting + print SDPROF "# vim:syntax=apparmor\n"; - # keep track of when the file was last updated - print SDPROF "# Last Modified: " . localtime(time) . "\n"; + # keep track of when the file was last updated + print SDPROF "# Last Modified: " . localtime(time) . "\n"; - # print out initial comment - if($sd{$profile}{$profile}{initial_comment}) { - $sd{$profile}{$profile}{initial_comment} =~ s/\\n/\n/g; - print SDPROF $sd{$profile}{$profile}{initial_comment}; - print SDPROF "\n"; - } - - # dump variables defined in this file - if($variables{$filename}) { - for my $var (sort keys %{$variables{$filename}}) { - if ($var =~ m/^@/) { - my @values = sort @{$variables{$filename}{$var}}; - @values = map { escape($_) } @values; - my $values = join (" ", @values); - print SDPROF "$var = "; - print SDPROF $values; - } elsif ($var =~ m/^\$/) { - print SDPROF "$var = "; - print SDPROF ${$variables{$filename}{$var}}; - } elsif ($var =~ m/^\#/) { - my $inc = $var; - $inc =~ s/^\#//; - print SDPROF "#include <$inc>"; - } - print SDPROF "\n"; + # print out initial comment + if ($sd{$profile}{$profile}{initial_comment}) { + $sd{$profile}{$profile}{initial_comment} =~ s/\\n/\n/g; + print SDPROF $sd{$profile}{$profile}{initial_comment}; + print SDPROF "\n"; } - } - print SDPROF "\n"; + # dump variables defined in this file + if ($variables{$filename}) { + for my $var (sort keys %{ $variables{$filename} }) { + if ($var =~ m/^@/) { + my @values = sort @{ $variables{$filename}{$var} }; + @values = map { escape($_) } @values; + my $values = join(" ", @values); + print SDPROF "$var = "; + print SDPROF $values; + } elsif ($var =~ m/^\$/) { + print SDPROF "$var = "; + print SDPROF ${ $variables{$filename}{$var} }; + } elsif ($var =~ m/^\#/) { + my $inc = $var; + $inc =~ s/^\#//; + print SDPROF "#include <$inc>"; + } + print SDPROF "\n"; + } + } - writepiece(\*SDPROF, $profile); + print SDPROF "\n"; - close(SDPROF); + writepiece(\*SDPROF, $profile); + + close(SDPROF); } - - sub getprofileflags { - my $filename = shift; + my $filename = shift; - my $flags = "enforce"; + my $flags = "enforce"; - if(open(PROFILE, "$filename")) { - while() { - if(m/^\s*\/\S+\s+(flags=\(.+\)\s+)*{\s*$/) { - $flags = $1; + if (open(PROFILE, "$filename")) { + while () { + if (m/^\s*\/\S+\s+(flags=\(.+\)\s+)*{\s*$/) { + $flags = $1; + close(PROFILE); + $flags =~ s/flags=\((.+)\)/$1/; + return $flags; + } + } close(PROFILE); - $flags =~ s/flags=\((.+)\)/$1/; - return $flags; - } } - close(PROFILE); - } - return $flags; + return $flags; } - sub matchliteral { - my ($sd_regexp, $literal) = @_; + my ($sd_regexp, $literal) = @_; - my $p_regexp = convert_regexp($sd_regexp); + my $p_regexp = convert_regexp($sd_regexp); - # check the log entry against our converted regexp... - my $matches = eval { $literal =~ /^$p_regexp$/; }; + # check the log entry against our converted regexp... + my $matches = eval { $literal =~ /^$p_regexp$/; }; - # doesn't match if we've got a broken regexp - return undef if $@; + # doesn't match if we've got a broken regexp + return undef if $@; - return $matches; + return $matches; } sub reload ($) { - my $bin = shift; + my $bin = shift; - # don't try to reload profile if AppArmor is not running - return unless check_for_subdomain(); + # don't try to reload profile if AppArmor is not running + return unless check_for_subdomain(); - # don't reload the profile if the corresponding executable doesn't exist - my $fqdbin = findexecutable($bin) or return; + # don't reload the profile if the corresponding executable doesn't exist + my $fqdbin = findexecutable($bin) or return; - my $filename = getprofilefilename($fqdbin); + my $filename = getprofilefilename($fqdbin); - system("/bin/cat '$filename' | $parser -I$profiledir -r >/dev/null 2>&1"); + system("/bin/cat '$filename' | $parser -I$profiledir -r >/dev/null 2>&1"); } sub loadinclude { - my $which= shift; - my $error_handler = shift; + my $which = shift; + my $error_handler = shift; - # don't bother loading it again if we already have - return 0 if $include{$which}; + # don't bother loading it again if we already have + return 0 if $include{$which}; - my @loadincludes = ( $which ); - while(my $incfile = shift @loadincludes) { + my @loadincludes = ($which); + while (my $incfile = shift @loadincludes) { - # load the include from the directory we found earlier... - open(INCLUDE, "$profiledir/$incfile") or fatal_error "Can't find include file $incfile: $!"; + # load the include from the directory we found earlier... + open(INCLUDE, "$profiledir/$incfile") + or fatal_error "Can't find include file $incfile: $!"; - while() { - chomp; + while () { + chomp; - if(/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { # boolean definition - } elsif(/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { # variable additions - } elsif(/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { # variable definitions - } elsif(m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { # conditional -- boolean - } elsif(m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- variable defined - } elsif(m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- boolean defined - } elsif(m/^\s*\}\s*$/) { # end of a profile or conditional - } elsif(m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { # path entry - my ($path, $mode) = ($1, $2); + if (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { + # boolean definition + } elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { + # variable additions + } elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { + # variable definitions + } elsif (m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { + # conditional -- boolean + } elsif (m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { + # conditional -- variable defined + } elsif (m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { + # conditional -- boolean defined + } elsif (m/^\s*\}\s*$/) { + # end of a profile or conditional + } elsif (m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { + # path entry - # strip off any trailing spaces. - $path =~ s/\s+$//; + my ($path, $mode) = ($1, $2); - $path = $1 if $path =~ /^"(.+)"$/; + # strip off any trailing spaces. + $path =~ s/\s+$//; - # make sure they don't have broken regexps in the profile - my $p_re = convert_regexp($path); - eval { "foo" =~ m/^$p_re$/; }; - if($@) { - return &$error_handler(sprintf(gettext('Include file %s contains invalid regexp %s.'), $incfile, $path)); + $path = $1 if $path =~ /^"(.+)"$/; + + # make sure they don't have broken regexps in the profile + my $p_re = convert_regexp($path); + eval { "foo" =~ m/^$p_re$/; }; + if ($@) { + return &$error_handler(sprintf(gettext('Include file %s contains invalid regexp %s.'), $incfile, $path)); + } + + $include{$incfile}{path}{$path} = $mode; + } elsif (/^\s*capability\s+(.+)\s*,\s*$/) { + + my $capability = $1; + $include{$incfile}{capability}{$capability} = 1; + + } elsif (/^\s*#include <(.+)>\s*$/) { + # include stuff + + my $newinclude = $1; + push @loadincludes, $newinclude unless $include{$newinclude}; + $include{$incfile}{include}{$newinclude} = 1; + + } elsif (/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) { + } else { + + # we don't care about blank lines or comments + next if /^\s*$/; + next if /^\s*\#/; + + # we hit something we don't understand in a profile... + return &$error_handler(sprintf(gettext('Include file %s contains syntax errors or is not a valid #include file.'), $incfile)); + } } - - $include{$incfile}{path}{$path} = $mode; - } elsif(/^\s*capability\s+(.+)\s*,\s*$/) { - - my $capability = $1; - $include{$incfile}{capability}{$capability} = 1; - - } elsif(/^\s*#include <(.+)>\s*$/) { # include stuff - - my $newinclude = $1; - push @loadincludes, $newinclude unless $include{$newinclude}; - $include{$incfile}{include}{$newinclude} = 1; - - } elsif(/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) { - } else { - - # we don't care about blank lines or comments - next if /^\s*$/; - next if /^\s*\#/; - - # we hit something we don't understand in a profile... - return &$error_handler(sprintf(gettext('Include file %s contains syntax errors or is not a valid #include file.'), $incfile)); - } + close(INCLUDE); } - close(INCLUDE); - } - return 0; + + return 0; } -sub rematchfrag{ - my ($frag, $path) = @_; +sub rematchfrag { + my ($frag, $path) = @_; - my $combinedmode = ""; - my @matches; + my $combinedmode = ""; + my @matches; - for my $entry (keys %{$frag->{path}}) { + for my $entry (keys %{ $frag->{path} }) { - my $regexp = convert_regexp($entry); + my $regexp = convert_regexp($entry); - # check the log entry against our converted regexp... - if($path =~ /^$regexp$/) { - # regexp matches, add it's mode to the list to check against - $combinedmode .= $frag->{path}{$entry}; - push @matches, $entry; + # check the log entry against our converted regexp... + if ($path =~ /^$regexp$/) { + + # regexp matches, add it's mode to the list to check against + $combinedmode .= $frag->{path}{$entry}; + push @matches, $entry; + } } - } - return wantarray ? ($combinedmode, @matches) : $combinedmode; + return wantarray ? ($combinedmode, @matches) : $combinedmode; } sub matchincludes { - my ($frag, $path) = @_; + my ($frag, $path) = @_; - my $combinedmode = ""; - my @matches; + my $combinedmode = ""; + my @matches; + # scan the include fragments for this profile looking for matches + my @includelist = keys %{ $frag->{include} }; + while (my $include = shift @includelist) { + loadinclude($include, \&fatal_error); + my ($cm, @m) = rematchfrag($include{$include}, $path); + if ($cm) { + $combinedmode .= $cm; + push @matches, @m; + } - # scan the include fragments for this profile looking for matches - my @includelist = keys %{$frag->{include}}; - while(my $include = shift @includelist) { - loadinclude($include, \&fatal_error); - my ($cm, @m) = rematchfrag($include{$include}, $path); - if($cm) { - $combinedmode .= $cm; - push @matches, @m; + # check if a literal version is in the current include fragment + if ($include{$include}{path}{$path}) { + $combinedmode .= $include{$include}{path}{$path}; + } + + # if this fragment includes others, check them too + if (keys %{ $include{$include}{include} }) { + push @includelist, keys %{ $include{$include}{include} }; + } } - # check if a literal version is in the current include fragment - if($include{$include}{path}{$path}) { - $combinedmode .= $include{$include}{path}{$path}; - } - - # if this fragment includes others, check them too - if(keys %{$include{$include}{include}}) { - push @includelist, keys %{$include{$include}{include}}; - } - } - - return wantarray ? ($combinedmode, @matches) : $combinedmode; + return wantarray ? ($combinedmode, @matches) : $combinedmode; } sub matchinclude { - my ($incname, $path) = @_; + my ($incname, $path) = @_; - my $combinedmode = ""; - my @matches; + my $combinedmode = ""; + my @matches; - # scan the include fragments for this profile looking for matches - my @includelist = ( $incname ); - while(my $include = shift @includelist) { - my ($cm, @m) = rematchfrag($include{$include}, $path); - if($cm) { - $combinedmode .= $cm; - push @matches, @m; + # scan the include fragments for this profile looking for matches + my @includelist = ($incname); + while (my $include = shift @includelist) { + my ($cm, @m) = rematchfrag($include{$include}, $path); + if ($cm) { + $combinedmode .= $cm; + push @matches, @m; + } + + # check if a literal version is in the current include fragment + if ($include{$include}{path}{$path}) { + $combinedmode .= $include{$include}{path}{$path}; + } + + # if this fragment includes others, check them too + if (keys %{ $include{$include}{include} }) { + push @includelist, keys %{ $include{$include}{include} }; + } } - # check if a literal version is in the current include fragment - if($include{$include}{path}{$path}) { - $combinedmode .= $include{$include}{path}{$path}; + if ($combinedmode) { + return wantarray ? ($combinedmode, @matches) : $combinedmode; + } else { + return; } - - # if this fragment includes others, check them too - if(keys %{$include{$include}{include}}) { - push @includelist, keys %{$include{$include}{include}}; - } - } - - if($combinedmode) { - return wantarray ? ($combinedmode, @matches) : $combinedmode; - } else { - return; - } } sub readconfig () { - my $which; + my $which; - if(open(LPCONF, "$confdir/logprof.conf")) { - while() { - chomp; + if (open(LPCONF, "$confdir/logprof.conf")) { + while () { + chomp; - next if /^\s*#/; + next if /^\s*#/; - if(m/^\[(\S+)\]/) { - $which = $1; - } elsif(m/^\s*(\S+)\s*=\s*(.+)\s*$/) { - my ($key, $value) = ($1, $2); - if($which eq "defaulthat") { - $defaulthat{$key} = $value; - } elsif($which eq "qualifiers") { - $qualifiers{$key} = $value; - } elsif($which eq "globs") { - $globmap{$key} = $value; - } elsif($which eq "required_hats") { - $required_hats{$key} = $value; + if (m/^\[(\S+)\]/) { + $which = $1; + } elsif (m/^\s*(\S+)\s*=\s*(.+)\s*$/) { + my ($key, $value) = ($1, $2); + if ($which eq "defaulthat") { + $defaulthat{$key} = $value; + } elsif ($which eq "qualifiers") { + $qualifiers{$key} = $value; + } elsif ($which eq "globs") { + $globmap{$key} = $value; + } elsif ($which eq "required_hats") { + $required_hats{$key} = $value; + } + } elsif (m/^\s*(\S+)\s*$/) { + my $val = $1; + if ($which eq "custom_includes") { + push @custom_includes, $val; + } + } } - } elsif(m/^\s*(\S+)\s*$/) { - my $val = $1; - if($which eq "custom_includes") { - push @custom_includes, $val; - } - } + close(LPCONF); } - close(LPCONF); - } } sub loadincludes { -if(opendir(SDDIR, $profiledir )) { - my @incdirs = grep { (! /^\./) && (-d "$profiledir/$_") } readdir(SDDIR); - close(SDDIR); + if (opendir(SDDIR, $profiledir)) { + my @incdirs = grep { (!/^\./) && (-d "$profiledir/$_") } readdir(SDDIR); + close(SDDIR); - while(my $id = shift @incdirs) { - if(opendir(SDDIR, "$profiledir/$id" )) { - for my $path (grep { ! /^\./ } readdir(SDDIR)) { - chomp($path); - next if $path =~ /\.rpm(save|new)$/; - if(-f "$profiledir/$id/$path") { - my $file = "$id/$path"; - $file =~ s/$profiledir\///; - loadinclude($file, \&fatal_error); - } elsif(-d "$id/$path") { - push @incdirs, "$id/$path"; + while (my $id = shift @incdirs) { + if (opendir(SDDIR, "$profiledir/$id")) { + for my $path (grep { !/^\./ } readdir(SDDIR)) { + chomp($path); + next if $path =~ /\.rpm(save|new)$/; + if (-f "$profiledir/$id/$path") { + my $file = "$id/$path"; + $file =~ s/$profiledir\///; + loadinclude($file, \&fatal_error); + } elsif (-d "$id/$path") { + push @incdirs, "$id/$path"; + } + } + closedir(SDDIR); + } } - } - closedir(SDDIR); } - } -} } sub globcommon ($) { - my $path = shift; + my $path = shift; - my @globs; + my @globs; - # glob library versions in both foo-5.6.so and baz.so.9.2 form - if($path =~ m/[\d\.]+\.so$/ || $path =~ m/\.so\.[\d\.]+$/) { - my $libpath = $path; - $libpath =~ s/[\d\.]+\.so$/*.so/; - $libpath =~ s/\.so\.[\d\.]+$/.so.*/; - push @globs, $libpath if $libpath ne $path; - } - - for my $glob (keys %globmap) { - if($path =~ /$glob/) { - my $globbedpath = $path; - $globbedpath =~ s/$glob/$globmap{$glob}/g; - push @globs, $globbedpath if $globbedpath ne $path; + # glob library versions in both foo-5.6.so and baz.so.9.2 form + if ($path =~ m/[\d\.]+\.so$/ || $path =~ m/\.so\.[\d\.]+$/) { + my $libpath = $path; + $libpath =~ s/[\d\.]+\.so$/*.so/; + $libpath =~ s/\.so\.[\d\.]+$/.so.*/; + push @globs, $libpath if $libpath ne $path; } - } - if(wantarray) { - return sort { length($b) <=> length($a) } uniq(@globs); - } else { - my @list = sort { length($b) <=> length($a) } uniq(@globs); - return $list[$#list]; - } + for my $glob (keys %globmap) { + if ($path =~ /$glob/) { + my $globbedpath = $path; + $globbedpath =~ s/$glob/$globmap{$glob}/g; + push @globs, $globbedpath if $globbedpath ne $path; + } + } + + if (wantarray) { + return sort { length($b) <=> length($a) } uniq(@globs); + } else { + my @list = sort { length($b) <=> length($a) } uniq(@globs); + return $list[$#list]; + } } # this is an ugly, nasty function that attempts to see if one regexp # is a subset of another regexp sub matchregexp ($$) { - my ($new, $old) = @_; + my ($new, $old) = @_; - # bail out if old pattern has {foo,bar,baz} stuff in it - return undef if $old =~ /\{.*(\,.*)*\}/; + # bail out if old pattern has {foo,bar,baz} stuff in it + return undef if $old =~ /\{.*(\,.*)*\}/; - # are there any regexps at all in the old pattern? - if($old =~ /\[.+\]/ or $old =~ /\*/ or $old =~ /\?/) { + # are there any regexps at all in the old pattern? + if ($old =~ /\[.+\]/ or $old =~ /\*/ or $old =~ /\?/) { - # convert {foo,baz} to (foo|baz) - $new =~ y/\{\}\,/\(\)\|/ if $new =~ /\{.*\,.*\}/; + # convert {foo,baz} to (foo|baz) + $new =~ y/\{\}\,/\(\)\|/ if $new =~ /\{.*\,.*\}/; - # \001 == SD_GLOB_RECURSIVE - # \002 == SD_GLOB_SIBLING + # \001 == SD_GLOB_RECURSIVE + # \002 == SD_GLOB_SIBLING - $new =~ s/\*\*/\001/g; - $new =~ s/\*/\002/g; + $new =~ s/\*\*/\001/g; + $new =~ s/\*/\002/g; - $old =~ s/\*\*/\001/g; - $old =~ s/\*/\002/g; + $old =~ s/\*\*/\001/g; + $old =~ s/\*/\002/g; + + # strip common prefix + my $prefix = commonprefix($new, $old); + if ($prefix) { + + # make sure we don't accidentally gobble up a trailing * or ** + $prefix =~ s/(\001|\002)$//; + $new =~ s/^$prefix//; + $old =~ s/^$prefix//; + } + + # strip common suffix + my $suffix = commonsuffix($new, $old); + if ($suffix) { + + # make sure we don't accidentally gobble up a leading * or ** + $suffix =~ s/^(\001|\002)//; + $new =~ s/$suffix$//; + $old =~ s/$suffix$//; + } + + # if we boiled the differences down to a ** in the new entry, it matches + # whatever's in the old entry + return 1 if $new eq "\001"; + + # if we've paired things down to a * in new, old matches if there are no + # slashes left in the path + return 1 if ($new eq "\002" && $old =~ /^[^\/]+$/); + + # we'll bail out if we have more globs in the old version + return undef if $old =~ /\001|\002/; + + # see if we can match * globs in new against literal elements in old + $new =~ s/\002/[^\/]*/g; + + return 1 if $old =~ /^$new$/; + + } else { + + my $new_regexp = convert_regexp($new); + + # check the log entry against our converted regexp... + return 1 if $old =~ /^$new_regexp$/; - # strip common prefix - my $prefix = commonprefix($new, $old); - if($prefix) { - # make sure we don't accidentally gobble up a trailing * or ** - $prefix =~ s/(\001|\002)$//; - $new =~ s/^$prefix//; - $old =~ s/^$prefix//; } - # strip common suffix - my $suffix = commonsuffix($new, $old); - if($suffix) { - # make sure we don't accidentally gobble up a leading * or ** - $suffix =~ s/^(\001|\002)//; - $new =~ s/$suffix$//; - $old =~ s/$suffix$//; - } - - # if we boiled the differences down to a ** in the new entry, it matches - # whatever's in the old entry - return 1 if $new eq "\001"; - - # if we've paired things down to a * in new, old matches if there are no - # slashes left in the path - return 1 if ($new eq "\002" && $old =~ /^[^\/]+$/); - - # we'll bail out if we have more globs in the old version - return undef if $old =~ /\001|\002/; - - # see if we can match * globs in new against literal elements in old - $new =~ s/\002/[^\/]*/g; - - return 1 if $old =~ /^$new$/; - - } else { - - my $new_regexp = convert_regexp($new); - - # check the log entry against our converted regexp... - return 1 if $old =~ /^$new_regexp$/; - - } - - return undef; + return undef; } sub combine_name($$) { return ($_[0] eq $_[1]) ? $_[0] : "$_[0]^$_[1]"; } @@ -2969,177 +3162,181 @@ sub split_name ($) { my ($p, $h) = split(/\^/, $_[0]); $h ||= $p; ($p, $h); } ####################################################################### sub Text_PromptUser ($) { - my $question = shift; + my $question = shift; - my @headers = ( @{$question->{headers}} ); - my @functions = ( @{$question->{functions}} ); + my @headers = (@{ $question->{headers} }); + my @functions = (@{ $question->{functions} }); - my $default = $question->{default}; - my $options = $question->{options}; - my $selected = $question->{selected}; + my $default = $question->{default}; + my $options = $question->{options}; + my $selected = $question->{selected}; - my $helptext = $question->{helptext}; + my $helptext = $question->{helptext}; - push @functions, "CMD_HELP" if $helptext; + push @functions, "CMD_HELP" if $helptext; - my %keys; - my @menu_items; - for my $cmd (@functions) { - # make sure we know about this particular command - my $cmdmsg = "PromptUser: " . gettext("Unknown command") . " $cmd"; - fatal_error $cmdmsg unless $CMDS{$cmd}; + my %keys; + my @menu_items; + for my $cmd (@functions) { - # grab the localized text to use for the menu for this command - my $menutext = gettext($CMDS{$cmd}); + # make sure we know about this particular command + my $cmdmsg = "PromptUser: " . gettext("Unknown command") . " $cmd"; + fatal_error $cmdmsg unless $CMDS{$cmd}; - # figure out what the hotkey for this menu item is - my $menumsg = "PromptUser: " . gettext("Invalid hotkey in") . " '$menutext'"; - $menutext =~ /\((\S)\)/ or fatal_error $menumsg; + # grab the localized text to use for the menu for this command + my $menutext = gettext($CMDS{$cmd}); - # we want case insensitive comparisons so we'll force things to lowercase - my $key = lc($1); + # figure out what the hotkey for this menu item is + my $menumsg = "PromptUser: " . gettext("Invalid hotkey in") . " '$menutext'"; + $menutext =~ /\((\S)\)/ or fatal_error $menumsg; - # check if we're already using this hotkey for this prompt - my $hotkeymsg = "PromptUser: " . gettext("Duplicate hotkey for") . " $cmd: $menutext"; - fatal_error $hotkeymsg if $keys{$key}; + # we want case insensitive comparisons so we'll force things to + # lowercase + my $key = lc($1); - # keep track of which command they're picking if they hit this hotkey - $keys{$key} = $cmd; + # check if we're already using this hotkey for this prompt + my $hotkeymsg = "PromptUser: " . gettext("Duplicate hotkey for") . " $cmd: $menutext"; + fatal_error $hotkeymsg if $keys{$key}; - if($default && $default eq $cmd) { - $menutext = "[$menutext]"; - } + # keep track of which command they're picking if they hit this hotkey + $keys{$key} = $cmd; - push @menu_items, $menutext; - } - - # figure out the key for the default option - my $default_key; - if($default && $CMDS{$default}) { - my $defaulttext = gettext($CMDS{$default}); - - # figure out what the hotkey for this menu item is - my $defmsg = "PromptUser: " . gettext("Invalid hotkey in default item") . " '$defaulttext'"; - $defaulttext =~ /\((\S)\)/ or fatal_error $defmsg; - - # we want case insensitive comparisons so we'll force things to lowercase - $default_key = lc($1); - - my $defkeymsg = "PromptUser: " . gettext("Invalid default") . " $default"; - fatal_error $defkeymsg unless $keys{$default_key}; - } - - my $widest = 0; - my @poo = @headers; - while(my $header = shift @poo) { - my $value = shift @poo; - $widest = length($header) if length($header) > $widest; - } - $widest++; - - my $format = '%-' . $widest . "s \%s\n"; - - my $function_regexp = '^('; - $function_regexp .= join("|", keys %keys); - $function_regexp .= '|\d' if $options; - $function_regexp .= ')$'; - - my $ans = "XXXINVALIDXXX"; - while($ans !~ /$function_regexp/i) { - # build up the prompt... - my $prompt = "\n"; - my @poo = @headers; - while(my $header = shift @poo) { - my $value = shift @poo; - $prompt .= sprintf($format, "$header:", $value); - } - $prompt .= "\n"; - if($options) { - for (my $i = 0; $options->[$i]; $i++) { - my $f = ($selected == $i) ? ' [%d - %s]' : ' %d - %s '; - $prompt .= sprintf("$f\n", $i+1, $options->[$i]); - } - $prompt .= "\n"; - } - $prompt .= join(" / ", @menu_items); - print "$prompt\n"; - - # get their input... - $ans = lc(getkey); - - if($ans && $keys{$ans} && $keys{$ans} eq "CMD_HELP") { - print "\n$helptext\n"; - $ans = undef; - } - - # pick the default if they hit return... - $ans = $default_key if ord($ans) == 10; - - # ugly code to handle escape sequences so you can up/down in the list - if(ord($ans) == 27) { - $ans = getkey; - if(ord($ans) == 91) { - $ans = getkey; - if(ord($ans) == 65) { - if($options) { - if($selected > 0) { - $ans = $selected; - } else { - $ans = "again"; - } - } else { - $ans = "again"; - } - } elsif(ord($ans) == 66) { - if($options) { - if($selected <= scalar(@$options)) { - $ans = $selected + 2; - } else { - $ans = "again"; - } - } - } else { - $ans = "again"; + if ($default && $default eq $cmd) { + $menutext = "[$menutext]"; } - } else { - $ans = "again"; - } + + push @menu_items, $menutext; } - # handle option poo - if($options && ($ans =~ /^\d$/)) { - if($ans > 0 && $ans <= scalar(@$options)) { - $selected = $ans - 1; - } - $ans = undef; - } - } + # figure out the key for the default option + my $default_key; + if ($default && $CMDS{$default}) { + my $defaulttext = gettext($CMDS{$default}); - # pull our command back from our hotkey map - $ans = $keys{$ans} if $keys{$ans}; + # figure out what the hotkey for this menu item is + my $defmsg = "PromptUser: " . gettext("Invalid hotkey in default item") . " '$defaulttext'"; + $defaulttext =~ /\((\S)\)/ or fatal_error $defmsg; + + # we want case insensitive comparisons so we'll force things to + # lowercase + $default_key = lc($1); + + my $defkeymsg = "PromptUser: " . gettext("Invalid default") . " $default"; + fatal_error $defkeymsg unless $keys{$default_key}; + } + + my $widest = 0; + my @poo = @headers; + while (my $header = shift @poo) { + my $value = shift @poo; + $widest = length($header) if length($header) > $widest; + } + $widest++; + + my $format = '%-' . $widest . "s \%s\n"; + + my $function_regexp = '^('; + $function_regexp .= join("|", keys %keys); + $function_regexp .= '|\d' if $options; + $function_regexp .= ')$'; + + my $ans = "XXXINVALIDXXX"; + while ($ans !~ /$function_regexp/i) { + + # build up the prompt... + my $prompt = "\n"; + my @poo = @headers; + while (my $header = shift @poo) { + my $value = shift @poo; + $prompt .= sprintf($format, "$header:", $value); + } + $prompt .= "\n"; + if ($options) { + for (my $i = 0; $options->[$i]; $i++) { + my $f = ($selected == $i) ? ' [%d - %s]' : ' %d - %s '; + $prompt .= sprintf("$f\n", $i + 1, $options->[$i]); + } + $prompt .= "\n"; + } + $prompt .= join(" / ", @menu_items); + print "$prompt\n"; + + # get their input... + $ans = lc(getkey); + + if ($ans && $keys{$ans} && $keys{$ans} eq "CMD_HELP") { + print "\n$helptext\n"; + $ans = undef; + } + + # pick the default if they hit return... + $ans = $default_key if ord($ans) == 10; + + # ugly code to handle escape sequences so you can up/down in the list + if (ord($ans) == 27) { + $ans = getkey; + if (ord($ans) == 91) { + $ans = getkey; + if (ord($ans) == 65) { + if ($options) { + if ($selected > 0) { + $ans = $selected; + } else { + $ans = "again"; + } + } else { + $ans = "again"; + } + } elsif (ord($ans) == 66) { + if ($options) { + if ($selected <= scalar(@$options)) { + $ans = $selected + 2; + } else { + $ans = "again"; + } + } + } else { + $ans = "again"; + } + } else { + $ans = "again"; + } + } + + # handle option poo + if ($options && ($ans =~ /^\d$/)) { + if ($ans > 0 && $ans <= scalar(@$options)) { + $selected = $ans - 1; + } + $ans = undef; + } + } + + # pull our command back from our hotkey map + $ans = $keys{$ans} if $keys{$ans}; # if($options) { # die "ERROR: not looking for array when options passed" unless wantarray; - if($options) { - return ($ans, $options->[$selected]); + if ($options) { + return ($ans, $options->[$selected]); } else { - return ($ans, $selected); + return ($ans, $selected); } + # } else { # die "ERROR: looking for list when options not passed" if wantarray; # return $ans; # } } -unless(-x $ldd) { - $ldd = which("ldd") or fatal_error "Can't find ldd."; +unless (-x $ldd) { + $ldd = which("ldd") or fatal_error "Can't find ldd."; } -unless(-x $parser) { - $parser = which("apparmor_parser") || which("subdomain_parser") - or fatal_error "Can't find apparmor_parser."; +unless (-x $parser) { + $parser = which("apparmor_parser") || which("subdomain_parser") + or fatal_error "Can't find apparmor_parser."; } - 1; diff --git a/utils/aa-eventd b/utils/aa-eventd index 881869b67..020703b49 100755 --- a/utils/aa-eventd +++ b/utils/aa-eventd @@ -39,23 +39,21 @@ use Immunix::Severity; my $productname = "apparmor"; -my $cfgdir = "/etc/$productname"; -my $dbdir = "/var/log/$productname"; +my $cfgdir = "/etc/$productname"; +my $dbdir = "/var/log/$productname"; -my $cfgfile = "$cfgdir/notify.cfg"; -my $errlog = "$dbdir/event-dispatch.log"; +my $cfgfile = "$cfgdir/notify.cfg"; +my $errlog = "$dbdir/event-dispatch.log"; -my $logfile = "/var/log/audit/audit.log"; -my $syslogfile = "/var/log/messages"; +my $logfile = "/var/log/audit/audit.log"; +my $syslogfile = "/var/log/messages"; ########################################################################## # options variables my $pidfile = ''; -GetOptions( - 'pidfile|p=s' => \$pidfile -); +GetOptions('pidfile|p=s' => \$pidfile); my $DEBUG = 0; @@ -103,111 +101,112 @@ my @terse_buffer; my $date_module = "None"; my %templates = ( - "path" => "(time,counter,type,profile,sdmode,mode,resource,prog,pid,severity) VALUES(?,?,'path',?,?,?,?,?,?,?)", - "link" => "(time,counter,type,profile,sdmode,resource,target,prog,pid,severity) VALUES(?,?,'link',?,?,?,?,?,?,?)", - "chattr" => "(time,counter,type,profile,sdmode,resource,mode,prog,pid,severity) VALUES(?,?,'chattr',?,?,?,?,?,?,?)", - "capability" => "(time,counter,type,profile,sdmode,resource,prog,pid,severity) VALUES(?,?,'capability',?,?,?,?,?,?)", - "unknown_hat" => "(time,counter,type,profile,sdmode,resource,pid) VALUES(?,?,'unknown_hat',?,?,?,?)", - "fork" => "(time,counter,type,profile,sdmode,pid,resource) VALUES(?,?,'fork',?,?,?,?)", - "changing_profile" => "(time,counter,type,profile,sdmode,pid) VALUES(?,?,'changing_profile',?,?,?)", - "profile_replacement" => "(time,counter,type,profile,sdmode,prog,pid,severity) VALUES(?,?,'profile_replacement',?,?,?,?,?)", - "removed" => "(time,counter,type,severity) VALUES(?,?,'removed',?)", - "initialized" => "(time,counter,type,resource,severity) VALUES(?,?,'initialized',?,?)", - "ctrl_var" => "(time,counter,type,resource,mode,severity) VALUES(?,?,'ctrl_var',?,?,?)", + "path" => "(time,counter,type,profile,sdmode,mode,resource,prog,pid,severity) VALUES(?,?,'path',?,?,?,?,?,?,?)", + "link" => "(time,counter,type,profile,sdmode,resource,target,prog,pid,severity) VALUES(?,?,'link',?,?,?,?,?,?,?)", + "chattr" => "(time,counter,type,profile,sdmode,resource,mode,prog,pid,severity) VALUES(?,?,'chattr',?,?,?,?,?,?,?)", + "capability" => "(time,counter,type,profile,sdmode,resource,prog,pid,severity) VALUES(?,?,'capability',?,?,?,?,?,?)", + "unknown_hat" => "(time,counter,type,profile,sdmode,resource,pid) VALUES(?,?,'unknown_hat',?,?,?,?)", + "fork" => "(time,counter,type,profile,sdmode,pid,resource) VALUES(?,?,'fork',?,?,?,?)", + "changing_profile" => "(time,counter,type,profile,sdmode,pid) VALUES(?,?,'changing_profile',?,?,?)", + "profile_replacement" => "(time,counter,type,profile,sdmode,prog,pid,severity) VALUES(?,?,'profile_replacement',?,?,?,?,?)", + "removed" => "(time,counter,type,severity) VALUES(?,?,'removed',?)", + "initialized" => "(time,counter,type,resource,severity) VALUES(?,?,'initialized',?,?)", + "ctrl_var" => "(time,counter,type,resource,mode,severity) VALUES(?,?,'ctrl_var',?,?,?)", ); ########################################################################## # generic functions sub errlog ($) { - my $mesg = shift; + my $mesg = shift; - my $localtime = localtime(time); - print ERRLOG "[$localtime] $mesg\n"; + my $localtime = localtime(time); + print ERRLOG "[$localtime] $mesg\n"; } sub readconfig () { - my $cfg = { }; + my $cfg = {}; - # record when we read the config file - $cfg->{load_time} = time; + # record when we read the config file + $cfg->{load_time} = time; - if(open(CFG, $cfgfile)) { + if (open(CFG, $cfgfile)) { - # yank in the values we need - while () { - $cfg->{$1} = $2 if /^(\S+)\s+(.+)\s*$/; + # yank in the values we need + while () { + $cfg->{$1} = $2 if /^(\S+)\s+(.+)\s*$/; + } + close(CFG); } - close(CFG); - } - return $cfg; + return $cfg; } sub daemonize { - chdir '/' or die "Can't chdir to /: $!"; - open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; - open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; - defined(my $pid = fork) or die "Can't fork: $!"; - exit if $pid; - setsid or die "Can't start a new session: $!"; - open STDERR, '>&STDOUT' or die "Can't dup stdout: $!"; + chdir '/' or die "Can't chdir to /: $!"; + open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; + open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; + defined(my $pid = fork) or die "Can't fork: $!"; + exit if $pid; + setsid or die "Can't start a new session: $!"; + open STDERR, '>&STDOUT' or die "Can't dup stdout: $!"; } sub parsedate ($) { - my $time = shift; - my $timestamp = 0; - if ($date_module eq 'TimeDate') { - $timestamp = Date::Parse::str2time($time); - } elsif ($date_module eq 'DateManip') { - $timestamp = Date::Manip::UnixDate(Date::Manip::ParseDateString($time), '%s'); - } else { - errlog "No date module found, exiing"; - kill HUP => -$$; - } + my $time = shift; + my $timestamp = 0; + if ($date_module eq 'TimeDate') { + $timestamp = Date::Parse::str2time($time); + } elsif ($date_module eq 'DateManip') { + $timestamp = Date::Manip::UnixDate(Date::Manip::ParseDateString($time), '%s'); + } else { + errlog "No date module found, exiing"; + kill HUP => -$$; + } - return $timestamp; + return $timestamp; } ########################################################################## # database handling functions sub connect_database ($) { - my $dbdir = shift; + my $dbdir = shift; - my $dbh = DBI->connect("dbi:SQLite:dbname=$dbdir/events.db","",""); + my $dbh = DBI->connect("dbi:SQLite:dbname=$dbdir/events.db", "", ""); - # we'll do the commits ourselves so performance doesn't suck - $dbh->{AutoCommit} = 0; + # we'll do the commits ourselves so performance doesn't suck + $dbh->{AutoCommit} = 0; - # bump up our cache size a little - $dbh->do("PRAGMA cache_size = 20000;"); + # bump up our cache size a little + $dbh->do("PRAGMA cache_size = 20000;"); - # figure out if the tables already exist or not - my %existing_tables; - my $sth = $dbh->prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"); - $sth->execute; - while(my @row = $sth->fetchrow_array) { - $existing_tables{$row[0]} = 1; - } - $sth->finish; + # figure out if the tables already exist or not + my %existing_tables; + my $sth = $dbh->prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"); + $sth->execute; + while (my @row = $sth->fetchrow_array) { + $existing_tables{ $row[0] } = 1; + } + $sth->finish; - # create the info table and fill in the appropriate values for this db - unless($existing_tables{info}) { + # create the info table and fill in the appropriate values for this db + unless ($existing_tables{info}) { - my $host = `hostname -f`; - chomp $host; + my $host = `hostname -f`; + chomp $host; - $dbh->do("CREATE TABLE info (name,value)"); - $sth = $dbh->prepare("INSERT INTO info(name,value) VALUES(?,?)"); - $sth->execute("version", "0.1"); - $sth->execute("host", "$host"); - } + $dbh->do("CREATE TABLE info (name,value)"); + $sth = $dbh->prepare("INSERT INTO info(name,value) VALUES(?,?)"); + $sth->execute("version", "0.1"); + $sth->execute("host", "$host"); + } - # create the events table - unless($existing_tables{events}) { - $dbh->do("CREATE TABLE events ( + # create the events table + unless ($existing_tables{events}) { + $dbh->do( + "CREATE TABLE events ( id INTEGER PRIMARY KEY AUTOINCREMENT, time INTEGER NOT NULL, counter INTEGER NOT NULL, @@ -220,754 +219,782 @@ sub connect_database ($) { profile, prog, severity INTEGER - )"); + )" + ); - # set up the indexes we want - my @indexes = qw(time type sdmode mode resource profile prog severity); - for my $index (@indexes) { - $dbh->do("CREATE INDEX " . $index . "_idx ON events($index)"); + # set up the indexes we want + my @indexes = qw(time type sdmode mode resource profile prog severity); + for my $index (@indexes) { + $dbh->do("CREATE INDEX " . $index . "_idx ON events($index)"); + } } - } - # make sure our changes actually get saved - $dbh->commit || errlog "Error commiting changes: $!"; + # make sure our changes actually get saved + $dbh->commit || errlog "Error commiting changes: $!"; - # mark the db as up to date as of now - $last_flush_time = time; + # mark the db as up to date as of now + $last_flush_time = time; - return $dbh; + return $dbh; } ########################################################################## sub verbose_notify_handler { - my ($email, $file, $last, $level, $unknown) = @_; + my ($email, $file, $last, $level, $unknown) = @_; - $last = localtime($last); + $last = localtime($last); - my $now = time; + my $now = time; - my $host = `hostname -f`; - chomp $host; + my $host = `hostname -f`; + chomp $host; - my $subj = "Verbose Security Report for $host."; - my $mesg = "The following security events occured since $last:\n\n"; + my $subj = "Verbose Security Report for $host."; + my $mesg = "The following security events occured since $last:\n\n"; - my @events; - if(open(V, $file)) { - while() { - chomp; - if(/^(\d+) (\d+) (.+)$/) { - my ($timestamp, $counter, $logmsg) = ($1, $2, $3); - push @events, [ $timestamp, $counter ]; - $mesg .= "$logmsg\n"; - } + my @events; + if (open(V, $file)) { + while () { + chomp; + if (/^(\d+) (\d+) (.+)$/) { + my ($timestamp, $counter, $logmsg) = ($1, $2, $3); + push @events, [ $timestamp, $counter ]; + $mesg .= "$logmsg\n"; + } + } + close(V); + + if (@events) { + if ($DEBUG) { + my $count = scalar @events; + errlog "[$count events] sending verbose notification to $email."; + } + + # actually send out the notification... + open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); + print MAIL "To: $email\n"; + print MAIL "Subject: $subj\n\n"; + print MAIL "$mesg\n"; + print MAIL ".\n"; + close(MAIL); + } + + # delete the verbose notification logfile once we've processed it + unlink($file); } - close(V); - - if(@events) { - if($DEBUG) { - my $count = scalar @events; - errlog "[$count events] sending verbose notification to $email."; - } - # actually send out the notification... - open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); - print MAIL "To: $email\n"; - print MAIL "Subject: $subj\n\n"; - print MAIL "$mesg\n"; - print MAIL ".\n"; - close(MAIL); - } - - # delete the verbose notification logfile once we've processed it - unlink($file); - } } sub summary_notify_handler { - my ($email, $file, $last, $level, $unknown) = @_; + my ($email, $file, $last, $level, $unknown) = @_; - $last = localtime($last); + $last = localtime($last); - my $now = time; + my $now = time; - my $host = `hostname -f`; - chomp $host; + my $host = `hostname -f`; + chomp $host; - my $subj = "Summary Security Report for $host."; - my $mesg = "The following security events occured since $last:\n\n"; + my $subj = "Summary Security Report for $host."; + my $mesg = "The following security events occured since $last:\n\n"; - my @events; - if(open(V, $file)) { - while() { - chomp; - if(/^(\d+) (\d+) (.+)$/) { - my ($timestamp, $counter, $logmsg) = ($1, $2, $3); - push @events, [ $timestamp, $counter ]; - $mesg .= "$logmsg\n"; - } + my @events; + if (open(V, $file)) { + while () { + chomp; + if (/^(\d+) (\d+) (.+)$/) { + my ($timestamp, $counter, $logmsg) = ($1, $2, $3); + push @events, [ $timestamp, $counter ]; + $mesg .= "$logmsg\n"; + } + } + close(V); + + if (@events) { + if ($DEBUG) { + my $count = scalar @events; + errlog "[$count events] sending summary notification to $email."; + } + + # actually send out the notification... + open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); + print MAIL "To: $email\n"; + print MAIL "Subject: $subj\n\n"; + print MAIL "$mesg\n"; + print MAIL ".\n"; + close(MAIL); + } + + # delete the verbose notification logfile once we've processed it + unlink($file); } - close(V); - - if(@events) { - if($DEBUG) { - my $count = scalar @events; - errlog "[$count events] sending summary notification to $email."; - } - # actually send out the notification... - open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); - print MAIL "To: $email\n"; - print MAIL "Subject: $subj\n\n"; - print MAIL "$mesg\n"; - print MAIL ".\n"; - close(MAIL); - } - - # delete the verbose notification logfile once we've processed it - unlink($file); - } } sub terse_notify_handler { - my ($email, $file, $last, $level, $unknown) = @_; + my ($email, $file, $last, $level, $unknown) = @_; - $last = localtime($last); + $last = localtime($last); - my $now = time; + my $now = time; - my $host = `hostname -f`; - chomp $host; + my $host = `hostname -f`; + chomp $host; + my @events; + my $count = 0; + if (open(V, $file)) { + while () { + chomp; + if (/^(\d+) (\d+) (.+)$/) { + my ($timestamp, $counter, $logmsg) = ($1, $2, $3); + push @events, [ $timestamp, $counter ]; + $count++; + } + } + close(V); - my @events; - my $count = 0; - if(open(V, $file)) { - while() { - chomp; - if(/^(\d+) (\d+) (.+)$/) { - my ($timestamp, $counter, $logmsg) = ($1, $2, $3); - push @events, [ $timestamp, $counter ]; - $count++; - } + if ($count) { + if ($DEBUG) { + errlog "[$count events] sending terse notification to $email."; + } + my $subj = "Security Report for $host."; + my $mesg = "$host has had $count security events since $last."; + + # actually send out the notification... + open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); + print MAIL "To: $email\n"; + print MAIL "Subject: $subj\n\n"; + print MAIL "$mesg\n"; + print MAIL ".\n"; + close(MAIL); + } + + # delete the terse notification logfile once we've processed it + unlink($file); } - close(V); - - if($count) { - if($DEBUG) { - errlog "[$count events] sending terse notification to $email."; - } - my $subj = "Security Report for $host."; - my $mesg = "$host has had $count security events since $last."; - - # actually send out the notification... - open(MAIL, "| sendmail -F 'AppArmor Security Notification' $email"); - print MAIL "To: $email\n"; - print MAIL "Subject: $subj\n\n"; - print MAIL "$mesg\n"; - print MAIL ".\n"; - close(MAIL); - } - - # delete the terse notification logfile once we've processed it - unlink($file); - } } sub fork_into_background { - my ($name, $func, @args) = @_; + my ($name, $func, @args) = @_; - my $pid = fork; + my $pid = fork; - if(not defined $pid) { + if (not defined $pid) { - # something bad happened, just log it... - errlog "couldn't fork for \"$name\": $!" + # something bad happened, just log it... + errlog "couldn't fork for \"$name\": $!" - } elsif($pid == 0) { + } elsif ($pid == 0) { - # we're in the child process now... + # we're in the child process now... - # set our process name - $0 = $name; + # set our process name + $0 = $name; - # call our subroutine - my $ret = &$func(@args); + # call our subroutine + my $ret = &$func(@args); - exit($ret); - } + exit($ret); + } - return $pid; + return $pid; } ########################################################################## sub process_event ($$) { - my $dbh = shift; - my $logmsg = shift; - my $sth; + my $dbh = shift; + my $logmsg = shift; + my $sth; - my ($time, $mesg); - if($logmsg =~ /^(?:type=(?:APPARMOR|UNKNOWN\[1500\]) msg=|$REdate\s+\S+\s+(?:kernel:\s+)*)audit\((\d+).\d+:\d+\): (.+)$/) { - ($time, $mesg) = ($1, $2); + my ($time, $mesg); + if ($logmsg =~ /^(?:type=(?:APPARMOR|UNKNOWN\[1500\]) msg=|$REdate\s+\S+\s+(?:kernel:\s+)*)audit\((\d+).\d+:\d+\): (.+)$/) { + ($time, $mesg) = ($1, $2); - # have we rolled over to another second yet? - if($time ne $lasttime) { - $counter = 0; - $timestamp = $time; - $lasttime = $time; - } - } elsif ($logmsg =~ /^\s*($REdate)\s+\S+\s+(?:kernel:\s+)*(SubDomain|AppArmor):\s+(.+)$/) { - ($time, $mesg) = ($1, $3); + # have we rolled over to another second yet? + if ($time ne $lasttime) { + $counter = 0; + $timestamp = $time; + $lasttime = $time; + } + } elsif ($logmsg =~ /^\s*($REdate)\s+\S+\s+(?:kernel:\s+)*(SubDomain|AppArmor):\s+(.+)$/) { + ($time, $mesg) = ($1, $3); - # have we rolled over to another second yet? - if($time ne $lasttime) { - $counter = 0; - $timestamp = parsedate($time); - $lasttime = $time; - } - } else { - # not one of ours, just return - return; - } + # have we rolled over to another second yet? + if ($time ne $lasttime) { + $counter = 0; + $timestamp = parsedate($time); + $lasttime = $time; + } + } else { - $counter++; - - # some statistics... - $max = $counter if $counter > $max; - - # if we already have events in the db, make sure we don't try to re-enter - # duplicates if we start up again and parse the same logfile over again - if($last_inserted_time) { - return if $timestamp < $last_inserted_time; - - if($timestamp == $last_inserted_time) { - return if $counter <= $last_inserted_counter; + # not one of ours, just return + return; } - $last_inserted_time = undef; - } + $counter++; - # workaround for syslog uglyness. - if($mesg =~ s/(PERMITTING|REJECTING|AUDITING)-SYSLOGFIX/$1/) { - $mesg =~ s/%%/%/g; - } + # some statistics... + $max = $counter if $counter > $max; - if($mesg =~ /(PERMITTING|REJECTING|AUDITING) (\S+) access to (.+?) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $mode, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + # if we already have events in the db, make sure we don't try to re-enter + # duplicates if we start up again and parse the same logfile over again + if ($last_inserted_time) { + return if $timestamp < $last_inserted_time; - $profile .= "^$hat" if $profile ne $hat; - - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank($resource, $mode); - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + if ($timestamp == $last_inserted_time) { + return if $counter <= $last_inserted_counter; } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter, "dummy" ]; - } - } + $last_inserted_time = undef; } - push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; - $inserts++; - - } elsif($mesg =~ /(PERMITTING|REJECTING|AUDITING) link access from (.+?) to (.+?) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $link, $target, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - $profile .= "^$hat" if $profile ne $hat; - - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank($target, "l"); - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; - } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "link", $prog, $link, $target ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter ]; - } - } + # workaround for syslog uglyness. + if ($mesg =~ s/(PERMITTING|REJECTING|AUDITING)-SYSLOGFIX/$1/) { + $mesg =~ s/%%/%/g; } - push @commit_buffer, [ "link", $timestamp, $counter, $profile, $sdmode, $link, $target, $prog, $pid, $severity ]; - $inserts++; + if ($mesg =~ /(PERMITTING|REJECTING|AUDITING) (\S+) access to (.+?) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $mode, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - } elsif($mesg =~ /(PERMITTING|REJECTING|AUDITING) attribute \((\S*)\) change to (.+)? \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $attrch, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + $profile .= "^$hat" if $profile ne $hat; - $profile .= "^$hat" if $profile ne $hat; + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank($resource, $mode); - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank($resource, "w"); + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter, "dummy" ]; + } + } - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; } - } - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "attrch", $prog, $resource, $attrch ]; - } - } + push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; + $inserts++; - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter ]; + } elsif ($mesg =~ /(PERMITTING|REJECTING|AUDITING) link access from (.+?) to (.+?) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $link, $target, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + $profile .= "^$hat" if $profile ne $hat; + + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank($target, "l"); + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "link", $prog, $link, $target ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter ]; + } + } } - } + + push @commit_buffer, [ "link", $timestamp, $counter, $profile, $sdmode, $link, $target, $prog, $pid, $severity ]; + $inserts++; + + } elsif ($mesg =~ /(PERMITTING|REJECTING|AUDITING) attribute \((\S*)\) change to (.+)? \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $attrch, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + $profile .= "^$hat" if $profile ne $hat; + + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank($resource, "w"); + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "attrch", $prog, $resource, $attrch ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter ]; + } + } + } + + push @commit_buffer, [ "chattr", $timestamp, $counter, $profile, $sdmode, $resource, $attrch, $prog, $pid, $severity ]; + $inserts++; + + } elsif (m/(PERMITTING|REJECTING) (?:mk|rm)dir on (.+) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); + + $profile .= "^$hat" if $profile ne $hat; + + my $mode = "w"; + + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank($resource, $mode); + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter, "dummy" ]; + } + } + + } + + push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; + $inserts++; + } elsif (/(PERMITTING|REJECTING) xattr (\S+) on (.+) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $xattr_op, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + $profile .= "^$hat" if $profile ne $hat; + + my $mode; + if ($xattr_op eq "get" || $xattr_op eq "list") { + $mode = "r"; + } elsif ($xattr_op eq "set" || $xattr_op eq "remove") { + $mode = "w"; + } + + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank($resource, $mode); + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter, "dummy" ]; + } + } + + } + + push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; + $inserts++; + + } elsif ($mesg =~ /(PERMITTING|REJECTING|AUDITING) access to capability '(.+?)' \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $capability, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + $profile .= "^$hat" if $profile ne $hat; + + my $severity = ""; + if ($sdmode eq "REJECTING") { + $severity = $sevdb->rank(uc("cap_$capability")); + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "capability", $prog, $capability ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter ]; + } + } + } + + push @commit_buffer, [ "capability", $timestamp, $counter, $profile, $sdmode, $capability, $prog, $pid, $severity ]; + $inserts++; + + } elsif ($mesg =~ /LOGPROF-HINT unknown_hat (\S+) pid=(\d+) profile=(\S+) active=(\S+)/) { + my ($uhat, $pid, $profile, $hat) = ($1, $2, $3, $4); + + $profile .= "^$hat" if $profile ne $hat; + + push @commit_buffer, [ "unknown_hat", $timestamp, $counter, $profile, "PERMITTING", $uhat, $pid ]; + $inserts++; + + } elsif ($mesg =~ /LOGPROF-HINT fork pid=(\d+) child=(\d+) profile=(\S+) active=(\S+)/) { + my ($pid, $child, $profile, $hat) = ($1, $2, $3, $4); + + $profile .= "^$hat" if $profile ne $hat; + + push @commit_buffer, [ "fork", $timestamp, $counter, $profile, "PERMITTING", $pid, $child ]; + $inserts++; + + } elsif ($mesg =~ /LOGPROF-HINT changing_profile pid=(\d+) newprofile=(\S+)/) { + my ($pid, $newprofile) = ($1, $2); + + push @commit_buffer, [ "changing_profile", $timestamp, $counter, $newprofile, "PERMITTING", $pid ]; + $inserts++; + + } elsif ($mesg =~ /LOGPROF-HINT fork pid=(\d+) child=(\d+)/) { + my ($pid, $child) = ($1, $2); + + push @commit_buffer, [ "fork", $timestamp, $counter, "null-complain-profile", "PERMITTING", $pid, $child ]; + $inserts++; + + } elsif ($mesg =~ /LOGPROF-HINT changing_profile pid=(\d+)/) { + my $pid = $1; + + push @commit_buffer, [ "changing_profile", $timestamp, $counter, "null-complain-profile", "PERMITTING", $pid ]; + $inserts++; + + } elsif ($mesg =~ /(PERMITTING|REJECTING|AUDITING) access to profile replacement \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { + my ($sdmode, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); + + $profile .= "^$hat" if $profile ne $hat; + + my $severity = 10; + + # we only do notification for enforce mode events + if ($config->{verbose_freq}) { + if ( ($severity >= $config->{verbose_level}) + || (($severity == -1) && $config->{verbose_unknown})) + { + push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; + } + } + + if ($config->{summary_freq}) { + if ( ($severity >= $config->{summary_level}) + || (($severity == -1) && $config->{summary_unknown})) + { + push @summary_buffer, [ $timestamp, $counter, "profile_replacement", $prog ]; + } + } + + if ($config->{terse_freq}) { + if ( ($severity >= $config->{terse_level}) + || (($severity == -1) && $config->{terse_unknown})) + { + push @terse_buffer, [ $timestamp, $counter ]; + } + } + + push @commit_buffer, [ "profile_replacement", $timestamp, $counter, $profile, $sdmode, $prog, $pid, $severity ]; + $inserts++; + + } elsif ($mesg =~ /(SubDomain|AppArmor) protection removed/) { + + push @commit_buffer, [ "removed", $timestamp, $counter, 10 ]; + $inserts++; + + } elsif ($mesg =~ /(SubDomain|AppArmor) \(version (\S+)\) initialized/) { + my $version = $1; + + push @commit_buffer, [ "initialized", $timestamp, $counter, $version, 10 ]; + $inserts++; + + } elsif ($mesg =~ /Control variable '(\S+)' changed to (\S+)/) { + my ($variable, $value) = ($1, $2); + + push @commit_buffer, [ "ctrl_var", $timestamp, $counter, $variable, $value, 10 ]; + $inserts++; + + } else { + chomp $logmsg; + errlog "Unhandled log message: $logmsg"; } - - push @commit_buffer, [ "chattr", $timestamp, $counter, $profile, $sdmode, $resource, $attrch, $prog, $pid, $severity ]; - $inserts++; - - } elsif(m/(PERMITTING|REJECTING) (?:mk|rm)dir on (.+) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6); - - $profile .= "^$hat" if $profile ne $hat; - - my $mode = "w"; - - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank($resource, $mode); - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; - } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter, "dummy" ]; - } - } - - } - - push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; - $inserts++; - } elsif(/(PERMITTING|REJECTING) xattr (\S+) on (.+) \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $xattr_op, $resource, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - $profile .= "^$hat" if $profile ne $hat; - - my $mode; - if($xattr_op eq "get" || $xattr_op eq "list") { - $mode = "r"; - } elsif($xattr_op eq "set" || $xattr_op eq "remove") { - $mode = "w"; - } - - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank($resource, $mode); - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; - } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "path", $prog, $mode, $resource ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter, "dummy" ]; - } - } - - } - - push @commit_buffer, [ "path", $timestamp, $counter, $profile, $sdmode, $mode, $resource, $prog, $pid, $severity ]; - $inserts++; - - } elsif($mesg =~ /(PERMITTING|REJECTING|AUDITING) access to capability '(.+?)' \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $capability, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - $profile .= "^$hat" if $profile ne $hat; - - my $severity = ""; - if($sdmode eq "REJECTING") { - $severity = $sevdb->rank(uc("cap_$capability")); - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; - } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "capability", $prog, $capability ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter ]; - } - } - } - - push @commit_buffer, [ "capability", $timestamp, $counter, $profile, $sdmode, $capability, $prog, $pid, $severity ]; - $inserts++; - - } elsif($mesg =~ /LOGPROF-HINT unknown_hat (\S+) pid=(\d+) profile=(\S+) active=(\S+)/) { - my ($uhat, $pid, $profile, $hat) = ($1, $2, $3, $4); - - $profile .= "^$hat" if $profile ne $hat; - - push @commit_buffer, [ "unknown_hat", $timestamp, $counter, $profile, "PERMITTING", $uhat, $pid ]; - $inserts++; - - } elsif($mesg =~ /LOGPROF-HINT fork pid=(\d+) child=(\d+) profile=(\S+) active=(\S+)/) { - my ($pid, $child, $profile, $hat) = ($1, $2, $3, $4); - - $profile .= "^$hat" if $profile ne $hat; - - push @commit_buffer, [ "fork", $timestamp, $counter, $profile, "PERMITTING", $pid, $child ]; - $inserts++; - - } elsif($mesg =~ /LOGPROF-HINT changing_profile pid=(\d+) newprofile=(\S+)/) { - my ($pid, $newprofile) = ($1, $2); - - push @commit_buffer, [ "changing_profile", $timestamp, $counter, $newprofile, "PERMITTING", $pid ]; - $inserts++; - - } elsif($mesg =~ /LOGPROF-HINT fork pid=(\d+) child=(\d+)/) { - my ($pid, $child) = ($1, $2); - - push @commit_buffer, [ "fork", $timestamp, $counter, "null-complain-profile", "PERMITTING", $pid, $child ]; - $inserts++; - - } elsif($mesg =~ /LOGPROF-HINT changing_profile pid=(\d+)/) { - my $pid = $1; - - push @commit_buffer, [ "changing_profile", $timestamp, $counter, "null-complain-profile", "PERMITTING", $pid ]; - $inserts++; - - } elsif($mesg =~ /(PERMITTING|REJECTING|AUDITING) access to profile replacement \((\S+)\((\d+)\) profile (\S+) active (\S+)\)/) { - my ($sdmode, $prog, $pid, $profile, $hat) = ($1, $2, $3, $4, $5, $6, $7); - - $profile .= "^$hat" if $profile ne $hat; - - my $severity = 10; - - # we only do notification for enforce mode events - if($config->{verbose_freq}) { - if(($severity >= $config->{verbose_level}) || - (($severity == -1) && $config->{verbose_unknown})) { - push @verbose_buffer, [ $timestamp, $counter, $logmsg ]; - } - } - - if($config->{summary_freq}) { - if(($severity >= $config->{summary_level}) || - (($severity == -1) && $config->{summary_unknown})) { - push @summary_buffer, [ $timestamp, $counter, "profile_replacement", $prog ]; - } - } - - if($config->{terse_freq}) { - if(($severity >= $config->{terse_level}) || - (($severity == -1) && $config->{terse_unknown})) { - push @terse_buffer, [ $timestamp, $counter ]; - } - } - - push @commit_buffer, [ "profile_replacement", $timestamp, $counter, $profile, $sdmode, $prog, $pid, $severity ]; - $inserts++; - - } elsif($mesg =~ /(SubDomain|AppArmor) protection removed/) { - - push @commit_buffer, [ "removed", $timestamp, $counter, 10 ]; - $inserts++; - - } elsif($mesg =~ /(SubDomain|AppArmor) \(version (\S+)\) initialized/) { - my $version = $1; - - push @commit_buffer, [ "initialized", $timestamp, $counter, $version, 10 ]; - $inserts++; - - } elsif($mesg =~ /Control variable '(\S+)' changed to (\S+)/) { - my ($variable, $value) = ($1, $2); - - push @commit_buffer, [ "ctrl_var", $timestamp, $counter, $variable, $value, 10 ]; - $inserts++; - - } else { - chomp $logmsg; - errlog "Unhandled log message: $logmsg"; - } } sub dump_events { - my ($which, @events) = @_; + my ($which, @events) = @_; - if($DEBUG) { - my $count = scalar @events; - errlog "dumping $count events to $which db."; - } - - if(open(F, ">>$dbdir/$which.db")) { - for my $event (@events) { - my @event = @$event; - print F "@event\n"; + if ($DEBUG) { + my $count = scalar @events; + errlog "dumping $count events to $which db."; + } + + if (open(F, ">>$dbdir/$which.db")) { + for my $event (@events) { + my @event = @$event; + print F "@event\n"; + } + close(F); + } else { + errlog "can't write to $dbdir/$which.db: $!"; } - close(F); - } else { - errlog "can't write to $dbdir/$which.db: $!"; - } } sub check_timers ($) { - my $dbh = shift; + my $dbh = shift; - # what time is it right... NOW - my $now = time; + # what time is it right... NOW + my $now = time; - # make sure we commit periodically - if(($inserts > 10000) || ($now >= ($last_flush_time + $timeout))) { + # make sure we commit periodically + if (($inserts > 10000) || ($now >= ($last_flush_time + $timeout))) { - my $last_prepare = ""; - my $sth; + my $last_prepare = ""; + my $sth; - for my $event ( sort { $a->[0] cmp $b->[0] } @commit_buffer ) { - my @event = @{$event}; - my $type = shift @event; + for my $event (sort { $a->[0] cmp $b->[0] } @commit_buffer) { + my @event = @{$event}; + my $type = shift @event; - if($type ne $last_prepare) { - $sth = $dbh->prepare("INSERT INTO events $templates{$type};"); - $last_prepare = $type; - } + if ($type ne $last_prepare) { + $sth = $dbh->prepare("INSERT INTO events $templates{$type};"); + $last_prepare = $type; + } - $sth->execute(@event); - } + $sth->execute(@event); + } - $dbh->commit || errlog "Error commiting changes: $!"; + $dbh->commit || errlog "Error commiting changes: $!"; - # need to get the time again to include how much time it takes to - # actually write all this crap to the db - $now = time; + # need to get the time again to include how much time it takes to + # actually write all this crap to the db + $now = time; - if($DEBUG && $inserts) { - $total += $inserts; - my $delta = $now - $last_flush_time; - my $rate = int($inserts / $delta); - errlog "$rate/s $inserts in ${delta}s total=$total max=$max"; - } + if ($DEBUG && $inserts) { + $total += $inserts; + my $delta = $now - $last_flush_time; + my $rate = int($inserts / $delta); + errlog "$rate/s $inserts in ${delta}s total=$total max=$max"; + } - $last_flush_time = $now; + $last_flush_time = $now; - @commit_buffer = ( ); + @commit_buffer = (); - $max = 0; - $inserts = 0; + $max = 0; + $inserts = 0; - if(@verbose_buffer) { - # if we've got verbose events, dump them - dump_events("verbose", @verbose_buffer); + if (@verbose_buffer) { - # and clear out our buffer - @verbose_buffer = ( ); - } + # if we've got verbose events, dump them + dump_events("verbose", @verbose_buffer); - if(@terse_buffer) { - # if we've got terse events, dump them - dump_events("terse", @terse_buffer); + # and clear out our buffer + @verbose_buffer = (); + } + + if (@terse_buffer) { + + # if we've got terse events, dump them + dump_events("terse", @terse_buffer); + + # and clear out our buffer + @terse_buffer = (); + } + + # bail out if we don't have notification configured + return unless -f $cfgfile; + + # what time did we last read the config file? + my $load_time = $config->{load_time}; + + # check when the config file was last modified... + my $mtime = (stat($cfgfile))[9]; + + # if it's been changed since we last read the config file, we need to + # load the new settings + if ($load_time < $mtime) { + errlog "Reloading changed config file."; + $config = readconfig(); + } - # and clear out our buffer - @terse_buffer = ( ); } # bail out if we don't have notification configured return unless -f $cfgfile; - # what time did we last read the config file? - my $load_time = $config->{load_time}; + if ($config->{terse_freq}) { + if (($terse->{last_notify} + $config->{terse_freq}) <= $now) { + if (-f "$dbdir/terse.db") { + $DEBUG && errlog "doing terse notification..."; - # check when the config file was last modified... - my $mtime = (stat($cfgfile))[9]; + # get a temporary filename... + my ($fh, $filename) = tempfile("terseXXXXXX", DIR => $dbdir); - # if it's been changed since we last read the config file, we need to - # load the new settings - if($load_time < $mtime) { - errlog "Reloading changed config file."; - $config = readconfig(); - } + # overwrite the temp file we just created... + rename("$dbdir/terse.db", $filename); - } + if ($DEBUG) { + errlog "terse file is $filename"; + } - # bail out if we don't have notification configured - return unless -f $cfgfile; + # do the actual notification in the background + fork_into_background("terse-notification", + \&terse_notify_handler, + $config->{terse_email}, + $filename, + $terse->{last_notify}, + $config->{terse_level}, + $config->{terse_unknown}); - if($config->{terse_freq}) { - if(($terse->{last_notify} + $config->{terse_freq}) <= $now) { - if(-f "$dbdir/terse.db") { - $DEBUG && errlog "doing terse notification..."; - - # get a temporary filename... - my ($fh, $filename) = tempfile("terseXXXXXX", DIR => $dbdir); - - # overwrite the temp file we just created... - rename("$dbdir/terse.db", $filename); - - if($DEBUG) { - errlog "terse file is $filename"; + # ...keep track of when we last sent out a notify + $terse->{last_notify} = $now; + } } - - # do the actual notification in the background - fork_into_background("terse-notification", - \&terse_notify_handler, - $config->{terse_email}, - $filename, - $terse->{last_notify}, - $config->{terse_level}, - $config->{terse_unknown}); - - # ...keep track of when we last sent out a notify - $terse->{last_notify} = $now; - } } - } - if($config->{summary_freq}) { - if(($summary->{last_notify} + $config->{summary_freq}) <= $now) { - if(-f "$dbdir/summary.db") { - $DEBUG && errlog "doing summary notification..."; + if ($config->{summary_freq}) { + if (($summary->{last_notify} + $config->{summary_freq}) <= $now) { + if (-f "$dbdir/summary.db") { + $DEBUG && errlog "doing summary notification..."; - # get a temporary filename... - my ($fh, $filename) = tempfile("summaryXXXXXX", DIR => $dbdir); + # get a temporary filename... + my ($fh, $filename) = tempfile("summaryXXXXXX", DIR => $dbdir); - # overwrite the temp file we just created... - rename("$dbdir/summary.db", $filename); + # overwrite the temp file we just created... + rename("$dbdir/summary.db", $filename); - # do the actual notification in the background - fork_into_background("summary-notification", - \&summary_notify_handler, - $config->{summary_email}, - $filename, - $summary->{last_notify}, - $config->{summary_level}, - $config->{summary_unknown}); + # do the actual notification in the background + fork_into_background("summary-notification", + \&summary_notify_handler, + $config->{summary_email}, + $filename, + $summary->{last_notify}, + $config->{summary_level}, + $config->{summary_unknown}); - # ...keep track of when we last sent out a notify - $summary->{last_notify} = $now; - } - } - } - - if($config->{verbose_freq}) { - if(($verbose->{last_notify} + $config->{verbose_freq}) <= $now) { - if(-f "$dbdir/verbose.db") { - $DEBUG && errlog "doing verbose notification..."; - - # get a temporary filename... - my ($fh, $filename) = tempfile("verboseXXXXXX", DIR => $dbdir); - - # overwrite the temp file we just created... - rename("$dbdir/verbose.db", $filename); - - if($DEBUG) { - errlog "verbose file is $filename"; + # ...keep track of when we last sent out a notify + $summary->{last_notify} = $now; + } + } + } + + if ($config->{verbose_freq}) { + if (($verbose->{last_notify} + $config->{verbose_freq}) <= $now) { + if (-f "$dbdir/verbose.db") { + $DEBUG && errlog "doing verbose notification..."; + + # get a temporary filename... + my ($fh, $filename) = tempfile("verboseXXXXXX", DIR => $dbdir); + + # overwrite the temp file we just created... + rename("$dbdir/verbose.db", $filename); + + if ($DEBUG) { + errlog "verbose file is $filename"; + } + + # do the actual notification in the background + fork_into_background("verbose-notification", + \&verbose_notify_handler, + $config->{verbose_email}, + $filename, + $verbose->{last_notify}, + $config->{verbose_level}, + $config->{verbose_unknown}); + + # ...keep track of when we last sent out a notify + $verbose->{last_notify} = $now; + } } - - # do the actual notification in the background - fork_into_background("verbose-notification", - \&verbose_notify_handler, - $config->{verbose_email}, - $filename, - $verbose->{last_notify}, - $config->{verbose_level}, - $config->{verbose_unknown}); - - # ...keep track of when we last sent out a notify - $verbose->{last_notify} = $now; - } } - } } sub get_last_event { - my $dbh = shift; + my $dbh = shift; - my ($time, $counter); - # get the oldest timestamp... - my $sth = $dbh->prepare('SELECT MAX(time) FROM events'); - $sth->execute; - my @row = $sth->fetchrow_array || ( 0 ); - $time = $row[0]; - if($time) { - # get the highest counter for this timestamp... - $sth = $dbh->prepare("SELECT MAX(counter) FROM events WHERE time = $time"); + my ($time, $counter); + + # get the oldest timestamp... + my $sth = $dbh->prepare('SELECT MAX(time) FROM events'); $sth->execute; - @row = $sth->fetchrow_array || ( 0 ); - $counter = $row[0]; - } + my @row = $sth->fetchrow_array || (0); + $time = $row[0]; + if ($time) { - return ($time, $counter); + # get the highest counter for this timestamp... + $sth = $dbh->prepare("SELECT MAX(counter) FROM events WHERE time = $time"); + $sth->execute; + @row = $sth->fetchrow_array || (0); + $counter = $row[0]; + } + + return ($time, $counter); } ########################################################################## @@ -977,16 +1004,17 @@ my $finished; # make sure we exit if someone sends us the right signal sub sig_handler { - my $signame = shift; - - errlog("Caught signal '$signame'. Exiting..."); - $finished = 1; -}; + my $signame = shift; + errlog("Caught signal '$signame'. Exiting..."); + $finished = 1; +} # set up our error log without buffering open(ERRLOG, ">>$dbdir/event-dispatch.log"); -my $oldfd = select(ERRLOG); $| = 1; select($oldfd); +my $oldfd = select(ERRLOG); +$| = 1; +select($oldfd); errlog "Starting..."; @@ -998,52 +1026,69 @@ $config = readconfig(); daemonize; # automagically reap child processes -$SIG{INT} = \&sig_handler; +$SIG{INT} = \&sig_handler; $SIG{TERM} = \&sig_handler; $SIG{CHLD} = 'IGNORE'; # Sigh, portable dates in perl sucks eval "use Date::Parse"; if (!$@) { - $date_module = 'TimeDate' + $date_module = 'TimeDate'; } else { - eval "use Date::Manip"; - if (!$@) { - $date_module = 'DateManip' - } else { - errlog "Unable to load Date module; use either TimeDate or Date::Manip"; - $finished = 1; - } + eval "use Date::Manip"; + if (!$@) { + $date_module = 'DateManip'; + } else { + errlog "Unable to load Date module; use either TimeDate or Date::Manip"; + $finished = 1; + } } # if they want us to write a pid, do it -if($pidfile) { - if(open(PIDFILE, ">$pidfile")) { - print PIDFILE "$$\n"; - close(PIDFILE); - } +if ($pidfile) { + if (open(PIDFILE, ">$pidfile")) { + print PIDFILE "$$\n"; + close(PIDFILE); + } } my $dbh = connect_database($dbdir); ($last_inserted_time, $last_inserted_counter) = get_last_event($dbh); -my $auditlog=File::Tail->new(name=>$logfile, debug=>1, tail=>-1, interval=>1, maxinterval=>5, adjustafter=>20, errmode=>"return", ignore_noexistant=>1); -my $syslog=File::Tail->new(name=>$syslogfile, debug=>1, tail=>-1, interval=>1, maxinterval=>5, adjustafter=>20, errmode=>"return", ignore_noexistant=>1); +my $auditlog = File::Tail->new( + name => $logfile, + debug => 1, + tail => -1, + interval => 1, + maxinterval => 5, + adjustafter => 20, + errmode => "return", + ignore_noexistant => 1 +); +my $syslog = File::Tail->new( + name => $syslogfile, + debug => 1, + tail => -1, + interval => 1, + maxinterval => 5, + adjustafter => 20, + errmode => "return", + ignore_noexistant => 1 +); my $line = ''; # process complete lines from the buffer... while (not $finished) { - my ($nfound, $timeleft, @pending) = - File::Tail::select(undef, undef, undef, $timeout, ($auditlog, $syslog)); + my ($nfound, $timeleft, @pending) = File::Tail::select(undef, undef, undef, $timeout, ($auditlog, $syslog)); - foreach(@pending) { - process_event($dbh, $_->read); - } + foreach (@pending) { + process_event($dbh, $_->read); + } - # see if we should flush pending entries to disk and/or do notification - check_timers($dbh); -}; + # see if we should flush pending entries to disk and/or do notification + check_timers($dbh); +} # make sure we don't exit with any pending events not written to the db $dbh->commit || errlog "Error commiting changes: $!"; diff --git a/utils/audit b/utils/audit index 6b04006d8..c204898d3 100755 --- a/utils/audit +++ b/utils/audit @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- @@ -39,22 +39,22 @@ textdomain("apparmor-utils"); $UI_Mode = "text"; # options variables -my $help = ''; - +my $help = ''; + GetOptions( - 'dir|d=s' => \$profiledir, - 'help|h' => \$help, + 'dir|d=s' => \$profiledir, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - UI_Important("Can't find subdomain profiles in $profiledir."); - exit 1; + +unless (-d $profiledir) { + UI_Important("Can't find subdomain profiles in $profiledir."); + exit 1; } # read the settings in /etc/logprof.conf @@ -63,62 +63,63 @@ readconfig(); # what are we profiling? my @profiling = @ARGV; -unless(@profiling) { - @profiling = ( UI_GetString("Please enter the program to switch to audit mode: ", "") ); +unless (@profiling) { + @profiling = (UI_GetString("Please enter the program to switch to audit mode: ", "")); } for my $profiling (@profiling) { - next unless $profiling; + next unless $profiling; - my $fqdbin; - if(-e $profiling) { - $fqdbin = get_full_path($profiling); - chomp($fqdbin); - } else { - if($profiling !~ /\//) { - my $which = which($profiling); - if($which) { - $fqdbin = get_full_path($which); - } - } - } - - if(-e $fqdbin) { - - my $filename; - if($fqdbin =~ /^$profiledir\//) { - $filename = $fqdbin; + my $fqdbin; + if (-e $profiling) { + $fqdbin = get_full_path($profiling); + chomp($fqdbin); } else { - $filename = getprofilefilename($fqdbin); + if ($profiling !~ /\//) { + my $which = which($profiling); + if ($which) { + $fqdbin = get_full_path($which); + } + } } - # argh, skip directories - next unless -f $filename; + if (-e $fqdbin) { - # skip rpm backup files - next if $filename =~ /\.rpm(save|new)$/; + my $filename; + if ($fqdbin =~ /^$profiledir\//) { + $filename = $fqdbin; + } else { + $filename = getprofilefilename($fqdbin); + } - printf(gettext('Setting %s to audit mode.'), $fqdbin); - print "\n"; - setprofileflags($filename, "audit"); + # argh, skip directories + next unless -f $filename; - system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") if check_for_subdomain(); - } else { - if($profiling =~ /^[^\/]+$/) { - UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); - exit 1; + # skip rpm backup files + next if $filename =~ /\.rpm(save|new)$/; + + printf(gettext('Setting %s to audit mode.'), $fqdbin); + print "\n"; + setprofileflags($filename, "audit"); + + system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") + if check_for_subdomain(); } else { - UI_Info(sprintf(gettext('%s does not exist, please double-check the path.'). $profiling)); - exit 1; + if ($profiling =~ /^[^\/]+$/) { + UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); + exit 1; + } else { + UI_Info(sprintf(gettext('%s does not exist, please double-check the path.') . $profiling)); + exit 1; + } } - } } exit 0; sub usage { - UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to audit mode ]"), $0)); - exit 0; + UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to audit mode ]"), $0)); + exit 0; } diff --git a/utils/autodep b/utils/autodep index df097e2cf..3f1e1fc46 100755 --- a/utils/autodep +++ b/utils/autodep @@ -4,24 +4,23 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- - use strict; use FindBin; use Getopt::Long; @@ -43,15 +42,15 @@ textdomain("apparmor-utils"); $UI_Mode = "text"; # options variables -my $help = ''; -my $force = undef; - +my $help = ''; +my $force = undef; + GetOptions( - 'force' => \$force, - 'dir|d=s' => \$profiledir, - 'help|h' => \$help, + 'force' => \$force, + 'dir|d=s' => \$profiledir, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; @@ -59,10 +58,10 @@ my $sd_mountpoint = check_for_subdomain(); # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - UI_Important(sprintf(gettext('Can\'t find subdomain profiles in %s.'), $profiledir)); - exit 1; + +unless (-d $profiledir) { + UI_Important(sprintf(gettext('Can\'t find subdomain profiles in %s.'), $profiledir)); + exit 1; } # read the settings in /etc/logprof.conf @@ -71,59 +70,58 @@ readconfig(); # what are we profiling? my @profiling = @ARGV; -unless(@profiling) { - @profiling = ( UI_GetString(gettext("Please enter the program to create a profile for: "), "") ); +unless (@profiling) { + @profiling = (UI_GetString(gettext("Please enter the program to create a profile for: "), "")); } for my $profiling (@profiling) { - next unless $profiling; + next unless $profiling; - my $fqdbin; - if(-e $profiling) { - $fqdbin = get_full_path($profiling); - chomp($fqdbin); - } else { - if($profiling !~ /\//) { - my $which = which($profiling); - if($which) { - $fqdbin = get_full_path($which); - } - } - } - - # make sure that the app they're requesting to profile is not marked as - # not allowed to have it's own profile - if($qualifiers{$fqdbin}) { - unless($qualifiers{$fqdbin} =~ /p/) { - UI_Info(sprintf(gettext('%s is currently marked as a program that should not have it\'s own profile. Usually, programs are marked this way if creating a profile for them is likely to break the rest of the system. If you know what you\'re doing and are certain you want to create a profile for this program, edit the corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf.'), $fqdbin)); - exit 1; - } - } - - - if(-e $fqdbin) { - if(-e getprofilefilename($fqdbin) && !$force) { - UI_Info(sprintf(gettext('Profile for %s already exists - skipping.'), $fqdbin)); + my $fqdbin; + if (-e $profiling) { + $fqdbin = get_full_path($profiling); + chomp($fqdbin); } else { - autodep($fqdbin); - reload($fqdbin) if $sd_mountpoint; + if ($profiling !~ /\//) { + my $which = which($profiling); + if ($which) { + $fqdbin = get_full_path($which); + } + } } - } else { - if($profiling =~ /^[^\/]+$/) { - UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); - exit 1; + + # make sure that the app they're requesting to profile is not marked as + # not allowed to have it's own profile + if ($qualifiers{$fqdbin}) { + unless ($qualifiers{$fqdbin} =~ /p/) { + UI_Info(sprintf(gettext('%s is currently marked as a program that should not have it\'s own profile. Usually, programs are marked this way if creating a profile for them is likely to break the rest of the system. If you know what you\'re doing and are certain you want to create a profile for this program, edit the corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf.'), $fqdbin)); + exit 1; + } + } + + if (-e $fqdbin) { + if (-e getprofilefilename($fqdbin) && !$force) { + UI_Info(sprintf(gettext('Profile for %s already exists - skipping.'), $fqdbin)); + } else { + autodep($fqdbin); + reload($fqdbin) if $sd_mountpoint; + } } else { - UI_Info(sprintf(gettext('%s does not exist, please double-check the path.'). $profiling)); - exit 1; + if ($profiling =~ /^[^\/]+$/) { + UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); + exit 1; + } else { + UI_Info(sprintf(gettext('%s does not exist, please double-check the path.') . $profiling)); + exit 1; + } } - } } exit 0; sub usage { - UI_Info("usage: $0 [ --force ] [ -d /path/to/profiles ]"); - exit 0; + UI_Info("usage: $0 [ --force ] [ -d /path/to/profiles ]"); + exit 0; } diff --git a/utils/complain b/utils/complain index a97f5f612..20c156da6 100755 --- a/utils/complain +++ b/utils/complain @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- @@ -39,22 +39,22 @@ textdomain("apparmor-utils"); $UI_Mode = "text"; # options variables -my $help = ''; - +my $help = ''; + GetOptions( - 'dir|d=s' => \$profiledir, - 'help|h' => \$help, + 'dir|d=s' => \$profiledir, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - UI_Important("Can't find subdomain profiles in $profiledir."); - exit 1; + +unless (-d $profiledir) { + UI_Important("Can't find subdomain profiles in $profiledir."); + exit 1; } # read the settings in /etc/logprof.conf @@ -63,62 +63,63 @@ readconfig(); # what are we profiling? my @profiling = @ARGV; -unless(@profiling) { - @profiling = ( UI_GetString(gettext("Please enter the program to switch to complain mode: "), "") ); +unless (@profiling) { + @profiling = (UI_GetString(gettext("Please enter the program to switch to complain mode: "), "")); } for my $profiling (@profiling) { - next unless $profiling; + next unless $profiling; - my $fqdbin; - if(-e $profiling) { - $fqdbin = get_full_path($profiling); - chomp($fqdbin); - } else { - if($profiling !~ /\//) { - my $which = which($profiling); - if($which) { - $fqdbin = get_full_path($which); - } - } - } - - if(-e $fqdbin) { - - my $filename; - if($fqdbin =~ /^$profiledir\//) { - $filename = $fqdbin; + my $fqdbin; + if (-e $profiling) { + $fqdbin = get_full_path($profiling); + chomp($fqdbin); } else { - $filename = getprofilefilename($fqdbin); + if ($profiling !~ /\//) { + my $which = which($profiling); + if ($which) { + $fqdbin = get_full_path($which); + } + } } - # argh, skip directories - next unless -f $filename; + if (-e $fqdbin) { - # skip rpm backup files - next if $filename =~ /\.rpm(save|new)$/; + my $filename; + if ($fqdbin =~ /^$profiledir\//) { + $filename = $fqdbin; + } else { + $filename = getprofilefilename($fqdbin); + } - printf(gettext('Setting %s to complain mode.'), $fqdbin); - print "\n"; - setprofileflags($filename, "complain"); + # argh, skip directories + next unless -f $filename; - system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") if check_for_subdomain(); - } else { - if($profiling =~ /^[^\/]+$/) { - UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); - exit 1; + # skip rpm backup files + next if $filename =~ /\.rpm(save|new)$/; + + printf(gettext('Setting %s to complain mode.'), $fqdbin); + print "\n"; + setprofileflags($filename, "complain"); + + system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") + if check_for_subdomain(); } else { - UI_Info(sprintf(gettext('%s does not exist, please double-check the path.'), $profiling)); - exit 1; + if ($profiling =~ /^[^\/]+$/) { + UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); + exit 1; + } else { + UI_Info(sprintf(gettext('%s does not exist, please double-check the path.'), $profiling)); + exit 1; + } } - } } exit 0; sub usage { - UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to complain mode ]"), $0)); - exit 0; + UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to complain mode ]"), $0)); + exit 0; } diff --git a/utils/enforce b/utils/enforce index c3129d03f..809cf788f 100755 --- a/utils/enforce +++ b/utils/enforce @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- @@ -39,22 +39,22 @@ textdomain("apparmor-utils"); $UI_Mode = "text"; # options variables -my $help = ''; - +my $help = ''; + GetOptions( - 'dir|d=s' => \$profiledir, - 'help|h' => \$help, + 'dir|d=s' => \$profiledir, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - UI_Important("Can't find subdomain profiles in $profiledir."); - exit 1; + +unless (-d $profiledir) { + UI_Important("Can't find subdomain profiles in $profiledir."); + exit 1; } # read the settings in /etc/logprof.conf @@ -63,61 +63,62 @@ readconfig(); # what are we profiling? my @profiling = @ARGV; -unless(@profiling) { - @profiling = ( UI_GetString(gettext("Please enter the program to switch to enforce mode: "), "") ); +unless (@profiling) { + @profiling = (UI_GetString(gettext("Please enter the program to switch to enforce mode: "), "")); } for my $profiling (@profiling) { - next unless $profiling; + next unless $profiling; - my $fqdbin; - if(-e $profiling) { - $fqdbin = get_full_path($profiling); - chomp($fqdbin); - } else { - if($profiling !~ /\//) { - my $which = which($profiling); - if($which) { - $fqdbin = get_full_path($which); - } - } - } - - if(-e $fqdbin) { - my $filename; - if($fqdbin =~ /^$profiledir\//) { - $filename = $fqdbin; + my $fqdbin; + if (-e $profiling) { + $fqdbin = get_full_path($profiling); + chomp($fqdbin); } else { - $filename = getprofilefilename($fqdbin); + if ($profiling !~ /\//) { + my $which = which($profiling); + if ($which) { + $fqdbin = get_full_path($which); + } + } } - # argh, skip directories - next unless -f $filename; + if (-e $fqdbin) { + my $filename; + if ($fqdbin =~ /^$profiledir\//) { + $filename = $fqdbin; + } else { + $filename = getprofilefilename($fqdbin); + } - # skip rpm backup files - next if $filename =~ /\.rpm(save|new)$/; + # argh, skip directories + next unless -f $filename; - printf(gettext('Setting %s to enforce mode.'), $fqdbin); - print "\n"; - setprofileflags($filename, ""); + # skip rpm backup files + next if $filename =~ /\.rpm(save|new)$/; - system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") if check_for_subdomain(); - } else { - if($profiling =~ /^[^\/]+$/) { - UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); - exit 1; + printf(gettext('Setting %s to enforce mode.'), $fqdbin); + print "\n"; + setprofileflags($filename, ""); + + system("cat $filename | $parser -I$profiledir -r >/dev/null 2>&1") + if check_for_subdomain(); } else { - UI_Info(sprintf(gettext('%s does not exist, please double-check the path.'). $profiling)); - exit 1; + if ($profiling =~ /^[^\/]+$/) { + UI_Info(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' as a user with the correct PATH environment set up in order to find the fully-qualified path.'), $profiling, $profiling)); + exit 1; + } else { + UI_Info(sprintf(gettext('%s does not exist, please double-check the path.') . $profiling)); + exit 1; + } } - } } exit 0; sub usage { - UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to enforce mode ]"), $0)); - exit 0; + UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ program to switch to enforce mode ]"), $0)); + exit 0; } diff --git a/utils/genprof b/utils/genprof index 20d9119a7..795accc40 100755 --- a/utils/genprof +++ b/utils/genprof @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- @@ -39,55 +39,56 @@ setlocale(LC_MESSAGES, ""); textdomain("apparmor-utils"); # options variables -my $help = ''; - +my $help = ''; + GetOptions( - 'file|f=s' => \$filename, - 'dir|d=s' => \$profiledir, - 'help|h' => \$help, + 'file|f=s' => \$filename, + 'dir|d=s' => \$profiledir, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; my $sd_mountpoint = check_for_subdomain(); -unless($sd_mountpoint) { - fatal_error(gettext("SubDomain does not appear to be started. Please enable SubDomain and try again.")); +unless ($sd_mountpoint) { + fatal_error(gettext("SubDomain does not appear to be started. Please enable SubDomain and try again.")); } # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - fatal_error "Can't find subdomain profiles in $profiledir."; + +unless (-d $profiledir) { + fatal_error "Can't find subdomain profiles in $profiledir."; } # what are we profiling? my $profiling = shift; -unless($profiling) { - $profiling = UI_GetString(gettext("Please enter the program to profile: "), "") || exit 0; +unless ($profiling) { + $profiling = UI_GetString(gettext("Please enter the program to profile: "), "") + || exit 0; } my $fqdbin; -if(-e $profiling) { - $fqdbin = get_full_path($profiling); - chomp($fqdbin); +if (-e $profiling) { + $fqdbin = get_full_path($profiling); + chomp($fqdbin); } else { - if($profiling !~ /\//) { - my $which = which($profiling); - if($which) { - $fqdbin = get_full_path($which); + if ($profiling !~ /\//) { + my $which = which($profiling); + if ($which) { + $fqdbin = get_full_path($which); + } } - } } -unless($fqdbin && -e $fqdbin) { - if($profiling =~ /^[^\/]+$/) { - fatal_error(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' in the other window in order to find the fully-qualified path.'), $profiling, $profiling)); - } else { - fatal_error(sprintf(gettext('%s does not exist, please double-check the path.'), $profiling)); - } +unless ($fqdbin && -e $fqdbin) { + if ($profiling =~ /^[^\/]+$/) { + fatal_error(sprintf(gettext('Can\'t find %s in the system path list. If the name of the application is correct, please run \'which %s\' in the other window in order to find the fully-qualified path.'), $profiling, $profiling)); + } else { + fatal_error(sprintf(gettext('%s does not exist, please double-check the path.'), $profiling)); + } } # read the settings in /etc/logprof.conf @@ -95,73 +96,72 @@ readconfig(); # make sure that the app they're requesting to profile is not marked as # not allowed to have it's own profile -if($qualifiers{$fqdbin}) { - unless($qualifiers{$fqdbin} =~ /p/) { - fatal_error(sprintf(gettext("\%s is currently marked as a program that should not have it's own profile. Usually, programs are marked this way if creating a profile for them is likely to break the rest of the system. If you know what you're doing and are certain you want to create a profile for this program, edit the corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf."), $fqdbin)); - } +if ($qualifiers{$fqdbin}) { + unless ($qualifiers{$fqdbin} =~ /p/) { + fatal_error(sprintf(gettext("\%s is currently marked as a program that should not have it's own profile. Usually, programs are marked this way if creating a profile for them is likely to break the rest of the system. If you know what you're doing and are certain you want to create a profile for this program, edit the corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf."), $fqdbin)); + } } - # load all the include files loadincludes(); my $profilefilename = getprofilefilename($fqdbin); -if(-e $profilefilename) { - $helpers{$fqdbin} = getprofileflags($profilefilename) || "enforce"; +if (-e $profilefilename) { + $helpers{$fqdbin} = getprofileflags($profilefilename) || "enforce"; } else { - autodep($fqdbin); - $helpers{$fqdbin} = "enforce"; + autodep($fqdbin); + $helpers{$fqdbin} = "enforce"; } -if($helpers{$fqdbin} eq "enforce") { - complain($fqdbin); - reload($fqdbin); +if ($helpers{$fqdbin} eq "enforce") { + complain($fqdbin); + reload($fqdbin); } UI_Important(gettext("Please start the application to be profiled in \nanother window and exercise its functionality now.\n\nOnce completed, select the \"Scan\" button below in \norder to scan the system logs for AppArmor events. \n\nFor each AppArmor event, you will be given the \nopportunity to choose whether the access should be \nallowed or denied.")); -my $syslog = 1; -my $logmark = ""; +my $syslog = 1; +my $logmark = ""; my $done_profiling = 0; -$syslog = 0 if ( -e "/var/log/audit/audit.log" ); +$syslog = 0 if (-e "/var/log/audit/audit.log"); -while(not $done_profiling) { - if ( $syslog ) { - $logmark = `date | md5sum`; - chomp $logmark; - $logmark = $1 if $logmark =~ /^([0-9a-f]+)/; - system("/bin/logger -p kern.warn 'GenProf: $logmark'"); - } else { - $logmark = last_audit_entry_time(); - } +while (not $done_profiling) { + if ($syslog) { + $logmark = `date | md5sum`; + chomp $logmark; + $logmark = $1 if $logmark =~ /^([0-9a-f]+)/; + system("/bin/logger -p kern.warn 'GenProf: $logmark'"); + } else { + $logmark = last_audit_entry_time(); + } - my $q = { }; - $q->{headers} = [ gettext("Profiling"), $fqdbin ]; - $q->{functions} = [ "CMD_SCAN", "CMD_FINISHED" ]; - $q->{default} = "CMD_SCAN"; + my $q = {}; + $q->{headers} = [ gettext("Profiling"), $fqdbin ]; + $q->{functions} = [ "CMD_SCAN", "CMD_FINISHED" ]; + $q->{default} = "CMD_SCAN"; - my ($ans, $arg) = UI_PromptUser($q); + my ($ans, $arg) = UI_PromptUser($q); - if($ans eq "CMD_SCAN") { + if ($ans eq "CMD_SCAN") { - my $lp_ret = do_logprof_pass($logmark); + my $lp_ret = do_logprof_pass($logmark); - $done_profiling = 1 if $lp_ret eq "FINISHED"; + $done_profiling = 1 if $lp_ret eq "FINISHED"; - } else { + } else { - $done_profiling = 1; + $done_profiling = 1; - } + } } - + for my $p (sort keys %helpers) { - if($helpers{$p} eq "enforce") { - enforce($p); - reload($p); - } + if ($helpers{$p} eq "enforce") { + enforce($p); + reload($p); + } } UI_Info(gettext("Reloaded SubDomain profiles in enforce mode.")); @@ -169,18 +169,17 @@ UI_Info(sprintf(gettext('Finished generating profile for %s.'), $fqdbin)); exit 0; sub usage { - UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ -f /path/to/logfile ] [ program to profile ]"), $0)); - exit 0; + UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ -f /path/to/logfile ] [ program to profile ]"), $0)); + exit 0; } sub last_audit_entry_time { - - local $_ = `tail -1 /var/log/audit/audit.log`; - my $logmark; - if ( /^*msg\=audit\((\d+\.\d+\:\d+).*\).*$/ ) { - $logmark = $1; - } else { - $logmark = ""; - } - return $logmark; -} + local $_ = `tail -1 /var/log/audit/audit.log`; + my $logmark; + if (/^*msg\=audit\((\d+\.\d+\:\d+).*\).*$/) { + $logmark = $1; + } else { + $logmark = ""; + } + return $logmark; +} diff --git a/utils/logprof b/utils/logprof index 5d917cc83..f299b67f3 100755 --- a/utils/logprof +++ b/utils/logprof @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- @@ -39,24 +39,24 @@ textdomain("apparmor-utils"); setup_yast(); # options variables -my $help = ''; +my $help = ''; my $logmark; - + GetOptions( - 'file|f=s' => \$filename, - 'dir|d=s' => \$profiledir, - 'logmark|m=s' => \$logmark, - 'help|h' => \$help, + 'file|f=s' => \$filename, + 'dir|d=s' => \$profiledir, + 'logmark|m=s' => \$logmark, + 'help|h' => \$help, ); - + # tell 'em how to use it... &usage && exit if $help; # let's convert it to full path... $profiledir = get_full_path($profiledir); - -unless(-d $profiledir) { - fatal_error "Can't find subdomain profiles in $profiledir."; + +unless (-d $profiledir) { + fatal_error "Can't find subdomain profiles in $profiledir."; } # read the settings in /etc/logprof.conf @@ -72,7 +72,7 @@ shutdown_yast(); exit 0; sub usage { - UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ -f /path/to/logfile ] [ -m \"mark in log to start processing after\""), $0)); - exit 0; + UI_Info(sprintf(gettext("usage: \%s [ -d /path/to/profiles ] [ -f /path/to/logfile ] [ -m \"mark in log to start processing after\""), $0)); + exit 0; } diff --git a/utils/unconfined b/utils/unconfined index d49e6d949..d50d77ffb 100755 --- a/utils/unconfined +++ b/utils/unconfined @@ -4,20 +4,20 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2005 Novell, Inc. All Rights Reserved. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. -# -# To contact Novell about this file by physical or electronic mail, +# +# To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com. # ---------------------------------------------------------------------- # @@ -34,74 +34,77 @@ use POSIX; setlocale(LC_MESSAGES, ""); textdomain("apparmor-utils"); - # options variables my $paranoid = ''; my $help = ''; GetOptions( - 'paranoid' => \$paranoid, - 'help|h' => \$help, + 'paranoid' => \$paranoid, + 'help|h' => \$help, ); # tell 'em how to use it... &usage && exit if $help; sub usage { - printf (gettext("Usage: %s [ --paranoid ]\n"), $0); - exit 0; + printf(gettext("Usage: %s [ --paranoid ]\n"), $0); + exit 0; } my $subdomainfs = check_for_subdomain(); -die gettext("SubDomain does not appear to be started. Please enable SubDomain and try again.")."\n" unless $subdomainfs; +die gettext("SubDomain does not appear to be started. Please enable SubDomain and try again.") . "\n" + unless $subdomainfs; my @pids; -if($paranoid) { - opendir(PROC, "/proc") or die gettext("Can't read /proc\n"); - @pids = grep { /^\d+$/ } readdir(PROC); - closedir(PROC); +if ($paranoid) { + opendir(PROC, "/proc") or die gettext("Can't read /proc\n"); + @pids = grep { /^\d+$/ } readdir(PROC); + closedir(PROC); } else { - if(open(NETSTAT, "/bin/netstat -nlp |")) { - while() { - chomp; - push @pids, $5 if /^(tcp|udp)\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\s+)\s+(\d+)\/(\S+)/; + if (open(NETSTAT, "/bin/netstat -nlp |")) { + while () { + chomp; + push @pids, $5 + if /^(tcp|udp)\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\s+)\s+(\d+)\/(\S+)/; + } + close(NETSTAT); } - close(NETSTAT); - } } for my $pid (sort { $a <=> $b } @pids) { - my $prog = readlink "/proc/$pid/exe" or next; - my $attr; - if(open(CURRENT, "/proc/$pid/attr/current")) { - while() { - chomp; - $attr = $_ if(/^\// || /^null/); + my $prog = readlink "/proc/$pid/exe" or next; + my $attr; + if (open(CURRENT, "/proc/$pid/attr/current")) { + while () { + chomp; + $attr = $_ if (/^\// || /^null/); + } + close(CURRENT); } - close(CURRENT); - } - if(not $attr) { - if($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) { - #my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1]; - my $cmdline = `cat /proc/$pid/cmdline`; - $cmdline =~ s/\0/ /g; - $cmdline =~ s/\s+$//; - chomp $cmdline; - print "$pid $prog ($cmdline) " . gettext("not confined\n"); + if (not $attr) { + if ($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) { + + #my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1]; + my $cmdline = `cat /proc/$pid/cmdline`; + $cmdline =~ s/\0/ /g; + $cmdline =~ s/\s+$//; + chomp $cmdline; + print "$pid $prog ($cmdline) " . gettext("not confined\n"); + } else { + print "$pid $prog " . gettext("not confined\n"); + } } else { - print "$pid $prog " . gettext("not confined\n"); + if ($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) { + + #my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1]; + my $cmdline = `cat /proc/$pid/cmdline`; + $cmdline =~ s/\0/ /g; + $cmdline =~ s/\s+$//; + chomp $cmdline; + print "$pid $prog ($cmdline) " . gettext("confined by") . " '$attr'\n"; + } else { + print "$pid $prog " . gettext("confined by") . " '$attr'\n"; + } } - } else { - if($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) { - #my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1]; - my $cmdline = `cat /proc/$pid/cmdline`; - $cmdline =~ s/\0/ /g; - $cmdline =~ s/\s+$//; - chomp $cmdline; - print "$pid $prog ($cmdline) " . gettext("confined by") . " '$attr'\n"; - } else { - print "$pid $prog " . gettext("confined by") . " '$attr'\n"; - } - } }