CVE-2020-1712: Use After Free (CWE-416)
value: A heap use-after-free vulnerability was found in systemd before version v245-rc1, where asynchronous Polkit queries are performed while handling dbus messages. A local unprivileged attacker can abuse this flaw to crash systemd services or potentially execute code and elevate their privileges, by sending specially crafted dbus messages.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.8
Published: 2020-03-31T17:15Z
Repository: https://github.com/systemd/systemd
Language: C
Files changed: src/libsystemd/libsystemd.sym, src/libsystemd/sd-bus/sd-bus.c, src/systemd/sd-bus.h
Commit date: 2020-02-04 18:47:31 +0100
Commit message: sd-bus: introduce API for re-enqueuing incoming messages

When authorizing via PolicyKit we want to process incoming method calls
twice: once to process and figure out that we need PK authentication,
and a second time after we aquired PK authentication to actually execute
the operation. With this new call sd_bus_enqueue_for_read() we have a
way to put an incoming message back into the read queue for this
purpose.

This might have other uses too, for example debugging.

Diff: diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index ccc23e1257b..08b915cf7c5 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -683,10 +683,11 @@ global:
         sd_event_source_disable_unref;
 } LIBSYSTEMD_241;
 
 LIBSYSTEMD_245 {
 global:
+        sd_bus_enqeue_for_read;
         sd_bus_message_dump;
         sd_bus_message_sensitive;
         sd_event_add_child_pidfd;
         sd_event_source_get_child_pidfd;
         sd_event_source_get_child_pidfd_own;
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index b53d4dd854f..c1db48f47a4 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -4205,5 +4205,29 @@ _public_ int sd_bus_get_close_on_exit(sd_bus *bus) {
         assert_return(bus, -EINVAL);
         assert_return(bus = bus_resolve(bus), -ENOPKG);
 
         return bus->close_on_exit;
 }
+
+_public_ int sd_bus_enqeue_for_read(sd_bus *bus, sd_bus_message *m) {
+        int r;
+
+        assert_return(bus, -EINVAL);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
+        assert_return(m, -EINVAL);
+        assert_return(m->sealed, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        if (!BUS_IS_OPEN(bus->state))
+                return -ENOTCONN;
+
+        /* Re-enqeue a message for reading. This is primarily useful for PolicyKit-style authentication,
+         * where we want accept a message, then determine we need to interactively authenticate the user, and
+         * when we have that process the message again. */
+
+        r = bus_rqueue_make_room(bus);
+        if (r < 0)
+                return r;
+
+        bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus);
+        return 0;
+}
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index ff2c0e9ef00..821b06ea926 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -205,10 +205,11 @@ int sd_bus_get_events(sd_bus *bus);
 int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec);
 int sd_bus_process(sd_bus *bus, sd_bus_message **r);
 int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r);
 int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec);
 int sd_bus_flush(sd_bus *bus);
+int sd_bus_enqeue_for_read(sd_bus *bus, sd_bus_message *m);
 
 sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus);
 sd_bus_message* sd_bus_get_current_message(sd_bus *bus);
 sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus);
 void* sd_bus_get_current_userdata(sd_bus *bus);

Fixed code:         sd_bus_enqeue_for_read;

_public_ int sd_bus_enqeue_for_read(sd_bus *bus, sd_bus_message *m) {
        int r;

        assert_return(bus, -EINVAL);
        assert_return(bus = bus_resolve(bus), -ENOPKG);
        assert_return(m, -EINVAL);
        assert_return(m->sealed, -EINVAL);
        assert_return(!bus_pid_changed(bus), -ECHILD);

        if (!BUS_IS_OPEN(bus->state))
                return -ENOTCONN;

        /* Re-enqeue a message for reading. This is primarily useful for PolicyKit-style authentication,
         * where we want accept a message, then determine we need to interactively authenticate the user, and
         * when we have that process the message again. */

        r = bus_rqueue_make_room(bus);
        if (r < 0)
                return r;

        bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus);
        return 0;
}
int sd_bus_enqeue_for_read(sd_bus *bus, sd_bus_message *m);