[apparmor] [PATCH 2/3] Add userspace support for stacking

Steve Beattie steve at nxnw.org
Thu Jan 12 13:40:31 UTC 2012


On Fri, Jan 06, 2012 at 09:53:17AM -0800, John Johansen wrote:
> 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 <john.johansen at canonical.com>
> ---
>  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,

s/Setups/sets up/ ?

> +and set the specified I<profile> as the top of the stack.  This tightens the

s/set/sets/ ?

> +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

s/The there/If there/ ?

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

s/it.  Except/it, except/ ? (Otherwise the second sentence is just a
sentence fragment.)

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

A separate question from this patch, but how are threads affected by
this? For example, if one thread calls aa_stackcon() and another thread
calls exec(), will it have the aa_stackcon_onexec() applied to it? If a process
calls aa_stackcon_onexec() and then makes a clone() call, what will the
behavior of subsequent exec()s be?

> +
> +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 at 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

Same issue for system() as with aa-exec here

> +	   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

"setup in new layer in the profile context stack" sounds awkward to me

> +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

s/by the by the/by the/

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

"is desired both" ?

> +
> +* 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

-- 
Steve Beattie
<sbeattie at ubuntu.com>
http://NxNW.org/~steve/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20120112/f5668964/attachment.pgp>


More information about the AppArmor mailing list