2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-30 05:47:59 +00:00

merge apparmor trunk back into the tree

This commit is contained in:
Steve Beattie 2014-02-20 17:37:42 -08:00
commit e4c534fc99
143 changed files with 14348 additions and 1950 deletions

View File

@ -17,6 +17,7 @@
#include "http_config.h"
#include "http_request.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "util_filter.h"
#include "apr.h"
@ -35,9 +36,18 @@
#define DEFAULT_HAT "HANDLING_UNTRUSTED_INPUT"
#define DEFAULT_URI_HAT "DEFAULT_URI"
/* Compatibility with apache 2.2 */
#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER < 3
#define APLOG_TRACE1 APLOG_DEBUG
server_rec *ap_server_conf = NULL;
#endif
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(apparmor);
#endif
module AP_MODULE_DECLARE_DATA apparmor_module;
static unsigned int magic_token = 0;
static unsigned long magic_token = 0;
static int inside_default_hat = 0;
typedef struct {
@ -68,9 +78,10 @@ immunix_init (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
apr_file_read (file, (void *) &magic_token, &size);
apr_file_close (file);
} else {
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "Failed to open /dev/urandom");
ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf,
"Failed to open /dev/urandom");
}
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "Opened /dev/urandom successfully");
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "Opened /dev/urandom successfully");
return OK;
}
@ -83,35 +94,31 @@ immunix_child_init (apr_pool_t *p, server_rec *s)
{
int ret;
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "init: calling change_hat");
ret = change_hat (DEFAULT_HAT, magic_token);
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
"init: calling change_hat with '%s'", DEFAULT_HAT);
ret = aa_change_hat(DEFAULT_HAT, magic_token);
if (ret < 0) {
change_hat (NULL, magic_token);
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "Failed to change_hat to '%s'",
DEFAULT_HAT);
ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf,
"Failed to change_hat to '%s'", DEFAULT_HAT);
} else {
inside_default_hat = 1;
}
}
#ifdef DEBUG
static void
debug_dump_uri (apr_uri_t * uri)
debug_dump_uri(request_rec *r)
{
if (uri)
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "Dumping uri info "
apr_uri_t *uri = &r->parsed_uri;
if (uri)
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping uri info "
"scheme='%s' host='%s' path='%s' query='%s' fragment='%s'",
uri->scheme, uri->hostname, uri->path, uri->query,
uri->fragment);
else
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "Asked to dump NULL uri");
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Asked to dump NULL uri");
}
#else
static void
debug_dump_uri (apr_uri_t * __unused uri) { }
#endif
/*
immunix_enter_hat will attempt to change_hat in the following order:
(1) to a hatname in a location directive
@ -128,9 +135,12 @@ immunix_enter_hat (request_rec *r)
ap_get_module_config (r->per_dir_config, &apparmor_module);
immunix_srv_cfg * scfg = (immunix_srv_cfg *)
ap_get_module_config (r->server->module_config, &apparmor_module);
const char *aa_hat_array[5] = { NULL, NULL, NULL, NULL, NULL };
int i = 0;
char *aa_con, *aa_mode, *aa_hat;
debug_dump_uri (&r->parsed_uri);
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "in immunix_enter_hat (%s) n:0x%lx p:0x%lx main:0x%lx",
debug_dump_uri(r);
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "in immunix_enter_hat (%s) n:0x%lx p:0x%lx main:0x%lx",
dcfg->path, (unsigned long) r->next, (unsigned long) r->prev,
(unsigned long) r->main);
@ -139,41 +149,79 @@ immunix_enter_hat (request_rec *r)
return OK;
if (inside_default_hat) {
change_hat (NULL, magic_token);
aa_change_hat(NULL, magic_token);
inside_default_hat = 0;
}
if (dcfg != NULL && dcfg->hat_name != NULL) {
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "calling change_hat [dcfg] %s", dcfg->hat_name);
sd_ret = change_hat (dcfg->hat_name, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
} else {
return OK;
}
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"[dcfg] adding hat '%s' to aa_change_hat vector", dcfg->hat_name);
aa_hat_array[i++] = dcfg->hat_name;
}
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "calling change_hat [uri] %s", r->uri);
sd_ret = change_hat (r->uri, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"[uri] adding uri '%s' to aa_change_hat vector", r->uri);
aa_hat_array[i++] = r->uri;
if (scfg) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Dumping scfg info: "
"scfg='0x%lx' scfg->hat_name='%s'",
(unsigned long) scfg, scfg->hat_name);
} else {
return OK;
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "scfg is null");
}
if (scfg != NULL && scfg->hat_name != NULL) {
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "calling change_hat [scfg] %s", scfg->hat_name);
sd_ret = change_hat (scfg->hat_name, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
} else {
return OK;
if (scfg != NULL) {
if (scfg->hat_name != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"[scfg] adding hat '%s' to aa_change_hat vector", scfg->hat_name);
aa_hat_array[i++] = scfg->hat_name;
} else {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"[scfg] adding server_name '%s' to aa_change_hat vector",
r->server->server_hostname);
aa_hat_array[i++] = r->server->server_hostname;
}
}
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "calling change_hat DEFAULT_URI");
sd_ret = change_hat (DEFAULT_URI_HAT, magic_token);
if (sd_ret < 0) change_hat (NULL, magic_token);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"[default] adding '%s' to aa_change_hat vector", DEFAULT_URI_HAT);
aa_hat_array[i++] = DEFAULT_URI_HAT;
sd_ret = aa_change_hatv(aa_hat_array, magic_token);
if (sd_ret < 0) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_change_hatv call failed");
}
/* Check to see if a defined AAHatName or AADefaultHatName would
* apply, but wasn't the hat we landed up in; report a warning if
* that's the case. */
sd_ret = aa_getcon(&aa_con, &aa_mode);
if (sd_ret < 0) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_getcon call failed");
} else {
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
"AA checks: aa_getcon result is '%s', mode '%s'", aa_con, aa_mode);
/* TODO: use libapparmor get hat_name fn here once it is implemented */
aa_hat = strstr(aa_con, "//");
if (aa_hat != NULL && strcmp(aa_mode, "enforce") == 0) {
aa_hat += 2; /* skip "//" */
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
"AA checks: apache is in hat '%s', mode '%s'", aa_hat, aa_mode);
if (dcfg != NULL && dcfg->hat_name != NULL) {
if (strcmp(aa_hat, dcfg->hat_name) != 0)
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
"AAHatName '%s' applies, but does not appear to be a hat in the apache apparmor policy",
dcfg->hat_name);
} else if (scfg != NULL && scfg->hat_name != NULL) {
if (strcmp(aa_hat, scfg->hat_name) != 0 &&
strcmp(aa_hat, r->uri) != 0)
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
"AADefaultHatName '%s' applies, but does not appear to be a hat in the apache apparmor policy",
scfg->hat_name);
}
}
free(aa_con);
}
return OK;
}
@ -186,14 +234,18 @@ immunix_exit_hat (request_rec *r)
ap_get_module_config (r->per_dir_config, &apparmor_module);
/* immunix_srv_cfg * scfg = (immunix_srv_cfg *)
ap_get_module_config (r->server->module_config, &apparmor_module); */
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "exiting change_hat - dir hat %s path %s", dcfg->hat_name, dcfg->path);
change_hat (NULL, magic_token);
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "exiting change_hat: dir hat %s dir path %s",
dcfg->hat_name, dcfg->path);
sd_ret = change_hat (DEFAULT_HAT, magic_token);
/* can convert the following back to aa_change_hat() when the
* aa_change_hat() bug addressed in trunk commit 2329 lands in most
* system libapparmors */
aa_change_hatv(NULL, magic_token);
sd_ret = aa_change_hat(DEFAULT_HAT, magic_token);
if (sd_ret < 0) {
change_hat (NULL, magic_token);
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "Failed to change_hat to '%s'",
DEFAULT_HAT);
ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
"Failed to change_hat to '%s'", DEFAULT_HAT);
} else {
inside_default_hat = 1;
}
@ -204,7 +256,7 @@ immunix_exit_hat (request_rec *r)
static const char *
aa_cmd_ch_path (cmd_parms * cmd, void * mconfig, const char * parm1)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "config change hat %s",
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "directory config change hat %s",
parm1 ? parm1 : "DEFAULT");
immunix_dir_cfg * dcfg = mconfig;
if (parm1 != NULL) {
@ -221,7 +273,7 @@ static const char *
immunix_cmd_ch_path (cmd_parms * cmd, void * mconfig, const char * parm1)
{
if (path_warn_once == 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "ImmHatName is "
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmHatName is "
"deprecated, please use AAHatName instead");
path_warn_once = 1;
}
@ -231,9 +283,10 @@ immunix_cmd_ch_path (cmd_parms * cmd, void * mconfig, const char * parm1)
static const char *
aa_cmd_ch_srv (cmd_parms * cmd, void * mconfig, const char * parm1)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "config change hat %s",
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "server config change hat %s",
parm1 ? parm1 : "DEFAULT");
immunix_srv_cfg * scfg = mconfig;
immunix_srv_cfg * scfg = (immunix_srv_cfg *)
ap_get_module_config(cmd->server->module_config, &apparmor_module);
if (parm1 != NULL) {
scfg->hat_name = parm1;
} else {
@ -248,7 +301,7 @@ static const char *
immunix_cmd_ch_srv (cmd_parms * cmd, void * mconfig, const char * parm1)
{
if (srv_warn_once == 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "ImmDefaultHatName is "
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "ImmDefaultHatName is "
"deprecated, please use AADefaultHatName instead");
srv_warn_once = 1;
}
@ -260,9 +313,9 @@ immunix_create_dir_config (apr_pool_t * p, char * path)
{
immunix_dir_cfg * newcfg = (immunix_dir_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "in immunix_create_dir (%s)", path ? path : ":no path:");
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_create_dir (%s)", path ? path : ":no path:");
if (newcfg == NULL) {
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "immunix_create_dir: couldn't alloc dir config");
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "immunix_create_dir: couldn't alloc dir config");
return NULL;
}
newcfg->path = apr_pstrdup (p, path ? path : ":no path:");
@ -277,7 +330,7 @@ immunix_merge_dir_config (apr_pool_t * p, void * parent, void * child)
{
immunix_dir_cfg * newcfg = (immunix_dir_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "in immunix_merge_dir ()");
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_merge_dir ()");
if (newcfg == NULL)
return NULL;
@ -290,9 +343,9 @@ immunix_create_srv_config (apr_pool_t * p, server_rec * srv)
{
immunix_srv_cfg * newcfg = (immunix_srv_cfg *) apr_pcalloc(p, sizeof(* newcfg));
ap_log_error (APLOG_MARK, APLOG_DEBUG, 0, NULL, "in immunix_create_srv");
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf, "in immunix_create_srv");
if (newcfg == NULL) {
ap_log_error (APLOG_MARK, APLOG_ERR, 0, NULL, "immunix_create_srv: couldn't alloc srv config");
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "immunix_create_srv: couldn't alloc srv config");
return NULL;
}

View File

@ -41,10 +41,12 @@ apparmor is also functioning.
Once mod_apparmor is loaded within Apache, all requests to Apache will
cause mod_apparmor to attempt to change into a hat named by the URI
(e.g. /app/some.cgi). If no such hat is found, it will fall back to
(e.g. /app/some.cgi). If no such hat is found, it will first fall
back by attempting to change into a hat that matches the ServerName
for the server/vhost. If that hat is not found, it will fall back to
attempting to use the hat DEFAULT_URI; if that also does not exist,
it will fall back to using the global Apache profile. Most static web
pages can simply make use of the DEFAULT_URI hat.
it will fall back to using the global Apache profile. Most static
web pages can simply make use of the DEFAULT_URI hat.
Additionally, before any requests come in to Apache, mod_apparmor
will attempt to change hat into the HANDLING_UNTRUSTED_INPUT hat.
@ -70,13 +72,14 @@ behavior described above.
=item B<AADefaultHatName>
AADefaultHatName allows you to specify a default hat to be used for
virtual hosts and other Apache server directives, so that you can have
different defaults for different virtual hosts. This can be overridden by
the AAHatName directive and is checked for only if there isn't a matching
AAHatName or hat named by the URI. If the AADefaultHatName hat does not
exist, it falls back to the DEFAULT_URI hat if it exists (as described
above).
AADefaultHatName allows you to specify a default hat to be used
for virtual hosts and other Apache server directives, so that you
can have different defaults for different virtual hosts. This can
be overridden by the AAHatName directive and is checked for only if
there isn't a matching AAHatName or hat named by the URI. The default
value of AADefaultHatName is the ServerName for the server/vhost
configuration. If the AADefaultHatName hat does not exist, it falls
back to the DEFAULT_URI hat if it exists (as described above).
=back
@ -98,8 +101,9 @@ applies, otherwise it will
2. try to aa_change_hat(2) into the URI itself, otherwise it will
3. try to aa_change_hat(2) into an AADefaultHatName hat if it has been defined
for the server/vhost, otherwise it will
3. try to aa_change_hat(2) into an AADefaultHatName hat, either the
ServerName (the default) or the configuration value specified by the
AADefaultHatName directive, for the server/vhost, otherwise it will
4. try to aa_change_hat(2) into the DEFAULT_URI hat, if it exists, otherwise it
will
@ -112,7 +116,8 @@ will
mod_apparmor() currently only supports apache2, and has only been tested
with the prefork MPM configuration -- threaded configurations of Apache
may not work correctly.
may not work correctly. For Apache 2.4 users, you should enable the mpm_prefork
module.
There are likely other bugs lurking about; if you find any, please report
them at L<https://bugs.launchpad.net/apparmor/+filebug>.

69
deprecated/utils/Makefile Normal file
View File

@ -0,0 +1,69 @@
# ----------------------------------------------------------------------
# Copyright (c) 1999, 2004-2009 NOVELL (All rights reserved)
# Copyright (c) 2010-2011, 2014 Canonical Ltd.
#
# 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 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.
# ----------------------------------------------------------------------
# NOTE: this Makefile has been adjusted from the original to assist in
# the installation of the Immunix perl modules, if they're still needed
# by users. Because the utilities conflict with their replacments, make
# install *will* *not* install them.
NAME = apparmor-utils
all:
COMMONDIR=../../common/
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
MODDIR = Immunix
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
aa-unconfined aa-disable
MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \
${MODDIR}/Config.pm ${MODDIR}/Severity.pm
all:
# need some better way of determining this
DESTDIR=/
BINDIR=${DESTDIR}/usr/sbin
CONFDIR=${DESTDIR}/etc/apparmor
VENDOR_PERL=$(shell perl -e 'use Config; print $$Config{"vendorlib"};')
PERLDIR=${DESTDIR}${VENDOR_PERL}/${MODDIR}
.PHONY: install
install:
install -d ${PERLDIR}
install -m 644 ${MODULES} ${PERLDIR}
.PHONY: clean
ifndef VERBOSE
.SILENT: clean
endif
clean: _clean
rm -f core core.* *.o *.s *.a *~
rm -f Make.rules
rm -rf staging/ build/
.PHONY: check
.SILENT: check
check:
for i in ${MODULES} ${PERLTOOLS} ; do \
perl -c $$i || exit 1; \
done

132
deprecated/utils/aa-audit Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
#
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use FindBin;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString("Please enter the program to switch to audit mode: ", ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to audit mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "audit");
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}

122
deprecated/utils/aa-autodep Executable file
View File

@ -0,0 +1,122 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
#
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use FindBin;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
my $force = undef;
GetOptions(
'force' => \$force,
'dir|d=s' => \$profiledir,
'help|h' => \$help,
);
# tell 'em how to use it...
&usage && exit if $help;
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 AppArmor profiles in %s.'), $profiledir));
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to create a profile for: "), ""));
}
for my $profiling (@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));
} else {
autodep($fqdbin);
reload($fqdbin) if $sd_mountpoint;
}
} 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;
} 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;
}

131
deprecated/utils/aa-complain Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use FindBin;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to switch to complain mode: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to complain mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "complain");
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}

152
deprecated/utils/aa-disable Executable file
View File

@ -0,0 +1,152 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# Copyright (c) 2005-2010 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, 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 Canonical, Inc.
#
# To contact Canonical about this file by physical or electronic mail,
# you may find current contact information at www.canonical.com.
# ----------------------------------------------------------------------
use strict;
use FindBin;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
use File::Basename;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
my $disabledir = "$profiledir/disable";
unless (-d $disabledir) {
UI_Important("Can't find AppArmor disable directory '$disabledir'.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program whose profile should be disabled: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip package manager backup files
next if isSkippableFile($filename);
my ($bname, $dname, $suffix) = File::Basename::fileparse($filename);
if ($bname eq "") {
UI_Info(sprintf(gettext('Could not find basename for %s.'), $filename));
exit 1;
}
printf(gettext('Disabling %s.'), $fqdbin);
print "\n";
my $link = "$disabledir/$bname";
if (! -e $link) {
if (symlink($filename, $link) != 1) {
UI_Info(sprintf(gettext('Could not create %s symlink.'), $link));
exit 1;
}
}
my $cmd_info = qx(cat $filename | $parser -I$profiledir -R 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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 have profile disabled ]"), $0));
exit 0;
}

142
deprecated/utils/aa-enforce Executable file
View File

@ -0,0 +1,142 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
#
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use FindBin;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to switch to enforce mode: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to enforce mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "");
# remove symlink in $profiledir/force-complain as well
my $complainlink = $filename;
$complainlink =~ s/^$profiledir/$profiledir\/force-complain/;
-e $complainlink and unlink($complainlink);
# remove symlink in $profiledir/disable as well
my $disablelink = $filename;
$disablelink =~ s/^$profiledir/$profiledir\/disable/;
-e $disablelink and unlink($disablelink);
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}

216
deprecated/utils/aa-genprof Executable file
View File

@ -0,0 +1,216 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use Getopt::Long;
use Immunix::AppArmor;
use Data::Dumper;
use Locale::gettext;
use POSIX;
sub sysctl_read($) {
my $path = shift;
my $value = undef;
if (open(SYSCTL, "<$path")) {
$value = int(<SYSCTL>);
}
close(SYSCTL);
return $value;
}
sub sysctl_write($$) {
my $path = shift;
my $value = shift;
return if (!defined($value));
if (open(SYSCTL, ">$path")) {
print SYSCTL $value;
close(SYSCTl);
}
}
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
# options variables
my $help = '';
GetOptions(
'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("AppArmor does not appear to be started. Please enable AppArmor and try again."));
}
# let's convert it to full path...
$profiledir = get_full_path($profiledir);
unless (-d $profiledir) {
fatal_error "Can't find AppArmor profiles in $profiledir.";
}
# what are we profiling?
my $profiling = shift;
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);
} else {
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));
}
}
# make sure that the app they're requesting to profile is not marked as
# not allowed to have it's own profile
check_qualifiers($fqdbin);
# load all the include files
loadincludes();
my $profilefilename = getprofilefilename($fqdbin);
if (-e $profilefilename) {
$helpers{$fqdbin} = getprofileflags($profilefilename) || "enforce";
} else {
autodep($fqdbin);
$helpers{$fqdbin} = "enforce";
}
if ($helpers{$fqdbin} eq "enforce") {
complain($fqdbin);
reload($fqdbin);
}
# When reading from syslog, it is possible to hit the default kernel
# printk ratelimit. This will result in audit entries getting skipped,
# making profile generation inaccurate. When using genprof, disable
# the printk ratelimit, and restore it on exit.
my $ratelimit_sysctl = "/proc/sys/kernel/printk_ratelimit";
my $ratelimit_saved = sysctl_read($ratelimit_sysctl);
END { sysctl_write($ratelimit_sysctl, $ratelimit_saved); }
sysctl_write($ratelimit_sysctl, 0);
UI_Info(gettext("\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:\nhttp://wiki.apparmor.net/index.php/Profiles"));
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 $done_profiling = 0;
$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("$logger -p kern.warn 'GenProf: $logmark'");
} else {
$logmark = last_audit_entry_time();
}
eval {
my $q = {};
$q->{headers} = [ gettext("Profiling"), $fqdbin ];
$q->{functions} = [ "CMD_SCAN", "CMD_FINISHED" ];
$q->{default} = "CMD_SCAN";
my ($ans, $arg) = UI_PromptUser($q);
if ($ans eq "CMD_SCAN") {
my $lp_ret = do_logprof_pass($logmark);
$done_profiling = 1 if $lp_ret eq "FINISHED";
} else {
$done_profiling = 1;
}
};
if ($@) {
if ($@ =~ /FINISHING/) {
$done_profiling = 1;
} else {
die $@;
}
}
}
for my $p (sort keys %helpers) {
if ($helpers{$p} eq "enforce") {
enforce($p);
reload($p);
}
}
UI_Info(gettext("Reloaded AppArmor profiles in enforce mode."));
UI_Info(gettext("\nPlease consider contributing your new profile! See\nthe following wiki page for more information:\nhttp://wiki.apparmor.net/index.php/Profiles\n"));
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;
}
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;
}

72
deprecated/utils/aa-logprof Executable file
View File

@ -0,0 +1,72 @@
#!/usr/bin/perl
# ----------------------------------------------------------------------
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
use strict;
use Data::Dumper;
use Getopt::Long;
use Locale::gettext;
use POSIX;
use Immunix::AppArmor;
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
setup_yast();
# options variables
my $help = '';
my $logmark;
GetOptions(
'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 AppArmor profiles in $profiledir.";
}
# load all the include files
loadincludes();
do_logprof_pass($logmark);
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;
}

113
deprecated/utils/aa-unconfined Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/perl -w
# ----------------------------------------------------------------------
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
#
# unconfined -
# audit local system for processes listening on network connections
# that are not currently running with a profile.
use strict;
use Getopt::Long;
use Immunix::AppArmor;
use Locale::gettext;
use POSIX;
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
# options variables
my $paranoid = '';
my $help = '';
GetOptions(
'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;
}
my $subdomainfs = check_for_subdomain();
die gettext("AppArmor does not appear to be started. Please enable AppArmor 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);
} else {
if (open(NETSTAT, "LANG=C /bin/netstat -nlp |")) {
while (<NETSTAT>) {
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);
}
}
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 (<CURRENT>) {
chomp;
$attr = $_ if (/^\// || /^null/);
}
close(CURRENT);
}
my $cmdline = `cat /proc/$pid/cmdline`;
my $pname = (split(/\0/, $cmdline))[0];
if ($pname =~ /\// && !($pname eq $prog)) {
$pname = "($pname) ";
} else {
$pname = "";
}
if (not $attr) {
if ($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) {
#my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1];
$cmdline =~ s/\0/ /g;
$cmdline =~ s/\s+$//;
chomp $cmdline;
print "$pid $prog ($cmdline) " . gettext("not confined\n");
} else {
print "$pid $prog $pname" . gettext("not confined\n");
}
} else {
if ($prog =~ m/^(\/usr\/bin\/python|\/usr\/bin\/perl|\/bin\/bash)$/) {
#my $scriptname = (split(/\0/, `cat /proc/$pid/cmdline`))[1];
$cmdline =~ s/\0/ /g;
$cmdline =~ s/\s+$//;
chomp $cmdline;
print "$pid $prog ($cmdline) " . gettext("confined by") . " '$attr'\n";
} else {
print "$pid $prog $pname" . gettext("confined by") . " '$attr'\n";
}
}
}

View File

@ -18,8 +18,8 @@ INCLUDES = $(all_includes)
# release, then
# - set AA_LIB_AGE to 0.
#
AA_LIB_CURRENT = 1
AA_LIB_REVISION = 2
AA_LIB_CURRENT = 2
AA_LIB_REVISION = 0
AA_LIB_AGE = 0
SUFFIXES = .pc.in .pc

View File

@ -355,7 +355,7 @@ int aa_change_hat(const char *subprofile, unsigned long token)
int rc = -1;
int len = 0;
char *buf = NULL;
const char *fmt = "changehat %016x^%s";
const char *fmt = "changehat %016lx^%s";
/* both may not be null */
if (!(token || subprofile)) {

View File

@ -60,7 +60,7 @@ static inline Chars* insert_char_range(Chars* cset, uchar a, uchar b)
%lex-param {YYLEX_PARAM}
%parse-param {Node **root}
%parse-param {const char *text}
%name-prefix = "regex_"
%name-prefix "regex_"
%token <c> CHAR
%type <c> regex_char cset_char1 cset_char cset_charN

View File

@ -177,8 +177,10 @@ static void process_name(const void *nodep, VISIT value, int __unused level)
return;
/* aliases create alternate names */
alt = (struct alt_name *) calloc(1, sizeof(struct alt_name));
if (!alt)
if (!alt) {
free(n);
return;
}
alt->name = n;
alt->next = prof->altnames;
prof->altnames = alt;

View File

@ -133,13 +133,19 @@ void set_base_dir(char *dir)
int add_search_dir(const char *dir)
{
char *t;
size_t len;
if (npath >= MAX_PATH) {
PERROR(_("Error: Could not add directory %s to search path.\n"),
dir);
return 0;
}
if (!dir || strlen(dir) <= 0)
if (!dir)
return 1;
len = strlen(dir);
if (len == 0)
return 1;
t = strdup(dir);
@ -149,8 +155,8 @@ int add_search_dir(const char *dir)
}
/*strip trailing /'s */
while (t[strlen(t) - 1] == '/')
t[strlen(t) - 1] = 0;
while (len > 0 && t[--len] == '/')
t[len] = '\0';
path[npath] = t;
npath++;

View File

@ -273,8 +273,7 @@ LT_EQUAL <=
<INCLUDE>{
(\<([^\> \t\n]+)\>|\"([^\" \t\n]+)\") { /* <filename> */
char *filename = strdup(yytext);
filename[strlen(filename) - 1] = '\0';
char *filename = strndup(yytext, yyleng - 1);
include_filename(filename + 1, *filename == '<');
free(filename);
yy_pop_state();
@ -488,7 +487,12 @@ LT_EQUAL <=
}
}
#include/.*\r?\n { PUSH(INCLUDE); }
#include/.*\r?\n {
/* Don't use push here as we don't want #include echoed out. It needs
* to be handled specially
*/
yy_push_state(INCLUDE);
}
#.*\r?\n { /* normal comment */
DUMP_AND_DEBUG("comment(%d): %s\n", current_lineno, yytext);
@ -536,7 +540,6 @@ LT_EQUAL <=
{OPEN_PAREN} { PUSH_AND_RETURN(LIST_VAL_MODE, TOK_OPENPAREN); }
{VARIABLE_NAME} {
DUMP_PREPROCESS;
int token = get_keyword_token(yytext);
int state = INITIAL;

View File

@ -87,6 +87,8 @@ char *cacheloc = NULL;
/* per-profile settings */
int force_complain = 0;
/* Make sure to update BOTH the short and long_options */
static const char *short_options = "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkL:O:po:";
struct option long_options[] = {
{"add", 0, 0, 'a'},
{"binary", 0, 0, 'B'},
@ -570,7 +572,7 @@ static int process_arg(int c, char *optarg)
break;
default:
display_usage(progname);
exit(0);
exit(1);
break;
}
@ -583,7 +585,7 @@ static int process_args(int argc, char *argv[])
int count = 0;
option = OPTION_ADD;
while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:po:", long_options, &o)) != -1)
while ((c = getopt_long(argc, argv, short_options, long_options, &o)) != -1)
{
count += process_arg(c, optarg);
}
@ -611,6 +613,7 @@ static int process_config_file(const char *name)
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
process_arg(c, optarg);
fclose(f);
return 1;
}

View File

@ -174,6 +174,9 @@ static struct keyword_table rlimit_table[] = {
#endif
#ifdef RLIMIT_RTPRIO
{"rtprio", RLIMIT_RTPRIO},
#endif
#ifdef RLIMIT_RTTIME
{"rttime", RLIMIT_RTTIME},
#endif
/* terminate */
{NULL, 0}

View File

@ -24,7 +24,10 @@
#include <sys/apparmor.h>
#define _(s) gettext(s)
#include <iomanip>
#include <string>
#include <sstream>
/* #define DEBUG */
@ -39,9 +42,11 @@
enum error_type {
e_no_error,
e_parse_error,
e_buffer_overflow
};
/* match any char except \000 0 or more times */
static const char *default_match_pattern = "[^\\000]*";
/* Filters out multiple slashes (except if the first two are slashes,
* that's a distinct namespace in linux) and trailing slashes.
* NOTE: modifies in place the contents of the path argument */
@ -374,11 +379,6 @@ static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
}
/* check error again, as above STORE may have set it */
if (error != e_no_error) {
if (error == e_buffer_overflow) {
PERROR(_("%s: Internal buffer overflow detected, %d characters exceeded\n"),
progname, PATH_MAX);
}
PERROR(_("%s: Unable to parse input line '%s'\n"),
progname, aare);
@ -634,7 +634,7 @@ static int build_list_val_expr(std::string& buffer, struct value_list *list)
int pos;
if (!list) {
buffer.append("[^\\000]*");
buffer.append(default_match_pattern);
return TRUE;
}
@ -667,12 +667,18 @@ static int convert_entry(std::string& buffer, char *entry)
if (ptype == ePatternInvalid)
return FALSE;
} else {
buffer.append("[^\\000]*");
buffer.append(default_match_pattern);
}
return TRUE;
}
static int clear_and_convert_entry(std::string& buffer, char *entry)
{
buffer.clear();
return convert_entry(buffer, entry);
}
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int inv_flags)
{
@ -681,7 +687,7 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
if (flags == MS_ALL_FLAGS) {
/* all flags are optional */
len = snprintf(p, size, "[^\\000]*");
len = snprintf(p, size, "%s", default_match_pattern);
if (len < 0 || len >= size)
return FALSE;
return TRUE;
@ -721,7 +727,7 @@ static int build_mnt_opts(std::string& buffer, struct value_list *opts)
int pos;
if (!opts) {
buffer.append("[^\\000]*");
buffer.append(default_match_pattern);
return TRUE;
}
@ -772,12 +778,9 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
vec[0] = mntbuf.c_str();
}
/* skip device */
devbuf.clear();
if (!convert_entry(devbuf, NULL))
goto fail;
vec[1] = devbuf.c_str();
vec[1] = default_match_pattern;
/* skip type */
vec[2] = devbuf.c_str();
vec[2] = default_match_pattern;
flags = entry->flags;
inv_flags = entry->inv_flags;
@ -823,14 +826,11 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(mntbuf, entry->mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
devbuf.clear();
if (!convert_entry(devbuf, entry->device))
if (!clear_and_convert_entry(devbuf, entry->device))
goto fail;
vec[1] = devbuf.c_str();
typebuf.clear();
if (!convert_entry(typebuf, NULL))
goto fail;
vec[2] = typebuf.c_str();
/* skip type */
vec[2] = default_match_pattern;
flags = entry->flags;
inv_flags = entry->inv_flags;
@ -858,11 +858,8 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
goto fail;
vec[0] = mntbuf.c_str();
/* skip device and type */
devbuf.clear();
if (!convert_entry(devbuf, NULL))
goto fail;
vec[1] = devbuf.c_str();
vec[2] = devbuf.c_str();
vec[1] = default_match_pattern;
vec[2] = default_match_pattern;
flags = entry->flags;
inv_flags = entry->inv_flags;
@ -888,15 +885,11 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(mntbuf, entry->mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
devbuf.clear();
if (!convert_entry(devbuf, entry->device))
if (!clear_and_convert_entry(devbuf, entry->device))
goto fail;
vec[1] = devbuf.c_str();
/* skip type */
typebuf.clear();
if (!convert_entry(typebuf, NULL))
goto fail;
vec[2] = typebuf.c_str();
vec[2] = default_match_pattern;
flags = entry->flags;
inv_flags = entry->inv_flags;
@ -923,8 +916,7 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(mntbuf, entry->mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
devbuf.clear();
if (!convert_entry(devbuf, entry->device))
if (!clear_and_convert_entry(devbuf, entry->device))
goto fail;
vec[1] = devbuf.c_str();
typebuf.clear();
@ -985,8 +977,7 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(mntbuf, entry->mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
devbuf.clear();
if (!convert_entry(devbuf, entry->device))
if (!clear_and_convert_entry(devbuf, entry->device))
goto fail;
vec[1] = devbuf.c_str();
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
@ -1015,7 +1006,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
std::string pathbuf;
std::string ifacebuf;
std::string memberbuf;
char buffer[128];
std::ostringstream buffer;
const char *vec[6];
pattern_t ptype;
@ -1024,8 +1015,8 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
if (!entry) /* shouldn't happen */
return TRUE;
sprintf(buffer, "\\x%02x", AA_CLASS_DBUS);
busbuf.append(buffer);
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DBUS;
busbuf.append(buffer.str());
if (entry->bus) {
ptype = convert_aaregex_to_pcre(entry->bus, 0, busbuf, &pos);
@ -1033,7 +1024,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
goto fail;
} else {
/* match any char except \000 0 or more times */
busbuf.append("[^\\000]*");
busbuf.append(default_match_pattern);
}
vec[0] = busbuf.c_str();
@ -1044,7 +1035,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
vec[1] = namebuf.c_str();
} else {
/* match any char except \000 0 or more times */
vec[1] = "[^\\000]*";
vec[1] = default_match_pattern;
}
if (entry->peer_label) {
@ -1055,7 +1046,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
vec[2] = peer_labelbuf.c_str();
} else {
/* match any char except \000 0 or more times */
vec[2] = "[^\\000]*";
vec[2] = default_match_pattern;
}
if (entry->path) {
@ -1065,7 +1056,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
vec[3] = pathbuf.c_str();
} else {
/* match any char except \000 0 or more times */
vec[3] = "[^\\000]*";
vec[3] = default_match_pattern;
}
if (entry->interface) {
@ -1075,7 +1066,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
vec[4] = ifacebuf.c_str();
} else {
/* match any char except \000 0 or more times */
vec[4] = "[^\\000]*";
vec[4] = default_match_pattern;
}
if (entry->member) {
@ -1085,7 +1076,7 @@ static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry
vec[5] = memberbuf.c_str();
} else {
/* match any char except \000 0 or more times */
vec[5] = "[^\\000]*";
vec[5] = default_match_pattern;
}
if (entry->mode & AA_DBUS_BIND) {
@ -1252,41 +1243,36 @@ static int test_filter_slashes(void)
do { \
std::string tbuf; \
std::string tbuf2 = "testprefix"; \
char *test_string; \
char *output_string = NULL; \
std::string expected_str2; \
pattern_t ptype; \
int pos; \
\
test_string = strdup((input)); \
ptype = convert_aaregex_to_pcre(test_string, 0, tbuf, &pos); \
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
(input), expected_str, tbuf.c_str()); \
(input), (expected_str), tbuf.c_str()); \
MY_TEST(strcmp(tbuf.c_str(), (expected_str)) == 0, output_string); \
MY_TEST(ptype == (expected_type), "simple regex conversion type check for '" input "'"); \
free(output_string); \
/* ensure convert_aaregex_to_pcre appends only to passed ref string */ \
expected_str2 = tbuf2; \
expected_str2.append((expected_str)); \
ptype = convert_aaregex_to_pcre(test_string, 0, tbuf2, &pos); \
ptype = convert_aaregex_to_pcre((input), 0, tbuf2, &pos); \
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
(input), expected_str2.c_str(), tbuf2.c_str()); \
MY_TEST((tbuf2 == expected_str2), output_string); \
free(test_string); free(output_string); \
free(output_string); \
} \
while (0)
#define MY_REGEX_FAIL_TEST(input) \
do { \
std::string tbuf; \
char *test_string; \
pattern_t ptype; \
int pos; \
\
test_string = strdup((input)); \
ptype = convert_aaregex_to_pcre(test_string, 0, tbuf, &pos); \
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
MY_TEST(ptype == ePatternInvalid, "simple regex conversion invalid type check for '" input "'"); \
free(test_string); \
} \
while (0)

View File

@ -137,11 +137,11 @@ void free_var_string(struct var_string *var)
static void trim_trailing_slash(std::string& str)
{
for (std::string::reverse_iterator rit = str.rbegin();
rit != str.rend() && *rit == '/'; ++rit) {
/* yuck, reverse_iterators are ugly */
str.erase(--rit.base());
}
std::size_t found = str.find_last_not_of('/');
if (found != std::string::npos)
str.erase(found + 1);
else
str.clear(); // str is all '/'
}
static void write_replacement(const char separator, const char* value,
@ -177,10 +177,11 @@ static int expand_by_alternations(struct set_value **valuelist,
exit(1);
}
free(*name);
value = get_next_set_value(valuelist);
if (!value) {
/* only one entry for the variable, so just sub it in */
free(*name);
if (asprintf(name, "%s%s%s",
split_var->prefix ? split_var->prefix : "",
first_value,
@ -201,7 +202,6 @@ static int expand_by_alternations(struct set_value **valuelist,
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
}
free(*name);
if (asprintf(name, "%s%s}%s",
split_var->prefix ? split_var->prefix : "",
replacement.c_str(),

View File

@ -754,6 +754,7 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
value = RLIM_INFINITY;
} else {
const char *seconds = "seconds";
const char *milliseconds = "ms";
const char *minutes = "minutes";
const char *hours = "hours";
const char *days = "days";
@ -779,6 +780,22 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
break;
case RLIMIT_RTTIME:
/* RTTIME is measured in microseconds */
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (*end == '\0') {
value = tmp;
} else if (strstr(milliseconds, end) == milliseconds) {
value = tmp * 1000;
} else if (strstr(seconds, end) == seconds) {
value = tmp * 1000 * 1000;
} else if (strstr(minutes, end) == minutes) {
value = tmp * 1000 * 1000 * 60;
} else {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
break;
case RLIMIT_NOFILE:
case RLIMIT_NPROC:
case RLIMIT_LOCKS:

View File

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple allow other flag test
#=EXRESULT PASS
profile test {
allow other /tmp/** rw,
}

View File

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple audit allow other flag test
#=EXRESULT PASS
profile test {
audit allow other /tmp/** rw,
}

View File

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple deny other flag test
#=EXRESULT PASS
profile test {
deny other /tmp/** rw,
}

View File

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple other flag test
#=EXRESULT PASS
profile test {
audit other /tmp/** rw,
}

View File

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple realtime time rlimit test
#=EXRESULT PASS
profile rlimit {
set rlimit rttime <= 60minutes,
}

View File

@ -24,23 +24,6 @@ VALGRIND_ERROR_CODE = 151
VALGRIND_ARGS = ['--leak-check=full', '--error-exitcode=%d' % (VALGRIND_ERROR_CODE)]
VALGRIND_SUPPRESSIONS = '''
{
valgrind-add_search_dir-obsessive-overreads
Memcheck:Addr4
fun:_Z*add_search_dir*
fun:_Z*process_arg*
fun:main
}
{
valgrind-yylex-obsessive-overreads
Memcheck:Addr4
fun:_Z?yylex?
fun:_Z*yyparse*
fun:_Z*process_profile*
fun:main
}
{
valgrind-serialize_profile-obsessive-overreads
Memcheck:Addr4

View File

@ -44,6 +44,7 @@ local:
for profile in ${TOPLEVEL_PROFILES}; do \
fn=$$(basename $$profile); \
echo "# Site-specific additions and overrides for '$$fn'" > ${PROFILES_SOURCE}/local/$$fn; \
grep "include\\s\\s*<local/$$fn>" "$$profile" >/dev/null || { echo "$$profile doesn't contain #include <local/$$fn>" ; exit 1; } ; \
done; \
.PHONY: install

View File

@ -35,6 +35,7 @@
# DRI
/usr/lib{,32,64}/dri/** mr,
/usr/lib/@{multiarch}/dri/** mr,
/usr/lib/fglrx/dri/** mr,
/dev/dri/** rw,
/etc/drirc r,
owner @{HOME}/.drirc r,

View File

@ -56,7 +56,7 @@ owner @{HOME}/.pulse-cookie rwk,
owner @{HOME}/.pulse/ rw,
owner @{HOME}/.pulse/* rwk,
owner /{,var/}run/user/*/pulse/ rw,
owner /{,var/}run/user/*/pulse/* rwk,
owner /{,var/}run/user/*/pulse/{native,pid} rwk,
owner @{HOME}/.config/pulse/cookie rwk,
owner /tmp/pulse-*/ rw,
owner /tmp/pulse-*/* rw,

View File

@ -52,3 +52,6 @@
# poppler CMap tables
/usr/share/poppler/cMap/** r,
# data files for LibThai
/usr/share/libthai/thbrk.tri r,

View File

@ -21,6 +21,12 @@
/etc/passwd r,
/etc/protocols r,
# When using sssd, the passwd and group files are stored in an alternate path
# and the nss plugin also needs to talk to a pipe
/var/lib/sss/mc/group r,
/var/lib/sss/mc/passwd r,
/var/lib/sss/pipes/nss rw,
/etc/resolv.conf r,
# on systems using resolvconf, /etc/resolv.conf is a symlink to
# /{,var/}run/resolvconf/resolv.conf and a file sometimes referenced in
@ -50,7 +56,7 @@
/etc/default/nss r,
# avahi-daemon is used for mdns4 resolution
/{,var/}run/avahi-daemon/socket w,
/{,var/}run/avahi-daemon/socket rw,
# nis
#include <abstractions/nis>

View File

@ -13,12 +13,12 @@
/usr/lib{,32,64}/python2.[4567]/**.{pyc,so} mr,
/usr/lib{,32,64}/python2.[4567]/**.{egg,py,pth} r,
/usr/lib{,32,64}/python2.[4567]/{site,dist}-packages/ r,
/usr/lib{,32,64}/python3.3/lib-dynload/*.so mr,
/usr/lib{,32,64}/python3.[234]/lib-dynload/*.so mr,
/usr/local/lib{,32,64}/python2.[4567]/**.{pyc,so} mr,
/usr/local/lib{,32,64}/python2.[4567]/**.{egg,py,pth} r,
/usr/local/lib{,32,64}/python2.[4567]/{site,dist}-packages/ r,
/usr/local/lib{,32,64}/python3.3/lib-dynload/*.so mr,
/usr/local/lib{,32,64}/python3.[234]/lib-dynload/*.so mr,
# Site-wide configuration
/etc/python2.[4567]/** r,

View File

@ -55,3 +55,6 @@
# Virus scanners
/usr/bin/clamscan Cx -> sanitized_helper,
# gxine (LP: #1057642)
/var/lib/xine/gxine.desktop r,

View File

@ -33,3 +33,9 @@
/usr/lib/@{multiarch}/xfce4/exo-1/exo-helper-1 ixr,
/etc/xdg/xdg-xubuntu/xfce4/helpers.rc r,
/etc/xdg/xfce4/helpers.rc r,
# unity webapps integration. Could go in its own abstraction
owner /run/user/*/dconf/user rw,
owner @{HOME}/.local/share/unity-webapps/availableapps*.db rwk,
/usr/bin/debconf-communicate Cxr -> sanitized_helper,
owner @{HOME}/.config/libaccounts-glib/accounts.db rk,

View File

@ -38,6 +38,9 @@ profile sanitized_helper {
network inet,
network inet6,
# Allow all DBus communications
dbus,
# Allow exec of anything, but under this profile. Allow transition
# to other profiles if they exist.
/bin/* Pixr,

View File

@ -0,0 +1,167 @@
# vim:syntax=apparmor
# ------------------------------------------------------------------
#
# Copyright (C) 2013-2014 Canonical Ltd.
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
#
# Rules common to applications running under Unity 7
#
#include <abstractions/gnome>
# Allow connecting to session bus and where to connect to services
dbus (send)
bus=session
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=Hello
peer=(name=org.freedesktop.DBus),
dbus (send)
bus=session
path=/org/freedesktop/{db,DB}us
interface=org.freedesktop.DBus
member={Add,Remove}Match
peer=(name=org.freedesktop.DBus),
# NameHasOwner and GetNameOwner could leak running processes and apps
# depending on how services are implemented
dbus (send)
bus=session
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=GetNameOwner
peer=(name=org.freedesktop.DBus),
dbus (send)
bus=session
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=NameHasOwner
peer=(name=org.freedesktop.DBus),
# Allow starting services on the session bus (actual communications with
# the service are mediated elsewhere)
dbus (send)
bus=session
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=StartServiceByName
peer=(name=org.freedesktop.DBus),
# Allow connecting to system bus and where to connect to services. Put these
# here so we don't need to repeat these rules in multiple places (actual
# communications with any system services is mediated elsewhere). This does
# allow apps to brute-force enumerate system services, but our system
# services aren't a secret.
/{,var/}run/dbus/system_bus_socket rw,
dbus (send)
bus=system
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=Hello
peer=(name=org.freedesktop.DBus),
dbus (send)
bus=system
path=/org/freedesktop/{db,DB}us
interface=org.freedesktop.DBus
member={Add,Remove}Match
peer=(name=org.freedesktop.DBus),
# NameHasOwner and GetNameOwner could leak running processes and apps
# depending on how services are implemented
dbus (send)
bus=system
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=GetNameOwner
peer=(name=org.freedesktop.DBus),
dbus (send)
bus=system
path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member=NameHasOwner
peer=(name=org.freedesktop.DBus),
#
# Access required for connecting to/communication with Unity HUD
#
dbus (send)
bus=session
path="/com/canonical/hud",
dbus (send)
bus=session
interface="com.canonical.hud.*",
dbus (send)
bus=session
path="/com/canonical/hud/applications/*",
dbus (receive)
bus=session
path="/com/canonical/hud",
dbus (receive)
bus=session
interface="com.canonical.hud.*",
#
# Allow access for connecting to/communication with the appmenu
#
# dbusmenu
dbus (send)
bus=session
interface="com.canonical.AppMenu.*",
dbus (receive, send)
bus=session
path=/com/canonical/menu/**,
# gmenu
dbus (receive, send)
bus=session
interface=org.gtk.Actions,
dbus (receive, send)
bus=session
interface=org.gtk.Menus,
#
# Access required for using freedesktop notifications
#
dbus (send)
bus=session
path=/org/freedesktop/Notifications
member=GetCapabilities,
dbus (send)
bus=session
path=/org/freedesktop/Notifications
member=GetServerInformation,
dbus (send)
bus=session
path=/org/freedesktop/Notifications
member=Notify,
dbus (receive)
bus=session
member="Notify"
peer=(name="org.freedesktop.DBus"),
dbus (receive)
bus=session
path=/org/freedesktop/Notifications
member=NotificationClosed,
dbus (send)
bus=session
path=/org/freedesktop/Notifications
member=CloseNotification,
# accessibility
dbus (send)
bus=session
peer=(name=org.a11y.Bus),
dbus (receive)
bus=session
interface=org.a11y.atspi*,
dbus (receive, send)
bus=accessibility,
#
# Deny potentially dangerous access
#
deny dbus bus=session path=/com/canonical/[Uu]nity/[Dd]ebug**,

View File

@ -0,0 +1,7 @@
#
# Access required for connecting to/communicating with the Unity Launcher
#
dbus (send)
bus=session
interface="com.canonical.Unity.LauncherEntry"
member="Update",

View File

@ -0,0 +1,7 @@
#
# Access required for connecting to/communicating with the Unity messaging
# indicator
#
dbus (receive, send)
bus=session
path="/com/canonical/indicator/messages/*",

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2014 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -15,7 +16,9 @@
owner @{HOME}/[dD]ownload{,s}/ r,
owner @{HOME}/[dD]ownload{,s}/** rwl,
owner @{HOME}/[a-zA-Z0-9]* rwl,
owner @{HOME}/Desktop/ r,
owner @{HOME}/Desktop/* rwl,
owner @{HOME}/@{XDG_DESKTOP_DIR}/ r,
owner @{HOME}/@{XDG_DESKTOP_DIR}/* rwl,
owner @{HOME}/@{XDG_DOWNLOAD_DIR}/ r,
owner @{HOME}/@{XDG_DOWNLOAD_DIR}/* rwl,
owner "@{HOME}/My Downloads/" r,
owner "@{HOME}/My Downloads/**" rwl,

View File

@ -1,6 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2014 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -9,12 +10,12 @@
# ------------------------------------------------------------------
# per-user write directories
owner @{HOME}/ r,
owner @{HOME}/Desktop/ r,
owner @{HOME}/Documents/ r,
owner @{HOME}/Public/ r,
owner @{HOME}/[a-zA-Z0-9]*/ rw,
owner @{HOME}/[a-zA-Z0-9]* rwl,
owner @{HOME}/Desktop/** rwl,
owner @{HOME}/Documents/** rwl,
owner @{HOME}/Public/** rwl,
owner @{HOME}/ r,
owner @{HOME}/@{XDG_DESKTOP_DIR}/ r,
owner @{HOME}/@{XDG_DOCUMENTS_DIR}/ r,
owner @{HOME}/@{XDG_PUBLICSHARE_DIR}/ r,
owner @{HOME}/[a-zA-Z0-9]*/ rw,
owner @{HOME}/[a-zA-Z0-9]* rwl,
owner @{HOME}/@{XDG_DESKTOP_DIR}/** rwl,
owner @{HOME}/@{XDG_DOCUMENTS_DIR}/** rwl,
owner @{HOME}/@{XDG_PUBLICSHARE_DIR}/** rwl,

View File

@ -13,7 +13,9 @@
/tmp/.winbindd/pipe rw,
/var/{lib,run}/samba/winbindd_privileged/pipe rw,
/etc/samba/smb.conf r,
/etc/samba/dhcp.conf r,
/usr/lib*/samba/valid.dat r,
/usr/lib*/samba/upcase.dat r,
/usr/lib*/samba/lowcase.dat r,
/usr/share/samba/codepages/{lowcase,upcase,valid}.dat r,

View File

@ -0,0 +1,20 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim:ft=apparmor
# @{DOVECOT_MAILSTORE} is a space-separated list of all directories
# where dovecot is allowed to store and read mails
#
# The default value is quite broad to avoid breaking existing setups.
# Please change @{DOVECOT_MAILSTORE} to (only) contain the directory
# you use, and remove everything else.
@{DOVECOT_MAILSTORE}=@{HOME}/Maildir/ @{HOME}/mail/ @{HOME}/Mail/ /var/vmail/ /var/mail/ /var/spool/mail/

View File

@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2006-2009 Novell/SUSE
# Copyright (C) 2010-2011 Canonical Ltd.
# Copyright (C) 2010-2014 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -17,3 +17,4 @@
#include <tunables/proc>
#include <tunables/alias>
#include <tunables/kernelvars>
#include <tunables/xdg-user-dirs>

View File

@ -0,0 +1,24 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2014 Canonical Ltd.
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# Define the common set of XDG user directories (usually defined in
# /etc/xdg/user-dirs.defaults)
@{XDG_DESKTOP_DIR}="Desktop"
@{XDG_DOWNLOAD_DIR}="Downloads"
@{XDG_TEMPLATES_DIR}="Templates"
@{XDG_PUBLICSHARE_DIR}="Public"
@{XDG_DOCUMENTS_DIR}="Documents"
@{XDG_MUSIC_DIR}="Music"
@{XDG_PICTURES_DIR}="Pictures"
@{XDG_VIDEOS_DIR}="Videos"
# Also, include files in tunables/xdg-user-dirs.d for site-specific adjustments
# to the various XDG directories
#include <tunables/xdg-user-dirs.d>

View File

@ -0,0 +1,21 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2014 Canonical Ltd.
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# The following may be used to add additional entries such as for
# translations. See tunables/xdg-user-dirs for details. Eg:
#@{XDG_MUSIC_DIR}+="Musique"
#@{XDG_DESKTOP_DIR}+=""
#@{XDG_DOWNLOAD_DIR}+=""
#@{XDG_TEMPLATES_DIR}+=""
#@{XDG_PUBLICSHARE_DIR}+=""
#@{XDG_DOCUMENTS_DIR}+=""
#@{XDG_MUSIC_DIR}+=""
#@{XDG_PICTURES_DIR}+=""
#@{XDG_VIDEOS_DIR}+=""

View File

@ -0,0 +1,25 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/anvil {
#include <abstractions/base>
capability setgid,
capability setuid,
capability sys_chroot,
/usr/lib/dovecot/anvil mr,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.anvil>
}

View File

@ -0,0 +1,42 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/auth {
#include <abstractions/authentication>
#include <abstractions/base>
#include <abstractions/mysql>
#include <abstractions/nameservice>
deny capability block_suspend,
capability audit_write,
capability setgid,
capability setuid,
/etc/my.cnf r,
/etc/my.cnf.d/ r,
/etc/my.cnf.d/*.cnf r,
/etc/dovecot/dovecot-database.conf.ext r,
/etc/dovecot/dovecot-sql.conf.ext r,
/usr/lib/dovecot/auth mr,
# kerberos replay cache
/var/tmp/imap_* rw,
/var/tmp/pop_* rw,
/var/tmp/sieve_* rw,
/var/tmp/smtp_* rw,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.auth>
}

View File

@ -0,0 +1,32 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/config {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/ssl_keys>
deny capability block_suspend,
capability dac_override,
capability setgid,
/etc/dovecot/** r,
/usr/bin/doveconf rix,
/usr/lib/dovecot/config mr,
/usr/lib/dovecot/managesieve Px,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.config>
}

View File

@ -1,6 +1,19 @@
# Author: Dulmandakh Sukhbaatar <dulmandakh@gmail.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009 Dulmandakh Sukhbaatar <dulmandakh@gmail.com>
# Copyright (C) 2009-2012 Canonical Ltd.
# Copyright (C) 2011-2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
#include <tunables/dovecot>
/usr/lib/dovecot/deliver {
#include <abstractions/base>
#include <abstractions/nameservice>
@ -8,20 +21,16 @@
capability setgid,
capability setuid,
@{DOVECOT_MAILSTORE}/ rw,
@{DOVECOT_MAILSTORE}/** rwkl,
# http://www.postfix.org/SASL_README.html#server_dovecot
/etc/dovecot/dovecot.conf r,
/etc/dovecot/{auth,conf}.d/*.conf r,
/etc/dovecot/dovecot-postfix.conf r,
/etc/dovecot/dovecot-postfix.conf r, # ???
@{HOME} r,
@{HOME}/Maildir/ rw,
@{HOME}/Maildir/** klrw,
@{HOME}/mail/ rw,
@{HOME}/mail/* klrw,
@{HOME}/mail/.imap/** klrw,
@{HOME} r, # ???
/usr/lib/dovecot/deliver mr,
/var/mail/* klrw,
/var/spool/mail/* klrw,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.deliver>

View File

@ -0,0 +1,30 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/dict {
#include <abstractions/base>
#include <abstractions/mysql>
#include <abstractions/nameservice>
capability setgid,
capability setuid,
network inet stream,
/etc/dovecot/dovecot-database.conf.ext r,
/etc/dovecot/dovecot-dict-sql.conf.ext r,
/usr/lib/dovecot/dict mr,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.dict>
}

View File

@ -1,6 +1,17 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2013 Canonical Ltd.
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/dovecot-auth {
#include <abstractions/authentication>
#include <abstractions/base>

View File

@ -0,0 +1,33 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
#include <tunables/dovecot>
/usr/lib/dovecot/dovecot-lda {
#include <abstractions/base>
#include <abstractions/nameservice>
capability setgid,
capability setuid,
@{DOVECOT_MAILSTORE}/ rw,
@{DOVECOT_MAILSTORE}/** rwkl,
/etc/dovecot/** r,
/proc/*/mounts r,
/{var/,}run/dovecot/mounts r,
/usr/bin/doveconf mrix,
/usr/lib/dovecot/dovecot-lda mrix,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.dovecot-lda>
}

View File

@ -1,6 +1,18 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2010 Canonical Ltd.
# Copyright (C) 2011-2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
#include <tunables/dovecot>
/usr/lib/dovecot/imap {
#include <abstractions/base>
#include <abstractions/nameservice>
@ -8,18 +20,11 @@
capability setgid,
capability setuid,
@{HOME} r,
@{HOME}/Maildir/ rw,
@{HOME}/Maildir/** klrw,
@{HOME}/Mail/ rw,
@{HOME}/Mail/* klrw,
@{HOME}/Mail/.imap/** klrw,
@{HOME}/mail/ rw,
@{HOME}/mail/* klrw,
@{HOME}/mail/.imap/** klrw,
@{DOVECOT_MAILSTORE}/ rw,
@{DOVECOT_MAILSTORE}/** rwkl,
@{HOME} r, # ???
/usr/lib/dovecot/imap mr,
/var/mail/* klrw,
/var/spool/mail/* klrw,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.imap>

View File

@ -1,4 +1,14 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2011 Canonical Ltd.
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/imap-login {

View File

@ -0,0 +1,35 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
#include <tunables/dovecot>
/usr/lib/dovecot/lmtp {
#include <abstractions/base>
#include <abstractions/nameservice>
deny capability block_suspend,
capability dac_override,
capability setgid,
capability setuid,
@{DOVECOT_MAILSTORE}/ rw,
@{DOVECOT_MAILSTORE}/** rwkl,
/proc/*/mounts r,
/tmp/dovecot.lmtp.* rw,
/usr/lib/dovecot/lmtp mr,
/{var/,}run/dovecot/mounts r,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.lmtp>
}

View File

@ -0,0 +1,25 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/log {
#include <abstractions/base>
deny capability block_suspend,
capability setgid,
/usr/lib/dovecot/log mr,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.log>
}

View File

@ -0,0 +1,23 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/managesieve {
#include <abstractions/base>
/etc/dovecot/** r,
/usr/bin/doveconf rix,
/usr/lib/dovecot/managesieve mrix,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.managesieve>
}

View File

@ -1,4 +1,15 @@
# Author: Dulmandakh Sukhbaatar <dulmandakh@gmail.com>
# ------------------------------------------------------------------
#
# Copyright (c) 2009 Dulmandakh Sukhbaatar <dulmandakh@gmail.com>
# Copyright (C) 2009-2011 Canonical Ltd.
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/managesieve-login {

View File

@ -1,6 +1,18 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2010 Canonical Ltd.
# Copyright (C) 2011-2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
#include <tunables/dovecot>
/usr/lib/dovecot/pop3 {
#include <abstractions/base>
#include <abstractions/nameservice>
@ -8,13 +20,10 @@
capability setgid,
capability setuid,
/var/mail/* klrw,
/var/spool/mail/* klrw,
@{HOME} r,
@{HOME}/mail/* klrw,
@{HOME}/mail/.imap/** klrw,
@{HOME}/Maildir/ rw,
@{HOME}/Maildir/** klrw,
@{DOVECOT_MAILSTORE}/ rw,
@{DOVECOT_MAILSTORE}/** rwkl,
@{HOME} r, # ???
/usr/lib/dovecot/pop3 mr,
# Site-specific additions and overrides. See local/README for details.

View File

@ -1,6 +1,17 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2011 Canonical Ltd.
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/pop3-login {
#include <abstractions/base>
#include <abstractions/nameservice>

View File

@ -0,0 +1,27 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/lib/dovecot/ssl-params {
#include <abstractions/base>
deny capability block_suspend,
capability setgid,
/usr/lib/dovecot/ssl-params mr,
/var/lib/dovecot/ssl-parameters.dat rw,
/var/lib/dovecot/ssl-parameters.dat.tmp rwk,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.ssl-params>
}

View File

@ -0,0 +1,83 @@
# Author: Marc Deslauriers <marc.deslauriers@ubuntu.com>
#include <tunables/global>
/usr/sbin/apache2 {
# This profile is completely permissive.
# It is designed to target specific applications using mod_apparmor,
# hats, and the apache2.d directory.
#
# In order to enable this profile, you must:
#
# 1- Enable it:
# sudo aa-enforce /etc/apparmor.d/usr.sbin.apache2
#
# 2- Load the mpm_prefork and mod_apparmor modules:
# sudo a2dismod <other non-prefork mpm>
# sudo a2enmod mpm_prefork
# sudo a2enmod apparmor
# sudo service apache2 restart
#
# 3- Place an appropriate profile containing the desired hat in the
# /etc/apparmor.d/apache2.d directory. Such profiles should probably
# include the "apache2-common" abstraction.
#
# 4- Use the "AADefaultHatName" apache configuration option to specify a
# hat to be used for a given apache virtualhost or "AAHatName" for
# a given apache directory or location directive.
#
#
# There is an example profile for phpsysinfo included in the
# apparmor-profiles package. To try it:
#
# 1- Install the phpsysinfo and the apparmor-profiles packages:
# sudo apt-get install phpsysinfo apparmor-profiles
#
# 2- Enable the main apache2 profile
# sudo aa-enforce /etc/apparmor.d/usr.sbin.apache2
#
# 3- Configure apache with the following:
# <Directory /var/www/phpsysinfo/>
# AAHatName phpsysinfo
# </Directory>
#
#include <abstractions/base>
#include <abstractions/nameservice>
capability dac_override,
capability kill,
capability net_bind_service,
capability setgid,
capability setuid,
capability sys_tty_config,
/ rw,
/** mrwlkix,
^DEFAULT_URI {
#include <abstractions/base>
#include <abstractions/nameservice>
/ rw,
/** mrwlkix,
}
^HANDLING_UNTRUSTED_INPUT {
#include <abstractions/nameservice>
/ rw,
/** mrwlkix,
}
# This directory contains web application
# package-specific apparmor files.
#include <apache2.d>
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.sbin.apache2>
}

View File

@ -29,6 +29,8 @@
/etc/dnsmasq.d/ r,
/etc/dnsmasq.d/* r,
/etc/ethers r,
/etc/NetworkManager/dnsmasq.d/ r,
/etc/NetworkManager/dnsmasq.d/* r,
/usr/sbin/dnsmasq mr,
@ -56,6 +58,7 @@
/{,var/}run/nm-dns-dnsmasq.conf r,
/{,var/}run/sendsigs.omit.d/*dnsmasq.pid w,
/{,var/}run/NetworkManager/dnsmasq.conf r,
/{,var/}run/NetworkManager/dnsmasq.pid w,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.sbin.dnsmasq>

View File

@ -1,37 +1,61 @@
# Author: Kees Cook <kees@ubuntu.com>
# ------------------------------------------------------------------
#
# Copyright (C) 2009-2013 Canonical Ltd.
# Copyright (C) 2011-2013 Christian Boltz
#
# 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 published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim: ft=apparmor
#include <tunables/global>
/usr/sbin/dovecot {
#include <abstractions/authentication>
#include <abstractions/base>
#include <abstractions/mysql>
#include <abstractions/nameservice>
#include <abstractions/ssl_certs>
#include <abstractions/ssl_keys>
capability chown,
capability dac_override,
capability fsetid,
capability kill,
capability net_bind_service,
capability setgid,
capability setuid,
capability sys_chroot,
capability fsetid,
/etc/dovecot/** r,
/etc/mtab r,
/etc/lsb-release r,
/etc/SuSE-release r,
@{PROC}/@{pid}/mounts r,
@{PROC}/filesystems r,
/usr/bin/doveconf rix,
/usr/lib/dovecot/anvil Px,
/usr/lib/dovecot/auth Px,
/usr/lib/dovecot/config Px,
/usr/lib/dovecot/dict Px,
/usr/lib/dovecot/dovecot-auth Pxmr,
/usr/lib/dovecot/imap Pxmr,
/usr/lib/dovecot/imap-login Pxmr,
/usr/lib/dovecot/lmtp Px,
/usr/lib/dovecot/log Px,
/usr/lib/dovecot/managesieve Px,
/usr/lib/dovecot/managesieve-login Pxmr,
/usr/lib/dovecot/pop3 Px,
/usr/lib/dovecot/pop3-login Pxmr,
# temporarily commented out while testing
#/usr/lib/dovecot/managesieve Px,
/usr/lib/dovecot/managesieve-login Pxmr,
/usr/lib/dovecot/ssl-build-param ixr,
/usr/sbin/dovecot mr,
/usr/lib/dovecot/ssl-build-param rix,
/usr/lib/dovecot/ssl-params Px,
/usr/sbin/dovecot mrix,
/var/lib/dovecot/ w,
/var/lib/dovecot/* krw,
/var/lib/dovecot/* rwkl,
/var/spool/postfix/private/auth w,
/var/spool/postfix/private/dovecot-lmtp w,
/{,var/}run/dovecot/ rw,
/{,var/}run/dovecot/** rw,
link /{,var/}run/dovecot/** -> /var/lib/dovecot/**,

View File

@ -36,7 +36,6 @@
/var/cache/samba/** rwk,
/var/cache/samba/printing/printers.tdb mrw,
/var/lib/samba/** rwk,
/var/lib/sss/mc/passwd r,
/var/lib/sss/pubconf/kdcinfo.* r,
/{,var/}run/cups/cups.sock rw,
/{,var/}run/dbus/system_bus_socket rw,

View File

@ -1,33 +1,32 @@
# Last Modified: Mon Mar 26 20:28:18 2012
#include <tunables/global>
/usr/sbin/winbindd {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/samba>
deny capability block_suspend,
capability ipc_lock,
capability setuid,
/etc/samba/dhcp.conf r,
/etc/samba/passdb.tdb rwk,
/etc/samba/secrets.tdb rwk,
@{PROC}/sys/kernel/core_pattern r,
/tmp/.winbindd/ w,
/tmp/krb5cc_* rwk,
/usr/lib*/samba/idmap/*.so mr,
/usr/lib*/samba/nss_info/*.so mr,
/usr/lib*/samba/pdb/*.so mr,
/usr/sbin/winbindd mr,
/var/lib/samba/account_policy.tdb rwk,
/var/lib/samba/gencache.tdb rwk,
/var/lib/samba/gencache_notrans.tdb rwk,
/var/lib/samba/group_mapping.tdb rwk,
/var/lib/samba/messages.tdb rwk,
/var/lib/samba/netsamlogon_cache.tdb rwk,
/var/lib/samba/serverid.tdb rwk,
/var/lib/samba/winbindd_cache.tdb rwk,
/var/lib/samba/winbindd_privileged/pipe w,
/var/log/samba/cores/ rw,
/var/log/samba/cores/winbindd/ rw,
/var/log/samba/cores/winbindd/** rw,
/var/log/samba/log.wb-* w,
/var/cache/samba/*.tdb rwk,
/var/lib/samba/smb_krb5/krb5.conf.* rw,
/var/lib/samba/smb_tmp_krb5.* rw,
/var/lib/samba/winbindd_cache.tdb* rwk,
/var/log/samba/log.winbindd rw,
/{var/,}run/samba/winbindd.pid rwk,
/{var/,}run/samba/winbindd/ rw,
/{var/,}run/samba/winbindd/pipe w,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.sbin.winbindd>

View File

@ -186,16 +186,16 @@ changehat_pthread: changehat_pthread.c changehat.h
${CC} ${CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS} -pthread
dbus_common.o: dbus_common.c dbus_common.h
${CC} ${CFLAGS} ${LDFLAGS} $^ -c ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
${CC} ${CFLAGS} ${LDFLAGS} $< -c ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
dbus_eavesdrop: dbus_eavesdrop.c dbus_common.o
${CC} ${CFLAGS} ${LDFLAGS} $^ -o dbus_eavesdrop ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
dbus_message: dbus_message.c dbus_common.o
${CC} ${CFLAGS} ${LDFLAGS} $^ -o dbus_message ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
dbus_service: dbus_message dbus_service.c dbus_common.o
${CC} ${CFLAGS} ${LDFLAGS} $(filter-out dbus_message, $^) -o dbus_service ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
${CC} ${CFLAGS} ${LDFLAGS} $(filter-out dbus_message, $^) -o $@ ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
tests: all
@if [ `whoami` = "root" ] ;\

View File

@ -26,16 +26,14 @@ common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
MODDIR = Immunix
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
aa-unconfined aa-notify aa-disable aa-exec
TOOLS = ${PERLTOOLS} aa-decode aa-status
MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \
${MODDIR}/Config.pm ${MODDIR}/Severity.pm
PYTOOLS = aa-easyprof
PERLTOOLS = aa-exec aa-notify
PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \
aa-autodep aa-audit aa-complain aa-enforce aa-disable \
aa-status aa-unconfined
TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode
PYSETUP = python-tools-setup.py
MANPAGES = ${TOOLS:=.8} logprof.conf.5 ${PYTOOLS:=.8}
MANPAGES = ${TOOLS:=.8} logprof.conf.5
all: ${MANPAGES} ${HTMLMANPAGES}
$(MAKE) -C po all
@ -45,12 +43,10 @@ all: ${MANPAGES} ${HTMLMANPAGES}
DESTDIR=/
BINDIR=${DESTDIR}/usr/sbin
CONFDIR=${DESTDIR}/etc/apparmor
VENDOR_PERL=$(shell perl -e 'use Config; print $$Config{"vendorlib"};')
PERLDIR=${DESTDIR}${VENDOR_PERL}/${MODDIR}
PYPREFIX=/usr
po/${NAME}.pot: ${TOOLS} ${PYTOOLS}
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${MODULES} ${PYTOOLS}"
po/${NAME}.pot: ${TOOLS}
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${MODULES}"
.PHONY: install
install: ${MANPAGES} ${HTMLMANPAGES}
@ -59,8 +55,6 @@ install: ${MANPAGES} ${HTMLMANPAGES}
install -d ${BINDIR}
ln -sf aa-status ${BINDIR}/apparmor_status
install -m 755 ${TOOLS} ${BINDIR}
install -d ${PERLDIR}
install -m 644 ${MODULES} ${PERLDIR}
$(MAKE) -C po install DESTDIR=${DESTDIR} NAME=${NAME}
$(MAKE) install_manpages DESTDIR=${DESTDIR}
$(MAKE) -C vim install DESTDIR=${DESTDIR}
@ -78,7 +72,7 @@ clean: _clean
$(MAKE) -C vim clean
rm -rf staging/ build/
rm -f apparmor/*.pyc
rm -rf test/__pycache__/
rm -rf test/__pycache__/ apparmor/__pycache__/
# ${CAPABILITIES} is defined in common/Make.rules
.PHONY: check_severity_db
@ -100,7 +94,7 @@ check: check_severity_db
perl -c $$i || exit 1; \
done
tmpfile=$$(mktemp --tmpdir aa-pyflakes-XXXXXX); \
for i in ${PYTOOLS} apparmor aa-status test/*.py; do \
for i in ${PYTOOLS} apparmor test/*.py; do \
echo Checking $$i; \
pyflakes $$i 2>&1 | grep -v "undefined name '_'" > $$tmpfile; \
test -s $$tmpfile && cat $$tmpfile && rm -f $$tmpfile && exit 1; \

3
utils/README.md Normal file
View File

@ -0,0 +1,3 @@
Known Bugs:
Will allow multiple letters in the () due to translation/unicode issues with regexing the key.
User input will probably bug out in a different locale.

View File

@ -1,7 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -12,121 +11,30 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
import traceback
use strict;
use FindBin;
use Getopt::Long;
import apparmor.tools
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
parser = argparse.ArgumentParser(description=_('Switch the given programs to audit mode'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-r', '--remove', action='store_true', help=_('remove audit mode'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
parser.add_argument('--trace', action='store_true', help=_('Show full trace'))
args = parser.parse_args()
use Locale::gettext;
use POSIX;
try:
audit = apparmor.tools.aa_tools('audit', args)
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString("Please enter the program to switch to audit mode: ", ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to audit mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "audit");
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}
audit.act()
except Exception as e:
if not args.trace:
print(e.value + "\n")
else:
traceback.print_exc()

View File

@ -2,17 +2,30 @@
=head1 NAME
aa-audit - set a AppArmor security profile to I<audit> mode.
aa-audit - set an AppArmor security profile to I<audit> mode.
=head1 SYNOPSIS
B<aa-audit I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...]>
B<aa-audit I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-r>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-r --remove>
Removes the audit mode for the profile.
=head1 DESCRIPTION
B<aa-audit> is used to set the audit mode for one or more profiles to audit.
B<aa-audit> is used to set one or more profiles to audit mode.
In this mode security policy is enforced and all access (successes and failures) are logged to the system log.
The I<--remove> option can be used to remove the audit mode for the profile.
=head1 BUGS
If you find any bugs, please report them at

View File

@ -1,7 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -12,111 +11,21 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
use strict;
use FindBin;
use Getopt::Long;
import apparmor.tools
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
parser = argparse.ArgumentParser(description=_('Generate a basic AppArmor profile by guessing requirements'))
parser.add_argument('--force', action='store_true', default=False, help=_('overwrite existing profile'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
args = parser.parse_args()
use Locale::gettext;
use POSIX;
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
my $force = undef;
GetOptions(
'force' => \$force,
'dir|d=s' => \$profiledir,
'help|h' => \$help,
);
# tell 'em how to use it...
&usage && exit if $help;
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 AppArmor profiles in %s.'), $profiledir));
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to create a profile for: "), ""));
}
for my $profiling (@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));
} else {
autodep($fqdbin);
reload($fqdbin) if $sd_mountpoint;
}
} 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;
} 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;
}
autodep = apparmor.tools.aa_tools('autodep', args)
autodep.act()

View File

@ -26,7 +26,18 @@ aa-autodep - guess basic AppArmor profile requirements
=head1 SYNOPSIS
B<aa-autodep I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...]>
B<aa-autodep I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-f>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-f --force>
Overwrites any existing AppArmor profile for the executable with the generated minimal AppArmor profile.
=head1 DESCRIPTION
@ -37,6 +48,9 @@ a base profile containing a base include directive which includes basic
profile entries needed by most programs. The profile is generated by
recursively calling ldd(1) on the executables listed on the command line.
The I<--force> option will overwrite any existing profile for the executable with
the newly generated minimal AppArmor profile.
=head1 BUGS
This program does not perform full static analysis of executables, so

31
utils/aa-cleanprof Executable file
View File

@ -0,0 +1,31 @@
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# 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.
#
# ----------------------------------------------------------------------
import argparse
import apparmor.tools
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
parser = argparse.ArgumentParser(description=_('Cleanup the profiles for the given programs'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
parser.add_argument('-s', '--silent', action='store_true', help=_('Silently overwrite with a clean profile'))
args = parser.parse_args()
clean = apparmor.tools.aa_tools('cleanprof', args)
clean.act()

39
utils/aa-cleanprof.pod Normal file
View File

@ -0,0 +1,39 @@
=pod
=head1 NAME
aa-cleanprof - clean an existing AppArmor security profile.
=head1 SYNOPSIS
B<aa-cleanprof I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-s>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-s --silent>
Silently overwrites the profile without user prompt.
=head1 DESCRIPTION
B<aa-cleanprof> is used to perform a cleanup on one or more profiles.
The tool removes any existing superfluous rules (rules that are covered
under an include or another rule), reorders the rules to group similar rules
together and removes all comments from the file.
=head1 BUGS
If you find any bugs, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), aa-enforce(1), aa-complain(1), aa-disable(1),
aa_change_hat(2), and L<http://wiki.apparmor.net>.
=cut

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -11,121 +11,21 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
use strict;
use FindBin;
use Getopt::Long;
import apparmor.tools
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
use Locale::gettext;
use POSIX;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to switch to complain mode: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to complain mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "complain");
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}
parser = argparse.ArgumentParser(description=_('Switch the given program to complain mode'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-r', '--remove', action='store_true', help=_('remove complain mode'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
args = parser.parse_args()
complain = apparmor.tools.aa_tools('complain', args)
#print(args)
complain.act()

View File

@ -22,17 +22,31 @@
=head1 NAME
aa-complain - set a AppArmor security profile to I<complain> mode.
aa-complain - set an AppArmor security profile to I<complain> mode.
=head1 SYNOPSIS
B<aa-complain I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...]>
B<aa-complain I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-r>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-r --remove>
Removes the complain mode for the profile.
=head1 DESCRIPTION
B<aa-complain> is used to set the enforcement mode for one or more profiles to
complain. In this mode security policy is not enforced but rather access
violations are logged to the system log.
B<aa-complain> is used to set the enforcement mode for one or more profiles to I<complain> mode.
In this mode security policy is not enforced but rather access violations
are logged to the system log.
The I<--remove> option can be used to remove the complain mode for the profile,
setting it to enforce mode by default.
=head1 BUGS

View File

@ -1,7 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005-2010 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Inc. All Rights Reserved.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -12,141 +11,22 @@
# 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 Canonical, Inc.
#
# To contact Canonical about this file by physical or electronic mail,
# you may find current contact information at www.canonical.com.
# ----------------------------------------------------------------------
import argparse
use strict;
use FindBin;
use Getopt::Long;
import apparmor.tools
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
parser = argparse.ArgumentParser(description=_('Disable the profile for the given programs'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-r', '--revert', action='store_true', help=_('enable the profile for the given programs'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
args = parser.parse_args()
use Locale::gettext;
use POSIX;
use File::Basename;
disable = apparmor.tools.aa_tools('disable', args)
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
my $disabledir = "$profiledir/disable";
unless (-d $disabledir) {
UI_Important("Can't find AppArmor disable directory '$disabledir'.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program whose profile should be disabled: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip package manager backup files
next if isSkippableFile($filename);
my ($bname, $dname, $suffix) = File::Basename::fileparse($filename);
if ($bname eq "") {
UI_Info(sprintf(gettext('Could not find basename for %s.'), $filename));
exit 1;
}
printf(gettext('Disabling %s.'), $fqdbin);
print "\n";
my $link = "$disabledir/$bname";
if (! -e $link) {
if (symlink($filename, $link) != 1) {
UI_Info(sprintf(gettext('Could not create %s symlink.'), $link));
exit 1;
}
}
my $cmd_info = qx(cat $filename | $parser -I$profiledir -R 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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 have profile disabled ]"), $0));
exit 0;
}
disable.act()

View File

@ -26,15 +26,28 @@ aa-disable - disable an AppArmor security profile
=head1 SYNOPSIS
B<aa-disable I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...]>
B<aa-disable I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-r>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-r --revert>
Enables the profile and loads it.
=head1 DESCRIPTION
B<aa-disable> is used to disable the enforcement mode for one or more
profiles. This command will unload the profile from the kernel and
prevent the profile from being loaded on AppArmor startup. The
I<aa-enforce> and I<aa-complain> utilities may be used to to change this
behavior.
B<aa-disable> is used to I<disable> one or more profiles.
This command will unload the profile from the kernel and prevent the
profile from being loaded on AppArmor startup.
The I<aa-enforce> and I<aa-complain> utilities may be used to to change
this behavior.
The I<--revert> option can be used to enable the profile.
=head1 BUGS

View File

@ -1,7 +1,7 @@
#! /usr/bin/env python
# ------------------------------------------------------------------
#
# Copyright (C) 2011-2012 Canonical Ltd.
# Copyright (C) 2011-2013 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -22,6 +22,7 @@ if __name__ == "__main__":
(opt, args) = apparmor.easyprof.parse_args()
binary = None
manifest = None
m = usage()
if opt.show_policy_group and not opt.policy_groups:
@ -33,33 +34,65 @@ if __name__ == "__main__":
if len(args) >= 1:
binary = args[0]
try:
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, opt)
except AppArmorException as e:
error(e.value)
except Exception:
raise
# parse_manifest() returns a list of tuples (binary, options). Create a
# list of these profile tuples to support multiple profiles in one manifest
profiles = []
if opt.manifest:
try:
# should hide this in a common function
if sys.version_info[0] >= 3:
f = open(opt.manifest, "r", encoding="utf-8")
else:
f = open(opt.manifest, "r")
manifest = f.read()
except EnvironmentError as e:
error("Could not read '%s': %s (%d)\n" % (opt.manifest,
os.strerror(e.errno),
e.errno))
profiles = apparmor.easyprof.parse_manifest(manifest, opt)
else: # fake up a tuple list when processing command line args
profiles.append( (binary, opt) )
if opt.list_templates:
apparmor.easyprof.print_basefilenames(easyp.get_templates())
sys.exit(0)
elif opt.template and opt.show_template:
files = [os.path.join(easyp.dirs['templates'], opt.template)]
apparmor.easyprof.print_files(files)
sys.exit(0)
elif opt.list_policy_groups:
apparmor.easyprof.print_basefilenames(easyp.get_policy_groups())
sys.exit(0)
elif opt.policy_groups and opt.show_policy_group:
for g in opt.policy_groups.split(','):
files = [os.path.join(easyp.dirs['policygroups'], g)]
count = 0
for (binary, options) in profiles:
if len(profiles) > 1:
count += 1
try:
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, options)
except AppArmorException as e:
error(e.value)
except Exception:
raise
if options.list_templates:
apparmor.easyprof.print_basefilenames(easyp.get_templates())
sys.exit(0)
elif options.template and options.show_template:
files = [os.path.join(easyp.dirs['templates'], options.template)]
apparmor.easyprof.print_files(files)
sys.exit(0)
elif binary == None:
error("Must specify full path to binary\n%s" % m)
sys.exit(0)
elif options.list_policy_groups:
apparmor.easyprof.print_basefilenames(easyp.get_policy_groups())
sys.exit(0)
elif options.policy_groups and options.show_policy_group:
for g in options.policy_groups.split(','):
files = [os.path.join(easyp.dirs['policygroups'], g)]
apparmor.easyprof.print_files(files)
sys.exit(0)
elif binary == None and not options.profile_name and \
not options.manifest:
error("Must specify binary and/or profile name\n%s" % m)
# if we made it here, generate a profile
params = apparmor.easyprof.gen_policy_params(binary, opt)
p = easyp.gen_policy(**params)
sys.stdout.write('%s\n' % p)
params = apparmor.easyprof.gen_policy_params(binary, options)
if options.manifest and options.verify_manifest and \
not apparmor.easyprof.verify_manifest(params):
error("Manifest file requires review")
if options.output_format == "json":
sys.stdout.write('%s\n' % easyp.gen_manifest(params))
else:
params['no_verify'] = options.no_verify
try:
easyp.output_policy(params, count, opt.output_directory)
except AppArmorException as e:
error(e)

View File

@ -78,8 +78,15 @@ Like --read-path but also allow owner writes in additions to reads.
=item -n NAME, --name=NAME
Specify NAME of policy. If not specified, NAME is set to the name of the
binary. The NAME of the policy is often used as part of the path in the
various templates.
binary. The NAME of the policy is typically only used for profile meta
data and does not specify the AppArmor profile name.
=item --profile-name=PROFILENAME
Specify the AppArmor profile name. When set, uses 'profile PROFILENAME' in the
profile. When set and specifying a binary, uses 'profile PROFILENAME BINARY'
in the profile. If not set, the binary will be used as the profile name and
profile attachment.
=item --template-var="@{VAR}=VALUE"
@ -110,6 +117,32 @@ Display policy groups specified with --policy.
Use PATH instead of system policy-groups directory.
=item --policy-version=VERSION
Must be used with --policy-vendor and is used to specify the version of policy
groups and templates. When specified, B<aa-easyprof> looks for the subdirectory
VENDOR/VERSION within the policy-groups and templates directory. The specified
version must be a positive decimal number compatible with the JSON Number type.
Eg, when using:
=over
$ aa-easyprof --templates-dir=/usr/share/apparmor/easyprof/templates \
--policy-groups-dir=/usr/share/apparmor/easyprof/policygroups \
--policy-vendor="foo" \
--policy-version=1.0
=back
Then /usr/share/apparmor/easyprof/templates/foo/1.0 will be searched for
templates and /usr/share/apparmor/easyprof/policygroups/foo/1.0 for policy
groups.
=item --policy-vendor=VENDOR
Must be used with --policy-version and is used to specify the vendor for policy
groups and templates. See --policy-version for more information.
=item --author
Specify author of the policy.
@ -122,6 +155,104 @@ Specify copyright of the policy.
Specify comment for the policy.
=item -m MANIFEST, --manifest=MANIFEST
B<aa-easyprof> also supports using a JSON manifest file for specifying options
related to policy. Unlike command line arguments, the JSON file may specify
multiple profiles. The structure of the JSON is:
{
"security": {
"profiles": {
"<profile name 1>": {
... attributes specific to this profile ...
},
"<profile name 2>": {
...
}
}
}
}
Each profile JSON object (ie, everything under a profile name) may specify any
fields related to policy. The "security" JSON container object is optional and
may be omitted. An example manifest file demonstrating all fields is:
{
"security": {
"profiles": {
"com.example.foo": {
"abstractions": [
"audio",
"gnome"
],
"author": "Your Name",
"binary": "/opt/foo/**",
"comment": "Unstructured single-line comment",
"copyright": "Unstructured single-line copyright statement",
"name": "My Foo App",
"policy_groups": [
"networking",
"user-application"
],
"policy_vendor": "somevendor",
"policy_version": 1.0,
"read_path": [
"/tmp/foo_r",
"/tmp/bar_r/"
],
"template": "user-application",
"template_variables": {
"APPNAME": "foo",
"VAR1": "bar",
"VAR2": "baz"
},
"write_path": [
"/tmp/foo_w",
"/tmp/bar_w/"
]
}
}
}
}
A manifest file does not have to include all the fields. Eg, a manifest file
for an Ubuntu SDK application might be:
{
"security": {
"profiles": {
"com.ubuntu.developer.myusername.MyCoolApp": {
"policy_groups": [
"networking",
"online-accounts"
],
"policy_vendor": "ubuntu",
"policy_version": 1.0,
"template": "ubuntu-sdk",
"template_variables": {
"APPNAME": "MyCoolApp",
"APPVERSION": "0.1.2"
}
}
}
}
}
=item --verify-manifest
When used with --manifest, warn about potentially unsafe definitions in the
manifest file.
=item --output-format=FORMAT
Specify either B<text> (default if unspecified) for AppArmor policy output or
B<json> for JSON manifest format.
=item --output-directory=DIR
Specify output directory for profile. If unspecified, policy is sent to stdout.
=back
=head1 EXAMPLE
@ -130,7 +261,41 @@ Example usage for a program named 'foo' which is installed in /opt/foo:
=over
$ aa-easyprof --template=user-application --template-var="@{APPNAME}=foo" --policy-groups=opt-application,user-application /opt/foo/bin/FooApp
$ aa-easyprof --template=user-application --template-var="@{APPNAME}=foo" \
--policy-groups=opt-application,user-application \
/opt/foo/bin/FooApp
=back
When using a manifest file:
=over
$ aa-easyprof --manifest=manifest.json
=back
To output a manifest file based on aa-easyprof arguments:
=over
$ aa-easyprof --output-format=json \
--author="Your Name" \
--comment="Unstructured single-line comment" \
--copyright="Unstructured single-line copyright statement" \
--name="My Foo App" \
--profile-name="com.example.foo" \
--template="user-application" \
--policy-groups="user-application,networking" \
--abstractions="audio,gnome" \
--read-path="/tmp/foo_r" \
--read-path="/tmp/bar_r/" \
--write-path="/tmp/foo_w" \
--write-path=/tmp/bar_w/ \
--template-var="@{APPNAME}=foo" \
--template-var="@{VAR1}=bar" \
--template-var="@{VAR2}=baz" \
"/opt/foo/**"
=back

View File

@ -1,7 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (c) 2011 Canonical, Ltd.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -12,131 +11,23 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
use strict;
use FindBin;
use Getopt::Long;
import apparmor.tools
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
parser = argparse.ArgumentParser(description=_('Switch the given program to enforce mode'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-r', '--remove', action='store_true', help=_('switch to complain mode'))
parser.add_argument('program', type=str, nargs='+', help=_('name of program'))
args = parser.parse_args()
# Flipping the remove flag since complain = !enforce
args.remove = not args.remove
use Locale::gettext;
use POSIX;
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
$UI_Mode = "text";
# options variables
my $help = '';
GetOptions(
'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 AppArmor profiles in $profiledir.");
exit 1;
}
# what are we profiling?
my @profiling = @ARGV;
unless (@profiling) {
@profiling = (UI_GetString(gettext("Please enter the program to switch to enforce mode: "), ""));
}
for my $profiling (@profiling) {
next unless $profiling;
my $fqdbin;
if (-e $profiling) {
$fqdbin = get_full_path($profiling);
chomp($fqdbin);
} else {
if ($profiling !~ /\//) {
opendir(DIR,$profiledir);
my @tmp_fqdbin = grep ( /$profiling/, readdir(DIR));
closedir(DIR);
if (scalar @tmp_fqdbin eq 1) {
$fqdbin = "$profiledir/$tmp_fqdbin[0]";
} else {
my $which = which($profiling);
if ($which) {
$fqdbin = get_full_path($which);
}
}
}
}
if (-e $fqdbin) {
my $filename;
if ($fqdbin =~ /^$profiledir\//) {
$filename = $fqdbin;
} else {
$filename = getprofilefilename($fqdbin);
}
# argh, skip directories
next unless -f $filename;
# skip rpm backup files
next if isSkippableFile($filename);
printf(gettext('Setting %s to enforce mode.'), $fqdbin);
print "\n";
setprofileflags($filename, "");
# remove symlink in $profiledir/force-complain as well
my $complainlink = $filename;
$complainlink =~ s/^$profiledir/$profiledir\/force-complain/;
-e $complainlink and unlink($complainlink);
# remove symlink in $profiledir/disable as well
my $disablelink = $filename;
$disablelink =~ s/^$profiledir/$profiledir\/disable/;
-e $disablelink and unlink($disablelink);
my $cmd_info = qx(cat $filename | $parser -I$profiledir -r 2>&1 1>/dev/null);
if ($? != 0) {
UI_Info($cmd_info);
exit $?;
}
# 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;
} 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;
}
enforce = apparmor.tools.aa_tools('complain', args)
enforce.act()

View File

@ -27,16 +27,30 @@ being disabled or I<complain> mode.
=head1 SYNOPSIS
B<aa-enforce I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...]>
B<aa-enforce I<E<lt>executableE<gt>> [I<E<lt>executableE<gt>> ...] [I<-d /path/to/profiles>] [I<-r>]>
=head1 OPTIONS
B<-d --dir / path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-r --remove>
Removes the enforce mode for the profile.
=head1 DESCRIPTION
B<aa-enforce> is used to set the enforcement mode for one or more profiles
to I<enforce>. This command is only relevant in conjunction with the
I<aa-complain> utility which sets a profile to complain mode and the
I<aa-disable> utility which unloads and disables a profile. The default
mode for a security policy is enforce and the I<aa-complain> utility must
be run to change this behavior.
B<aa-enforce> is used to set one or more profiles to I<enforce> mode.
This command is only relevant in conjunction with the I<aa-complain> utility
which sets a profile to complain mode and the I<aa-disable> utility which
unloads and disables a profile.
The default mode for a security policy is enforce and the I<aa-complain>
utility must be run to change this behavior.
The I<--remove> option can be used to remove the enforce mode for the profile,
setting it to complain mode.
=head1 BUGS

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -11,206 +11,154 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
import atexit
import os
import re
import subprocess
import sys
use strict;
use Getopt::Long;
import apparmor.aa as apparmor
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
use Data::Dumper;
def sysctl_read(path):
value = None
with open(path, 'r') as f_in:
value = int(f_in.readline())
return value
use Locale::gettext;
use POSIX;
def sysctl_write(path, value):
if not value:
return
with open(path, 'w') as f_out:
f_out.write(str(value))
sub sysctl_read($) {
my $path = shift;
my $value = undef;
if (open(SYSCTL, "<$path")) {
$value = int(<SYSCTL>);
}
close(SYSCTL);
return $value;
}
def last_audit_entry_time():
out = subprocess.check_output(['tail', '-1', '/var/log/audit/audit.log'], shell=True)
logmark = None
if re.search('^*msg\=audit\((\d+\.\d+\:\d+).*\).*$', out):
logmark = re.search('^*msg\=audit\((\d+\.\d+\:\d+).*\).*$', out).groups()[0]
else:
logmark = ''
return logmark
sub sysctl_write($$) {
my $path = shift;
my $value = shift;
return if (!defined($value));
if (open(SYSCTL, ">$path")) {
print SYSCTL $value;
close(SYSCTl);
}
}
def restore_ratelimit():
sysctl_write(ratelimit_sysctl, ratelimit_saved)
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
parser = argparse.ArgumentParser(description=_('Generate profile for the given program'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
parser.add_argument('program', type=str, help=_('name of program to profile'))
args = parser.parse_args()
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
# options variables
my $help = '';
GetOptions(
'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("AppArmor does not appear to be started. Please enable AppArmor and try again."));
}
# let's convert it to full path...
$profiledir = get_full_path($profiledir);
unless (-d $profiledir) {
fatal_error "Can't find AppArmor profiles in $profiledir.";
}
# what are we profiling?
my $profiling = shift;
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);
} else {
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));
}
}
profiling = args.program
profiledir = args.dir
filename = args.file
# make sure that the app they're requesting to profile is not marked as
# not allowed to have it's own profile
check_qualifiers($fqdbin);
if filename:
if not os.path.isfile(filename):
raise apparmor.AppArmorException(_('The logfile %s does not exist. Please check the path') % filename)
else:
apparmor.filename = filename
# load all the include files
loadincludes();
aa_mountpoint = apparmor.check_for_apparmor()
if not aa_mountpoint:
raise apparmor.AppArmorException(_('It seems AppArmor was not started. Please enable AppArmor and try again.'))
my $profilefilename = getprofilefilename($fqdbin);
if (-e $profilefilename) {
$helpers{$fqdbin} = getprofileflags($profilefilename) || "enforce";
} else {
autodep($fqdbin);
$helpers{$fqdbin} = "enforce";
}
if profiledir:
apparmor.profile_dir = apparmor.get_full_path(profiledir)
if not os.path.isdir(apparmor.profile_dir):
raise apparmor.AppArmorException(_("%s is not a directory.") %profiledir)
if ($helpers{$fqdbin} eq "enforce") {
complain($fqdbin);
reload($fqdbin);
}
program = None
#if os.path.exists(apparmor.which(profiling.strip())):
if os.path.exists(profiling):
program = apparmor.get_full_path(profiling)
else:
if '/' not in profiling:
which = apparmor.which(profiling)
if which:
program = apparmor.get_full_path(which)
if not program or not os.path.exists(program):
if '/' not in profiling:
raise apparmor.AppArmorException(_("Can't find %s in the system path list. If the name of the application\nis correct, please run 'which %s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") %(profiling, profiling))
else:
raise apparmor.AppArmorException(_('%s does not exists, please double-check the path.') %profiling)
# Check if the program has been marked as not allowed to have a profile
apparmor.check_qualifiers(program)
apparmor.loadincludes()
profile_filename = apparmor.get_profile_filename(program)
if os.path.exists(profile_filename):
apparmor.helpers[program] = apparmor.get_profile_flags(profile_filename, program)
else:
apparmor.autodep(program)
apparmor.helpers[program] = 'enforce'
if apparmor.helpers[program] == 'enforce':
apparmor.complain(program)
apparmor.reload(program)
# When reading from syslog, it is possible to hit the default kernel
# printk ratelimit. This will result in audit entries getting skipped,
# making profile generation inaccurate. When using genprof, disable
# the printk ratelimit, and restore it on exit.
my $ratelimit_sysctl = "/proc/sys/kernel/printk_ratelimit";
my $ratelimit_saved = sysctl_read($ratelimit_sysctl);
END { sysctl_write($ratelimit_sysctl, $ratelimit_saved); }
sysctl_write($ratelimit_sysctl, 0);
ratelimit_sysctl = '/proc/sys/kernel/printk_ratelimit'
ratelimit_saved = sysctl_read(ratelimit_sysctl)
sysctl_write(ratelimit_sysctl, 0)
UI_Info(gettext("\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:\nhttp://wiki.apparmor.net/index.php/Profiles"));
atexit.register(restore_ratelimit)
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."));
apparmor.UI_Info(_('\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:')+'\nhttp://wiki.apparmor.net/index.php/Profiles')
my $syslog = 1;
my $logmark = "";
my $done_profiling = 0;
apparmor.UI_Important(_('Please start the application to be profiled in\nanother window and exercise its functionality now.\n\nOnce completed, select the "Scan" option 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.'))
$syslog = 0 if (-e "/var/log/audit/audit.log");
syslog = True
logmark = ''
done_profiling = False
while (not $done_profiling) {
if ($syslog) {
$logmark = `date | md5sum`;
chomp $logmark;
$logmark = $1 if $logmark =~ /^([0-9a-f]+)/;
system("$logger -p kern.warn 'GenProf: $logmark'");
} else {
$logmark = last_audit_entry_time();
}
eval {
if os.path.exists('/var/log/audit/audit.log'):
syslog = False
my $q = {};
$q->{headers} = [ gettext("Profiling"), $fqdbin ];
$q->{functions} = [ "CMD_SCAN", "CMD_FINISHED" ];
$q->{default} = "CMD_SCAN";
passno = 0
while not done_profiling:
if syslog:
logmark = subprocess.check_output(['date | md5sum'], shell=True)
logmark = logmark.decode('ascii').strip()
logmark = re.search('^([0-9a-f]+)', logmark).groups()[0]
t=subprocess.call("%s -p kern.warn 'GenProf: %s'"%(apparmor.logger, logmark), shell=True)
my ($ans, $arg) = UI_PromptUser($q);
else:
logmark = last_audit_entry_time()
if ($ans eq "CMD_SCAN") {
q=apparmor.hasher()
q['headers'] = [_('Profiling'), program]
q['functions'] = ['CMD_SCAN', 'CMD_FINISHED']
q['default'] = 'CMD_SCAN'
ans, arg = apparmor.UI_PromptUser(q, 'noexit')
my $lp_ret = do_logprof_pass($logmark);
if ans == 'CMD_SCAN':
lp_ret = apparmor.do_logprof_pass(logmark, passno)
passno += 1
if lp_ret == 'FINISHED':
done_profiling = True
else:
done_profiling = True
$done_profiling = 1 if $lp_ret eq "FINISHED";
for p in sorted(apparmor.helpers.keys()):
if apparmor.helpers[p] == 'enforce':
apparmor.enforce(p)
apparmor.reload(p)
} else {
$done_profiling = 1;
}
};
if ($@) {
if ($@ =~ /FINISHING/) {
$done_profiling = 1;
} else {
die $@;
}
}
}
for my $p (sort keys %helpers) {
if ($helpers{$p} eq "enforce") {
enforce($p);
reload($p);
}
}
UI_Info(gettext("Reloaded AppArmor profiles in enforce mode."));
UI_Info(gettext("\nPlease consider contributing your new profile! See\nthe following wiki page for more information:\nhttp://wiki.apparmor.net/index.php/Profiles\n"));
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;
}
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;
}
apparmor.UI_Info(_('\nReloaded AppArmor profiles in enforce mode.'))
apparmor.UI_Info(_('\nPlease consider contributing your new profile!\nSee the following wiki page for more information:')+'\nhttp://wiki.apparmor.net/index.php/Profiles\n')
apparmor.UI_Info(_('Finished generating profile for %s.')%program)
sys.exit(0)

View File

@ -26,15 +26,23 @@ aa-genprof - profile generation utility for AppArmor
=head1 SYNOPSIS
B<aa-genprof I<E<lt>executableE<gt>> [I<-d /path/to/profiles>]>
B<aa-genprof I<E<lt>executableE<gt>> [I<-d /path/to/profiles>] [I<-f /path/to/logfile>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-f --file /path/to/logfile>
Specifies the location of logfile.
Default locations are read from F</etc/apparmor/logprof.conf>.
Typical defaults are:
/var/log/audit/audit.log
/var/log/syslog
/var/log/messages
=head1 DESCRIPTION
@ -64,7 +72,7 @@ using aa-logprof(1).
After the user finishes selecting profile entries based on violations
that were detected during the program execution, aa-genprof will reload
the updated profiles in complain mode and again prompt the user for (S)can and
(D)one. This cycle can then be repeated as necessary until all application
(F)inish. This cycle can then be repeated as necessary until all application
functionality has been exercised without generating access violations.
When the user eventually hits (F)inish, aa-genprof will set the main profile,

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (c) 2005 Novell, Inc. All Rights Reserved.
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@ -11,62 +11,43 @@
# 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,
# you may find current contact information at www.novell.com.
# ----------------------------------------------------------------------
import argparse
import os
use strict;
use Data::Dumper;
use Getopt::Long;
use Locale::gettext;
use POSIX;
import apparmor.aa as apparmor
use Immunix::AppArmor;
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
# force $PATH to be sane
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
parser = argparse.ArgumentParser(description=_('Process log entries to generate profiles'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
parser.add_argument('-m', '--mark', type=str, help=_('mark in the log to start processing after'))
args = parser.parse_args()
# initialize the local poo
setlocale(LC_MESSAGES, "");
textdomain("apparmor-utils");
profiledir = args.dir
filename = args.file
logmark = args.mark or ''
setup_yast();
# options variables
my $help = '';
my $logmark;
if filename:
if not os.path.isfile(filename):
raise apparmor.AppArmorException(_('The logfile %s does not exist. Please check the path') % filename)
else:
apparmor.filename = filename
GetOptions(
'file|f=s' => \$filename,
'dir|d=s' => \$profiledir,
'logmark|m=s' => \$logmark,
'help|h' => \$help,
);
aa_mountpoint = apparmor.check_for_apparmor()
if not aa_mountpoint:
raise apparmor.AppArmorException(_('It seems AppArmor was not started. Please enable AppArmor and try again.'))
# tell 'em how to use it...
&usage && exit if $help;
if profiledir:
apparmor.profile_dir = apparmor.get_full_path(profiledir)
if not os.path.isdir(apparmor.profile_dir):
raise apparmor.AppArmorException("%s is not a directory."%profiledir)
# let's convert it to full path...
$profiledir = get_full_path($profiledir);
apparmor.loadincludes()
unless (-d $profiledir) {
fatal_error "Can't find AppArmor profiles in $profiledir.";
}
# load all the include files
loadincludes();
do_logprof_pass($logmark);
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;
}
apparmor.do_logprof_pass(logmark)

View File

@ -22,7 +22,7 @@
=head1 NAME
aa-logprof - utility program for managing AppArmor security profiles
aa-logprof - utility for updating AppArmor security profiles
=head1 SYNOPSIS
@ -32,12 +32,17 @@ B<aa-logprof [I<-d /path/to/profiles>] [I<-f /path/to/logfile>] [I<-m E<lt>mark
B<-d --dir /path/to/profiles>
The path to where the AppArmor security profiles are stored
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
B<-f --file /path/to/logfile>
The path to the location of the logfile that contains AppArmor
security events.
Specifies the location of logfile that contains AppArmor security events.
Default locations are read from F</etc/apparmor/logprof.conf>.
Typical defaults are:
/var/log/audit/audit.log
/var/log/syslog
/var/log/messages
B< -m --logmark "mark">
@ -47,9 +52,8 @@ B< -m --logmark "mark">
=head1 DESCRIPTION
B<aa-logprof> is an interactive tool used to review AppArmor's
complain mode output and generate new entries for AppArmor security
profiles.
B<aa-logprof> is an interactive tool used to review AppArmor generated
messages and update AppArmor security profiles.
Running aa-logprof will scan the log file and if there are new AppArmor
events that are not covered by the existing profile set, the user will
@ -71,11 +75,17 @@ The user is then presented with info about the access including profile,
path, old mode if there was a previous entry in the profile for this path,
new mode, the suggestion list, and given these options:
(A)llow, (D)eny, (N)ew, (G)lob last piece, (Q)uit
(A)llow, (D)eny, (I)gnore, (N)ew, (G)lob last piece, (Q)uit
If the AppArmor profile was in complain mode when the event was generated,
the default for this option is (A)llow, otherwise, it's (D)eny.
The (D)eny option adds a "deny" rule to the AppArmor profile, which
silences logging.
The (I)gnore option allows user to ignore the event, without making any
changes to the AppArmor profile.
The suggestion list is presented as a numbered list with includes
at the top, the literal path in the middle, and the suggested globs
at the bottom. If any globs are being suggested, the shortest glob
@ -109,9 +119,9 @@ Adding r access to /usr/share/themes/** would delete an entry for r
access to /usr/share/themes/foo/*.gif if it exists in the profile.
If (Q)uit is selected at this point, aa-logprof will ignore all new pending
capability and path accesses.
accesses.
After all of the path accesses have been handled, logrof will write all
After all of the accesses have been handled, logrof will write all
updated profiles to the disk and reload them if AppArmor is running.
=head2 New Process (Execution) Events

678
utils/aa-mergeprof Executable file
View File

@ -0,0 +1,678 @@
#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# 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.
#
# ----------------------------------------------------------------------
import argparse
import re
import apparmor.aa
import apparmor.aamode
import apparmor.severity
import apparmor.cleanprofile as cleanprofile
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
parser = argparse.ArgumentParser(description=_('Perform a 3way merge on the given profiles'))
parser.add_argument('mine', type=str, help=_('your profile'))
parser.add_argument('base', type=str, help=_('base profile'))
parser.add_argument('other', type=str, help=_('other profile'))
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts'))
args = parser.parse_args()
profiles = [args.mine, args.base, args.other]
def main():
mergeprofiles = Merge(profiles)
#Get rid of common/superfluous stuff
mergeprofiles.clear_common()
if not args.auto:
mergeprofiles.ask_the_questions('other')
mergeprofiles.clear_common()
mergeprofiles.ask_the_questions('base')
q = apparmor.aa.hasher()
q['title'] = 'Changed Local Profiles'
q['headers'] = []
q['explanation'] = _('The following local profiles were changed. Would you like to save them?')
q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
q['default'] = 'CMD_VIEW_CHANGES'
q['options'] = []
q['selected'] = 0
ans = ''
arg = None
programs = list(mergeprofiles.user.aa.keys())
program = programs[0]
while ans != 'CMD_SAVE_CHANGES':
ans, arg = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_SAVE_CHANGES':
apparmor.aa.write_profile_ui_feedback(program)
apparmor.aa.reload_base(program)
elif ans == 'CMD_VIEW_CHANGES':
for program in programs:
apparmor.aa.original_aa[program] = apparmor.aa.deepcopy(apparmor.aa.aa[program])
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
apparmor.aa.display_changes_with_comments(mergeprofiles.user.filename, newprofile)
class Merge(object):
def __init__(self, profiles):
user, base, other = profiles
#Read and parse base profile and save profile data, include data from it and reset them
apparmor.aa.read_profile(base, True)
self.base = cleanprofile.Prof(base)
self.reset()
#Read and parse other profile and save profile data, include data from it and reset them
apparmor.aa.read_profile(other, True)
self.other = cleanprofile.Prof(other)
self.reset()
#Read and parse user profile
apparmor.aa.read_profile(profiles[0], True)
self.user = cleanprofile.Prof(user)
def reset(self):
apparmor.aa.aa = apparmor.aa.hasher()
apparmor.aa.filelist = apparmor.aa.hasher()
apparmor.aa.include = dict()
apparmor.aa.existing_profiles = apparmor.aa.hasher()
apparmor.aa.original_aa = apparmor.aa.hasher()
def clear_common(self):
deleted = 0
#Remove off the parts in other profile which are common/superfluous from user profile
user_other = cleanprofile.CleanProf(False, self.user, self.other)
deleted += user_other.compare_profiles()
#Remove off the parts in base profile which are common/superfluous from user profile
user_base = cleanprofile.CleanProf(False, self.user, self.base)
deleted += user_base.compare_profiles()
#Remove off the parts in other profile which are common/superfluous from base profile
# base_other = cleanprofile.CleanProf(False, self.base, self.other) # XXX base_other not used?
deleted += user_base.compare_profiles()
def conflict_mode(self, profile, hat, allow, path, mode, new_mode, old_mode):
m = new_mode
o = old_mode
new_mode = apparmor.aa.flatten_mode(new_mode)
old_mode = apparmor.aa.flatten_mode(old_mode)
conflict_modes = set('uUpPcCiIxX')
conflict_x= (old_mode | new_mode) & conflict_modes
if conflict_x:
#We may have conflicting x modes
if conflict_x & set('x'):
conflict_x.remove('x')
if conflict_x & set('X'):
conflict_x.remove('X')
if len(conflict_x) > 1:
q = apparmor.aa.hasher()
q['headers'] = [_('Path'), path]
q['headers'] += [_('Select the appropriate mode'), '']
options = []
options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(new_mode)))# - (old_mode & conflict_x))))
options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(old_mode)))#(old_mode | new_mode) - (new_mode & conflict_x))))
q['options'] = options
q['functions'] = ['CMD_ALLOW', 'CMD_ABORT']
done = False
while not done:
ans, selected = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_ALLOW':
if selected == 0:
self.user.aa[profile][hat][allow]['path'][path][mode] = m#apparmor.aa.owner_flatten_mode(new_mode)#(old_mode | new_mode) - (old_mode & conflict_x)
return m
elif selected == 1:
return o
pass#self.user.aa[profile][hat][allow][path][mode] = (old_mode | new_mode) - (new_mode & conflict_x)
else:
raise apparmor.aa.AppArmorException(_('Unknown selection'))
done = True
def ask_the_questions(self, other):
if other == 'other':
other = self.other
else:
other = self.base
#print(other.aa)
#Add the file-wide includes from the other profile to the user profile
done = False
options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.filelist[other.filename]['include'].keys())))
q = apparmor.aa.hasher()
q['options'] = options
default_option = 1
q['selected'] = default_option - 1
q['headers'] = [_('File includes'), _('Select the ones you wish to add')]
q['functions'] = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q['default'] = 'CMD_ALLOW'
while not done and options:
ans, selected = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_IGNORE_ENTRY':
done = True
elif ans == 'CMD_ALLOW':
selection = options[selected]
inc = apparmor.aa.re_match_include(selection)
self.user.filelist[self.user.filename]['include'][inc] = True
options.pop(selected)
apparmor.aa.UI_Info(_('Adding %s to the file.') % selection)
sev_db = apparmor.aa.sev_db
if not sev_db:
sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
for profile in sorted(other.aa.keys()):
for hat in sorted(other.aa[profile].keys()):
#Add the includes from the other profile to the user profile
done = False
options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.aa[profile][hat]['include'].keys())))
q = apparmor.aa.hasher()
q['options'] = options
default_option = 1
q['selected'] = default_option - 1
q['headers'] = [_('File includes'), _('Select the ones you wish to add')]
q['functions'] = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q['default'] = 'CMD_ALLOW'
while not done and options:
ans, selected = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_IGNORE_ENTRY':
done = True
elif ans == 'CMD_ALLOW':
selection = options[selected]
inc = apparmor.aa.re_match_include(selection)
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
options.pop(selected)
apparmor.aa.UI_Info(_('Adding %s to the file.') % selection)
if deleted:
apparmor.aa.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
#Add the capabilities
for allow in ['allow', 'deny']:
if other.aa[profile][hat].get(allow, False):
continue
for capability in sorted(other.aa[profile][hat][allow]['capability'].keys()):
severity = sev_db.rank('CAP_%s' % capability)
default_option = 1
options = []
newincludes = apparmor.aa.match_cap_includes(self.user.aa[profile][hat], capability)
q = apparmor.aa.hasher()
if newincludes:
options += list(map(lambda inc: '#include <%s>' %inc, sorted(set(newincludes))))
if options:
options.append('capability %s' % capability)
q['options'] = [options]
q['selected'] = default_option - 1
q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q['headers'] += [_('Capability'), capability]
q['headers'] += [_('Severity'), severity]
audit_toggle = 0
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q['default'] = 'CMD_ALLOW'
done = False
while not done:
ans, selected = apparmor.aa.UI_PromptUser(q)
# Ignore the log entry
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
if ans == 'CMD_ALLOW':
selection = ''
if options:
selection = options[selected]
match = apparmor.aa.re_match_include(selection)
if match:
deleted = False
inc = match
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
apparmor.aa.UI_Info(_('Adding %s to profile.') % selection)
if deleted:
apparmor.aa.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
self.user.aa[profile][hat]['allow']['capability'][capability]['set'] = True
self.user.aa[profile][hat]['allow']['capability'][capability]['audit'] = other.aa[profile][hat]['allow']['capability'][capability]['audit']
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Adding capability %s to profile.'), capability)
done = True
elif ans == 'CMD_DENY':
self.user.aa[profile][hat]['deny']['capability'][capability]['set'] = True
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Denying capability %s to profile.') % capability)
done = True
else:
done = False
# Process all the path entries.
for allow in ['allow', 'deny']:
for path in sorted(other.aa[profile][hat][allow]['path'].keys()):
#print(path, other.aa[profile][hat][allow]['path'][path])
mode = other.aa[profile][hat][allow]['path'][path]['mode']
if self.user.aa[profile][hat][allow]['path'].get(path, False):
mode = self.conflict_mode(profile, hat, allow, path, 'mode', other.aa[profile][hat][allow]['path'][path]['mode'], self.user.aa[profile][hat][allow]['path'][path]['mode'])
self.conflict_mode(profile, hat, allow, path, 'audit', other.aa[profile][hat][allow]['path'][path]['audit'], self.user.aa[profile][hat][allow]['path'][path]['audit'])
apparmor.aa.changed[profile] = True
continue
# Lookup modes from profile
allow_mode = set()
allow_audit = set()
deny_mode = set()
deny_audit = set()
fmode, famode, fm = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'allow', path)
if fmode:
allow_mode |= fmode
if famode:
allow_audit |= famode
cm, cam, m = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'deny', path)
if cm:
deny_mode |= cm
if cam:
deny_audit |= cam
imode, iamode, im = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'allow', path)
if imode:
allow_mode |= imode
if iamode:
allow_audit |= iamode
cm, cam, m = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'deny', path)
if cm:
deny_mode |= cm
if cam:
deny_audit |= cam
if deny_mode & apparmor.aa.AA_MAY_EXEC:
deny_mode |= apparmor.aamode.ALL_AA_EXEC_TYPE
# Mask off the denied modes
mode = mode - deny_mode
# If we get an exec request from some kindof event that generates 'PERMITTING X'
# check if its already in allow_mode
# if not add ix permission
if mode & apparmor.aa.AA_MAY_EXEC:
# Remove all type access permission
mode = mode - apparmor.aamode.ALL_AA_EXEC_TYPE
if not allow_mode & apparmor.aa.AA_MAY_EXEC:
mode |= apparmor.aa.str_to_mode('ix')
# m is not implied by ix
### If we get an mmap request, check if we already have it in allow_mode
##if mode & AA_EXEC_MMAP:
## # ix implies m, so we don't need to add m if ix is present
## if contains(allow_mode, 'ix'):
## mode = mode - AA_EXEC_MMAP
if not mode:
continue
matches = []
if fmode:
matches += fm
if imode:
matches += im
if not apparmor.aa.mode_contains(allow_mode, mode):
default_option = 1
options = []
newincludes = []
include_valid = False
for incname in apparmor.aa.include.keys():
include_valid = False
# If already present skip
if self.user.aa[profile][hat][incname]:
continue
if incname.startswith(apparmor.aa.profile_dir):
incname = incname.replace(apparmor.aa.profile_dir+'/', '', 1)
include_valid = apparmor.aa.valid_include('', incname)
if not include_valid:
continue
cm, am, m = apparmor.aa.match_include_to_path(incname, 'allow', path)
if cm and apparmor.aa.mode_contains(cm, mode):
dm = apparmor.aa.match_include_to_path(incname, 'deny', path)[0]
# If the mode is denied
if not mode & dm:
if not list(filter(lambda s: '/**' == s, m)):
newincludes.append(incname)
# Add new includes to the options
if newincludes:
options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
# We should have literal the path in options list too
options.append(path)
# Add any the globs matching path from logprof
globs = apparmor.aa.glob_common(path)
if globs:
matches += globs
# Add any user entered matching globs
for user_glob in apparmor.aa.user_globs:
if apparmor.aa.matchliteral(user_glob, path):
matches.append(user_glob)
matches = list(set(matches))
if path in matches:
matches.remove(path)
options += apparmor.aa.order_globs(matches, path)
default_option = len(options)
sev_db.unload_variables()
sev_db.load_variables(apparmor.aa.get_profile_filename(profile))
severity = sev_db.rank(path, apparmor.aa.mode_to_str(mode))
sev_db.unload_variables()
audit_toggle = 0
owner_toggle = 0
if apparmor.aa.cfg['settings']['default_owner_prompt']:
owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
done = False
while not done:
q = apparmor.aa.hasher()
q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat),
_('Path'), path]
if allow_mode:
mode |= allow_mode
tail = ''
s = ''
prompt_mode = None
if owner_toggle == 0:
prompt_mode = apparmor.aa.flatten_mode(mode)
tail = ' ' + _('(owner permissions off)')
elif owner_toggle == 1:
prompt_mode = mode
elif owner_toggle == 2:
prompt_mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
tail = ' ' + _('(force new perms to owner)')
else:
prompt_mode = apparmor.aa.owner_flatten_mode(mode)
tail = ' ' + _('(force all rule perms to owner)')
if audit_toggle == 1:
s = apparmor.aa.mode_to_str_user(allow_mode)
if allow_mode:
s += ', '
s += 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode - allow_mode) + tail
elif audit_toggle == 2:
s = 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode) + tail
else:
s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
q['headers'] += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
_('New Mode'), s]
else:
s = ''
tail = ''
prompt_mode = None
if audit_toggle:
s = 'audit'
if owner_toggle == 0:
prompt_mode = apparmor.aa.flatten_mode(mode)
tail = ' ' + _('(owner permissions off)')
elif owner_toggle == 1:
prompt_mode = mode
else:
prompt_mode = apparmor.aa.owner_flatten_mode(mode)
tail = ' ' + _('(force perms to owner)')
s = apparmor.aa.mode_to_str_user(prompt_mode)
q['headers'] += [_('Mode'), s]
q['headers'] += [_('Severity'), severity]
q['options'] = options
q['selected'] = default_option - 1
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
'CMD_FINISHED', 'CMD_OTHER']
q['default'] = 'CMD_ALLOW'
ans, selected = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
if ans == 'CMD_OTHER':
audit_toggle, owner_toggle = apparmor.aa.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
elif ans == 'CMD_USER_TOGGLE':
owner_toggle += 1
if not allow_mode and owner_toggle == 2:
owner_toggle += 1
if owner_toggle > 3:
owner_toggle = 0
elif ans == 'CMD_ALLOW':
path = options[selected]
done = True
match = apparmor.aa.re_match_include(path)
if match:
inc = match
deleted = 0
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Adding %s to profile.') % path)
if deleted:
apparmor.aa.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
else:
if self.user.aa[profile][hat]['allow']['path'][path].get('mode', False):
mode |= self.user.aa[profile][hat]['allow']['path'][path]['mode']
deleted = []
for entry in self.user.aa[profile][hat]['allow']['path'].keys():
if path == entry:
continue
if apparmor.aa.matchregexp(path, entry):
if apparmor.aa.mode_contains(mode, self.user.aa[profile][hat]['allow']['path'][entry]['mode']):
deleted.append(entry)
for entry in deleted:
self.user.aa[profile][hat]['allow']['path'].pop(entry)
deleted = len(deleted)
if owner_toggle == 0:
mode = apparmor.aa.flatten_mode(mode)
#elif owner_toggle == 1:
# mode = mode
elif owner_toggle == 2:
mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
elif owner_toggle == 3:
mode = apparmor.aa.owner_flatten_mode(mode)
if not self.user.aa[profile][hat]['allow'].get(path, False):
self.user.aa[profile][hat]['allow']['path'][path]['mode'] = self.user.aa[profile][hat]['allow']['path'][path].get('mode', set()) | mode
tmpmode = set()
if audit_toggle == 1:
tmpmode = mode- allow_mode
elif audit_toggle == 2:
tmpmode = mode
self.user.aa[profile][hat]['allow']['path'][path]['audit'] = self.user.aa[profile][hat]['allow']['path'][path].get('audit', set()) | tmpmode
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Adding %s %s to profile') % (path, apparmor.aa.mode_to_str_user(mode)))
if deleted:
apparmor.aa.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
elif ans == 'CMD_DENY':
path = options[selected].strip()
# Add new entry?
self.user.aa[profile][hat]['deny']['path'][path]['mode'] = self.user.aa[profile][hat]['deny']['path'][path].get('mode', set()) | (mode - allow_mode)
self.user.aa[profile][hat]['deny']['path'][path]['audit'] = self.user.aa[profile][hat]['deny']['path'][path].get('audit', set())
apparmor.aa.changed[profile] = True
done = True
elif ans == 'CMD_NEW':
arg = options[selected]
if not apparmor.aa.re_match_include(arg):
ans = apparmor.aa.UI_GetString(_('Enter new path: '), arg)
# if ans:
# if not matchliteral(ans, path):
# ynprompt = _('The specified path does not match this log entry:\n\n Log Entry: %s\n Entered Path: %s\nDo you really want to use this path?') % (path,ans)
# key = apparmor.aa.UI_YesNo(ynprompt, 'n')
# if key == 'n':
# continue
apparmor.aa.user_globs.append(ans)
options.append(ans)
default_option = len(options)
elif ans == 'CMD_GLOB':
newpath = options[selected].strip()
if not apparmor.aa.re_match_include(newpath):
newpath = apparmor.aa.glob_path(newpath)
if newpath not in options:
options.append(newpath)
default_option = len(options)
else:
default_option = options.index(newpath) + 1
elif ans == 'CMD_GLOBEXT':
newpath = options[selected].strip()
if not apparmor.aa.re_match_include(newpath):
newpath = apparmor.aa.glob_path_withext(newpath)
if newpath not in options:
options.append(newpath)
default_option = len(options)
else:
default_option = options.index(newpath) + 1
elif re.search('\d', ans):
default_option = ans
#
for allow in ['allow', 'deny']:
for family in sorted(other.aa[profile][hat][allow]['netdomain']['rule'].keys()):
# severity handling for net toggles goes here
for sock_type in sorted(other.aa[profile][hat][allow]['netdomain']['rule'][family].keys()):
if apparmor.aa.profile_known_network(self.user.aa[profile][hat], family, sock_type):
continue
default_option = 1
options = []
newincludes = apparmor.aa.match_net_includes(self.user.aa[profile][hat], family, sock_type)
q = apparmor.aa.hasher()
if newincludes:
options += list(map(lambda s: '#include <%s>'%s, sorted(set(newincludes))))
if True:#options:
options.append('network %s %s' % (family, sock_type))
q['options'] = options
q['selected'] = default_option - 1
q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q['headers'] += [_('Network Family'), family]
q['headers'] += [_('Socket Type'), sock_type]
audit_toggle = 0
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
'CMD_ABORT', 'CMD_FINISHED']
q['default'] = 'CMD_ALLOW'
done = False
while not done:
ans, selected = apparmor.aa.UI_PromptUser(q)
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
if ans.startswith('CMD_AUDIT'):
audit_toggle = not audit_toggle
audit = ''
if audit_toggle:
audit = 'audit'
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
'CMD_ABORT', 'CMD_FINISHED']
else:
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
'CMD_ABORT', 'CMD_FINISHED']
q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q['headers'] += [_('Network Family'), audit + family]
q['headers'] += [_('Socket Type'), sock_type]
elif ans == 'CMD_ALLOW':
#print(options, selected)
selection = options[selected]
done = True
if apparmor.aa.re_match_include(selection): #re.search('#include\s+<.+>$', selection):
inc = apparmor.aa.re_match_include(selection) #re.search('#include\s+<(.+)>$', selection).groups()[0]
deleted = 0
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Adding %s to profile') % selection)
if deleted:
apparmor.aa.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
else:
self.user.aa[profile][hat]['allow']['netdomain']['audit'][family][sock_type] = audit_toggle
self.user.aa[profile][hat]['allow']['netdomain']['rule'][family][sock_type] = True
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Adding network access %s %s to profile.') % (family, sock_type))
elif ans == 'CMD_DENY':
done = True
self.user.aa[profile][hat]['deny']['netdomain']['rule'][family][sock_type] = True
apparmor.aa.changed[profile] = True
apparmor.aa.UI_Info(_('Denying network access %s %s to profile') % (family, sock_type))
else:
done = False
if __name__ == '__main__':
main()

33
utils/aa-mergeprof.pod Normal file
View File

@ -0,0 +1,33 @@
=pod
=head1 NAME
aa-mergeprof - merge AppArmor security profiles.
=head1 SYNOPSIS
B<aa-mergeprof I<E<lt>mineE<gt>> I<E<lt>userE<gt>> I<E<lt>otherE<gt>> [I<-d /path/to/profiles>]>
=head1 OPTIONS
B<-d --dir /path/to/profiles>
Specifies where to look for the AppArmor security profile set.
Defaults to /etc/apparmor.d.
=head1 DESCRIPTION
B<aa-mergeprof>
=head1 BUGS
If you find any bugs, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), aa_change_hat(2), aa-genprof(1),
aa-logprof(1), aa-enforce(1), aa-audit(1), aa-complain(1),
aa-disable(1), and L<http://wiki.apparmor.net>.
=cut

Some files were not shown because too many files have changed in this diff Show More