@@ -170,6 +170,8 @@ int bus_test_polkit(
170170typedef struct AsyncPolkitQueryAction {
171171 char * action ;
172172 char * * details ;
173+
174+ LIST_FIELDS (struct AsyncPolkitQueryAction , authorized );
173175} AsyncPolkitQueryAction ;
174176
175177static AsyncPolkitQueryAction * async_polkit_query_action_free (AsyncPolkitQueryAction * a ) {
@@ -185,6 +187,8 @@ static AsyncPolkitQueryAction *async_polkit_query_action_free(AsyncPolkitQueryAc
185187DEFINE_TRIVIAL_CLEANUP_FUNC (AsyncPolkitQueryAction * , async_polkit_query_action_free );
186188
187189typedef 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
202206static 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
228239static 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
537571Hashmap * 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