Skip to content

Commit 2f50a4f

Browse files
committed
bus-polkit: allow to auth. a bus call for multiple actions
In #20155, verify_shutdown_creds() needs to authenticate for both org.freedesktop.login1.hibernate-multiple-sessions and org.freedesktop.login1.hibernate-ignore-inhibit . Previously, the second authentication attempt would fail with -ESTALE. Fixes #20155.
1 parent 959301c commit 2f50a4f

File tree

1 file changed

+87
-53
lines changed

1 file changed

+87
-53
lines changed

src/shared/bus-polkit.c

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ int bus_test_polkit(
170170
typedef struct AsyncPolkitQueryAction {
171171
char *action;
172172
char **details;
173+
174+
LIST_FIELDS(struct AsyncPolkitQueryAction, authorized);
173175
} AsyncPolkitQueryAction;
174176

175177
static AsyncPolkitQueryAction *async_polkit_query_action_free(AsyncPolkitQueryAction *a) {
@@ -185,6 +187,8 @@ static AsyncPolkitQueryAction *async_polkit_query_action_free(AsyncPolkitQueryAc
185187
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQueryAction*, async_polkit_query_action_free);
186188

187189
typedef struct AsyncPolkitQuery {
190+
unsigned n_ref;
191+
188192
AsyncPolkitQueryAction *action;
189193

190194
sd_bus_message *request;
@@ -193,13 +197,15 @@ typedef struct AsyncPolkitQuery {
193197
Hashmap *registry;
194198
sd_event_source *defer_event_source;
195199

196-
AsyncPolkitQueryAction *authorized_action;
200+
LIST_HEAD(AsyncPolkitQueryAction, authorized_actions);
197201
AsyncPolkitQueryAction *denied_action;
198202
AsyncPolkitQueryAction *error_action;
199203
sd_bus_error error;
200204
} AsyncPolkitQuery;
201205

202206
static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
207+
AsyncPolkitQueryAction *a;
208+
203209
if (!q)
204210
return NULL;
205211

@@ -214,7 +220,11 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
214220

215221
sd_event_source_disable_unref(q->defer_event_source);
216222

217-
async_polkit_query_action_free(q->authorized_action);
223+
while ((a = q->authorized_actions)) {
224+
LIST_REMOVE(authorized, q->authorized_actions, a);
225+
async_polkit_query_action_free(a);
226+
}
227+
218228
async_polkit_query_action_free(q->denied_action);
219229
async_polkit_query_action_free(q->error_action);
220230

@@ -223,7 +233,8 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
223233
return mfree(q);
224234
}
225235

226-
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_free);
236+
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free);
237+
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref);
227238

228239
static int async_polkit_defer(sd_event_source *s, void *userdata) {
229240
AsyncPolkitQuery *q = ASSERT_PTR(userdata);
@@ -233,7 +244,7 @@ static int async_polkit_defer(sd_event_source *s, void *userdata) {
233244
/* This is called as idle event source after we processed the async polkit reply, hopefully after the
234245
* method call we re-enqueued has been properly processed. */
235246

236-
async_polkit_query_free(q);
247+
async_polkit_query_unref(q);
237248
return 0;
238249
}
239250

@@ -271,13 +282,14 @@ static int async_polkit_read_reply(sd_bus_message *reply, AsyncPolkitQuery *q) {
271282
if (r < 0)
272283
return r;
273284

274-
assert(!q->authorized_action);
285+
/* It's currently expected that processing of a DBus message shall be interrupted on the first
286+
* auth. error */
275287
assert(!q->denied_action);
276288
assert(!q->error_action);
277289
assert(!sd_bus_error_is_set(&q->error));
278290

279291
if (authorized)
280-
q->authorized_action = TAKE_PTR(a);
292+
LIST_PREPEND(authorized, q->authorized_actions, TAKE_PTR(a));
281293
else if (challenge) {
282294
q->error_action = TAKE_PTR(a);
283295
return sd_bus_error_set(&q->error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
@@ -307,14 +319,19 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
307319
* We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
308320
* i.e. after the second time the message is processed is complete. */
309321

310-
assert(!q->defer_event_source);
311-
r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
312-
if (r < 0)
313-
return r;
322+
if (!q->defer_event_source) {
323+
r = sd_event_add_defer(
324+
sd_bus_get_event(sd_bus_message_get_bus(reply)),
325+
&q->defer_event_source,
326+
async_polkit_defer,
327+
q);
328+
if (r < 0)
329+
return r;
314330

315-
r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
316-
if (r < 0)
317-
return r;
331+
r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
332+
if (r < 0)
333+
return r;
334+
}
318335

319336
r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
320337
if (r < 0)
@@ -341,7 +358,7 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
341358
if (r < 0) {
342359
log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
343360
(void) sd_bus_reply_method_errno(q->request, r, NULL);
344-
async_polkit_query_free(q);
361+
async_polkit_query_unref(q);
345362
}
346363
return r;
347364
}
@@ -356,17 +373,17 @@ _pure_ static int async_polkit_query_check_action(
356373
assert(action);
357374
assert(ret_error);
358375

359-
if (q->authorized_action)
360-
/* If the operation we want to authenticate changed between the first and the second time,
361-
* let's not use this authentication, it might be out of date as the object and context we
362-
* operate on might have changed. */
363-
return streq(q->authorized_action->action, action) && strv_equal(q->action->details, (char**) details) ? 1 : -ESTALE;
376+
LIST_FOREACH(authorized, a, q->authorized_actions)
377+
if (streq(a->action, action) && strv_equal(a->details, (char**) details))
378+
return 1;
364379

365-
if (q->error_action)
380+
if (q->error_action && streq(q->error_action->action, action))
366381
return sd_bus_error_copy(ret_error, &q->error);
367382

368-
assert(q->denied_action);
369-
return -EACCES;
383+
if (q->denied_action && streq(q->denied_action->action, action))
384+
return -EACCES;
385+
386+
return 0;
370387
}
371388

372389
#endif
@@ -396,21 +413,26 @@ _pure_ static int async_polkit_query_check_action(
396413
*
397414
* A step-by-step description of how it works:
398415
*
399-
* 1. A D-Bus method handler calls bus_verify_polkit_async(), passing it the D-Bus message being
400-
* processed and the polkit action to verify.
401-
* 2. bus_verify_polkit_async() checks the registry for the message and action combination. Let's
402-
* assume this is the first call, so it finds nothing.
403-
* 3. A new AsyncPolkitQuery object is created and an async. D-Bus call to polkit is made. The
404-
* function then returns 0. The method handler returns 1 to tell sd-bus that the processing of
416+
* 1. A D-Bus method handler calls bus_verify_polkit_async(), passing it the D-Bus message being
417+
* processed and the polkit action to verify.
418+
* 2. bus_verify_polkit_async() checks the registry for an existing query object associated with the
419+
* message. Let's assume this is the first call, so it finds nothing.
420+
* 3. A new AsyncPolkitQuery object is created and an async. D-Bus call to polkit is made. The
421+
* function then returns 0. The method handler returns 1 to tell sd-bus that the processing of
405422
* the message has been interrupted.
406-
* 4. (Later) A reply from polkit is received and async_polkit_callback() is called.
407-
* 5. async_polkit_callback() reads the reply and stores result into the passed query.
408-
* 6. async_polkit_callback() enqueues the original message again.
409-
* 7. (Later) The same D-Bus method handler is called for the same message. It calls
410-
* bus_verify_polkit_async() again.
411-
* 8. bus_verify_polkit_async() checks the registry for the message and action combination. It finds
412-
* an existing query and returns its result.
413-
* 9. The method handler continues processing of the message.
423+
* 4. (Later) A reply from polkit is received and async_polkit_callback() is called.
424+
* 5. async_polkit_callback() reads the reply and stores its result in the passed query.
425+
* 6. async_polkit_callback() enqueues the original message again.
426+
* 7. (Later) The same D-Bus method handler is called for the same message. It calls
427+
* bus_verify_polkit_async() again.
428+
* 8. bus_verify_polkit_async() checks the registry for an existing query object associated with the
429+
* message. It finds one and returns the result for the action.
430+
* 9. The method handler continues processing of the message. If there's another action that needs
431+
* to be verified:
432+
* 10. bus_verify_polkit_async() is called again for the new action. The registry already contains a
433+
* query for the message, but the new action hasn't been seen yet, hence steps 4-8 are repeated.
434+
* 11. (In the method handler again.) bus_verify_polkit_async() returns query results for both
435+
* actions and the processing continues as in step 9.
414436
*
415437
* Memory handling:
416438
*
@@ -425,6 +447,7 @@ _pure_ static int async_polkit_query_check_action(
425447
*
426448
* -> foo_method(m)
427449
* -> bus_verify_polkit_async(m, a)
450+
* -> async_polkit_query_ref(q)
428451
* -> bus_call_method_async(q)
429452
* <- bus_verify_polkit_async(m, a) = 0
430453
* <- foo_method(m) = 1
@@ -438,10 +461,11 @@ _pure_ static int async_polkit_query_check_action(
438461
* -> bus_verify_polkit_async(m, a)
439462
* <- bus_verify_polkit_async(m, a) = 1/-EACCES/error
440463
* ...
464+
* // possibly another call to bus_verify_polkit_async with action a2
441465
* <- foo_method(m)
442466
* ...
443467
* -> async_polkit_defer(q)
444-
* -> async_polkit_query_free(q)
468+
* -> async_polkit_query_unref(q)
445469
* <- async_polkit_defer(q)
446470
*/
447471

@@ -467,10 +491,13 @@ int bus_verify_polkit_async(
467491

468492
#if ENABLE_POLKIT
469493
AsyncPolkitQuery *q = hashmap_get(*registry, call);
470-
/* This is the second invocation of this function, and there's already a response from
471-
* polkit, let's process it */
472-
if (q)
473-
return async_polkit_query_check_action(q, action, details, ret_error);
494+
/* This is a repeated invocation of this function, hence let's check if we've already got
495+
* a response from polkit for this action */
496+
if (q) {
497+
r = async_polkit_query_check_action(q, action, details, ret_error);
498+
if (r != 0)
499+
return r;
500+
}
474501
#endif
475502

476503
r = sd_bus_query_sender_privilege(call, capability);
@@ -481,7 +508,7 @@ int bus_verify_polkit_async(
481508

482509
#if ENABLE_POLKIT
483510
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
484-
_cleanup_(async_polkit_query_freep) AsyncPolkitQuery *q_new = NULL;
511+
_cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q_new = NULL;
485512

486513
int c = sd_bus_message_get_allow_interactive_authorization(call);
487514
if (c < 0)
@@ -497,14 +524,19 @@ int bus_verify_polkit_async(
497524
if (r < 0)
498525
return r;
499526

500-
q = q_new = new(AsyncPolkitQuery, 1);
501-
if (!q)
502-
return -ENOMEM;
527+
if (!q) {
528+
q = q_new = new(AsyncPolkitQuery, 1);
529+
if (!q)
530+
return -ENOMEM;
503531

504-
*q = (AsyncPolkitQuery) {
505-
.request = sd_bus_message_ref(call),
506-
};
532+
*q = (AsyncPolkitQuery) {
533+
.n_ref = 1,
534+
.request = sd_bus_message_ref(call),
535+
};
536+
} else
537+
async_polkit_query_ref(q);
507538

539+
assert(!q->action);
508540
q->action = new(AsyncPolkitQueryAction, 1);
509541
if (!q->action)
510542
return -ENOMEM;
@@ -516,11 +548,13 @@ int bus_verify_polkit_async(
516548
if (!q->action->action || !q->action->details)
517549
return -ENOMEM;
518550

519-
r = hashmap_put(*registry, call, q);
520-
if (r < 0)
521-
return r;
551+
if (!q->registry) {
552+
r = hashmap_put(*registry, call, q);
553+
if (r < 0)
554+
return r;
522555

523-
q->registry = *registry;
556+
q->registry = *registry;
557+
}
524558

525559
r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
526560
if (r < 0)
@@ -536,7 +570,7 @@ int bus_verify_polkit_async(
536570

537571
Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
538572
#if ENABLE_POLKIT
539-
return hashmap_free_with_destructor(registry, async_polkit_query_free);
573+
return hashmap_free_with_destructor(registry, async_polkit_query_unref);
540574
#else
541575
assert(hashmap_isempty(registry));
542576
return hashmap_free(registry);

0 commit comments

Comments
 (0)