[apparmor] Profile stacking
John Johansen
john.johansen at canonical.com
Sun Sep 5 11:22:54 BST 2010
One of the features that has been planned for a while is stacking and
composition of profiles. This will enable a task to be confined by
multiple profiles simultaneously. With the resultant permissions set
being the intersection of the set of stacked profiles.
There are a couple of use cases for profile stacking:
* user profiles, allowing a user to create and enforce their own profiles
independent of what the system is enforcing.
* child composition, this will allow a parent to launch a
child, that retains the parents restrictions while picking up new ones.
Both forms of composition could be mostly done in userspace by creating
a new profile that is the composition of the parent and child profiles.
However this is not always the best solution because it could require
creating an exponential set of new profiles (1 for each possible
combination). For user profiles it is even more problematic as it means
the profiles user load will have to go through a privileged daemon,
to handle the intersection and load.
At a minimum stacking needs
* A way to specify that an x transition should stack instead of doing
standard transitioning.
eg.
/child px+,
This would be considered for each profile confining a task, so it is
possible on an x transition that, the number of profiles in the stack
could double. eg. 2 becomes 4 if each profile in the stack specifies
composition.
Implementation wise stacking should also check that any new profile
being added to the stack isn't already on it.
* A specification of how stacking interacts with exec
the exec needs to be allowed by each profile and then the transitions
for each profile in the stack are applied. This allows each profile
in the stack to transition independently.
* A way to introspect the profiles on a task
We need a way to report all the profiles confining a task. Currently
AppArmor reports the single profile via
/proc/<pid>/attr/current
We need to create a standard to be able to report more than 1 profile.
I was thinking possibly of using //+ as a separator.
eg.
/profile1
/profile1//hat1
/profile2
/profile2//hat2
:namespace1:/profile3
/profile1//+/profile2
/profile1//hat1//+/profile2
/profile1//+/profile2//hat2
/profile1//hat1//+/profile2//hat2
/profile1//+:namespace1:/profile3
...
* A specification of how stacking works with file labeling and delegation
Files are labeled with a special label indicating the set of profiles in
the stack. When comparison of dynamic labeling is done it is against
the full set.
Delegation/ipc would require the ability to talk with all profiles in
the stack.
* An api
If only strict intersection is allowed then the profile probably doesn't
need any special rules like change_profile as this operation would
only ever result in a reduced permission set.
eg.
int compose_profile(const char *profile);
* A specification of how stacking interacts with change_hat and
change_profile.
currently my thinking is that the call must succeed for all profiles in
the stack or it fails. This would mean that for standard change_hat
all profiles would transition to the same hat. For change_hatv it would
be possible for each profile in the stack to transition to a different
profile in the vector.
* Decide details of user profile case. The user profile case is special
and has a few extra consideration over generic stacking.
+ is user profile attachment special and automatic or handled generically
- if special then when a user executes a task, a search is made for
the user namespace and if it exists, a search for the matching
profile is done.
This basically means
- user namespace and profiles don't have to exist before any user
tasks are executed. They will be automatically picked up and
used when added.
- non special via task confinement
This means all profile lookup would be done off of current task
confinement. So for a task to pickup user profiles it must already
be confined by a user profile (or the special user unconfined
profile).
This would mean that tasks launched without user profile confinement
would not get it unless they specifically asked for it.
The generic case of having all user tasks properly confined could
be setup by the pam_apparmor module. Where it creates the user
namespace, calls the api to create a stack and then setuids to
the user. All children tasks of the session then would inherit
both confinements.
This is less flexible than handling the user stacking specially
but requires less code/no special casing.
+ What rules a user profile can contain
- Does it make sense to allow a user profile to contain capability
rules? Possibly especially if the user has some system capabilities.
- can the user create their own namespaces
+ What users can load profiles
- probably a control, along with how much memory the users profiles can
use. etc.
- if pam_apparmor is used to setup the user namespace then, the
namespace can carry memory limits, and if it doesn't exist the user
is not allowed to load profiles.
Else will need special rules.
+ Who loads the user profiles?
- user login session script?
- pam_apparmor before any user task is run, ...
+ When are user profiles unloaded?
- when user no longer has any running tasks
- when user logs out (ref counted for multiple sessions?)
- when user explicitly unloads them
There is also the possibility of a slightly hybrid implementation of
user profiles, where pam_apparmor is used to create the user namespace,
but doesn't stack the user namespace on the first task, instead user
attachment decisions are made dynamically.
This avoids having a user unconfined profile on each user task as well
as the system, unconfined.
- Beyond basic profile intersection -
Profile composition could be carried beyond simple intersection. There are
a couple things that would be nice to have for some situations but I am not
sure they are worth the added complexity.
* The parent profile can control which rules can be intersected with the
child. This would make it so the child cannot remove certain permissions
granted by the parent. The trick would be how to specify these, would it
be per rule in the profile, or would it be per x transition (how would
compose_profile api be handled then?)
* A non strict intersection stacking. This one gets complex fast, and
I don't have a good way to handle this atm, and in general I think
its dangerous as it in some sense widens permissions.
The idea being that there are cases where you want a intersection of
parent and child unioned with some extra rules (usually from the child).
The general reason you would want this is to have a tighter parent
profile, so that it doesn't need to contain sufficient permissions
for children to run when intersected with the child profile. Basically
the child profile almost needs to be split into two parts, the part
that can be intersected and the minimum part to be able to run the
application.
In general I think much of this type of intersection can be handled
adequately with delegation and standard profile transitions.
More information about the AppArmor
mailing list