[apparmor] Retrofitting & access-control impedance mismatch for MinorFs

John Johansen john.johansen at canonical.com
Tue Jun 25 09:35:47 UTC 2013

On 06/24/2013 09:31 PM, Rob Meijer wrote:
> On Mon, June 24, 2013 22:22, John Johansen wrote:
>> On 06/24/2013 12:16 AM, Rob Meijer wrote:
>>> I'm working on a retrofit version of the MinorFs system and one
>>> particular
>>> 'fix' seems to lead to the large scale promotion of processes to
>>> confusable deputies.
>>> MinorFs consists of multiple user-space file-systems. At the core of the
>>> system we have a filesystem called CapFs that uses sparse-caps that give
>>> access to node's in set of decomposable attenuatable directed tree
>>> graphs
>>> of directories and files.
>>> I basically have two options for fitting the higher level file-systems
>>> on
>>> top of CapFs:
>>> 1) As overlay file-systems.
>>> 2) As redirecting file-systems.
>>> Where option one has the advantage of safely allowing even
>>> non-AppArmor-confined processes the use of their own multi-granular
>>> private storage, it does take away the ability to decompose,attenuate
>>> and
>>> delegate ones private storage sub-tree to other processes.
>>> Option two has the advantages of better performance and full
>>> decomposition
>>> attenuation and delegation support, but these come at a price for
>>> mixed-mode systems. The problem is that on Linux systems that come with
>>> AppArmor (the static least privilege access control system for Linux
>>> that
>>> MinorFs aims to complement with dynamic least privilege options), most
>>> notably Ubuntu and Suse, the set of available AppArmor profiles is
>>> rather
>>> incomplete. That means that for now on these systems part of the
>>> processes
>>> will be running AppArmor confined while other processes won't.
>>> An unconfined process has access to the special per process id
>>> directories
>>> under /proc (at least for processes running under the same user id) that
>>> contain some information on the internals of other processes, most
>>> notably
>>> information on open file handles that basically could contain the CapFs
>>> sparse-cap. An unconfined process thus would be able to see some of the
>>> authority tokens for files that other processes running under the same
>>> uid
>>> are accessing.
>>> My first thought on this was that this wouldn't be a problem, given that
>>> I
>>> would simply fully deny raw CapFs access to any non-AppArmor-confined
>>> process.
>>> On second thought however this 'solution' creates an other problem: A
>>> process that does run under an AppArmor confined profile could
>>> potentially
>>> become a confused deputy. That is, an unconfined program could steal an
>>> authority token from one confined program trough the /proc file-system,
>>> and while it can't use this token itself, it could delegate the stolen
>>> token to a second confined process that might than act as a confused
>>> deputy.
>> This really sounds like you want some global policy in this case
>> preventing
>> tasks from accessing what doesn't belong to them.
> A global 'default' policy that could prevent any processes from
> readlinking /proc/$(pid_other_than_self)/fd/* would be the ultimamate
> solution to my problem indeed. One that takes away any need for me to
> differentiate between confined and unconfined processes as an access
> control mechanism within CapFs.
>> This is possible in apparmor 2.x but is really hard to do.  apparmor 3
>> which is currently in dev makes it much easier to add and replace a
>> default
>> profile.
> That's amazing news. Could the above blocking of access to
> /proc/$(pid_other_than_self)/fd/* be easily expressed in such a default
> profile?
So the trick to doing this with apparmor 2.x. Is loading policy in the
initramfs, and then having early init re-exec it self so it picks up
the loaded policy.

Like I said it is a real pain

You can do that in apparmor3 but it also adds the ability to define a
default profile, that can be set as a kernel parameter. That profile
starts in "unconfined" mode but can be replaced and start enforcing
policy. Which allows us to get the policy load out of initramfs into
early boot and is adequate if you trust your early boot process

>>> I would really like to hear the thoughts of others on this mailing-list
>>> on
>>> this. Should I give up on combining a retrofit version of MinorFs with
>>> the
>>> non-retrofit features of decomposition, attenuation and delegation?
>>> Should
>>> I add the confinement check for CapFs access or is this a futile
>>> addition
>>> that is to easily bypassed?  Or should I just provide a massively
>>> permissive profile that could be used for all currently unconfined
>>> processes and allows almost everything except for the specific places
>>> under /proc where sparse-caps could be stolen?
>> So if I was to do the restrictions via apparmor I would make a default
>> profile
>> and disallow access to the special files. Any process that needs access to
>> a token file is under a different profile. So tighter than what you
>> propose
>> above, and you can have fairly loose special profiles that have access to
>> a subset of the special files.
>> I need to reread so of the minorfs details again before I could propose a
>> solution in greater detail.
> My problem is that if we have 3 processes running under the same uid:
> Alice: AppArmor confined, pid=1000, has been delegated a process private
>        directory by MinorFs that is freely accessible trough
>        /minor-mnt/cap/rw-3ffc8f682cfab4f753d32a9182ebbe8e30e34e19/
>        Alice has the following file currently open:
>        /minor-mnt/cap/rw-3ffc8f682cfab4f753d32a9182ebbe8e30e34e19/foo.xml
> Bob:   An other process, AppArmor confined, pid=1001, not programmed in a
>        way that makes it aware that it might be acting as a deputy. Bob can
>        not readlink /proc/1000/fd/*.
> Mallet: An unconfined 'trojan' process, pid=1002. Mallet can readlink
>        /proc/1000/fd/* and so 'steal' the sparse-cap to the private
>        directory of Alice.
> The ultimate solution is to keep Malet from stealing the sparse-cap.
> An other alternative is to make /minor-mnt/cap/* inaccessible to Malet
> by adding access control to the file-system that denies any access to
> unconfined processes. This alternative however turns Bob into a potential
> confusable deputy. A third solution is to delegate trough an overlay
> filesystem. This third solution however has major performance cost and
> takes away the the ability to decompose,attenuate and delegate ones
> private storage sub-tree to other processes.
> If as you hinted the ultimate solution of a default 'deny sparse-cap
> stealing' policy would be possible, this would take away the need to
> choose between two relatively disagreeable options.
right so a default profile could provide the ability to deny access to

Its a matter of how to setup this default. It can be done

system wide:
- From early boot by specifying the default profile (hard in 2.8)
- Approximated with an early loaded default profile with attachment
  specification (this technique changes pix, and pux behavior from
  what would be had with the unconfined profile).  Also processes
  started before the profile remain in the unconfined state.

    profile default /** {
      deny /minor-mnt/cap/* rw,

  This will attach to any executable but if a profile with a more
  specific match is provided it will match first.

per user:
- pam_apparmor can be used to provide a default profile for each
  user. So that the profile can be unique to the user.

I feel like I am still missing something, as I am not fully understanding
the confused deputy case.

>> As to whether 1 or 2. I really like "the ability to decompose,attenuate
>> and
>> delegate ones private storage sub-tree to other processes.", the question
>> is whether its worth the cost.
>> --
>> AppArmor mailing list
>> AppArmor at lists.ubuntu.com
>> Modify settings or unsubscribe at:
>> https://lists.ubuntu.com/mailman/listinfo/apparmor

More information about the AppArmor mailing list