Add the library interface and untility functions to provide access to profile stacking. This allows a task to have its confinement based off of multiple profiles.
Signed-off-by: John Johansen <[email protected]> --- libraries/libapparmor/doc/Makefile.am | 3 +- libraries/libapparmor/doc/aa_stackcon.pod | 120 +++++++++++++++++++++++ libraries/libapparmor/src/apparmor.h | 2 + libraries/libapparmor/src/kernel_interface.c | 43 ++++++++ libraries/libapparmor/src/libapparmor.map | 8 ++ libraries/libapparmor/swig/SWIG/libapparmor.i | 2 + utils/Makefile | 2 +- utils/aa-stack | 129 +++++++++++++++++++++++++ utils/aa-stack.pod | 100 +++++++++++++++++++ 9 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 libraries/libapparmor/doc/aa_stackcon.pod create mode 100644 utils/aa-stack create mode 100644 utils/aa-stack.pod diff --git a/libraries/libapparmor/doc/Makefile.am b/libraries/libapparmor/doc/Makefile.am index e21a075..dda2c88 100644 --- a/libraries/libapparmor/doc/Makefile.am +++ b/libraries/libapparmor/doc/Makefile.am @@ -2,7 +2,8 @@ POD2MAN = pod2man -man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2 +man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2\ + aa_stackcon.2 PODS = $(subst .2,.pod,$(man_MANS)) diff --git a/libraries/libapparmor/doc/aa_stackcon.pod b/libraries/libapparmor/doc/aa_stackcon.pod new file mode 100644 index 0000000..0efe738 --- /dev/null +++ b/libraries/libapparmor/doc/aa_stackcon.pod @@ -0,0 +1,120 @@ +# This publication is intellectual property of Canonical Ltd. Its contents +# can be duplicated, either in part or in whole, provided that a copyright +# label is visibly located on each copy. +# +# All information found in this book has been compiled with utmost +# attention to detail. However, this does not guarantee complete accuracy. +# Neither Canonical Ltd, the authors, nor the translators shall be held +# liable for possible errors or the consequences thereof. +# +# Many of the software and hardware descriptions cited in this book +# are registered trademarks. All trade names are subject to copyright +# restrictions and may be registered trade marks. Canonical Ltd. +# essentially adhere to the manufacturer's spelling. +# +# Names of products and trademarks appearing in this book (with or without +# specific notation) are likewise subject to trademark and trade protection +# laws and may thus fall under copyright restrictions. +# + + +=pod + +=head1 NAME + +aa_stackcon, aa_stackcon_onexec - add a new profile layer to a task + +=head1 SYNOPSIS + +B<#include E<lt>sys/apparmor.hE<gt>> + +B<int aa_stackcon(const char *profile);> + +B<int aa_stackcon_onexec(const char *profile);> + +Link with B<-lapparmor> when compiling. + +=head1 DESCRIPTION + +The aa_stackcon() function Setups a new layer in the profile context stack, +and set the specified I<profile> as the top of the stack. This tightens the +calling task's current mediation to the intersection of the previous +confinement and the new I<profile> that is being added to the context stack. + +The namespace of the specified I<profile> defines the current namespace for +the task, and hence determines which namespace policy administration is +done in. If the namespace is different from the previous namespace, +the profile(s) from that namespace remain on the task as part of its +confinement. + +If the specified I<profile> is the same as a profile already confining the +task (and is in the same policy namespace), the I<profile> will merge +with the existing profile and a new layer will not be added to the confinement +stack. + +The specified I<profile> can be a compound name including a namespace prefix. +If a namespace prefix is included, the specified profile will be searched +for in the specified namespace instead of the current namespace. If only +a namespace prefix is specified then name of the current profile will be +searched for in the specified namespace. The there is more than a single +profile confining the task, the name of the profile that defines the current +namespace will be used. + +Once a new stacking level has been created there is no way to undo it. Except +to remove all profiles confining the task from the system. + +Open file descriptors may have to be revalidated after the stacking to +account for the new mediation controls on the task. + +The aa_stackcon_onexec() function is like the aa_stackcon() function except +it specifies that the the stacking transition should take place on the +next exec instead of immediately. The delayed stacking takes precedence +over any exec transition rules within the confining profile. + +Delaying the stacking boundry has a couple of advantages: it can reduce +profile complexity and the exec boundary is a natural security layer where +potentially sensitive memory is unmapped. + +=head1 RETURN VALUE + +On success zero is returned. On error, -1 is returned, and +errno(3) is set appropriately. + +=head1 ERRORS + +=over 4 + +=item B<EINVAL> + +The apparmor kernel module is not loaded or the communication via the +F</proc/*/attr/current> file did not conform to protocol. + +=item B<ENOMEM> + +Insufficient kernel memory was available. + +=item B<ENOENT> + +The specified I<profile> or namespace does not exist. + +=item B<EACCES> + +The task does not have sufficient permissions to add a layer to it context +stack. + +=back + +=head1 BUGS + +None known. If you find any, please report them at +L<http://https://bugs.launchpad.net/apparmor/+filebug>. Note that using +aa_stackcon(2) without execve(2) provides no memory barriers between +different areas of a program; if address space separation is required, then +separate processes should be used. + +=head1 SEE ALSO + +aa-stack(8), apparmor(7), apparmor.d(5), apparmor_parser(8), and +L<http://wiki.apparmor.net>. + +=cut diff --git a/libraries/libapparmor/src/apparmor.h b/libraries/libapparmor/src/apparmor.h index c93bee8..9adb63f 100644 --- a/libraries/libapparmor/src/apparmor.h +++ b/libraries/libapparmor/src/apparmor.h @@ -36,6 +36,8 @@ extern int (change_hat)(const char *subprofile, unsigned int magic_token); extern int aa_change_hat(const char *subprofile, unsigned long magic_token); extern int aa_change_profile(const char *profile); extern int aa_change_onexec(const char *profile); +extern int aa_stackcon(const char *profile); +extern int aa_stackcon_onexec(const char *profile); extern int aa_change_hatv(const char *subprofiles[], unsigned long token); extern int (aa_change_hat_vargs)(unsigned long token, int count, ...); diff --git a/libraries/libapparmor/src/kernel_interface.c b/libraries/libapparmor/src/kernel_interface.c index 33fdda9..bc29d99 100644 --- a/libraries/libapparmor/src/kernel_interface.c +++ b/libraries/libapparmor/src/kernel_interface.c @@ -408,6 +408,49 @@ int aa_change_onexec(const char *profile) return rc; } +int aa_stackcon(const char *profile) +{ + char *buf = NULL; + int len; + int rc; + + if (!profile) { + errno = EINVAL; + return -1; + } + + len = asprintf(&buf, "stackcon %s", profile); + if (len < 0) + return -1; + + rc = setprocattr(aa_gettid(), "current", buf, len); + + free(buf); + return rc; +} + +int aa_stackcon_onexec(const char *profile) +{ + char *buf = NULL; + int len; + int rc; + + if (!profile) { + errno = EINVAL; + return -1; + } + + len = asprintf(&buf, "stackcon %s", profile); + if (len < 0) + return -1; + + rc = setprocattr(aa_gettid(), "exec", buf, len); + + free(buf); + return rc; +} + + /* create an alias for the old change_hat@IMMUNIX_1.0 symbol */ extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat"))); symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0); diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map index 444278e..b5ff2ca 100644 --- a/libraries/libapparmor/src/libapparmor.map +++ b/libraries/libapparmor/src/libapparmor.map @@ -35,3 +35,11 @@ APPARMOR_1.1 { local: *; } APPARMOR_1.0; + +APPARMOR_3.0 { + global: + aa_stackcon; + aa_stackcon_onexec; + local: + *; +} APPARMOR_1.1; diff --git a/libraries/libapparmor/swig/SWIG/libapparmor.i b/libraries/libapparmor/swig/SWIG/libapparmor.i index f0ebf5a..9cd6d5c 100644 --- a/libraries/libapparmor/swig/SWIG/libapparmor.i +++ b/libraries/libapparmor/swig/SWIG/libapparmor.i @@ -18,6 +18,8 @@ extern int aa_find_mountpoint(char **mnt); extern int aa_change_hat(const char *subprofile, unsigned long magic_token); extern int aa_change_profile(const char *profile); extern int aa_change_onexec(const char *profile); +extern int aa_stackcon(const char *profile); +extern int aa_stackcon_onexec(const char *profile); extern int aa_change_hatv(const char *subprofiles[], unsigned long token); extern int aa_change_hat_vargs(unsigned long token, int count, ...); extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len, diff --git a/utils/Makefile b/utils/Makefile index f4f8707..5baa26d 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -28,7 +28,7 @@ endif MODDIR = Immunix PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \ - aa-unconfined aa-notify aa-disable aa-exec + aa-unconfined aa-notify aa-disable aa-exec aa-stack TOOLS = ${PERLTOOLS} aa-decode aa-status MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \ ${MODDIR}/Config.pm ${MODDIR}/Severity.pm diff --git a/utils/aa-stack b/utils/aa-stack new file mode 100644 index 0000000..e147715 --- /dev/null +++ b/utils/aa-stack @@ -0,0 +1,129 @@ +#!/usr/bin/perl +# ------------------------------------------------------------------ +# +# Copyright (C) 2009-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 published by the Free Software Foundation. +# +# ------------------------------------------------------------------ + +use strict; +use warnings; +use Errno; + +require LibAppArmor; +require POSIX; +require Time::Local; +require File::Basename; + +my $opt_d = ''; +my $opt_h = ''; +my $opt_p = ''; +my $opt_n = ''; +my $opt_i = ''; +my $opt_v = ''; +my $opt_f = ''; + +sub _warn { + my $msg = $_[0]; + print STDERR "aa-stack: WARN: $msg\n"; +} +sub _error { + my $msg = $_[0]; + print STDERR "aa-stack: ERROR: $msg\n"; + exit 1 +} + +sub _debug { + $opt_d or return; + my $msg = $_[0]; + print STDERR "aa-stack: DEBUG: $msg\n"; +} + +sub _verbose { + $opt_v or return; + my $msg = $_[0]; + print STDERR "$msg\n"; +} + +sub usage() { + my $s = <<'EOF'; +USAGE: aa-stack [OPTIONS] <prog> <args> + +Confine <prog> with a new confinement context that is based off of the old +context with a new layer set to PROFILE. + +OPTIONS: + -p PROFILE, --profile=PROFILE PROFILE to confine <prog> with + -n NAMESPACE, --namespace=NAMESPACE NAMESPACE to confine <prog> in + -f FILE, --file=FILE file to load a profile from before stacking + -i, --immediate change profile immediately instead of at exec + -v, --verbose show messages with stats + -h, --help display this help + +EOF + print $s; +} + +use Getopt::Long; + +GetOptions( + 'debug|d' => \$opt_d, + 'help|h' => \$opt_h, + 'profile|p=s' => \$opt_p, + 'namespace|n=s' => \$opt_n, + 'file|f=s' => \$opt_f, + 'immediate|i' => \$opt_i, + 'verbose|v' => \$opt_v, +); + +if ($opt_h) { + usage(); + exit(0); +} + +if ($opt_n || $opt_p) { + my $test; + my $prof; + + if ($opt_n) { + $prof = ":$opt_n:"; + } + + $prof .= $opt_p; + + if ($opt_f) { + my $ns = ""; + if ($opt_n) { + $ns = "-n $opt_n"; + } + system("apparmor_parser $ns -f $opt_f") == 0 + or _error("aborting could not load $opt_f"); + } + + if ($opt_i) { + _verbose("aa_stackcon(\"$prof\")"); + $test = LibAppArmor::aa_stackcon($prof); + _debug("$test = aa_stackcon(\"$prof\"); $!"); + } else { + _verbose("aa_stackcon_onexec(\"$prof\")"); + $test = LibAppArmor::aa_stackcon_onexec($prof); + _debug("$test = aa_stackcon_onexec(\"$prof\"); $!"); + } + + if ($test != 0) { + if ($!{ENOENT} || $!{EACCESS}) { + my $pre = ($opt_p) ? "profile" : "namespace"; + _error("$pre \'$prof\' does not exist\n"); + } elsif ($!{EINVAL}) { + _error("AppArmor interface not available\n"); + } else { + _error("$!\n"); + } + } +} + +_verbose("exec @ARGV"); +exec @ARGV; diff --git a/utils/aa-stack.pod b/utils/aa-stack.pod new file mode 100644 index 0000000..fa7e9c9 --- /dev/null +++ b/utils/aa-stack.pod @@ -0,0 +1,100 @@ +# This publication is intellectual property of Canonical Ltd. Its contents +# can be duplicated, either in part or in whole, provided that a copyright +# label is visibly located on each copy. +# +# All information found in this book has been compiled with utmost +# attention to detail. However, this does not guarantee complete accuracy. +# Neither Canonical Ltd, the authors, nor the translators shall be held +# liable for possible errors or the consequences thereof. +# +# Many of the software and hardware descriptions cited in this book +# are registered trademarks. All trade names are subject to copyright +# restrictions and may be registered trade marks. Canonical Ltd +# essentially adheres to the manufacturer's spelling. +# +# Names of products and trademarks appearing in this book (with or without +# specific notation) are likewise subject to trademark and trade protection +# laws and may thus fall under copyright restrictions. +# + + +=pod + +=head1 NAME + +aa-stack - setup in new layer in the profile context stack, and confine the +program with the specified profile as the top of the stack. + + +=head1 SYNOPSIS + +B<aa-stack> [options] [I<E<lt>executableE<gt>> ...] + +=head1 DESCRIPTION + +B<aa-stack> is used to launch a program confined by the by the current +context plus a new layer with the specified profile as the intial confinement +for the new layer in the confinement stack. + +If a namespace and profile are specified the profile within the specified +namespace is used. If only a profile is specified then the tasks current +namespace is used. If only a namespace is specified then the current* profile +name is used in the new namespace. If neither a profile or namespace is defined +then executable is run from the current confinement. + +The program is subject to mediation by all layers in the new context, and +if a permission request is denied by any layer of the context the request will +be denied. + +When switching to a new namespace if the unconfined state is desired both +then -p unconfined should be used. + +* The current profile name is based off of the top profile on the stack, ie. + the last profile/namespace combination added, or its decendent after + exec transitions. + +=head1 OPTIONS +B<aa-stack> accepts the following arguments: + +=over 4 + +=item -p PROFILE, --profile=PROFILE + +confine I<E<lt>executableE<gt>> with PROFILE. If the PROFILE is not specified +use the current profile name (likely unconfined). + +=item -n NAMESPACE, --namespace=NAMESPACE + +use profiles in NAMESPACE. This will result in confinement transitioning +to using the new profile namespace. + +=item -f FILE, --file=FILE + +a file or directory containing profiles to load before performing the stack. + +=item -i, --immediate + +transition to PROFILE before doing executing I<E<lt>executableE<gt>>. This +subjects the running of I<E<lt>executableE<gt>> to the exec transition rules +of the current profile. + +=item -v, --verbose + +show commands being performed + +=item -d, --debug + +show commands and error codes + +=head1 BUGS + +If you find any bugs, please report them at +L<http://https://bugs.launchpad.net/apparmor/+filebug>. + +=head1 SEE ALSO + +aa-confine(8), aa-namespace(8), apparmor(7), apparmor-stacking(7), +apparmor.d(5), aa_stackcon(3), aa_stackcon_onexec(3) and +L<http://wiki.apparmor.net>. + +=cut -- 1.7.7.3 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
