diff --git a/management/yastui/src/agents/ag_subdomain b/management/yastui/src/agents/ag_subdomain index 49052434d..e73e50d09 100755 --- a/management/yastui/src/agents/ag_subdomain +++ b/management/yastui/src/agents/ag_subdomain @@ -27,24 +27,24 @@ use Data::Dumper; sub getSubdomainStatus { - my $sdStatus = "disabled"; + my $sdStatus = "disabled"; - # Ok check that there are profiles loaded to - # determine status - my $mountpoint = Immunix::SubDomain::check_for_subdomain(); - if ( $mountpoint ) { - open( PROFILES, "cat $mountpoint/profiles|" ); - while () { - # Ensure we have loaded profiles - # not just a loaded module - if ( /\// ) { - $sdStatus = "enabled"; - last; - } - } - close PROFILES; + # Ok check that there are profiles loaded to + # determine status + my $mountpoint = Immunix::SubDomain::check_for_subdomain(); + if ( $mountpoint ) { + open( PROFILES, "cat $mountpoint/profiles|" ); + while () { + # Ensure we have loaded profiles + # not just a loaded module + if ( /\// ) { + $sdStatus = "enabled"; + last; + } } - return $sdStatus; + close PROFILES; + } + return $sdStatus; } sub getNotifySettings { @@ -79,43 +79,58 @@ sub getNotifyStatus { return $noteStatus; } +sub profileSyntaxCheck { + my $errlist = []; + Immunix::SubDomain::checkIncludeSyntax($errlist); + Immunix::SubDomain::checkProfileSyntax($errlist); + my @errlist = Immunix::SubDomain::uniq(@$errlist); + return \@errlist; +} + + # Main ################################################################################ -my $line = ; -my ($command, $path, $argument) = Immunix::Ycp::ParseCommand($line); +while ( ) { + my ($command, $path, $argument) = Immunix::Ycp::ParseCommand($_); -my $result = undef; -my $donereturn = 0; -if ( $command && $path && $argument ) { - if ( $argument eq 'sd-all') { - my %hResult = ''; # hashed result, duh - $hResult{'sd-status'} = getSubdomainStatus(); - $hResult{'sd-notify'} = getNotifyStatus(); - Immunix::Ycp::ycpReturnHashAsMap( %hResult ); - $donereturn = 1; - } elsif ( $argument eq 'sd-status') { - $result = getSubdomainStatus(); - } elsif ( $argument eq 'sd-notify') { - $result = getNotifyStatus(); - } elsif ( $argument eq 'sd-notify-settings') { - $result = getNotifySettings(); - Immunix::Ycp::ycpReturn($result); - $donereturn = 1; - } - - Immunix::Ycp::ycpReturnSkalarAsString( $result ) if ( ! $donereturn ); - -} else { - - my $ycpCmd = ycpGetCommand() || ""; - my $ycpArg = ycpGetArgType() || ""; - $result = "Unknown instruction $ycpCmd or argument: $ycpArg\n"; - Immunix::Ycp::ycpReturnSkalarAsString( $result ); + my $result = undef; + my $donereturn = 0; + if ( $command && $path && $argument ) { + if ( $argument eq 'sd-all') { + my %hResult = ''; # hashed result, duh + $hResult{'sd-status'} = getSubdomainStatus(); + $hResult{'sd-notify'} = getNotifyStatus(); + Immunix::Ycp::ycpReturnHashAsMap( %hResult ); + $donereturn = 1; + } elsif ( $argument eq 'sd-status') { + $result = getSubdomainStatus(); + } elsif ( $argument eq 'sd-notify') { + $result = getNotifyStatus(); + } elsif ( $command eq "Read" and $argument eq 'custom-includes') { + Immunix::SubDomain::readconfig(); + Immunix::Ycp::ycpReturn(\@Immunix::SubDomain::custom_includes); + $donereturn = 1; + } elsif ( $command eq "Execute" and $argument eq 'profile-syntax-check') { + $result = profileSyntaxCheck(); + Immunix::Ycp::ycpReturn($result); + $donereturn = 1; + } elsif ( $argument eq 'sd-notify-settings') { + $result = getNotifySettings(); + Immunix::Ycp::ycpReturn($result); + $donereturn = 1; + } + Immunix::Ycp::ycpReturnSkalarAsString( $result ) if ( ! $donereturn ); + } else { + my $ycpCmd = ycpGetCommand() || ""; + my $ycpArg = ycpGetArgType() || ""; + $result = "Unknown instruction $ycpCmd or argument: $ycpArg\n"; + Immunix::Ycp::ycpReturnSkalarAsString( $result ); + } + print "\n"; } - exit 0; diff --git a/management/yastui/src/agents/ag_subdomain_profiles b/management/yastui/src/agents/ag_subdomain_profiles index db5e7614f..544afcff9 100755 --- a/management/yastui/src/agents/ag_subdomain_profiles +++ b/management/yastui/src/agents/ag_subdomain_profiles @@ -94,7 +94,7 @@ while ( ) { } } elsif ( $command eq "Read") { $UI_Mode = "yast"; - Immunix::SubDomain::readprofile("$profiledir/$argument"); + Immunix::SubDomain::readprofile("$profiledir/$argument", \&$Immunix::SubDomain::fatal_error); Immunix::Ycp::Return( \%sd ); } elsif ( $command eq "Write" and $path eq ".delete") { if ( $argument ne "" ) { diff --git a/management/yastui/src/clients/GenProf.ycp b/management/yastui/src/clients/GenProf.ycp index 5482c6078..acc7ccec2 100644 --- a/management/yastui/src/clients/GenProf.ycp +++ b/management/yastui/src/clients/GenProf.ycp @@ -12,6 +12,7 @@ import "Wizard"; import "Popup"; import "Sequencer"; + include "subdomain/apparmor_profile_check.ycp"; include "subdomain/apparmor_packages.ycp"; textdomain "yast2-apparmor"; @@ -37,6 +38,9 @@ if (!installAppArmorPackages()) { return; } + if (!checkProfileSyntax()) { + return; + } // initiate the handshake with the backend agent map agent_data = (map) SCR::Read(.genprof); diff --git a/management/yastui/src/clients/LogProf.ycp b/management/yastui/src/clients/LogProf.ycp index ffc02cb6b..3b4f00dbf 100644 --- a/management/yastui/src/clients/LogProf.ycp +++ b/management/yastui/src/clients/LogProf.ycp @@ -13,6 +13,7 @@ import "Popup"; import "Sequencer"; include "subdomain/apparmor_packages.ycp"; + include "subdomain/apparmor_profile_check.ycp"; textdomain "yast2-apparmor"; boolean done = false; @@ -37,6 +38,9 @@ if (!installAppArmorPackages()) { return; } + if (!checkProfileSyntax()) { + return; + } // initiate the handshake with the backend agent map agent_data = (map) SCR::Read(.logprof); diff --git a/management/yastui/src/clients/SD_AddProfile.ycp b/management/yastui/src/clients/SD_AddProfile.ycp index bf15b73c7..9937fff6a 100644 --- a/management/yastui/src/clients/SD_AddProfile.ycp +++ b/management/yastui/src/clients/SD_AddProfile.ycp @@ -12,6 +12,7 @@ import "Wizard"; import "Popup"; import "Sequencer"; include "subdomain/apparmor_packages.ycp"; +include "subdomain/apparmor_profile_check.ycp"; include "subdomain/profile_dialogs.ycp"; textdomain "yast2-apparmor"; @@ -86,6 +87,9 @@ any ret = nil; if (!installAppArmorPackages()) { return ret; } +if (!checkProfileSyntax()) { + return ret; +} ret = MainSequence(); return ret; } diff --git a/management/yastui/src/clients/SD_DeleteProfile.ycp b/management/yastui/src/clients/SD_DeleteProfile.ycp index b680012d0..8ea4d8ebf 100644 --- a/management/yastui/src/clients/SD_DeleteProfile.ycp +++ b/management/yastui/src/clients/SD_DeleteProfile.ycp @@ -12,6 +12,7 @@ import "Wizard"; import "Popup"; import "Sequencer"; include "subdomain/apparmor_packages.ycp"; +include "subdomain/apparmor_profile_check.ycp"; include "subdomain/profile_dialogs.ycp"; textdomain "yast2-apparmor"; @@ -67,6 +68,11 @@ any ret = nil; if (!installAppArmorPackages()) { return ret; } + +if (!checkProfileSyntax()) { + return true; +} + ret = MainSequence(); return ret; } diff --git a/management/yastui/src/clients/SD_EditProfile.ycp b/management/yastui/src/clients/SD_EditProfile.ycp index a62dc87f9..95c1124c5 100644 --- a/management/yastui/src/clients/SD_EditProfile.ycp +++ b/management/yastui/src/clients/SD_EditProfile.ycp @@ -12,6 +12,7 @@ import "Wizard"; import "Popup"; import "Sequencer"; include "subdomain/apparmor_packages.ycp"; +include "subdomain/apparmor_profile_check.ycp"; include "subdomain/profile_dialogs.ycp"; textdomain "yast2-apparmor"; @@ -67,9 +68,16 @@ define any MainSequence() ``{ // YEAH BABY RUN BABY RUN // any ret = nil; + if (!installAppArmorPackages()) { return ret; } + +if (!checkProfileSyntax()) { + return ret; +} + + ret = MainSequence(); return ret; } diff --git a/management/yastui/src/clients/SD_Report.ycp b/management/yastui/src/clients/SD_Report.ycp index a70abe936..330cf32c6 100644 --- a/management/yastui/src/clients/SD_Report.ycp +++ b/management/yastui/src/clients/SD_Report.ycp @@ -13,6 +13,7 @@ import "Wizard"; import "Popup"; import "Sequencer"; include "subdomain/apparmor_packages.ycp"; +include "subdomain/apparmor_profile_check.ycp"; include "subdomain/reporting_dialogues.ycp"; include "subdomain/report_helptext.ycp"; textdomain "yast2-apparmor"; @@ -29,11 +30,6 @@ define any mainSequence() ``{ "schedReport" : ``(displaySchedForm()), "viewreport" : ``(displayArchForm()), "runReport" : ``(displayRunForm()) -/* - "addSched" : ``(addSchedForm()), - "editSched" : ``(editSchedForm()), - "delSched" : ``(delSchedForm()) -*/ ]; map sequence = $[ @@ -48,44 +44,18 @@ define any mainSequence() ``{ "schedReport": $[ `back : `ws_start, `abort : `abort, -// `add : "mainreport", -// `edit : "editSched", -// `del : "delSched", `viewrep : "viewreport", `runrep : "runReport", `next : "runReport", `finish : `ws_finish ], -/* - "addSched" : $[ - `back : "schedReport", - `abort : `abort, - `save : `finish, - `finish : `ws_finish - ], - "editSched" : $[ - `back : "schedReport", - `abort : `abort, - `save : `finish, - `finish : `ws_finish - ], - "delSched" : $[ - `back : "schedReport", - `abort : `abort, - `save : `finish, - `finish : `ws_finish - ], -*/ "viewreport" : $[ - //`back : "viewreport", `back : "mainreport", `abort : `abort, - //`next : `finish, `next : "mainreport", `finish : `ws_finish ], "runReport": $[ - //`back : "runReport", `back : `back, `abort : `abort, `next : `finish, @@ -116,9 +86,13 @@ any ret = nil; if (!installAppArmorPackages()) { return ret; } + +checkProfileSyntax(); + ret = mainSequence(); return ret; + } diff --git a/management/yastui/src/include/subdomain/profile_dialogs.ycp b/management/yastui/src/include/subdomain/profile_dialogs.ycp index 2fc35774d..c355d97c2 100644 --- a/management/yastui/src/include/subdomain/profile_dialogs.ycp +++ b/management/yastui/src/include/subdomain/profile_dialogs.ycp @@ -622,15 +622,34 @@ define symbol DisplayProfileForm(string pathname, boolean hat) { return `showhat; } } else if ( id == `include ) { - string newInclude = UI::AskForExistingFile( "/etc/apparmor.d", "", _("Select File To Include")); - if ( newInclude == nil || (string)newInclude == "" ) { + any ci = SCR::Read(.subdomain, "custom-includes"); + list customIncludes = tolist(ci); + string newInclude = UI::AskForExistingFile( "/etc/apparmor.d/abstractions", "", _("Select File To Include")); + if ( newInclude == nil || (string)newInclude == "" ) { continue; } - integer includeRootBad = find (newInclude, "/etc/apparmor.d"); - if ( includeRootBad == -1 ) { - Popup::Error("AppArmor include files must be located in the directory /etc/apparmor.d"); + list validIncludes = [ "/etc/apparmor.d/abstractions", "/etc/apparmor.d/program-chunks", "/etc/apparmor.d/tunables" ]; + foreach( any incPath, (list) customIncludes, { + string incPathStr = tostring(incPath); + validIncludes = add( validIncludes, "/etc/apparmor.d/" + incPathStr); + }); + + integer result = 0; + boolean includePathOK = false; + foreach( string pathToCheck, (list) validIncludes, { + result = find (newInclude, pathToCheck); + if ( result != -1 ) { + includePathOK = true; + } + }); + + if ( ! includePathOK ) { + string pathListMsg = ""; + foreach( string pathItem, (list) validIncludes, { + pathListMsg = pathListMsg + "\n " + pathItem; + }); + Popup::Error(_("Invalid #include file. Include files must be located in one of these directores: \n") + pathListMsg ); } else { - // string includeName = substring(newInclude, 17 ); string includeName = substring(newInclude, 16 ); includes = add( includes, includeName, 1 ); profile["include"] = includes; @@ -647,7 +666,7 @@ define symbol DisplayProfileForm(string pathname, boolean hat) { if ( ! hat ) { if (Popup::YesNoHeadline(_("Save changes to the Profile"), "Would you like to save the changes to this profile? \n(Note: after saving the changes the AppArmor profiles will be reloaded.)")) { - map argmap = $[ "PROFILE_HASH" : Settings["PROFILE_MAP"]:$[], + map argmap = $[ "PROFILE_HASH" : Settings["PROFILE_MAP"]:$[], "PROFILE_NAME" : pathname ]; any result = SCR::Write(.subdomain_profiles, argmap); @@ -677,7 +696,7 @@ define symbol DisplayProfileForm(string pathname, boolean hat) { - + // // Select a profile to edit and populate diff --git a/management/yastui/yast2-apparmor.spec.in b/management/yastui/yast2-apparmor.spec.in index 20e73aea1..cd2a7ff4b 100644 --- a/management/yastui/yast2-apparmor.spec.in +++ b/management/yastui/yast2-apparmor.spec.in @@ -17,7 +17,7 @@ Summary: Yast2 plugins for AppArmor management Name: yast2-apparmor Version: @@immunix_version@@ -Release: 7.10 +Release: 7.11 Group: Productivity/Security Source0: %{name}-%{version}-@@repo_version@@.tar.gz License: GPL and LGPL @@ -88,6 +88,8 @@ REPDIR3='/var/log/apparmor/reports-exported' %preun %changelog +* Thu Oct 5 2006 Dominic Reynolds 2.0-7.11 +- Add syntax checks for profiles and display to user. * Wed May 31 2006 Dominic Reynolds 2.0-7.10 - Fixes for https://bugzilla.novell.com/show_bug.cgi?id=175388, https://bugzilla.novell.com/show_bug.cgi?id=172061. Added support diff --git a/utils/SubDomain.pm b/utils/SubDomain.pm index 170090275..bcdf94633 100755 --- a/utils/SubDomain.pm +++ b/utils/SubDomain.pm @@ -36,7 +36,7 @@ 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); +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); no warnings 'all'; @@ -95,6 +95,7 @@ our %qualifiers; our %required_hats; our %defaulthat; our %globmap; +our @custom_includes; # these are globs that the user specifically entered. we'll keep track of # them so that if one later matches, we'll suggest it again. @@ -1359,7 +1360,7 @@ sub do_logprof_pass { %variables = ( ); UI_Info(sprintf(gettext('Reading log entries from %s.'), $filename)); - UI_Info(sprintf(gettext('Updating subdomain profiles in %s.'), $profiledir)); + UI_Info(sprintf(gettext('Updating AppArmor profiles in %s.'), $profiledir)); readprofiles(); @@ -1778,15 +1779,24 @@ sub do_logprof_pass { # 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 if $incname =~ /abstractions/; + push @newincludes, $incname; } } } @@ -2135,18 +2145,70 @@ sub contains ($$) { return 1; } + +sub checkIncludeSyntax($) { + 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; +} + +sub checkProfileSyntax ($) { + my $errors = shift; + # 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; +} + +sub printMessageErrorHandler ($) { + 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"); + readprofile("$profiledir/$file", \&fatal_error); } closedir(SDDIR); } -sub readprofile ($) { +sub readprofile ($$) { my $file = shift; - + my $error_handler = shift; if(open(SDPROF, "$file")) { my ($profile, $hat, $in_contained_hat); my $initial_comment = ""; @@ -2162,7 +2224,7 @@ sub readprofile ($) { # if we run into the start of a profile while we're already in a # profile, something's wrong... if($profile) { - fatal_error "$profile profile in $file contains syntax errors."; + return &$error_handler( "$profile profile in $file contains syntax errors."); } # we hit the start of a profile, keep track of it... @@ -2202,7 +2264,7 @@ sub readprofile ($) { # if we hit the end of a profile when we're not in one, something's # wrong... if(not $profile) { - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + return &$error_handler( sprintf(gettext('%s contains syntax errors.'), $file)); } if($in_contained_hat) { @@ -2232,7 +2294,7 @@ sub readprofile ($) { } elsif(m/^\s*capability\s+(\S+)\s*,\s*$/) { # capability entry if(not $profile) { - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); } my $capability = $1; @@ -2246,7 +2308,7 @@ sub readprofile ($) { } 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) { - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); } my ($path, $mode) = ($1, $2); @@ -2260,7 +2322,7 @@ sub readprofile ($) { my $p_re = convert_regexp($path); eval { "foo" =~ m/^$p_re$/; }; if($@) { - fatal_error sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path); + return &$error_handler(sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path)); } $sd{$profile}{$hat}{path}{$path} = $mode; @@ -2276,12 +2338,12 @@ sub readprofile ($) { } $variables{$file}{"#" . $include} = 1; # sorry } - - loadinclude($include); + my $ret = loadinclude($include, $error_handler); + return $ret if ( $ret != 0 ); } elsif(/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) { if(not $profile) { - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); } # XXX - BUGBUGBUG - don't strip netdomain entries @@ -2301,7 +2363,7 @@ sub readprofile ($) { # if we hit the start of a contained hat when we're not in a profile # something is wrong... if(not $profile) { - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file)); } $in_contained_hat = 1; @@ -2338,20 +2400,19 @@ sub readprofile ($) { } else { # we hit something we don't understand in a profile... - fatal_error(sprintf(gettext('%s contains syntax errors.'), $file)); + 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) { - fatal_error "Reached the end of $file while we were still inside the $profile 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($) { @@ -2476,7 +2537,7 @@ sub writeprofile ($) { open(SDPROF, ">$filename") or fatal_error "Can't write new AppArmor profile $filename: $!"; - # stick in a vim mode line to turn on subdomain syntax highlighting + # 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 @@ -2557,7 +2618,7 @@ sub matchliteral { sub reload ($) { my $bin = shift; - # don't try to reload profile if subdomain is not running + # 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 @@ -2570,9 +2631,10 @@ sub reload ($) { sub loadinclude { my $which= shift; + my $error_handler = shift; # don't bother loading it again if we already have - return if $include{$which}; + return 0 if $include{$which}; my @loadincludes = ( $which ); while(my $incfile = shift @loadincludes) { @@ -2602,7 +2664,7 @@ sub loadinclude { my $p_re = convert_regexp($path); eval { "foo" =~ m/^$p_re$/; }; if($@) { - fatal_error sprintf(gettext('Include %s contains invalid regexp %s.'), $incfile, $path); + return &$error_handler(sprintf(gettext('Include file %s contains invalid regexp %s.'), $incfile, $path)); } $include{$incfile}{path}{$path} = $mode; @@ -2625,12 +2687,12 @@ sub loadinclude { next if /^\s*\#/; # we hit something we don't understand in a profile... - fatal_error sprintf(gettext('%s contains syntax errors.'), $incfile); + return &$error_handler(sprintf(gettext('Include file %s contains syntax errors or is not a valid #include file.'), $incfile)); } - } close(INCLUDE); } + return 0; } sub rematchfrag{ @@ -2664,7 +2726,7 @@ sub matchincludes { # scan the include fragments for this profile looking for matches my @includelist = keys %{$frag->{include}}; while(my $include = shift @includelist) { - loadinclude($include); + loadinclude($include, \&fatal_error); my ($cm, @m) = rematchfrag($include{$include}, $path); if($cm) { $combinedmode .= $cm; @@ -2741,6 +2803,11 @@ sub readconfig () { } 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; + } } } close(LPCONF); @@ -2760,7 +2827,7 @@ if(opendir(SDDIR, $profiledir )) { if(-f "$profiledir/$id/$path") { my $file = "$id/$path"; $file =~ s/$profiledir\///; - loadinclude($file); + loadinclude($file, \&fatal_error); } elsif(-d "$id/$path") { push @incdirs, "$id/$path"; } diff --git a/utils/apparmor-utils.spec.in b/utils/apparmor-utils.spec.in index 0aa5f5ac1..f2f6b48c5 100644 --- a/utils/apparmor-utils.spec.in +++ b/utils/apparmor-utils.spec.in @@ -25,7 +25,7 @@ Summary: AppArmor userlevel utilities that are useful in creating AppArmor profiles. Name: apparmor-utils Version: @@immunix_version@@ -Release: 6 +Release: 7 Group: Productivity/Security Source0: %{name}-%{version}-@@repo_version@@.tar.gz License: GPL @@ -95,6 +95,8 @@ fi %changelog +* Thu Oct 5 2006 - 2.0-7 +- add support syntax checking for profiles. * Thu Jun 01 2006 - jmichael@suse.de - add support for the new m mode (#175388) - add support for the new Px/Ux modes (#172061) diff --git a/utils/logprof.conf b/utils/logprof.conf index 5ff564a65..50c7757fc 100644 --- a/utils/logprof.conf +++ b/utils/logprof.conf @@ -98,3 +98,14 @@ ^/etc/pam.d/[^\/]+$ = /etc/pam.d/* ^/etc/profile.d/[^\/]+\.sh$ = /etc/profile.d/*.sh + +[custom_includes] + # custom directory locations to look for #includes + # + # One directory name per-line - each name should be a valid directory + # containing possble #include candidate fies under the profile dir + # which by default is /etc/apparmor.d. + # So an entry of my-includes will allow /etc/apparmor.d/my-includes to + # be used by the yast UI and profiling tools as a source of #include + # files. +