unix syntax does not easily accommodate unix autobind sockets

Bug #1867216 reported by Jamie Strandboge
12
This bug affects 2 people
Affects Status Importance Assigned to Milestone
AppArmor
New
Undecided
Unassigned
snapd
Triaged
Wishlist
Unassigned

Bug Description

'man 2 unix' describes the 'Autobind feature' as such:

Autobind feature
  If a bind(2) call specifies addrlen as sizeof(sa_family_t), or the SO_PASS‐
  CRED socket option was specified for a socket that was not explicitly bound
  to an address, then the socket is autobound to an abstract address. The
  address consists of a null byte followed by 5 bytes in the character set
  [0-9a-f]. Thus, there is a limit of 2^20 autobind addresses. (From Linux
  2.1.15, when the autobind feature was added, 8 bytes were used, and the
  limit was thus 2^32 autobind addresses. The change to 5 bytes came in
  Linux 2.3.15.)

Eg:

$ cat ./autobind.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

int main() {
    int fd;

    fd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (fd < 0) {
        return -errno;
    }

    struct sockaddr_un un;
    un.sun_family = AF_UNIX;
    if (bind(fd, (struct sockaddr *) &un, sizeof(sa_family_t)) < 0) {
            perror("bind");
            return -errno;
    }

    return 0;
}

$ gcc -o autobind ./autobind.c

$ cat ./profile
  /etc/ld.so.cache r,
  /{usr/,}lib/@{multiarch}/ld* m,
  /{usr/,}lib/@{multiarch}/** r,
  /{usr/,}lib/@{multiarch}/lib*.so* mr,
  /{usr/,}lib/@{multiarch}/**/lib*.so* mr,
  unix (create, getattr, getopt, setopt, shutdown),

  unix (bind) type=dgram addr=none,
  unix (bind) type=dgram addr="@[0-9][0-9][0-9][0-9][0-9]",
  unix (bind) type=dgram addr="@*",
  unix (bind) type=dgram addr="@\x00",
  #unix (bind) type=dgram addr="\x00x00", // invalid

  # most specific rule that can be used
  #unix (bind) type=dgram,

}

$ sudo apparmor_parser -r /tmp/apparmor.profile && aa-exec -p test -- ./autobind
bind: Permission denied
[243]

So far so good; the bind was mediated with the following logged denial:

apparmor="DENIED" operation="bind" profile="test" pid=735115 comm="autobind" family="unix" sock_type="dgram" protocol=0 requested_mask="bind" denied_mask="bind" addr=none

This looks like an anonymous socket, so I expected this to work:

  unix (bind) type=dgram addr=none,

but it doesn't. Considering the unix man page, I tried various addr=@ rules:

  unix (bind) type=dgram addr=none,
  unix (bind) type=dgram addr="@[0-9][0-9][0-9][0-9][0-9]",
  unix (bind) type=dgram addr="@*",
  unix (bind) type=dgram addr="@\x00",
  #unix (bind) type=dgram addr="\x00x00", // invalid

The best rule I can come up with is:

  unix (bind) type=dgram,

Unfortunately, that will allow binds for any abstract socket rules that uses this type (though we would still need send and receive rules to allow the communication).

We can see with strace the following (with -s 1024):

  socket(AF_UNIX, SOCK_DGRAM, 0) = 3
  bind(3, {sa_family=AF_UNIX}, 2) = -1 EACCES (Permission denied)

Notice how the bind doesn't show anything for sun_path, like with a named socket:

  socket(AF_UNIX, SOCK_STREAM, 0) = 3
  bind(3, {sa_family=AF_UNIX, sun_path="/tmp/foo"}, 10) = 0

or an abstract socket:

  socket(AF_UNIX, SOCK_STREAM, 0) = 3
  bind(3, {sa_family=AF_UNIX, sun_path=@"foo"}, 6) = 0

socketpair() works fine with our anonymous socket mediation when forking a child where when we have this denial:

apparmor="DENIED" operation="sendmsg" profile="test" pid=735525 comm="unnamed-fork" family="unix" sock_type="dgram" protocol=0 requested_mask="receive" denied_mask="receive" addr=none peer_addr=none peer="test"

and we can use this rule:

  unix type=dgram addr=none,

I'm not sure what the fix should be, but based on the log, it seems like 'unix (bind) type=dgram addr=none,' would be reasonable. If that isn't for some reason, perhaps new syntax would work: 'unix (autobind) type=dgram,'

This was discovered by tracking down the denial in the kubernetes snap. This is the code that triggers the denial:

https://github.com/coreos/go-systemd/blob/master/journal/journal.go#L211

A simple go program to reproduce this is:

package main
import (
        "fmt"
        "net"
        "sync/atomic"
        "unsafe"
)

var unixConnPtr unsafe.Pointer

func main() {
        autobind, err := net.ResolveUnixAddr("unixgram", "")
        if err != nil {
                fmt.Printf("Failed to autobind\n")
        }

        sock, err := net.ListenUnixgram("unixgram", autobind)
        if err != nil {
                fmt.Printf("Failed to listen on autobind\n")
        }
        atomic.StorePointer(&unixConnPtr, unsafe.Pointer(sock))

        // see github.com/coreos/go-systemd/journal/journal.go:Send() for
        // how to connect this up to the journal socket
}

description: updated
Revision history for this message
John Johansen (jjohansen) wrote :

This is fixed in apparmor 2.12.4, 2.13.5, and apparmor 3 by the expression addr=auto

eg.

  unix (bind) addr=auto,

Revision history for this message
Ian Johnson (anonymouse67) wrote :

@jjohansen, when this was fixed was it something in the kernel side or just the parser side? i.e. could we rely on just the version of apparmor_parser? (I'm not sure how to check the version of apparmor in the kernel to be honest)

Also, what do folks think about adding this rule to the default policy or alternatively maybe to the network interface:

```
unix (bind) addr=auto,
```

If I'm reading the man page correctly, that only allows binding an anonymous socket, it doesn't allow binding to a named socket in the way that other applications using specific addresses would.

Considering that this is only available in 2.12.5, 2.13.5 and 3.x versions of apparmor, we might need to make adding that rule conditional on having apparmor versions above those versions.

I ask (and am adding a snapd task here) since new versions of the docker snap are affected by the same go-systemd problem that triggered the kubernetes change here, and in docker's case docker will think that journald is not available at all and be entirely silent when things go wrong.

Revision history for this message
Jamie Strandboge (jdstrand) wrote :

I'm not sure we would want to add 'unix (bind) addr=auto,' to the default policy. For snapd, it seems we could add the rule to the docker-support interface (I don't know that another flavor is warranted for that)

I'm hesitant by default in apparmor abstractions or the snapd default template/auto-connected interfaces because the rule isn't very specific so adding it by default when most applications wouldn't need it opens up the potential for abuse. If this changes and there are many common use cases, we could revisit.

Revision history for this message
Ian Johnson (anonymouse67) wrote :

That's fair, I guess my reasoning for including it somewhere easily used is that it is a bit confusing if your "simple" Go application is using go-systemd and tries to simply write to journald it is denied, and it's not clear what the remedy is for applications that aren't docker or kubernetes. We can certainly add it to docker-support for now and that will resolve the 20.10.3 update problem we are running into, I was just trying to think ahead for other applications that might use go-systemd.

Revision history for this message
John Johansen (jjohansen) wrote :

@anonymouse67 the fix is userspace only, so it should work with any kernel that currently supports unix socket mediation.

The fix might be distro backported to versions other than 2.12.5 etc. The regular distro vs. upstream version issue. Its possible to check if a parser supports it by feeding it a stub profile.

```
  /f { unix (bind) addr=auto, }
```

But ideally I think the solution is snap vendoring apparmor userspace, so you don't have to worry about what the distro is carrying. Alex is working on this, he has a vendored 3.0.1 demo (I think there still is some more tweaking to do).

Changed in snapd:
importance: Undecided → Wishlist
status: New → Triaged
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.