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