Merge branch 'polkit-ref-count'

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-02-05 09:37:07 +01:00
commit ea0d0ede03
7 changed files with 235 additions and 62 deletions

2
TODO
View File

@ -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)

View File

@ -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',

View File

@ -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>

View File

@ -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;

View File

@ -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;
}

View File

@ -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:
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) {

View File

@ -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);