]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-polkit.c
bus-polkit: decrease indentation
[thirdparty/systemd.git] / src / shared / bus-polkit.c
CommitLineData
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
10static 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
LP
34#if ENABLE_POLKIT
35static int bus_message_append_strv_key_value(
36 sd_bus_message *m,
37 const char **l) {
38
95f82ae9
LP
39 int r;
40
41 assert(m);
42
43 r = sd_bus_message_open_container(m, 'a', "{ss}");
44 if (r < 0)
45 return r;
46
47 STRV_FOREACH_PAIR(k, v, l) {
48 r = sd_bus_message_append(m, "{ss}", *k, *v);
49 if (r < 0)
50 return r;
51 }
52
53 r = sd_bus_message_close_container(m);
54 if (r < 0)
55 return r;
56
57 return r;
58}
9f657af4
DT
59
60static int bus_message_new_polkit_auth_call(
61 sd_bus_message *m,
62 const char *action,
63 const char **details,
64 bool interactive,
65 sd_bus_message **ret) {
66
67 _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
68 const char *sender;
69 int r;
70
71 assert(m);
72 assert(action);
73 assert(ret);
74
75 sender = sd_bus_message_get_sender(m);
76 if (!sender)
77 return -EBADMSG;
78
79 r = sd_bus_message_new_method_call(
80 ASSERT_PTR(m->bus),
81 &c,
82 "org.freedesktop.PolicyKit1",
83 "/org/freedesktop/PolicyKit1/Authority",
84 "org.freedesktop.PolicyKit1.Authority",
85 "CheckAuthorization");
86 if (r < 0)
87 return r;
88
89 r = sd_bus_message_append(c, "(sa{sv})s", "system-bus-name", 1, "name", "s", sender, action);
90 if (r < 0)
91 return r;
92
93 r = bus_message_append_strv_key_value(c, details);
94 if (r < 0)
95 return r;
96
97 r = sd_bus_message_append(c, "us", interactive, NULL);
98 if (r < 0)
99 return r;
100
101 *ret = TAKE_PTR(c);
102 return 0;
103}
95f82ae9
LP
104#endif
105
269e4d2d
LP
106int bus_test_polkit(
107 sd_bus_message *call,
108 int capability,
109 const char *action,
110 const char **details,
111 uid_t good_user,
112 bool *_challenge,
773b1a79 113 sd_bus_error *ret_error) {
269e4d2d
LP
114
115 int r;
116
117 assert(call);
118 assert(action);
119
120 /* Tests non-interactively! */
121
122 r = check_good_user(call, good_user);
123 if (r != 0)
124 return r;
125
126 r = sd_bus_query_sender_privilege(call, capability);
127 if (r < 0)
128 return r;
129 else if (r > 0)
130 return 1;
269e4d2d 131
d32ac157
DT
132#if ENABLE_POLKIT
133 _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
134 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
135 int authorized = false, challenge = false;
269e4d2d 136
d32ac157
DT
137 r = bus_message_new_polkit_auth_call(call, action, details, /* interactive = */ false, &request);
138 if (r < 0)
139 return r;
269e4d2d 140
d32ac157
DT
141 r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
142 if (r < 0) {
143 /* Treat no PK available as access denied */
144 if (bus_error_is_unknown_service(ret_error)) {
145 sd_bus_error_free(ret_error);
146 return -EACCES;
269e4d2d
LP
147 }
148
d32ac157
DT
149 return r;
150 }
269e4d2d 151
d32ac157
DT
152 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
153 if (r < 0)
154 return r;
155
156 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
157 if (r < 0)
158 return r;
269e4d2d 159
d32ac157
DT
160 if (authorized)
161 return 1;
269e4d2d 162
d32ac157
DT
163 if (_challenge) {
164 *_challenge = challenge;
165 return 0;
269e4d2d
LP
166 }
167#endif
168
169 return -EACCES;
170}
171
172#if ENABLE_POLKIT
173
174typedef struct AsyncPolkitQuery {
7f569822
LP
175 char *action;
176 char **details;
177
269e4d2d 178 sd_bus_message *request, *reply;
269e4d2d 179 sd_bus_slot *slot;
63748626 180
269e4d2d 181 Hashmap *registry;
63748626 182 sd_event_source *defer_event_source;
269e4d2d
LP
183} AsyncPolkitQuery;
184
bc8187f7 185static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
269e4d2d 186 if (!q)
bc8187f7 187 return NULL;
269e4d2d
LP
188
189 sd_bus_slot_unref(q->slot);
190
191 if (q->registry && q->request)
192 hashmap_remove(q->registry, q->request);
193
194 sd_bus_message_unref(q->request);
195 sd_bus_message_unref(q->reply);
196
7f569822
LP
197 free(q->action);
198 strv_free(q->details);
199
63748626 200 sd_event_source_disable_unref(q->defer_event_source);
bc8187f7
DT
201
202 return mfree(q);
269e4d2d
LP
203}
204
63748626 205static int async_polkit_defer(sd_event_source *s, void *userdata) {
5a93e5df 206 AsyncPolkitQuery *q = ASSERT_PTR(userdata);
63748626
LP
207
208 assert(s);
209
210 /* This is called as idle event source after we processed the async polkit reply, hopefully after the
211 * method call we re-enqueued has been properly processed. */
212
213 async_polkit_query_free(q);
214 return 0;
215}
216
269e4d2d 217static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
99534007 218 AsyncPolkitQuery *q = ASSERT_PTR(userdata);
269e4d2d
LP
219 int r;
220
221 assert(reply);
269e4d2d 222
63748626 223 assert(q->slot);
269e4d2d 224 q->slot = sd_bus_slot_unref(q->slot);
63748626
LP
225
226 assert(!q->reply);
269e4d2d
LP
227 q->reply = sd_bus_message_ref(reply);
228
63748626
LP
229 /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
230 * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
231 * again.
232 *
233 * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
234 * i.e. after the second time the message is processed is complete. */
235
236 assert(!q->defer_event_source);
237 r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
238 if (r < 0)
239 goto fail;
240
241 r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
242 if (r < 0)
243 goto fail;
244
245 r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
246 if (r < 0)
247 goto fail;
248
269e4d2d 249 r = sd_bus_message_rewind(q->request, true);
63748626
LP
250 if (r < 0)
251 goto fail;
252
bc130b68 253 r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
63748626
LP
254 if (r < 0)
255 goto fail;
269e4d2d 256
63748626 257 return 1;
269e4d2d 258
63748626
LP
259fail:
260 log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
261 (void) sd_bus_reply_method_errno(q->request, r, NULL);
269e4d2d 262 async_polkit_query_free(q);
269e4d2d
LP
263 return r;
264}
265
d2c50a17
DT
266static int process_polkit_response(
267 AsyncPolkitQuery *q,
268 sd_bus_message *call,
269 const char *action,
270 const char **details,
271 Hashmap **registry,
272 sd_bus_error *ret_error) {
273
274 int authorized, challenge, r;
275
276 assert(q);
277 assert(call);
278 assert(action);
279 assert(registry);
280 assert(ret_error);
281
282 assert(q->action);
283 assert(q->reply);
284
285 /* If the operation we want to authenticate changed between the first and the second time,
286 * let's not use this authentication, it might be out of date as the object and context we
287 * operate on might have changed. */
288 if (!streq(q->action, action) || !strv_equal(q->details, (char**) details))
289 return -ESTALE;
290
291 if (sd_bus_message_is_method_error(q->reply, NULL)) {
292 const sd_bus_error *e;
293
294 e = sd_bus_message_get_error(q->reply);
295
296 /* Treat no PK available as access denied */
297 if (bus_error_is_unknown_service(e))
298 return -EACCES;
299
300 /* Copy error from polkit reply */
301 sd_bus_error_copy(ret_error, e);
302 return -sd_bus_error_get_errno(e);
303 }
304
305 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
306 if (r >= 0)
307 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
308 if (r < 0)
309 return r;
310
311 if (authorized)
312 return 1;
313
314 if (challenge)
315 return sd_bus_error_set(ret_error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
316
317 return -EACCES;
318}
319
269e4d2d
LP
320#endif
321
322int bus_verify_polkit_async(
323 sd_bus_message *call,
324 int capability,
325 const char *action,
326 const char **details,
327 bool interactive,
328 uid_t good_user,
329 Hashmap **registry,
773b1a79 330 sd_bus_error *ret_error) {
269e4d2d 331
269e4d2d
LP
332 int r;
333
334 assert(call);
335 assert(action);
336 assert(registry);
337
338 r = check_good_user(call, good_user);
339 if (r != 0)
340 return r;
341
342#if ENABLE_POLKIT
d5a99b7c 343 AsyncPolkitQuery *q = hashmap_get(*registry, call);
d2c50a17
DT
344 /* This is the second invocation of this function, and there's already a response from
345 * polkit, let's process it */
346 if (q)
347 return process_polkit_response(q, call, action, details, registry, ret_error);
269e4d2d
LP
348#endif
349
350 r = sd_bus_query_sender_privilege(call, capability);
351 if (r < 0)
352 return r;
353 else if (r > 0)
354 return 1;
355
63748626 356#if ENABLE_POLKIT
d5a99b7c
JJ
357 _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
358
359 int c = sd_bus_message_get_allow_interactive_authorization(call);
269e4d2d
LP
360 if (c < 0)
361 return c;
362 if (c > 0)
363 interactive = true;
364
365 r = hashmap_ensure_allocated(registry, NULL);
366 if (r < 0)
367 return r;
368
9f657af4 369 r = bus_message_new_polkit_auth_call(call, action, details, interactive, &pk);
269e4d2d
LP
370 if (r < 0)
371 return r;
372
f4425c72 373 q = new(AsyncPolkitQuery, 1);
269e4d2d
LP
374 if (!q)
375 return -ENOMEM;
376
f4425c72
LP
377 *q = (AsyncPolkitQuery) {
378 .request = sd_bus_message_ref(call),
f4425c72 379 };
269e4d2d 380
7f569822
LP
381 q->action = strdup(action);
382 if (!q->action) {
383 async_polkit_query_free(q);
384 return -ENOMEM;
385 }
386
387 q->details = strv_copy((char**) details);
388 if (!q->details) {
389 async_polkit_query_free(q);
390 return -ENOMEM;
391 }
392
269e4d2d
LP
393 r = hashmap_put(*registry, call, q);
394 if (r < 0) {
395 async_polkit_query_free(q);
396 return r;
397 }
398
399 q->registry = *registry;
400
401 r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
402 if (r < 0) {
403 async_polkit_query_free(q);
404 return r;
405 }
406
407 return 0;
408#endif
409
410 return -EACCES;
411}
412
3c2f8472 413Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
269e4d2d 414#if ENABLE_POLKIT
3c2f8472
YW
415 return hashmap_free_with_destructor(registry, async_polkit_query_free);
416#else
417 assert(hashmap_isempty(registry));
418 return hashmap_free(registry);
269e4d2d
LP
419#endif
420}