unix syntax does not easily accommodate unix autobind sockets

Bug #1867216 reported by Jamie Strandboge on 2020-03-12
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
AppArmor
Undecided
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
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers