Bug 80469

Summary: CVE-2014-3533: Security implications of Bug #79694 Misbehaviour reading a message with fdpassing
Product: dbus Reporter: Alban Crequy <alban.crequy>
Component: coreAssignee: Simon McVittie <smcv>
Status: RESOLVED FIXED QA Contact: Sjoerd Simons <sjoerd>
Severity: normal    
Priority: medium CC: alban.crequy, thiago, walters
Version: unspecified   
Hardware: Other   
OS: All   
Whiteboard:
i915 platform: i915 features:

Description Alban Crequy 2014-06-24 09:40:50 UTC
This bug is to discuss the security implications of Bug #79694 ("Misbehaviour reading a message with passing file descriptors (duplicate/missing fds)").

I can kick any connection off the bus with fdpassing, as long as the malicious program is allowed to send messages to the target.

I tested with this on the system bus:
$ ./test79694 com.redhat.PrinterDriversInstaller
And the process was terminated.

- The malicious program sends 2 messages to the target:
   - Message 1: - contains 2 file descriptors (53 and 54 in the logs)
                - is slightly larger than 2048 bytes so it needs 2 iterations
                  to read (*). See max_bytes_read_per_iteration.
   - Message 2: - contains 3 file descriptors (61, 62, and 63 in the logs)
                - is a small message

Strace logs of dbus-daemon:

* receive part 1/2 of message 1:

recvmsg(40, {msg_name(0)=NULL,
             msg_iov(1)=[{"l\1\1\1\f\10\0\0\2\0\0\0_\0\0\0\t\1u\0\2\0\0\0\10\1g\0\3hhs"..., 2048}],
             msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {53, 54}},
             msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 2048
fcntl(53, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
fcntl(53, F_SETFD, FD_CLOEXEC)          = 0
fcntl(54, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
fcntl(54, F_SETFD, FD_CLOEXEC)          = 0

* receive both part 2/2 of message 1 and message 2 in a single recvmsg() call:

recvmsg(40, {msg_name(0)=NULL, msg_iov(1)=[{"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"..., 2048}],
             msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {61, 62, 63}},
             msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 248
fcntl(61, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
fcntl(61, F_SETFD, FD_CLOEXEC)          = 0
fcntl(62, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
fcntl(62, F_SETFD, FD_CLOEXEC)          = 0
fcntl(63, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
fcntl(63, F_SETFD, FD_CLOEXEC)          = 0

* send message 1 to target. Notice it sends the correct fds and closes them correctly:

sendmsg(36,
        {msg_name(0)=NULL,
         msg_iov(2)=[{"l\1\1\1\f\10\0\0\2\0\0\0o\0\0\0\t\1u\0\2\0\0\0\10\1g\0\3hhs"..., 128},
                     {"\0\0\0\0\1\0\0\0\377\7\0\0XXXXXXXXXXXXXXXXXXXX"..., 2060}],
         msg_controllen=24,
         {cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {53, 54}},
          msg_flags=0}, MSG_NOSIGNAL) = 2188
close(53)                               = 0
close(54)                               = 0

* send message 2 to target: due to the bug, it sends the wrong fds (the fds are closed already):

sendmsg(36,
        {msg_name(0)=NULL,
         msg_iov(2)=[{"l\1\1\1\f\0\0\0\3\0\0\0o\0\0\0\t\1u\0\3\0\0\0\10\1g\0\3hhh"..., 128},
                     {"\0\0\0\0\1\0\0\0\2\0\0\0", 12}],
         msg_controllen=32,
         {cmsg_len=28, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {53, 54, 53}},
          msg_flags=0}, MSG_NOSIGNAL) = -1 EBADF (Bad file descriptor)

* Notice that sendmsg() returns -1 EBADF!
* Too bad for the innocent target! dbus-daemon kills the connection:

epoll_ctl(4, EPOLL_CTL_DEL, 36, {0, {u32=0, u64=0}}) = 0
close(36)                               = 0

* And closes the wrong fds:

close(53)                               = -1 EBADF (Bad file descriptor)
write(2, "Failed to close file descriptor:"..., 55) = 55
close(54)                               = -1 EBADF (Bad file descriptor)
write(2, "Failed to close file descriptor:"..., 55) = 55
close(53)                               = -1 EBADF (Bad file descriptor)
write(2, "Failed to close file descriptor:"..., 55) = 55

=> It is an effective way to kill any connection on the bus if the D-Bus  
   policy and SCMs allow the malicious program to send 2 D-Bus messages to the
   target. D-Bus libraries often terminate the program when the D-Bus
   connection is closed. So it can kill programs on the bus.

(*): If you wonder why message 1 needs to be bigger than
     max_bytes_read_per_iteration, it is because the kernel will not
     aggregate the two D-Bus messages in a single recvmsg() call otherwise:
     http://lxr.free-electrons.com/source/net/unix/af_unix.c#L2051
     2051                         if (siocb->scm->fp)
     2052                                 break;
     and the bug happends only when fds from several D-Bus messages are in
     the loader at the same time.

==========

The patch attached on Bug #79694 fixes the security issue.

Bug #80163 is a different bug but has a similar security impact. I suggest fixes for both could be released at the same time.
Comment 1 Colin Walters 2014-06-24 12:02:05 UTC
Can you attach the sources for "test79694"?
Comment 2 Simon McVittie 2014-06-26 14:01:32 UTC
If none of the other D-Bus maintainers review my patch for #79694 soon, I will treat Alban's positive review as sufficient for merge, and include it in the same DoS-fix release as Bug #80163.

(In reply to comment #1)
> Can you attach the sources for "test79694"?

Resolved elsewhere: Alban sent this by private email, because this bug is going to become public when unembargoed, and he wanted to err on the side of caution when passing around working exploits.
Comment 3 Simon McVittie 2014-07-02 16:07:11 UTC
Removing embargo. Fixed in dbus 1.8.6 and 1.6.22 which I just released.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct.