summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-02-05 09:37:07 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-02-05 10:16:59 +0100
commitea0d0ede03c6f18dbc5036c5e9cccf97e415ccc2 (patch)
tree90d6cca066dbe491b4ec75becb92c0e2beec39e9
parentMerge pull request #14536 from DaanDeMeyer/wait-online-max-operstate (diff)
parentFix typo in function name (diff)
downloadsystemd-ea0d0ede03c6f18dbc5036c5e9cccf97e415ccc2.tar.xz
systemd-ea0d0ede03c6f18dbc5036c5e9cccf97e415ccc2.zip
Merge branch 'polkit-ref-count'
-rw-r--r--TODO2
-rw-r--r--man/rules/meson.build1
-rw-r--r--man/sd_bus_enqueue_for_read.xml88
-rw-r--r--src/libsystemd/libsystemd.sym1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c24
-rw-r--r--src/shared/bus-polkit.c180
-rw-r--r--src/systemd/sd-bus.h1
7 files changed, 235 insertions, 62 deletions
diff --git a/TODO b/TODO
index 4a6c8d3512..d26b1be408 100644
--- a/TODO
+++ b/TODO
@@ -328,7 +328,7 @@ Features:
* the a-posteriori stopping of units bound to units that disappeared logic
should be reworked: there should be a queue of units, and we should only
- enqeue stop jobs from a defer event that processes queue instead of
+ enqueue stop jobs from a defer event that processes queue instead of
right-away when we find a unit that is bound to one that doesn't exist
anymore. (similar to how the stop-unneeded queue has been reworked the same
way)
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 591d7c48b5..98316b33b2 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -195,6 +195,7 @@ manpages = [
'sd_bus_open_user_with_description',
'sd_bus_open_with_description'],
''],
+ ['sd_bus_enqueue_for_read', '3', [], ''],
['sd_bus_error',
'3',
['SD_BUS_ERROR_MAKE_CONST',
diff --git a/man/sd_bus_enqueue_for_read.xml b/man/sd_bus_enqueue_for_read.xml
new file mode 100644
index 0000000000..3318a3031b
--- /dev/null
+++ b/man/sd_bus_enqueue_for_read.xml
@@ -0,0 +1,88 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_enqueue_for_read"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_enqueue_for_read</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_enqueue_for_read</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_enqueue_for_read</refname>
+
+ <refpurpose>Re-enqueue a bus message on a bus connection, for reading.</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_enqueue_for_read</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_enqueue_for_read()</function> may be used to re-enqueue an incoming bus message on
+ the local read queue, so that it is processed and dispatched locally again, similar to how an incoming
+ message from the peer is processed. Takes a bus connection object and the message to enqueue. A reference
+ is taken of the message and the caller's reference thus remains in possession of the caller. The message
+ is enqueued at the end of the queue, thus will be dispatched after all other already queued messages are
+ dispatched.</para>
+
+ <para>This call is primarily useful for dealing with incoming method calls that may be processed only
+ after an additional asynchronous operation completes. One example are PolicyKit authorization requests
+ that are determined to be necessary to authorize a newly incoming method call: when the PolicyKit response
+ is received the original method call may be re-enqueued to process it again, this time with the
+ authorization result known.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, this function return 0 or a positive integer. On failure, it returns a negative errno-style
+ error code.</para>
+
+ <refsect2>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus connection has been created in a different process.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index ccc23e1257..8b6ebbcf8b 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -685,6 +685,7 @@ global:
LIBSYSTEMD_245 {
global:
+ sd_bus_enqueue_for_read;
sd_bus_message_dump;
sd_bus_message_sensitive;
sd_event_add_child_pidfd;
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index b53d4dd854..7ad03680f4 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -4207,3 +4207,27 @@ _public_ int sd_bus_get_close_on_exit(sd_bus *bus) {
return bus->close_on_exit;
}
+
+_public_ int sd_bus_enqueue_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-enqueue a message for reading. This is primarily useful for PolicyKit-style authentication,
+ * where we accept a message, then determine we need to interactively authenticate the user, and then
+ * we want to 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/shared/bus-polkit.c b/src/shared/bus-polkit.c
index da4aee5086..0dbf3f60c8 100644
--- a/src/shared/bus-polkit.c
+++ b/src/shared/bus-polkit.c
@@ -30,6 +30,34 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) {
return sender_uid == good_user;
}
+#if ENABLE_POLKIT
+static int bus_message_append_strv_key_value(
+ sd_bus_message *m,
+ const char **l) {
+
+ const char **k, **v;
+ int r;
+
+ assert(m);
+
+ r = sd_bus_message_open_container(m, 'a', "{ss}");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(k, v, l) {
+ r = sd_bus_message_append(m, "{ss}", *k, *v);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ return r;
+}
+#endif
+
int bus_test_polkit(
sd_bus_message *call,
int capability,
@@ -37,7 +65,7 @@ int bus_test_polkit(
const char **details,
uid_t good_user,
bool *_challenge,
- sd_bus_error *e) {
+ sd_bus_error *ret_error) {
int r;
@@ -60,7 +88,7 @@ int bus_test_polkit(
_cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int authorized = false, challenge = false;
- const char *sender, **k, **v;
+ const char *sender;
sender = sd_bus_message_get_sender(call);
if (!sender)
@@ -84,17 +112,7 @@ int bus_test_polkit(
if (r < 0)
return r;
- r = sd_bus_message_open_container(request, 'a', "{ss}");
- if (r < 0)
- return r;
-
- STRV_FOREACH_PAIR(k, v, details) {
- r = sd_bus_message_append(request, "{ss}", *k, *v);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(request);
+ r = bus_message_append_strv_key_value(request, details);
if (r < 0)
return r;
@@ -102,11 +120,11 @@ int bus_test_polkit(
if (r < 0)
return r;
- r = sd_bus_call(call->bus, request, 0, e, &reply);
+ r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
if (r < 0) {
/* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
- sd_bus_error_free(e);
+ if (sd_bus_error_has_name(ret_error, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
+ sd_bus_error_free(ret_error);
return -EACCES;
}
@@ -137,15 +155,17 @@ int bus_test_polkit(
#if ENABLE_POLKIT
typedef struct AsyncPolkitQuery {
+ char *action;
+ char **details;
+
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) {
-
if (!q)
return;
@@ -157,9 +177,25 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) {
sd_bus_message_unref(q->request);
sd_bus_message_unref(q->reply);
+ free(q->action);
+ strv_free(q->details);
+
+ 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;
@@ -168,21 +204,46 @@ 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:
- async_polkit_query_free(q);
+ 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;
}
@@ -196,16 +257,14 @@ int bus_verify_polkit_async(
bool interactive,
uid_t good_user,
Hashmap **registry,
- sd_bus_error *error) {
+ sd_bus_error *ret_error) {
#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;
int r;
assert(call);
@@ -221,11 +280,17 @@ int bus_verify_polkit_async(
if (q) {
int authorized, challenge;
- /* This is the second invocation of this function, and
- * there's already a response from polkit, let's
- * process it */
+ /* This is the second invocation of this function, and there's already a response from
+ * polkit, let's process it */
assert(q->reply);
+ /* If the operation we want to authenticate changed between the first and the second time,
+ * let's not use this authentication, it might be out of date as the object and context we
+ * operate on might have changed. */
+ if (!streq(q->action, action) ||
+ !strv_equal(q->details, (char**) details))
+ return -ESTALE;
+
if (sd_bus_message_is_method_error(q->reply, NULL)) {
const sd_bus_error *e;
@@ -237,7 +302,7 @@ int bus_verify_polkit_async(
return -EACCES;
/* Copy error from polkit reply */
- sd_bus_error_copy(error, e);
+ sd_bus_error_copy(ret_error, e);
return -sd_bus_error_get_errno(e);
}
@@ -251,7 +316,7 @@ int bus_verify_polkit_async(
return 1;
if (challenge)
- return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
+ return sd_bus_error_set(ret_error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
return -EACCES;
}
@@ -263,20 +328,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;
@@ -305,17 +361,7 @@ int bus_verify_polkit_async(
if (r < 0)
return r;
- r = sd_bus_message_open_container(pk, 'a', "{ss}");
- if (r < 0)
- return r;
-
- STRV_FOREACH_PAIR(k, v, details) {
- r = sd_bus_message_append(pk, "{ss}", *k, *v);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(pk);
+ r = bus_message_append_strv_key_value(pk, details);
if (r < 0)
return r;
@@ -323,13 +369,25 @@ int bus_verify_polkit_async(
if (r < 0)
return r;
- q = new0(AsyncPolkitQuery, 1);
+ q = new(AsyncPolkitQuery, 1);
if (!q)
return -ENOMEM;
- q->request = sd_bus_message_ref(call);
- q->callback = callback;
- q->userdata = userdata;
+ *q = (AsyncPolkitQuery) {
+ .request = sd_bus_message_ref(call),
+ };
+
+ q->action = strdup(action);
+ if (!q->action) {
+ async_polkit_query_free(q);
+ return -ENOMEM;
+ }
+
+ q->details = strv_copy((char**) details);
+ if (!q->details) {
+ async_polkit_query_free(q);
+ return -ENOMEM;
+ }
r = hashmap_put(*registry, call, q);
if (r < 0) {
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index ff2c0e9ef0..e6f3298745 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -207,6 +207,7 @@ 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_enqueue_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);