[apparmor] sshd and profile transitions

Simon Deziel simon.deziel at gmail.com
Fri Oct 2 18:41:13 UTC 2015

On 09/30/2015 03:51 PM, John Johansen wrote:
> On 09/30/2015 08:08 AM, Simon Deziel wrote:
>> On 09/29/2015 05:56 PM, John Johansen wrote:
>>> On 09/29/2015 02:25 PM, Simon Deziel wrote:
>>>> Hi everyone,
>>>> My sshd is contained by the attached profile (also available here [1]).
>>>> Once logged in via SSH, I have an unconfined shell, at least according
>>>> to "ps Zaux | grep $$". As such, I would expect to be able to run
>>>> everything as usual but if I run a binary contained by Apparmor (like
>>>> tcpdump) I get the following denials:
>>>> apparmor="DENIED" operation="file_inherit" profile="/usr/sbin/tcpdump"
>>>> name="/dev/pts/0" pid=1529 comm="tcpdump" requested_mask="wr"
>>>> denied_mask="wr" fsuid=0 ouid=0
>>>> [...]
>>>> apparmor="DENIED" operation="getattr" info="Failed name lookup -
>>>> disconnected path" error=-13 profile="/usr/sbin/tcpdump"
>>>> name="apparmor/.null" pid=1529 comm="tcpdump" requested_mask="r"
>>>> denied_mask="r" fsuid=0 ouid=0
>>> Okay, so apparmor is checking all the open file descriptors that tcpdump
>>> is inheriting. Ones that are not allowed are being "closed" during the
>>> file inherit pass. I say "closed" because we don't actually close the
>>> file, the associated file descriptor may have significance to the
>>> application, eg fd 1 is stdout, etc. It is entirely possible the parent
>>> is passing an open file, and the fd # by args or environment etc. Truly
>>> closing the fd would make it available for reuse and that could cause
>>> strange failures.
>>> So instead of actually closing the file, apparmor redirects its to be a
>>> file to a special null device that apparmor sets up. In most cases
>>> (reads and writes) will not result in further logging, but some cases
>>> like getattr will result in logging extra denials about access to
>>> "apparmor/.null". This is just an artifact of how the lsm is setup, and
>>> how apparmor is handling things internally atm.
>> Interesting. Just to make it clear, I not only get logs but actual
>> denials as I cannot see any output from the packet captures. Saving the
>> trace to a file (-w) or using a shell redirection works though.
> Correct, these are actual denials. Specifically you get 1 file_inherit
> denial when apparmor replaces the open file. Reads and Writes to the
> file are redirected to the special null device so their is no denial,
> but other operations like getattr, setattr, etc will result in a
> denial to access the null object.
>> The inheritance thing is still a bit unclear to me. The AA contained
>> sshd process launches an uncontained bash process for my session so why
>> is AA still looking around for what's happening in that uncontained bash?
> So processes can pass open files down to their children, and those children
> can pass those files down to grandchildren etc. The open files take up
> resources in the processes descriptor table and have position and other
> information associated with them.
> So don't think of it as what is happening in the unconfined bash. The
> file is opened under confinement, during a domain transition (sort of
> in the original profile), certain resources are determined as not being
> allowed access on the other end of the transition.

I'm not sure what's the other end of the transition, tcpdump's profile?
If that's the case, helps me to think of it as Apparmor "leaking" the
confinement of the pts so it persists through unconfined processes.

> These resources instead of being closed are replaced with a special file
> object, because the child will not have access to it, even if the child
> or grand child does. It is true that an unconfined process is free to
> open the original files itself, but it can not access the special null
> file. So what is happening is during a transition the the file is
> duped (look up man 2 dup) to the special null file, and then at some
> point unconfined is getting this already opened file. This file no
> longer points to or references the original file in anyway, is all the
> unconfined process sees is the special null file which everyone (including
> unconfined) is denied access to.
> Unconfined is free to access the original file if it opens it directly.
> Or receives the file in such a way that it has not gone through an
> inheritance chain that ends up duping the file to the special null.
> As an aside, we have to be very careful about what gets passed from
> one process to the next during a profile transition as this can be a
> means of elevating privileges.
>>> Ideally we will get
>>> around to fixing these cases to not log as well, but we have a lot of
>>> higher priority items to tackle first.
>> Understood.
>>>> Adding "/dev/pts/[0-9]* rw" to the tcpdump profile fixes the problem but
>>>> it seems like the wrong way. FYI, this also happens with other programs
>>>> confined by AA.
>>> yeah I agree that it is the wrong way, but unfortunately atm it is what
>>> needs to be done. Really the parent should be delegating access of the
>>> pts fd to its child. However this ability has not landed yet. It will
>>> come, without apparmor is stuck doing an ambient authority which is not
>>> what we want.
>> Is it this ambient authority behavior that explains why AA still
>> mediates the uncontained bash process and thus prevents a clean
>> transition to tcpdump in that chain:
>> sshd (contained) -> bash (uncontained) -> tcpdump (contained)
> partially, and partially an implementation detail about how resources
> are passed through processes. Some things get scrubbed others don't.
> Eg. the file gets duped to null but if the file name had been passed as
> an environment variable and tcpdump had opened it directly then things
> would behave differently.

This brought me to try running tcpdump inside of tmux to have a
different pts. It worked so I tried more hacks and finally settled on:

 $ tail -n5 ~/.profile
 # kludge to change pts if PPID is contained by sshd's Apparmor profile
 if [ -e "/proc/$PPID/attr/current" ] && \
      grep -qw '^/usr/sbin/sshd' "/proc/$PPID/attr/current"; then
   exec script --quiet --return /dev/null

If you know of a cleaner way to change my pts, I'm all hears :)

> I am not a fan of the dup to null behavior but it is required to ensure
> that applications don't break in different ways.

Oh, I would have assume there was a way for Apparmor to stop monitoring
a given fd without having to close it or dup it to null.

> Eg. say the file on fd 2
> (stdout) is determined to be not allowed. If we close the file for that
> process fd 2 becomes the next available fd, and when the process opens
> its first file it gets opened on fd. So that first file also becomes
> the processes stdout for the length that it is held open.
> This type of thing is less likely with fds > 3 but if for some reason
> the application receives info that a file was passed on a given fd
> it may try using it directly as well (this does happen).

I could only understand portions of your detailed explanation but I am
grateful for the time you've taken. Also, thank you for hinting me in
the right direction for a semi-clean workaround.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 966 bytes
Desc: OpenPGP digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20151002/d3cef1bf/attachment.pgp>

More information about the AppArmor mailing list