]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
269e4d2d LP |
2 | |
3 | #include "bus-internal.h" | |
4 | #include "bus-message.h" | |
5 | #include "bus-polkit.h" | |
73d3ac8e | 6 | #include "bus-util.h" |
269e4d2d LP |
7 | #include "strv.h" |
8 | #include "user-util.h" | |
9 | ||
10 | static int check_good_user(sd_bus_message *m, uid_t good_user) { | |
11 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; | |
12 | uid_t sender_uid; | |
13 | int r; | |
14 | ||
15 | assert(m); | |
16 | ||
17 | if (good_user == UID_INVALID) | |
18 | return 0; | |
19 | ||
20 | r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); | |
21 | if (r < 0) | |
22 | return r; | |
23 | ||
24 | /* Don't trust augmented credentials for authorization */ | |
25 | assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); | |
26 | ||
27 | r = sd_bus_creds_get_euid(creds, &sender_uid); | |
28 | if (r < 0) | |
29 | return r; | |
30 | ||
31 | return sender_uid == good_user; | |
32 | } | |
33 | ||
95f82ae9 | 34 | #if ENABLE_POLKIT |
3a3d4d3b | 35 | static int bus_message_append_strv_key_value(sd_bus_message *m, const char **l) { |
95f82ae9 LP |
36 | int r; |
37 | ||
38 | assert(m); | |
39 | ||
40 | r = sd_bus_message_open_container(m, 'a', "{ss}"); | |
41 | if (r < 0) | |
42 | return r; | |
43 | ||
44 | STRV_FOREACH_PAIR(k, v, l) { | |
45 | r = sd_bus_message_append(m, "{ss}", *k, *v); | |
46 | if (r < 0) | |
47 | return r; | |
48 | } | |
49 | ||
50 | r = sd_bus_message_close_container(m); | |
51 | if (r < 0) | |
52 | return r; | |
53 | ||
54 | return r; | |
55 | } | |
9f657af4 DT |
56 | |
57 | static int bus_message_new_polkit_auth_call( | |
58 | sd_bus_message *m, | |
59 | const char *action, | |
60 | const char **details, | |
61 | bool interactive, | |
62 | sd_bus_message **ret) { | |
63 | ||
64 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; | |
65 | const char *sender; | |
66 | int r; | |
67 | ||
68 | assert(m); | |
69 | assert(action); | |
70 | assert(ret); | |
71 | ||
72 | sender = sd_bus_message_get_sender(m); | |
73 | if (!sender) | |
74 | return -EBADMSG; | |
75 | ||
76 | r = sd_bus_message_new_method_call( | |
77 | ASSERT_PTR(m->bus), | |
78 | &c, | |
79 | "org.freedesktop.PolicyKit1", | |
80 | "/org/freedesktop/PolicyKit1/Authority", | |
81 | "org.freedesktop.PolicyKit1.Authority", | |
82 | "CheckAuthorization"); | |
83 | if (r < 0) | |
84 | return r; | |
85 | ||
86 | r = sd_bus_message_append(c, "(sa{sv})s", "system-bus-name", 1, "name", "s", sender, action); | |
87 | if (r < 0) | |
88 | return r; | |
89 | ||
90 | r = bus_message_append_strv_key_value(c, details); | |
91 | if (r < 0) | |
92 | return r; | |
93 | ||
94 | r = sd_bus_message_append(c, "us", interactive, NULL); | |
95 | if (r < 0) | |
96 | return r; | |
97 | ||
98 | *ret = TAKE_PTR(c); | |
99 | return 0; | |
100 | } | |
95f82ae9 LP |
101 | #endif |
102 | ||
269e4d2d LP |
103 | int bus_test_polkit( |
104 | sd_bus_message *call, | |
269e4d2d LP |
105 | const char *action, |
106 | const char **details, | |
107 | uid_t good_user, | |
108 | bool *_challenge, | |
773b1a79 | 109 | sd_bus_error *ret_error) { |
269e4d2d LP |
110 | |
111 | int r; | |
112 | ||
113 | assert(call); | |
114 | assert(action); | |
115 | ||
116 | /* Tests non-interactively! */ | |
117 | ||
118 | r = check_good_user(call, good_user); | |
119 | if (r != 0) | |
120 | return r; | |
121 | ||
7b36fb9f | 122 | r = sd_bus_query_sender_privilege(call, -1); |
269e4d2d LP |
123 | if (r < 0) |
124 | return r; | |
f8636446 | 125 | if (r > 0) |
269e4d2d | 126 | return 1; |
269e4d2d | 127 | |
d32ac157 | 128 | #if ENABLE_POLKIT |
4d12d397 | 129 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL, *reply = NULL; |
d32ac157 | 130 | int authorized = false, challenge = false; |
269e4d2d | 131 | |
d32ac157 DT |
132 | r = bus_message_new_polkit_auth_call(call, action, details, /* interactive = */ false, &request); |
133 | if (r < 0) | |
134 | return r; | |
269e4d2d | 135 | |
d32ac157 DT |
136 | r = sd_bus_call(call->bus, request, 0, ret_error, &reply); |
137 | if (r < 0) { | |
138 | /* Treat no PK available as access denied */ | |
139 | if (bus_error_is_unknown_service(ret_error)) { | |
140 | sd_bus_error_free(ret_error); | |
141 | return -EACCES; | |
269e4d2d LP |
142 | } |
143 | ||
d32ac157 DT |
144 | return r; |
145 | } | |
269e4d2d | 146 | |
d32ac157 DT |
147 | r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); |
148 | if (r < 0) | |
149 | return r; | |
150 | ||
151 | r = sd_bus_message_read(reply, "bb", &authorized, &challenge); | |
152 | if (r < 0) | |
153 | return r; | |
269e4d2d | 154 | |
d32ac157 DT |
155 | if (authorized) |
156 | return 1; | |
269e4d2d | 157 | |
d32ac157 DT |
158 | if (_challenge) { |
159 | *_challenge = challenge; | |
160 | return 0; | |
269e4d2d LP |
161 | } |
162 | #endif | |
163 | ||
164 | return -EACCES; | |
165 | } | |
166 | ||
167 | #if ENABLE_POLKIT | |
168 | ||
c71901b2 | 169 | typedef struct AsyncPolkitQueryAction { |
7f569822 LP |
170 | char *action; |
171 | char **details; | |
2f50a4f3 DT |
172 | |
173 | LIST_FIELDS(struct AsyncPolkitQueryAction, authorized); | |
c71901b2 DT |
174 | } AsyncPolkitQueryAction; |
175 | ||
176 | static AsyncPolkitQueryAction *async_polkit_query_action_free(AsyncPolkitQueryAction *a) { | |
177 | if (!a) | |
178 | return NULL; | |
179 | ||
180 | free(a->action); | |
181 | strv_free(a->details); | |
182 | ||
183 | return mfree(a); | |
184 | } | |
185 | ||
959301cf DT |
186 | DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQueryAction*, async_polkit_query_action_free); |
187 | ||
c71901b2 | 188 | typedef struct AsyncPolkitQuery { |
2f50a4f3 DT |
189 | unsigned n_ref; |
190 | ||
c71901b2 | 191 | AsyncPolkitQueryAction *action; |
7f569822 | 192 | |
959301cf | 193 | sd_bus_message *request; |
269e4d2d | 194 | sd_bus_slot *slot; |
63748626 | 195 | |
269e4d2d | 196 | Hashmap *registry; |
63748626 | 197 | sd_event_source *defer_event_source; |
959301cf | 198 | |
2f50a4f3 | 199 | LIST_HEAD(AsyncPolkitQueryAction, authorized_actions); |
959301cf DT |
200 | AsyncPolkitQueryAction *denied_action; |
201 | AsyncPolkitQueryAction *error_action; | |
202 | sd_bus_error error; | |
269e4d2d LP |
203 | } AsyncPolkitQuery; |
204 | ||
bc8187f7 | 205 | static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) { |
269e4d2d | 206 | if (!q) |
bc8187f7 | 207 | return NULL; |
269e4d2d LP |
208 | |
209 | sd_bus_slot_unref(q->slot); | |
210 | ||
211 | if (q->registry && q->request) | |
212 | hashmap_remove(q->registry, q->request); | |
213 | ||
214 | sd_bus_message_unref(q->request); | |
269e4d2d | 215 | |
c71901b2 | 216 | async_polkit_query_action_free(q->action); |
7f569822 | 217 | |
63748626 | 218 | sd_event_source_disable_unref(q->defer_event_source); |
bc8187f7 | 219 | |
9aad490e | 220 | LIST_CLEAR(authorized, q->authorized_actions, async_polkit_query_action_free); |
2f50a4f3 | 221 | |
959301cf DT |
222 | async_polkit_query_action_free(q->denied_action); |
223 | async_polkit_query_action_free(q->error_action); | |
224 | ||
225 | sd_bus_error_free(&q->error); | |
226 | ||
bc8187f7 | 227 | return mfree(q); |
269e4d2d LP |
228 | } |
229 | ||
2f50a4f3 DT |
230 | DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free); |
231 | DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref); | |
10ee1413 | 232 | |
63748626 | 233 | static int async_polkit_defer(sd_event_source *s, void *userdata) { |
5a93e5df | 234 | AsyncPolkitQuery *q = ASSERT_PTR(userdata); |
63748626 LP |
235 | |
236 | assert(s); | |
237 | ||
238 | /* This is called as idle event source after we processed the async polkit reply, hopefully after the | |
239 | * method call we re-enqueued has been properly processed. */ | |
240 | ||
2f50a4f3 | 241 | async_polkit_query_unref(q); |
63748626 LP |
242 | return 0; |
243 | } | |
244 | ||
959301cf DT |
245 | static int async_polkit_read_reply(sd_bus_message *reply, AsyncPolkitQuery *q) { |
246 | _cleanup_(async_polkit_query_action_freep) AsyncPolkitQueryAction *a = NULL; | |
247 | int authorized, challenge, r; | |
248 | ||
249 | assert(reply); | |
250 | assert(q); | |
251 | ||
c1b2e1a8 LP |
252 | /* Processing of a PolicyKit checks is canceled on the first auth. error. */ |
253 | assert(!q->denied_action); | |
254 | assert(!q->error_action); | |
255 | assert(!sd_bus_error_is_set(&q->error)); | |
256 | ||
959301cf DT |
257 | assert(q->action); |
258 | a = TAKE_PTR(q->action); | |
259 | ||
260 | if (sd_bus_message_is_method_error(reply, NULL)) { | |
261 | const sd_bus_error *e; | |
262 | ||
263 | e = sd_bus_message_get_error(reply); | |
264 | ||
45b1c015 DT |
265 | if (bus_error_is_unknown_service(e)) |
266 | /* Treat no PK available as access denied */ | |
267 | q->denied_action = TAKE_PTR(a); | |
268 | else { | |
269 | /* Save error from polkit reply, so it can be returned when the same authorization | |
270 | * is attempted for second time */ | |
959301cf | 271 | q->error_action = TAKE_PTR(a); |
45b1c015 DT |
272 | r = sd_bus_error_copy(&q->error, e); |
273 | if (r == -ENOMEM) | |
274 | return r; | |
959301cf DT |
275 | } |
276 | ||
959301cf DT |
277 | return 0; |
278 | } | |
279 | ||
280 | r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); | |
281 | if (r >= 0) | |
282 | r = sd_bus_message_read(reply, "bb", &authorized, &challenge); | |
283 | if (r < 0) | |
284 | return r; | |
285 | ||
959301cf | 286 | if (authorized) |
2f50a4f3 | 287 | LIST_PREPEND(authorized, q->authorized_actions, TAKE_PTR(a)); |
959301cf DT |
288 | else if (challenge) { |
289 | q->error_action = TAKE_PTR(a); | |
45b1c015 | 290 | sd_bus_error_set_const(&q->error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required."); |
959301cf DT |
291 | } else |
292 | q->denied_action = TAKE_PTR(a); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
0df66e42 | 297 | static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q) { |
269e4d2d LP |
298 | int r; |
299 | ||
300 | assert(reply); | |
0df66e42 | 301 | assert(q); |
269e4d2d | 302 | |
63748626 | 303 | assert(q->slot); |
269e4d2d | 304 | q->slot = sd_bus_slot_unref(q->slot); |
63748626 | 305 | |
959301cf DT |
306 | r = async_polkit_read_reply(reply, q); |
307 | if (r < 0) | |
308 | return r; | |
269e4d2d | 309 | |
63748626 LP |
310 | /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the |
311 | * whole message processing again, and thus re-validating and re-retrieving the "userdata" field | |
312 | * again. | |
313 | * | |
314 | * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again, | |
315 | * i.e. after the second time the message is processed is complete. */ | |
316 | ||
2f50a4f3 DT |
317 | if (!q->defer_event_source) { |
318 | r = sd_event_add_defer( | |
319 | sd_bus_get_event(sd_bus_message_get_bus(reply)), | |
320 | &q->defer_event_source, | |
321 | async_polkit_defer, | |
322 | q); | |
323 | if (r < 0) | |
324 | return r; | |
63748626 | 325 | |
2f50a4f3 DT |
326 | r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE); |
327 | if (r < 0) | |
328 | return r; | |
329 | } | |
63748626 LP |
330 | |
331 | r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT); | |
332 | if (r < 0) | |
0df66e42 | 333 | return r; |
63748626 | 334 | |
269e4d2d | 335 | r = sd_bus_message_rewind(q->request, true); |
63748626 | 336 | if (r < 0) |
0df66e42 | 337 | return r; |
63748626 | 338 | |
bc130b68 | 339 | r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request); |
63748626 | 340 | if (r < 0) |
0df66e42 | 341 | return r; |
269e4d2d | 342 | |
63748626 | 343 | return 1; |
0df66e42 | 344 | } |
269e4d2d | 345 | |
0df66e42 DT |
346 | static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { |
347 | AsyncPolkitQuery *q = ASSERT_PTR(userdata); | |
348 | int r; | |
349 | ||
350 | assert(reply); | |
351 | ||
352 | r = async_polkit_process_reply(reply, q); | |
353 | if (r < 0) { | |
354 | log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m"); | |
355 | (void) sd_bus_reply_method_errno(q->request, r, NULL); | |
2f50a4f3 | 356 | async_polkit_query_unref(q); |
0df66e42 | 357 | } |
269e4d2d LP |
358 | return r; |
359 | } | |
360 | ||
d1e8e8b5 | 361 | static int async_polkit_query_check_action( |
d2c50a17 | 362 | AsyncPolkitQuery *q, |
d2c50a17 DT |
363 | const char *action, |
364 | const char **details, | |
d2c50a17 DT |
365 | sd_bus_error *ret_error) { |
366 | ||
d2c50a17 | 367 | assert(q); |
d2c50a17 | 368 | assert(action); |
d2c50a17 DT |
369 | assert(ret_error); |
370 | ||
2f50a4f3 DT |
371 | LIST_FOREACH(authorized, a, q->authorized_actions) |
372 | if (streq(a->action, action) && strv_equal(a->details, (char**) details)) | |
373 | return 1; | |
d2c50a17 | 374 | |
2f50a4f3 | 375 | if (q->error_action && streq(q->error_action->action, action)) |
959301cf | 376 | return sd_bus_error_copy(ret_error, &q->error); |
d2c50a17 | 377 | |
2f50a4f3 DT |
378 | if (q->denied_action && streq(q->denied_action->action, action)) |
379 | return -EACCES; | |
380 | ||
381 | return 0; | |
d2c50a17 DT |
382 | } |
383 | ||
269e4d2d LP |
384 | #endif |
385 | ||
361c4ee8 DT |
386 | /* bus_verify_polkit_async() handles verification of D-Bus calls with polkit. Because the polkit API |
387 | * is asynchronous, the whole thing is a bit complex and requires some support in the code that uses | |
388 | * it. It relies on sd-bus's support for interrupting the processing of a message. | |
389 | * | |
390 | * Requirements: | |
391 | * | |
392 | * * bus_verify_polkit_async() must be called before any changes to internal state. | |
393 | * * If bus_verify_polkit_async() has made a new polkit query (signaled by return value 0), | |
394 | * processing of the message should be interrupted. This is done by returning 1--which sd-bus | |
395 | * handles specially--and is usually accompanied by a comment. (The message will be queued for | |
396 | * processing again later when a reply from polkit is received.) | |
397 | * * The code needs to keep a hashmap, here called registry, in which bus_verify_polkit_async() | |
398 | * stores active queries. This hashmap's lifetime must be larger than the method handler's; | |
399 | * e.g., it can be a member of some "manager" object or a global variable. | |
400 | * | |
401 | * Return value: | |
402 | * | |
403 | * * 0 - a new polkit call has been made, which means the processing of the message should be | |
404 | * interrupted; | |
405 | * * 1 - the action has been allowed; | |
406 | * * -EACCES - the action has been denied; | |
407 | * * < 0 - an unspecified error. | |
408 | * | |
409 | * A step-by-step description of how it works: | |
410 | * | |
2f50a4f3 DT |
411 | * 1. A D-Bus method handler calls bus_verify_polkit_async(), passing it the D-Bus message being |
412 | * processed and the polkit action to verify. | |
413 | * 2. bus_verify_polkit_async() checks the registry for an existing query object associated with the | |
414 | * message. Let's assume this is the first call, so it finds nothing. | |
415 | * 3. A new AsyncPolkitQuery object is created and an async. D-Bus call to polkit is made. The | |
416 | * function then returns 0. The method handler returns 1 to tell sd-bus that the processing of | |
361c4ee8 | 417 | * the message has been interrupted. |
2f50a4f3 DT |
418 | * 4. (Later) A reply from polkit is received and async_polkit_callback() is called. |
419 | * 5. async_polkit_callback() reads the reply and stores its result in the passed query. | |
420 | * 6. async_polkit_callback() enqueues the original message again. | |
421 | * 7. (Later) The same D-Bus method handler is called for the same message. It calls | |
422 | * bus_verify_polkit_async() again. | |
423 | * 8. bus_verify_polkit_async() checks the registry for an existing query object associated with the | |
424 | * message. It finds one and returns the result for the action. | |
425 | * 9. The method handler continues processing of the message. If there's another action that needs | |
426 | * to be verified: | |
427 | * 10. bus_verify_polkit_async() is called again for the new action. The registry already contains a | |
428 | * query for the message, but the new action hasn't been seen yet, hence steps 4-8 are repeated. | |
429 | * 11. (In the method handler again.) bus_verify_polkit_async() returns query results for both | |
430 | * actions and the processing continues as in step 9. | |
361c4ee8 DT |
431 | * |
432 | * Memory handling: | |
433 | * | |
434 | * async_polkit_callback() registers a deferred call of async_polkit_defer() for the query, which | |
435 | * causes the query to be removed from the registry and freed. Deferred events are run with idle | |
436 | * priority, so this will happen after processing of the D-Bus message, when the query is no longer | |
437 | * needed. | |
438 | * | |
439 | * Schematically: | |
440 | * | |
441 | * (m - D-Bus message, a - polkit action, q - polkit query) | |
442 | * | |
443 | * -> foo_method(m) | |
444 | * -> bus_verify_polkit_async(m, a) | |
2f50a4f3 | 445 | * -> async_polkit_query_ref(q) |
361c4ee8 DT |
446 | * -> bus_call_method_async(q) |
447 | * <- bus_verify_polkit_async(m, a) = 0 | |
448 | * <- foo_method(m) = 1 | |
449 | * ... | |
450 | * -> async_polkit_callback(q) | |
451 | * -> sd_event_add_defer(async_polkit_defer, q) | |
452 | * -> sd_bus_enqueue_for_read(m) | |
453 | * <- async_polkit_callback(q) | |
454 | * ... | |
455 | * -> foo_method(m) | |
456 | * -> bus_verify_polkit_async(m, a) | |
457 | * <- bus_verify_polkit_async(m, a) = 1/-EACCES/error | |
458 | * ... | |
2f50a4f3 | 459 | * // possibly another call to bus_verify_polkit_async with action a2 |
361c4ee8 DT |
460 | * <- foo_method(m) |
461 | * ... | |
462 | * -> async_polkit_defer(q) | |
2f50a4f3 | 463 | * -> async_polkit_query_unref(q) |
361c4ee8 DT |
464 | * <- async_polkit_defer(q) |
465 | */ | |
466 | ||
7b36fb9f | 467 | int bus_verify_polkit_async_full( |
269e4d2d | 468 | sd_bus_message *call, |
269e4d2d LP |
469 | const char *action, |
470 | const char **details, | |
7b36fb9f | 471 | bool interactive, /* Use only for legacy method calls that have a separate "allow_interactive_authentication" field */ |
269e4d2d LP |
472 | uid_t good_user, |
473 | Hashmap **registry, | |
773b1a79 | 474 | sd_bus_error *ret_error) { |
269e4d2d | 475 | |
269e4d2d LP |
476 | int r; |
477 | ||
478 | assert(call); | |
479 | assert(action); | |
480 | assert(registry); | |
b56ee692 | 481 | assert(ret_error); |
269e4d2d LP |
482 | |
483 | r = check_good_user(call, good_user); | |
484 | if (r != 0) | |
485 | return r; | |
486 | ||
487 | #if ENABLE_POLKIT | |
b1ebc201 DT |
488 | _cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL; |
489 | ||
490 | q = async_polkit_query_ref(hashmap_get(*registry, call)); | |
2f50a4f3 DT |
491 | /* This is a repeated invocation of this function, hence let's check if we've already got |
492 | * a response from polkit for this action */ | |
493 | if (q) { | |
494 | r = async_polkit_query_check_action(q, action, details, ret_error); | |
495 | if (r != 0) | |
496 | return r; | |
497 | } | |
269e4d2d LP |
498 | #endif |
499 | ||
7b36fb9f | 500 | r = sd_bus_query_sender_privilege(call, -1); |
269e4d2d LP |
501 | if (r < 0) |
502 | return r; | |
f8636446 | 503 | if (r > 0) |
269e4d2d LP |
504 | return 1; |
505 | ||
63748626 | 506 | #if ENABLE_POLKIT |
d5a99b7c JJ |
507 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL; |
508 | ||
509 | int c = sd_bus_message_get_allow_interactive_authorization(call); | |
269e4d2d LP |
510 | if (c < 0) |
511 | return c; | |
512 | if (c > 0) | |
513 | interactive = true; | |
514 | ||
515 | r = hashmap_ensure_allocated(registry, NULL); | |
516 | if (r < 0) | |
517 | return r; | |
518 | ||
9f657af4 | 519 | r = bus_message_new_polkit_auth_call(call, action, details, interactive, &pk); |
269e4d2d LP |
520 | if (r < 0) |
521 | return r; | |
522 | ||
2f50a4f3 | 523 | if (!q) { |
b1ebc201 | 524 | q = new(AsyncPolkitQuery, 1); |
2f50a4f3 DT |
525 | if (!q) |
526 | return -ENOMEM; | |
269e4d2d | 527 | |
2f50a4f3 DT |
528 | *q = (AsyncPolkitQuery) { |
529 | .n_ref = 1, | |
530 | .request = sd_bus_message_ref(call), | |
531 | }; | |
b1ebc201 | 532 | } |
269e4d2d | 533 | |
2f50a4f3 | 534 | assert(!q->action); |
c71901b2 | 535 | q->action = new(AsyncPolkitQueryAction, 1); |
10ee1413 | 536 | if (!q->action) |
7f569822 | 537 | return -ENOMEM; |
7f569822 | 538 | |
c71901b2 DT |
539 | *q->action = (AsyncPolkitQueryAction) { |
540 | .action = strdup(action), | |
541 | .details = strv_copy((char**) details), | |
542 | }; | |
543 | if (!q->action->action || !q->action->details) | |
7f569822 | 544 | return -ENOMEM; |
7f569822 | 545 | |
2f50a4f3 DT |
546 | if (!q->registry) { |
547 | r = hashmap_put(*registry, call, q); | |
548 | if (r < 0) | |
549 | return r; | |
269e4d2d | 550 | |
2f50a4f3 DT |
551 | q->registry = *registry; |
552 | } | |
269e4d2d LP |
553 | |
554 | r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0); | |
10ee1413 | 555 | if (r < 0) |
269e4d2d | 556 | return r; |
10ee1413 | 557 | |
b1ebc201 | 558 | TAKE_PTR(q); |
269e4d2d LP |
559 | |
560 | return 0; | |
561 | #endif | |
562 | ||
563 | return -EACCES; | |
564 | } | |
565 | ||
3c2f8472 | 566 | Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) { |
269e4d2d | 567 | #if ENABLE_POLKIT |
2f50a4f3 | 568 | return hashmap_free_with_destructor(registry, async_polkit_query_unref); |
3c2f8472 YW |
569 | #else |
570 | assert(hashmap_isempty(registry)); | |
571 | return hashmap_free(registry); | |
269e4d2d LP |
572 | #endif |
573 | } |