157 lines
5.4 KiB
Diff
157 lines
5.4 KiB
Diff
From 2ec3c78b1d1ba907cd888aac3cdc3a86c03cda90 Mon Sep 17 00:00:00 2001
|
|
From: Jan Synacek <jsynacek@redhat.com>
|
|
Date: Fri, 31 Jan 2020 15:17:25 +0100
|
|
Subject: [PATCH] polkit: when authorizing via PK let's re-resolve
|
|
callback/userdata instead of caching it
|
|
|
|
Previously, when doing an async PK query we'd store the original
|
|
callback/userdata pair and call it again after the PK request is
|
|
complete. This is problematic, since PK queries might be slow and in the
|
|
meantime the userdata might be released and re-acquired. Let's avoid
|
|
this by always traversing through the message handlers so that we always
|
|
re-resolve the callback and userdata pair and thus can be sure it's
|
|
up-to-date and properly valid.
|
|
|
|
Resolves: CVE-2020-1712
|
|
---
|
|
src/shared/bus-util.c | 74 +++++++++++++++++++++++++++++--------------
|
|
1 file changed, 50 insertions(+), 24 deletions(-)
|
|
|
|
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
|
|
index 2d908eb45c..5ed68429be 100644
|
|
--- a/src/shared/bus-util.c
|
|
+++ b/src/shared/bus-util.c
|
|
@@ -319,10 +319,10 @@ int bus_test_polkit(
|
|
|
|
typedef struct AsyncPolkitQuery {
|
|
sd_bus_message *request, *reply;
|
|
- sd_bus_message_handler_t callback;
|
|
- void *userdata;
|
|
sd_bus_slot *slot;
|
|
+
|
|
Hashmap *registry;
|
|
+ sd_event_source *defer_event_source;
|
|
} AsyncPolkitQuery;
|
|
|
|
static void async_polkit_query_free(AsyncPolkitQuery *q) {
|
|
@@ -338,9 +338,22 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) {
|
|
sd_bus_message_unref(q->request);
|
|
sd_bus_message_unref(q->reply);
|
|
|
|
+ sd_event_source_disable_unref(q->defer_event_source);
|
|
free(q);
|
|
}
|
|
|
|
+static int async_polkit_defer(sd_event_source *s, void *userdata) {
|
|
+ AsyncPolkitQuery *q = userdata;
|
|
+
|
|
+ assert(s);
|
|
+
|
|
+ /* This is called as idle event source after we processed the async polkit reply, hopefully after the
|
|
+ * method call we re-enqueued has been properly processed. */
|
|
+
|
|
+ async_polkit_query_free(q);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
|
|
AsyncPolkitQuery *q = userdata;
|
|
@@ -349,19 +362,45 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
|
|
assert(reply);
|
|
assert(q);
|
|
|
|
+ assert(q->slot);
|
|
q->slot = sd_bus_slot_unref(q->slot);
|
|
+
|
|
+ assert(!q->reply);
|
|
q->reply = sd_bus_message_ref(reply);
|
|
|
|
+ /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
|
|
+ * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
|
|
+ * again.
|
|
+ *
|
|
+ * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
|
|
+ * i.e. after the second time the message is processed is complete. */
|
|
+
|
|
+ assert(!q->defer_event_source);
|
|
+ r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
|
|
+ if (r < 0)
|
|
+ goto fail;
|
|
+
|
|
+ r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
|
|
+ if (r < 0)
|
|
+ goto fail;
|
|
+
|
|
+ r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
|
|
+ if (r < 0)
|
|
+ goto fail;
|
|
+
|
|
r = sd_bus_message_rewind(q->request, true);
|
|
- if (r < 0) {
|
|
- r = sd_bus_reply_method_errno(q->request, r, NULL);
|
|
- goto finish;
|
|
- }
|
|
+ if (r < 0)
|
|
+ goto fail;
|
|
|
|
- r = q->callback(q->request, q->userdata, &error_buffer);
|
|
- r = bus_maybe_reply_error(q->request, r, &error_buffer);
|
|
+ r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
|
|
+ if (r < 0)
|
|
+ goto fail;
|
|
|
|
-finish:
|
|
+ return 1;
|
|
+
|
|
+fail:
|
|
+ log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
|
|
+ (void) sd_bus_reply_method_errno(q->request, r, NULL);
|
|
async_polkit_query_free(q);
|
|
|
|
return r;
|
|
@@ -382,11 +421,9 @@ int bus_verify_polkit_async(
|
|
#if ENABLE_POLKIT
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
|
|
AsyncPolkitQuery *q;
|
|
- const char *sender, **k, **v;
|
|
- sd_bus_message_handler_t callback;
|
|
- void *userdata;
|
|
int c;
|
|
#endif
|
|
+ const char *sender, **k, **v;
|
|
int r;
|
|
|
|
assert(call);
|
|
@@ -444,20 +481,11 @@ int bus_verify_polkit_async(
|
|
else if (r > 0)
|
|
return 1;
|
|
|
|
-#if ENABLE_POLKIT
|
|
- if (sd_bus_get_current_message(call->bus) != call)
|
|
- return -EINVAL;
|
|
-
|
|
- callback = sd_bus_get_current_handler(call->bus);
|
|
- if (!callback)
|
|
- return -EINVAL;
|
|
-
|
|
- userdata = sd_bus_get_current_userdata(call->bus);
|
|
-
|
|
sender = sd_bus_message_get_sender(call);
|
|
if (!sender)
|
|
return -EBADMSG;
|
|
|
|
+#if ENABLE_POLKIT
|
|
c = sd_bus_message_get_allow_interactive_authorization(call);
|
|
if (c < 0)
|
|
return c;
|
|
@@ -509,8 +537,6 @@ int bus_verify_polkit_async(
|
|
return -ENOMEM;
|
|
|
|
q->request = sd_bus_message_ref(call);
|
|
- q->callback = callback;
|
|
- q->userdata = userdata;
|
|
|
|
r = hashmap_put(*registry, call, q);
|
|
if (r < 0) {
|