Since the issue is that an fd is opened by the first app running in one
profile while transitioning to the snap-confine profile, there is an
option that would 'work'.

As a POC, I installed the hello-world snap and also created a test-
classic snap (just hello-world renamed with 'confinement: classic' and
installed with --classic --dangerous). Do nothing else, I then try to
reproduce the issue:

$ test-classic.sh
bash-5.0$ exec 3<> /run/user/$(id -u)/test.fd
bash-5.0$ test-classic.env > /dev/null
bash-5.0$

I see in the logs the familiar denial:

Oct 30 13:53:24 foo kernel: audit: type=1400 audit(1572461604.648:3444):
apparmor="DENIED" operation="file_inherit"
profile="/snap/core/8039/usr/lib/snapd/snap-confine"
name="/run/user/1000/test.fd" pid=24957 comm="snap-confine"
requested_mask="wr" denied_mask="wr" fsuid=1000 ouid=1000

I then updated /var/lib/snapd/apparmor/profiles/snap.test-classic.sh to
have:

  /usr/lib/snapd/snap-confine ix,
  /snap/core/8039/usr/lib/snapd/snap-confine ix,
  ^mount-namespace-capture-helper (complain) {
    file,
    unix,
    signal,
  }

and tried again:

$ test-classic.sh
bash-5.0$ exec 3<> /run/user/$(id -u)/test.fd
bash-5.0$ test-classic.env > /dev/null
bash-5.0$

and I observe in the logs there is no file_inherit denial.

This 'works' because the profile that snap-confine is running under is
the same as the classic snap and therefore has all the same accesses
that the snap does (I could've chosen the special 'unconfined', but
snap-confine will fail to run in that case).

Interestingly, if I run 'hello-world' from the classic snap:

$ test-classic.sh
bash-5.0$ exec 3<> /run/user/$(id -u)/test.fd
bash-5.0$ test-classic.env > /dev/null
bash-5.0$ hello-world.sh
bash-4.3$ cat /proc/self/fd/3
cat: /proc/self/fd/3: Permission denied

hello-world correctly gets the denials (first is inherit, 2nd
/apparmor/.null is how apparmor handles the access to the failed inherit
fd):

Oct 30 14:20:44 foo kernel: audit: type=1400 audit(1572463244.359:3449): 
apparmor="DENIED" operation="file_inherit" profile="snap.hello-world.sh" 
name="/run/user/1000/test.fd" pid=26175 comm="snap-exec" requested_mask="wr" 
denied_mask="wr" fsuid=1000 ouid=1000
Oct 30 14:21:56 foo kernel: audit: type=1400 audit(1572463316.344:3451): 
apparmor="DENIED" operation="open" profile="snap.hello-world.sh" 
name="/apparmor/.null" pid=26244 comm="cat" requested_mask="r" denied_mask="r" 
fsuid=1000 ouid=0

Now, I say 'works' because I don't care for how the snap-confine policy
is circumvented in the POC since a classic snap could then try to
exploit bugs in the setuid snap-confine. While one could argue that a
classic snap already has root on the system, many people will install
classic snaps that run as the user (ie, no daemons) and feel a bit
safer, but with the POC policy the snap could, running as the user, try
to exploit bugs in snap-confine to gain privileges.

There is possibly an acceptable way, but it would need to be
investigated to verify it works and for acceptable safety:

1. adjust the classic policy to use:

  /usr/lib/snapd/snap-confine ix,
  /snap/$SNAP_WITH_SNAPD/$SNAP_WITH_SNAPD_REVISION/usr/lib/snapd/snap-confine 
ix,

2. adjust snap-confine to:

  if apparmor enabled:
    if unconfined:
      if !change_profile(snap_confine_profile)
        die("snap-confine has elevated permissions and is not confined but 
should be...."

The idea is, adjust the classic policy to transition to the unconfined
profile when calling snap-confine (the $SNAP_WITH_SNAPD* stuff is to
avoid conflicting x modifiers with the parser). This should allow snap-
confine to have the open fds without file_inherit denials. Then snap-
confine tries as early as possible to transition itself to the snap-
confine profile, dying if it can't. The kernel may revalidate the fds at
change_profile (needs verifying) and change_profile is not as strong as
fork/exec profile change, but might be acceptable for this case. snap-
confine would be susceptible to LD_PRELOAD issues, but because it is
setuid, secure_exec is in effect and areas in the filesystem that the
binary would honor with LD_PRELOAD are writable only by root (and if
someone runs the classic snap under sudo to circumvent this, they can
simply change the host however he/she desires).

An alternative without modifying snap-confine would be to have two snap-
confine profiles, one for strict and one for classic, and adjust the
classic template to transition to the classic snap-confine template
which has rules allowing 'rw' access to files and 'unix' for sockets.

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/1849753

Title:
  AppArmor profile prohibits classic snap from inheriting file
  descriptors

To manage notifications about this bug go to:
https://bugs.launchpad.net/apparmor/+bug/1849753/+subscriptions

-- 
ubuntu-bugs mailing list
ubuntu-bugs@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to