• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14013585018

22 Mar 2025 03:54PM UTC coverage: 71.951% (+0.002%) from 71.949%
14013585018

push

github

web-flow
some dbus property fixes (#36830)

4 of 4 new or added lines in 1 file covered. (100.0%)

145 existing lines in 35 files now uncovered.

296620 of 412255 relevant lines covered (71.95%)

737629.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.75
/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 "process-util.h"
8
#include "strv.h"
9
#include "user-util.h"
10
#include "varlink-util.h"
11

12
static int bus_message_check_good_user(sd_bus_message *m, uid_t good_user) {
4,165✔
13
        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
4,165✔
14
        uid_t sender_uid;
4,165✔
15
        int r;
4,165✔
16

17
        assert(m);
4,165✔
18

19
        if (good_user == UID_INVALID)
4,165✔
20
                return false;
21

22
        r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
12✔
23
        if (r < 0)
12✔
24
                return r;
25

26
        /* Don't trust augmented credentials for authorization */
27
        assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
12✔
28

29
        r = sd_bus_creds_get_euid(creds, &sender_uid);
12✔
30
        if (r < 0)
12✔
31
                return r;
32

33
        return sender_uid == good_user;
12✔
34
}
35

36
#if ENABLE_POLKIT
37
static int bus_message_append_strv_key_value(sd_bus_message *m, const char **l) {
112✔
38
        int r;
112✔
39

40
        assert(m);
112✔
41

42
        r = sd_bus_message_open_container(m, 'a', "{ss}");
112✔
43
        if (r < 0)
112✔
44
                return r;
45

46
        STRV_FOREACH_PAIR(k, v, l) {
118✔
47
                r = sd_bus_message_append(m, "{ss}", *k, *v);
6✔
48
                if (r < 0)
6✔
49
                        return r;
50
        }
51

52
        return sd_bus_message_close_container(m);
112✔
53
}
54

55
static int bus_message_new_polkit_auth_call_for_bus(
105✔
56
                sd_bus_message *m,
57
                const char *action,
58
                const char **details,
59
                PolkitFlags flags,
60
                sd_bus_message **ret) {
61

62
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
105✔
63
        const char *sender;
105✔
64
        int r;
105✔
65

66
        assert(m);
105✔
67
        assert(action);
105✔
68
        assert(ret);
105✔
69

70
        sender = sd_bus_message_get_sender(m);
105✔
71
        if (!sender)
105✔
72
                return -EBADMSG;
73

74
        r = sd_bus_message_new_method_call(
315✔
75
                        ASSERT_PTR(m->bus),
105✔
76
                        &c,
77
                        "org.freedesktop.PolicyKit1",
78
                        "/org/freedesktop/PolicyKit1/Authority",
79
                        "org.freedesktop.PolicyKit1.Authority",
80
                        "CheckAuthorization");
81
        if (r < 0)
105✔
82
                return r;
83

84
        r = sd_bus_message_append(c, "(sa{sv})s", "system-bus-name", 1, "name", "s", sender, action);
105✔
85
        if (r < 0)
105✔
86
                return r;
87

88
        r = bus_message_append_strv_key_value(c, details);
105✔
89
        if (r < 0)
105✔
90
                return r;
91

92
        r = sd_bus_message_append(c, "us", (uint32_t) (flags & _POLKIT_MASK_PUBLIC), NULL);
105✔
93
        if (r < 0)
105✔
94
                return r;
95

96
        *ret = TAKE_PTR(c);
105✔
97
        return 0;
105✔
98
}
99
#endif
100

101
int bus_test_polkit(
×
102
                sd_bus_message *call,
103
                const char *action,
104
                const char **details,
105
                uid_t good_user,
106
                bool *_challenge,
107
                sd_bus_error *ret_error) {
108

109
        int r;
×
110

111
        assert(call);
×
112
        assert(action);
×
113

114
        /* Tests non-interactively! */
115

116
        r = bus_message_check_good_user(call, good_user);
×
117
        if (r != 0)
×
118
                return r;
×
119

120
        r = sd_bus_query_sender_privilege(call, -1);
×
121
        if (r < 0)
×
122
                return r;
123
        if (r > 0)
×
124
                return 1;
125

126
#if ENABLE_POLKIT
127
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL, *reply = NULL;
×
128
        int authorized = false, challenge = false;
×
129

130
        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, /* interactive = */ false, &request);
×
131
        if (r < 0)
×
132
                return r;
133

134
        r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
×
135
        if (r < 0) {
×
136
                /* Treat no PK available as access denied */
137
                if (bus_error_is_unknown_service(ret_error)) {
×
138
                        sd_bus_error_free(ret_error);
×
139
                        return -EACCES;
140
                }
141

142
                return r;
143
        }
144

145
        r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
×
146
        if (r < 0)
×
147
                return r;
148

149
        r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
×
150
        if (r < 0)
×
151
                return r;
152

153
        if (authorized)
×
154
                return 1;
155

156
        if (_challenge) {
×
157
                *_challenge = challenge;
×
158
                return 0;
×
159
        }
160
#endif
161

162
        return -EACCES;
163
}
164

165
#if ENABLE_POLKIT
166

167
typedef struct AsyncPolkitQueryAction {
168
        char *action;
169
        char **details;
170

171
        LIST_FIELDS(struct AsyncPolkitQueryAction, authorized);
172
} AsyncPolkitQueryAction;
173

174
static AsyncPolkitQueryAction *async_polkit_query_action_free(AsyncPolkitQueryAction *a) {
557✔
175
        if (!a)
557✔
176
                return NULL;
177

178
        free(a->action);
112✔
179
        strv_free(a->details);
112✔
180

181
        return mfree(a);
112✔
182
}
183

184
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQueryAction*, async_polkit_query_action_free);
112✔
185

186
typedef struct AsyncPolkitQuery {
187
        unsigned n_ref;
188

189
        AsyncPolkitQueryAction *action; /* action currently being processed */
190

191
        sd_bus *bus;
192
        sd_bus_message *request;        /* the original bus method call that triggered the polkit auth, NULL in case of varlink */
193
        sd_bus_slot *slot;
194
        sd_varlink *link;               /* the original varlink method call that triggered the polkit auth, NULL in case of bus */
195

196
        Hashmap *registry;
197
        sd_event_source *defer_event_source;
198

199
        LIST_HEAD(AsyncPolkitQueryAction, authorized_actions);  /* actions we successfully were authorized for */
200
        AsyncPolkitQueryAction *denied_action;                  /* if we received denial for an action, it's this one */
201
        AsyncPolkitQueryAction *absent_action;                  /* If polkit was absent for some action, it's this one */
202
        AsyncPolkitQueryAction *error_action;                   /* if we encountered any other error, it's this one */
203
        sd_bus_error error;                                     /* the precise error, in case error_action is set */
204
} AsyncPolkitQuery;
205

206
static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
112✔
207
        if (!q)
112✔
208
                return NULL;
209

210
        sd_bus_slot_unref(q->slot);
112✔
211

212
        if (q->registry) {
112✔
213
                if (q->request)
112✔
214
                        hashmap_remove(q->registry, q->request);
105✔
215
                if (q->link)
112✔
216
                        hashmap_remove(q->registry, q->link);
7✔
217
        }
218

219
        sd_bus_message_unref(q->request);
112✔
220

221
        sd_bus_unref(q->bus);
112✔
222
        sd_varlink_unref(q->link);
112✔
223

224
        async_polkit_query_action_free(q->action);
112✔
225

226
        sd_event_source_disable_unref(q->defer_event_source);
112✔
227

228
        LIST_CLEAR(authorized, q->authorized_actions, async_polkit_query_action_free);
221✔
229

230
        async_polkit_query_action_free(q->denied_action);
112✔
231
        async_polkit_query_action_free(q->absent_action);
112✔
232
        async_polkit_query_action_free(q->error_action);
112✔
233

234
        sd_bus_error_free(&q->error);
112✔
235

236
        return mfree(q);
112✔
237
}
238

239
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free);
4,401✔
240
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref);
4,177✔
241

UNCOV
242
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
×
243
                async_polkit_query_hash_ops,
244
                void,
245
                trivial_hash_func,
246
                trivial_compare_func,
247
                AsyncPolkitQuery,
248
                async_polkit_query_unref);
249

250
static int async_polkit_defer(sd_event_source *s, void *userdata) {
112✔
251
        AsyncPolkitQuery *q = ASSERT_PTR(userdata);
112✔
252

253
        assert(s);
112✔
254

255
        /* This is called as idle event source after we processed the async polkit reply, hopefully after the
256
         * method call we re-enqueued has been properly processed. */
257

258
        async_polkit_query_unref(q);
112✔
259
        return 0;
112✔
260
}
261

262
static int async_polkit_read_reply(sd_bus_message *reply, AsyncPolkitQuery *q) {
112✔
263
        _cleanup_(async_polkit_query_action_freep) AsyncPolkitQueryAction *a = NULL;
112✔
264
        int authorized, challenge, r;
112✔
265

266
        assert(reply);
112✔
267
        assert(q);
112✔
268

269
        /* Processing of a PolicyKit checks is canceled on the first auth. error. */
270
        assert(!q->denied_action);
112✔
271
        assert(!q->absent_action);
112✔
272
        assert(!q->error_action);
112✔
273
        assert(!sd_bus_error_is_set(&q->error));
112✔
274

275
        a = ASSERT_PTR(TAKE_PTR(q->action));
112✔
276

277
        if (sd_bus_message_is_method_error(reply, NULL)) {
112✔
278
                const sd_bus_error *e;
×
279

280
                e = sd_bus_message_get_error(reply);
×
281

282
                if (bus_error_is_unknown_service(e)) {
×
283
                        /* If PK is absent, then store this away, as it depends on the callers flags whether
284
                         * this means deny or allow */
285
                        log_debug("Polkit found to be unavailable while trying to authorize action '%s'.", a->action);
×
286
                        q->absent_action = TAKE_PTR(a);
×
287
                } else if (sd_bus_error_has_names(
×
288
                                           e,
289
                                           "org.freedesktop.PolicyKit1.Error.Failed",
290
                                           "org.freedesktop.PolicyKit1.Error.Cancelled",
291
                                           "org.freedesktop.PolicyKit1.Error.NotAuthorized")) {
292
                        /* Treat some of the well-known PK errors as denial. */
293
                        log_debug("Polkit authorization for action '%s' failed with an polkit error: %s", a->action, e->name);
×
294
                        q->denied_action = TAKE_PTR(a);
×
295
                } else {
296
                        /* Save error from polkit reply, so it can be returned when the same authorization
297
                         * is attempted for second time */
298
                        log_debug("Polkit authorization for action '%s' failed with an unexpected error: %s", a->action, e->name);
×
299
                        q->error_action = TAKE_PTR(a);
×
300
                        r = sd_bus_error_copy(&q->error, e);
×
301
                        if (r == -ENOMEM)
×
302
                                return r;
303
                }
304

305
                return 0;
×
306
        }
307

308
        r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
112✔
309
        if (r >= 0)
112✔
310
                r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
112✔
311
        if (r < 0)
112✔
312
                return r;
×
313

314
        if (authorized) {
112✔
315
                log_debug("Polkit authorization for action '%s' succeeded.", a->action);
109✔
316
                LIST_PREPEND(authorized, q->authorized_actions, TAKE_PTR(a));
109✔
317
        } else if (challenge) {
3✔
318
                log_debug("Polkit authorization for action requires '%s' interactive authentication, which we didn't allow.", a->action);
3✔
319
                q->error_action = TAKE_PTR(a);
3✔
320
                sd_bus_error_set_const(&q->error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
3✔
321
        } else {
322
                log_debug("Polkit authorization for action '%s' denied.", a->action);
×
323
                q->denied_action = TAKE_PTR(a);
×
324
        }
325

326
        return 0;
327
}
328

329
static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q) {
112✔
330
        int r;
112✔
331

332
        assert(reply);
112✔
333
        assert(q);
112✔
334

335
        assert(q->slot);
112✔
336
        q->slot = sd_bus_slot_unref(q->slot);
112✔
337

338
        r = async_polkit_read_reply(reply, q);
112✔
339
        if (r < 0)
112✔
340
                return r;
341

342
        /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
343
         * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
344
         * again.
345
         *
346
         * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
347
         * i.e. after the second time the message is processed is complete. */
348

349
        if (!q->defer_event_source) {
112✔
350
                r = sd_event_add_defer(
112✔
351
                                sd_bus_get_event(q->bus),
352
                                &q->defer_event_source,
353
                                async_polkit_defer,
354
                                q);
355
                if (r < 0)
112✔
356
                        return r;
357

358
                r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
112✔
359
                if (r < 0)
112✔
360
                        return r;
361
        }
362

363
        r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
112✔
364
        if (r < 0)
112✔
365
                return r;
366

367
        if (q->request) {
112✔
368
                r = sd_bus_message_rewind(q->request, true);
105✔
369
                if (r < 0)
105✔
370
                        return r;
371

372
                r = sd_bus_enqueue_for_read(q->bus, q->request);
105✔
373
                if (r < 0)
105✔
374
                        return r;
375
        }
376

377
        if (q->link) {
112✔
378
                r = sd_varlink_dispatch_again(q->link);
7✔
379
                if (r < 0)
7✔
380
                        return r;
×
381
        }
382

383
        return 1;
384
}
385

386
static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
112✔
387
        AsyncPolkitQuery *q = ASSERT_PTR(userdata);
112✔
388
        int r;
112✔
389

390
        assert(reply);
112✔
391

392
        r = async_polkit_process_reply(reply, q);
112✔
393
        if (r < 0) {
112✔
394
                log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
×
395
                if (q->request)
×
396
                        (void) sd_bus_reply_method_errno(q->request, r, NULL);
×
397
                if (q->link)
×
398
                        (void) sd_varlink_error_errno(q->link, r);
×
399
                async_polkit_query_unref(q);
×
400
        }
401
        return r;
112✔
402
}
403

404
static bool async_polkit_query_have_action(
112✔
405
                AsyncPolkitQuery *q,
406
                const char *action,
407
                const char **details) {
408

409
        assert(q);
112✔
410
        assert(action);
112✔
411

412
        LIST_FOREACH(authorized, a, q->authorized_actions)
112✔
413
                if (streq(a->action, action) && strv_equal(a->details, (char**) details))
109✔
414
                        return true;
415

416
        return false;
417
}
418

419
static int async_polkit_query_check_action(
112✔
420
                AsyncPolkitQuery *q,
421
                const char *action,
422
                const char **details,
423
                PolkitFlags flags,
424
                sd_bus_error *ret_error) {
425

426
        assert(q);
112✔
427
        assert(action);
112✔
428

429
        if (async_polkit_query_have_action(q, action, details))
112✔
430
                return 1; /* Allow! */
431

432
        if (q->error_action && streq(q->error_action->action, action))
3✔
433
                return sd_bus_error_copy(ret_error, &q->error);
3✔
434

435
        if (q->denied_action && streq(q->denied_action->action, action))
×
436
                return -EACCES; /* Deny! */
437

438
        if (q->absent_action)
×
439
                return FLAGS_SET(flags, POLKIT_DEFAULT_ALLOW) ? 1 /* Allow! */ : -EACCES /* Deny! */;
×
440

441
        /* Also deny if we've got an auth. failure for a previous action */
442
        if (q->denied_action || q->error_action)
×
443
                return -EALREADY;
×
444

445
        return 0; /* no reply yet */
446
}
447
#endif
448

449
/* bus_verify_polkit_async() handles verification of D-Bus calls with polkit. Because the polkit API
450
 * is asynchronous, the whole thing is a bit complex and requires some support in the code that uses
451
 * it. It relies on sd-bus's support for interrupting the processing of a message.
452
 *
453
 * Requirements:
454
 *
455
 * * bus_verify_polkit_async() must be called before any changes to internal state.
456
 * * If bus_verify_polkit_async() has made a new polkit query (signaled by return value 0),
457
 *   processing of the message should be interrupted. This is done by returning 1--which sd-bus
458
 *   handles specially--and is usually accompanied by a comment. (The message will be queued for
459
 *   processing again later when a reply from polkit is received.)
460
 * * The code needs to keep a hashmap, here called registry, in which bus_verify_polkit_async()
461
 *   stores active queries. This hashmap's lifetime must be larger than the method handler's;
462
 *   e.g., it can be a member of some "manager" object or a global variable.
463
 *
464
 * Return value:
465
 *
466
 * * 0 - a new polkit call has been made, which means the processing of the message should be
467
 *   interrupted;
468
 * * 1 - the action has been allowed;
469
 * * -EACCES - the action has been denied;
470
 * * < 0 - an unspecified error.
471
 *
472
 * A step-by-step description of how it works:
473
 *
474
 * 1.  A D-Bus method handler calls bus_verify_polkit_async(), passing it the D-Bus message being
475
 *     processed and the polkit action to verify.
476
 * 2.  bus_verify_polkit_async() checks the registry for an existing query object associated with the
477
 *     message. Let's assume this is the first call, so it finds nothing.
478
 * 3.  A new AsyncPolkitQuery object is created and an async. D-Bus call to polkit is made. The
479
 *     function then returns 0. The method handler returns 1 to tell sd-bus that the processing of
480
 *    the message has been interrupted.
481
 * 4.  (Later) A reply from polkit is received and async_polkit_callback() is called.
482
 * 5.  async_polkit_callback() reads the reply and stores its result in the passed query.
483
 * 6.  async_polkit_callback() enqueues the original message again.
484
 * 7.  (Later) The same D-Bus method handler is called for the same message. It calls
485
 *     bus_verify_polkit_async() again.
486
 * 8.  bus_verify_polkit_async() checks the registry for an existing query object associated with the
487
 *     message. It finds one and returns the result for the action.
488
 * 9.  The method handler continues processing of the message. If there's another action that needs
489
 *     to be verified:
490
 * 10. bus_verify_polkit_async() is called again for the new action. The registry already contains a
491
 *     query for the message, but the new action hasn't been seen yet, hence steps 4-8 are repeated.
492
 * 11. (In the method handler again.) bus_verify_polkit_async() returns query results for both
493
 *     actions and the processing continues as in step 9.
494
 *
495
 * Memory handling:
496
 *
497
 * async_polkit_callback() registers a deferred call of async_polkit_defer() for the query, which
498
 * causes the query to be removed from the registry and freed. Deferred events are run with idle
499
 * priority, so this will happen after processing of the D-Bus message, when the query is no longer
500
 * needed.
501
 *
502
 * Schematically:
503
 *
504
 * (m - D-Bus message, a - polkit action, q - polkit query)
505
 *
506
 * -> foo_method(m)
507
 *    -> bus_verify_polkit_async(m, a)
508
 *       -> async_polkit_query_ref(q)
509
 *       -> bus_call_method_async(q)
510
 *    <- bus_verify_polkit_async(m, a) = 0
511
 * <- foo_method(m) = 1
512
 * ...
513
 * -> async_polkit_callback(q)
514
 *    -> sd_event_add_defer(async_polkit_defer, q)
515
 *    -> sd_bus_enqueue_for_read(m)
516
 * <- async_polkit_callback(q)
517
 * ...
518
 * -> foo_method(m)
519
 *    -> bus_verify_polkit_async(m, a)
520
 *    <- bus_verify_polkit_async(m, a) = 1/-EACCES/error
521
 *    ...
522
 *    // possibly another call to bus_verify_polkit_async with action a2
523
 * <- foo_method(m)
524
 * ...
525
 * -> async_polkit_defer(q)
526
 *    -> async_polkit_query_unref(q)
527
 * <- async_polkit_defer(q)
528
 */
529

530
int bus_verify_polkit_async_full(
4,165✔
531
                sd_bus_message *call,
532
                const char *action,
533
                const char **details,
534
                uid_t good_user,
535
                PolkitFlags flags,
536
                Hashmap **registry,
537
                sd_bus_error *error) {
538

539
        int r;
4,165✔
540

541
        assert(call);
4,165✔
542
        assert(action);
4,165✔
543
        assert(registry);
4,165✔
544

545
        log_debug("Trying to acquire polkit authentication for '%s'.", action);
4,165✔
546

547
        r = bus_message_check_good_user(call, good_user);
4,165✔
548
        if (r != 0)
4,165✔
549
                return r;
4,165✔
550

551
#if ENABLE_POLKIT
552
        _cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL;
×
553

554
        q = async_polkit_query_ref(hashmap_get(*registry, call));
4,163✔
555
        /* This is a repeated invocation of this function, hence let's check if we've already got
556
         * a response from polkit for this action */
557
        if (q) {
4,163✔
558
                r = async_polkit_query_check_action(q, action, details, flags, error);
105✔
559
                if (r != 0) {
105✔
560
                        log_debug("Found matching previous polkit authentication for '%s'.", action);
105✔
561
                        return r;
105✔
562
                }
563
        }
564
#endif
565

566
        if (!FLAGS_SET(flags, POLKIT_ALWAYS_QUERY)) {
4,058✔
567
                /* Don't query PK if client is privileged */
568
                r = sd_bus_query_sender_privilege(call, /* capability= */ -1);
4,058✔
569
                if (r < 0)
4,058✔
570
                        return r;
571
                if (r > 0)
4,058✔
572
                        return 1;
573
        }
574

575
#if ENABLE_POLKIT
576
        int c = sd_bus_message_get_allow_interactive_authorization(call);
105✔
577
        if (c < 0)
105✔
578
                return c;
579
        if (c > 0)
105✔
580
                flags |= POLKIT_ALLOW_INTERACTIVE;
×
581

582
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
4,163✔
583
        r = bus_message_new_polkit_auth_call_for_bus(call, action, details, flags, &pk);
105✔
584
        if (r < 0)
105✔
585
                return r;
586

587
        if (!q) {
105✔
588
                q = new(AsyncPolkitQuery, 1);
105✔
589
                if (!q)
105✔
590
                        return -ENOMEM;
591

592
                *q = (AsyncPolkitQuery) {
105✔
593
                        .n_ref = 1,
594
                        .request = sd_bus_message_ref(call),
105✔
595
                        .bus = sd_bus_ref(sd_bus_message_get_bus(call)),
105✔
596
                };
597
        }
598

599
        assert(!q->action);
105✔
600
        q->action = new(AsyncPolkitQueryAction, 1);
105✔
601
        if (!q->action)
105✔
602
                return -ENOMEM;
603

604
        *q->action = (AsyncPolkitQueryAction) {
210✔
605
                .action = strdup(action),
105✔
606
                .details = strv_copy((char**) details),
105✔
607
        };
608
        if (!q->action->action || !q->action->details)
105✔
609
                return -ENOMEM;
610

611
        if (!q->registry) {
105✔
612
                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, call, q);
105✔
613
                if (r < 0)
105✔
614
                        return r;
615

616
                q->registry = *registry;
105✔
617
        }
618

619
        r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
105✔
620
        if (r < 0)
105✔
621
                return r;
622

623
        TAKE_PTR(q);
105✔
624

625
        return 0;
105✔
626
#else
627
        return FLAGS_SET(flags, POLKIT_DEFAULT_ALLOW) ? 1 : -EACCES;
628
#endif
629
}
630

631
static int varlink_check_good_user(sd_varlink *link, uid_t good_user) {
901✔
632
        int r;
901✔
633

634
        assert(link);
901✔
635

636
        if (good_user == UID_INVALID)
901✔
637
                return false;
901✔
638

639
        uid_t peer_uid;
×
640
        r = sd_varlink_get_peer_uid(link, &peer_uid);
×
641
        if (r < 0)
×
642
                return r;
643

644
        return good_user == peer_uid;
×
645
}
646

647
static int varlink_check_peer_privilege(sd_varlink *link) {
901✔
648
        int r;
901✔
649

650
        assert(link);
901✔
651

652
        uid_t peer_uid;
901✔
653
        r = sd_varlink_get_peer_uid(link, &peer_uid);
901✔
654
        if (r < 0)
901✔
655
                return r;
901✔
656

657
        uid_t our_uid = getuid();
901✔
658
        return peer_uid == our_uid ||
901✔
659
                (our_uid != 0 && peer_uid == 0);
19✔
660
}
661

662
#if ENABLE_POLKIT
663
static int bus_message_new_polkit_auth_call_for_varlink(
7✔
664
                sd_bus *bus,
665
                sd_varlink *link,
666
                const char *action,
667
                const char **details,
668
                PolkitFlags flags,
669
                sd_bus_message **ret) {
670

671
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
7✔
672
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
7✔
673
        int r;
7✔
674

675
        assert(bus);
7✔
676
        assert(link);
7✔
677
        assert(action);
7✔
678
        assert(ret);
7✔
679

680
        r = varlink_get_peer_pidref(link, &pidref);
7✔
681
        if (r < 0)
7✔
682
                return r;
683
        if (r == 0) /* if we couldn't get a pidfd this returns == 0 */
7✔
684
                return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Failed to get peer pidfd, cannot securely authenticate.");
×
685

686
        uid_t uid;
7✔
687
        r = sd_varlink_get_peer_uid(link, &uid);
7✔
688
        if (r < 0)
7✔
689
                return r;
690

691
        r = sd_bus_message_new_method_call(
7✔
692
                        bus,
693
                        &c,
694
                        "org.freedesktop.PolicyKit1",
695
                        "/org/freedesktop/PolicyKit1/Authority",
696
                        "org.freedesktop.PolicyKit1.Authority",
697
                        "CheckAuthorization");
698
        if (r < 0)
7✔
699
                return r;
700

701
        r = sd_bus_message_append(
14✔
702
                        c,
703
                        "(sa{sv})s",
704
                        "unix-process", 2,
705
                        "pidfd", "h", (uint32_t) pidref.fd,
7✔
706
                        "uid", "i", (int32_t) uid,
707
                        action);
708
        if (r < 0)
7✔
709
                return r;
710

711
        r = bus_message_append_strv_key_value(c, details);
7✔
712
        if (r < 0)
7✔
713
                return r;
714

715
        r = sd_bus_message_append(c, "us", (uint32_t) (flags & _POLKIT_MASK_PUBLIC), NULL);
7✔
716
        if (r < 0)
7✔
717
                return r;
718

719
        *ret = TAKE_PTR(c);
7✔
720
        return 0;
7✔
721
}
722

723
static bool varlink_allow_interactive_authentication(sd_varlink *link) {
7✔
724
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
7✔
725
        int r;
7✔
726

727
        assert(link);
7✔
728

729
        /* We look for the allowInteractiveAuthentication field in the message currently being dispatched,
730
         * always under the same name. */
731

732
        r = sd_varlink_get_current_parameters(link, &v);
7✔
733
        if (r < 0) {
7✔
734
                log_debug_errno(r, "Unable to query current parameters: %m");
×
735
                return false;
×
736
        }
737

738
        sd_json_variant *b;
7✔
739
        b = sd_json_variant_by_key(v, "allowInteractiveAuthentication");
7✔
740
        if (b) {
7✔
741
                if (sd_json_variant_is_boolean(b))
×
742
                        return sd_json_variant_boolean(b);
×
743

744
                log_debug("Incoming 'allowInteractiveAuthentication' field is not a boolean, ignoring.");
7✔
745
        }
746

747
        return false;
748
}
749
#endif
750

751
int varlink_verify_polkit_async_full(
901✔
752
                sd_varlink *link,
753
                sd_bus *bus,
754
                const char *action,
755
                const char **details,
756
                uid_t good_user,
757
                PolkitFlags flags,
758
                Hashmap **registry) {
759

760
        int r;
901✔
761

762
        assert(link);
901✔
763
        assert(registry);
901✔
764

765
        log_debug("Trying to acquire polkit authentication for '%s'.", action);
901✔
766

767
        /* This is the same as bus_verify_polkit_async_full(), but authenticates the peer of a varlink
768
         * connection rather than the sender of a bus message. */
769

770
        r = varlink_check_good_user(link, good_user);
901✔
771
        if (r != 0)
901✔
772
                return r;
901✔
773

774
        if (!FLAGS_SET(flags, POLKIT_ALWAYS_QUERY)) {
901✔
775
                r = varlink_check_peer_privilege(link);
901✔
776
                if (r != 0)
901✔
777
                        return r;
778
        }
779

780
#if ENABLE_POLKIT
781
        _cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL;
×
782

783
        q = async_polkit_query_ref(hashmap_get(*registry, link));
14✔
784
        /* This is a repeated invocation of this function, hence let's check if we've already got
785
         * a response from polkit for this action */
786
        if (q) {
14✔
787
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
7✔
788
                r = async_polkit_query_check_action(q, action, details, flags, &error);
7✔
789
                if (r != 0)
7✔
790
                        log_debug("Found matching previous polkit authentication for '%s'.", action);
7✔
791
                if (r < 0) {
7✔
792
                        if (!FLAGS_SET(flags, POLKIT_DONT_REPLY)) {
×
793
                                /* Reply with a nice error */
794
                                if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED))
×
795
                                        (void) sd_varlink_error(link, SD_VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED, NULL);
×
796
                                else if (ERRNO_IS_NEG_PRIVILEGE(r))
×
797
                                        (void) sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
×
798
                        }
799

800
                        return r;
×
801
                }
802
                if (r > 0)
7✔
803
                        return r;
804
        }
805

806
        _cleanup_(sd_bus_unrefp) sd_bus *mybus = NULL;
14✔
807
        if (!bus) {
7✔
808
                r = sd_bus_open_system(&mybus);
7✔
809
                if (r < 0)
7✔
810
                        return r;
811

812
                r = sd_bus_attach_event(mybus, sd_varlink_get_event(link), 0);
7✔
813
                if (r < 0)
7✔
814
                        return r;
815

816
                bus = mybus;
7✔
817
        }
818

819
        if (varlink_allow_interactive_authentication(link))
7✔
820
                flags |= POLKIT_ALLOW_INTERACTIVE;
×
821

822
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
7✔
823
        r = bus_message_new_polkit_auth_call_for_varlink(bus, link, action, details, flags, &pk);
7✔
824
        if (r < 0)
7✔
825
                return r;
826

827
        if (!q) {
7✔
828
                q = new(AsyncPolkitQuery, 1);
7✔
829
                if (!q)
7✔
830
                        return -ENOMEM;
831

832
                *q = (AsyncPolkitQuery) {
7✔
833
                        .n_ref = 1,
834
                        .link = sd_varlink_ref(link),
7✔
835
                        .bus = sd_bus_ref(bus),
7✔
836
                };
837
        }
838

839
        assert(!q->action);
7✔
840
        q->action = new(AsyncPolkitQueryAction, 1);
7✔
841
        if (!q->action)
7✔
842
                return -ENOMEM;
843

844
        *q->action = (AsyncPolkitQueryAction) {
14✔
845
                .action = strdup(action),
7✔
846
                .details = strv_copy((char**) details),
7✔
847
        };
848
        if (!q->action->action || !q->action->details)
7✔
849
                return -ENOMEM;
850

851
        if (!q->registry) {
7✔
852
                r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, link, q);
7✔
853
                if (r < 0)
7✔
854
                        return r;
855

856
                q->registry = *registry;
7✔
857
        }
858

859
        r = sd_bus_call_async(bus, &q->slot, pk, async_polkit_callback, q, 0);
7✔
860
        if (r < 0)
7✔
861
                return r;
862

863
        TAKE_PTR(q);
7✔
864

865
        return 0;
7✔
866
#else
867
        return FLAGS_SET(flags, POLKIT_DEFAULT_ALLOW) ? 1 : -EACCES;
868
#endif
869
}
870

871
bool varlink_has_polkit_action(sd_varlink *link, const char *action, const char **details, Hashmap **registry) {
×
872
        assert(link);
×
873
        assert(action);
×
874
        assert(registry);
×
875

876
        /* Checks if we already have acquired some action previously */
877

878
#if ENABLE_POLKIT
879
        AsyncPolkitQuery *q = hashmap_get(*registry, link);
×
880
        if (!q)
×
881
                return false;
882

883
        return async_polkit_query_have_action(q, action, details);
×
884
#else
885
        return false;
886
#endif
887
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc