Added checks for profile syntax and error dialogs in the yastui.

This commit is contained in:
Dominic Reynolds 2006-10-05 21:29:22 +00:00
parent c231a42cf4
commit 1fe7e92955
13 changed files with 230 additions and 114 deletions

View file

@ -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 (<PROFILES>) {
# 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 (<PROFILES>) {
# 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 = <STDIN>;
my ($command, $path, $argument) = Immunix::Ycp::ParseCommand($line);
while ( <STDIN> ) {
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;

View file

@ -94,7 +94,7 @@ while ( <STDIN> ) {
}
} 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 "" ) {

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 <any> 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 <string> validIncludes = [ "/etc/apparmor.d/abstractions", "/etc/apparmor.d/program-chunks", "/etc/apparmor.d/tunables" ];
foreach( any incPath, (list<any>) customIncludes, {
string incPathStr = tostring(incPath);
validIncludes = add( validIncludes, "/etc/apparmor.d/" + incPathStr);
});
integer result = 0;
boolean includePathOK = false;
foreach( string pathToCheck, (list<string>) validIncludes, {
result = find (newInclude, pathToCheck);
if ( result != -1 ) {
includePathOK = true;
}
});
if ( ! includePathOK ) {
string pathListMsg = "";
foreach( string pathItem, (list<string>) 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

View file

@ -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 <dreynolds@suse.de> 2.0-7.11
- Add syntax checks for profiles and display to user.
* Wed May 31 2006 Dominic Reynolds <dreynolds@suse.de> 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

View file

@ -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";
}

View file

@ -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 - <dreynolds@suse.de> 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)

View file

@ -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.