1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2013 Lennart Poettering
6 #if HAVE_VALGRIND_MEMCHECK_H
7 #include <valgrind/memcheck.h>
15 #include "alloc-util.h"
16 #include "bus-control.h"
17 #include "bus-internal.h"
18 #include "bus-message.h"
20 #include "capability-util.h"
21 #include "process-util.h"
22 #include "stdio-util.h"
23 #include "string-util.h"
25 #include "user-util.h"
27 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
30 assert_return(bus, -EINVAL);
31 assert_return(bus = bus_resolve(bus), -ENOPKG);
32 assert_return(unique, -EINVAL);
33 assert_return(!bus_pid_changed(bus), -ECHILD);
38 r = bus_ensure_running(bus);
42 *unique = bus->unique_name;
46 static int validate_request_name_parameters(
50 uint32_t *ret_param) {
58 assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
59 assert_return(service_name_is_valid(name), -EINVAL);
60 assert_return(name[0] != ':', -EINVAL);
65 /* Don't allow requesting the special driver and local names */
66 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
69 if (!BUS_IS_OPEN(bus->state))
72 if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
73 param |= BUS_NAME_ALLOW_REPLACEMENT;
74 if (flags & SD_BUS_NAME_REPLACE_EXISTING)
75 param |= BUS_NAME_REPLACE_EXISTING;
76 if (!(flags & SD_BUS_NAME_QUEUE))
77 param |= BUS_NAME_DO_NOT_QUEUE;
84 _public_ int sd_bus_request_name(
89 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
90 uint32_t ret, param = 0;
93 assert_return(bus, -EINVAL);
94 assert_return(bus = bus_resolve(bus), -ENOPKG);
95 assert_return(name, -EINVAL);
96 assert_return(!bus_pid_changed(bus), -ECHILD);
98 r = validate_request_name_parameters(bus, name, flags, ¶m);
102 r = sd_bus_call_method(
104 "org.freedesktop.DBus",
105 "/org/freedesktop/DBus",
106 "org.freedesktop.DBus",
116 r = sd_bus_message_read(reply, "u", &ret);
122 case BUS_NAME_ALREADY_OWNER:
125 case BUS_NAME_EXISTS:
128 case BUS_NAME_IN_QUEUE:
131 case BUS_NAME_PRIMARY_OWNER:
138 static int default_request_name_handler(
141 sd_bus_error *ret_error) {
148 if (sd_bus_message_is_method_error(m, NULL)) {
149 log_debug_errno(sd_bus_message_get_errno(m),
150 "Unable to request name, failing connection: %s",
151 sd_bus_message_get_error(m)->message);
153 bus_enter_closing(sd_bus_message_get_bus(m));
157 r = sd_bus_message_read(m, "u", &ret);
163 case BUS_NAME_ALREADY_OWNER:
164 log_debug("Already owner of requested service name, ignoring.");
167 case BUS_NAME_IN_QUEUE:
168 log_debug("In queue for requested service name.");
171 case BUS_NAME_PRIMARY_OWNER:
172 log_debug("Successfully acquired requested service name.");
175 case BUS_NAME_EXISTS:
176 log_debug("Requested service name already owned, failing connection.");
177 bus_enter_closing(sd_bus_message_get_bus(m));
181 log_debug("Unexpected response from RequestName(), failing connection.");
182 bus_enter_closing(sd_bus_message_get_bus(m));
186 _public_ int sd_bus_request_name_async(
188 sd_bus_slot **ret_slot,
191 sd_bus_message_handler_t callback,
197 assert_return(bus, -EINVAL);
198 assert_return(bus = bus_resolve(bus), -ENOPKG);
199 assert_return(name, -EINVAL);
200 assert_return(!bus_pid_changed(bus), -ECHILD);
202 r = validate_request_name_parameters(bus, name, flags, ¶m);
206 return sd_bus_call_method_async(
209 "org.freedesktop.DBus",
210 "/org/freedesktop/DBus",
211 "org.freedesktop.DBus",
213 callback ?: default_request_name_handler,
220 static int validate_release_name_parameters(
227 assert_return(service_name_is_valid(name), -EINVAL);
228 assert_return(name[0] != ':', -EINVAL);
230 if (!bus->bus_client)
233 /* Don't allow releasing the special driver and local names */
234 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
237 if (!BUS_IS_OPEN(bus->state))
243 _public_ int sd_bus_release_name(
247 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
251 assert_return(bus, -EINVAL);
252 assert_return(bus = bus_resolve(bus), -ENOPKG);
253 assert_return(name, -EINVAL);
254 assert_return(!bus_pid_changed(bus), -ECHILD);
256 r = validate_release_name_parameters(bus, name);
260 r = sd_bus_call_method(
262 "org.freedesktop.DBus",
263 "/org/freedesktop/DBus",
264 "org.freedesktop.DBus",
273 r = sd_bus_message_read(reply, "u", &ret);
279 case BUS_NAME_NON_EXISTENT:
282 case BUS_NAME_NOT_OWNER:
285 case BUS_NAME_RELEASED:
292 static int default_release_name_handler(
295 sd_bus_error *ret_error) {
302 if (sd_bus_message_is_method_error(m, NULL)) {
303 log_debug_errno(sd_bus_message_get_errno(m),
304 "Unable to release name, failing connection: %s",
305 sd_bus_message_get_error(m)->message);
307 bus_enter_closing(sd_bus_message_get_bus(m));
311 r = sd_bus_message_read(m, "u", &ret);
317 case BUS_NAME_NON_EXISTENT:
318 log_debug("Name asked to release is not taken currently, ignoring.");
321 case BUS_NAME_NOT_OWNER:
322 log_debug("Name asked to release is owned by somebody else, ignoring.");
325 case BUS_NAME_RELEASED:
326 log_debug("Name successfully released.");
330 log_debug("Unexpected response from ReleaseName(), failing connection.");
331 bus_enter_closing(sd_bus_message_get_bus(m));
335 _public_ int sd_bus_release_name_async(
337 sd_bus_slot **ret_slot,
339 sd_bus_message_handler_t callback,
344 assert_return(bus, -EINVAL);
345 assert_return(bus = bus_resolve(bus), -ENOPKG);
346 assert_return(name, -EINVAL);
347 assert_return(!bus_pid_changed(bus), -ECHILD);
349 r = validate_release_name_parameters(bus, name);
353 return sd_bus_call_method_async(
356 "org.freedesktop.DBus",
357 "/org/freedesktop/DBus",
358 "org.freedesktop.DBus",
360 callback ?: default_release_name_handler,
366 _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
367 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
368 _cleanup_strv_free_ char **x = NULL, **y = NULL;
371 assert_return(bus, -EINVAL);
372 assert_return(bus = bus_resolve(bus), -ENOPKG);
373 assert_return(acquired || activatable, -EINVAL);
374 assert_return(!bus_pid_changed(bus), -ECHILD);
376 if (!bus->bus_client)
379 if (!BUS_IS_OPEN(bus->state))
383 r = sd_bus_call_method(
385 "org.freedesktop.DBus",
386 "/org/freedesktop/DBus",
387 "org.freedesktop.DBus",
395 r = sd_bus_message_read_strv(reply, &x);
399 reply = sd_bus_message_unref(reply);
403 r = sd_bus_call_method(
405 "org.freedesktop.DBus",
406 "/org/freedesktop/DBus",
407 "org.freedesktop.DBus",
408 "ListActivatableNames",
415 r = sd_bus_message_read_strv(reply, &y);
419 *activatable = TAKE_PTR(y);
423 *acquired = TAKE_PTR(x);
428 _public_ int sd_bus_get_name_creds(
432 sd_bus_creds **creds) {
434 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
435 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
436 const char *unique = NULL;
440 assert_return(bus, -EINVAL);
441 assert_return(bus = bus_resolve(bus), -ENOPKG);
442 assert_return(name, -EINVAL);
443 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
444 assert_return(mask == 0 || creds, -EINVAL);
445 assert_return(!bus_pid_changed(bus), -ECHILD);
446 assert_return(service_name_is_valid(name), -EINVAL);
448 if (!bus->bus_client)
451 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
454 mask &= ~SD_BUS_CREDS_AUGMENT;
456 if (streq(name, "org.freedesktop.DBus.Local"))
459 if (streq(name, "org.freedesktop.DBus"))
460 return sd_bus_get_owner_creds(bus, mask, creds);
462 if (!BUS_IS_OPEN(bus->state))
465 /* Only query the owner if the caller wants to know it or if
466 * the caller just wants to check whether a name exists */
467 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
468 r = sd_bus_call_method(
470 "org.freedesktop.DBus",
471 "/org/freedesktop/DBus",
472 "org.freedesktop.DBus",
481 r = sd_bus_message_read(reply_unique, "s", &unique);
487 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
488 bool need_pid, need_uid, need_selinux, need_separate_calls;
493 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
494 c->unique_name = strdup(unique);
498 c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
501 need_pid = (mask & SD_BUS_CREDS_PID) ||
502 ((mask & SD_BUS_CREDS_AUGMENT) &&
503 (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
504 SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
505 SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
506 SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
507 SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
508 SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
509 SD_BUS_CREDS_SELINUX_CONTEXT|
510 SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)));
511 need_uid = mask & SD_BUS_CREDS_EUID;
512 need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT;
514 if (need_pid + need_uid + need_selinux > 1) {
516 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
518 r = sd_bus_call_method(
520 "org.freedesktop.DBus",
521 "/org/freedesktop/DBus",
522 "org.freedesktop.DBus",
523 "GetConnectionCredentials",
531 if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
534 /* If we got an unknown method error, fall back to the invidual calls... */
535 need_separate_calls = true;
536 sd_bus_error_free(&error);
539 need_separate_calls = false;
541 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
548 r = sd_bus_message_enter_container(reply, 'e', "sv");
554 r = sd_bus_message_read(reply, "s", &m);
558 if (need_uid && streq(m, "UnixUserID")) {
561 r = sd_bus_message_read(reply, "v", "u", &u);
566 c->mask |= SD_BUS_CREDS_EUID;
568 } else if (need_pid && streq(m, "ProcessID")) {
571 r = sd_bus_message_read(reply, "v", "u", &p);
576 if (mask & SD_BUS_CREDS_PID) {
578 c->mask |= SD_BUS_CREDS_PID;
581 } else if (need_selinux && streq(m, "LinuxSecurityLabel")) {
582 const void *p = NULL;
585 r = sd_bus_message_enter_container(reply, 'v', "ay");
589 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
594 c->label = strndup(p, sz);
598 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
600 r = sd_bus_message_exit_container(reply);
604 r = sd_bus_message_skip(reply, "v");
609 r = sd_bus_message_exit_container(reply);
614 r = sd_bus_message_exit_container(reply);
618 if (need_pid && pid == 0)
622 } else /* When we only need a single field, then let's use separate calls */
623 need_separate_calls = true;
625 if (need_separate_calls) {
629 r = sd_bus_call_method(
631 "org.freedesktop.DBus",
632 "/org/freedesktop/DBus",
633 "org.freedesktop.DBus",
634 "GetConnectionUnixProcessID",
642 r = sd_bus_message_read(reply, "u", &u);
647 if (mask & SD_BUS_CREDS_PID) {
649 c->mask |= SD_BUS_CREDS_PID;
652 reply = sd_bus_message_unref(reply);
658 r = sd_bus_call_method(
660 "org.freedesktop.DBus",
661 "/org/freedesktop/DBus",
662 "org.freedesktop.DBus",
663 "GetConnectionUnixUser",
667 unique ? unique : name);
671 r = sd_bus_message_read(reply, "u", &u);
676 c->mask |= SD_BUS_CREDS_EUID;
678 reply = sd_bus_message_unref(reply);
682 const void *p = NULL;
685 r = sd_bus_call_method(
687 "org.freedesktop.DBus",
688 "/org/freedesktop/DBus",
689 "org.freedesktop.DBus",
690 "GetConnectionSELinuxSecurityContext",
694 unique ? unique : name);
696 if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
699 /* no data is fine */
701 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
705 c->label = strndup(p, sz);
709 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
714 r = bus_creds_add_more(c, mask, pid, 0);
720 *creds = TAKE_PTR(c);
725 _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
726 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
727 bool do_label, do_groups;
731 assert_return(bus, -EINVAL);
732 assert_return(bus = bus_resolve(bus), -ENOPKG);
733 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
734 assert_return(ret, -EINVAL);
735 assert_return(!bus_pid_changed(bus), -ECHILD);
737 if (!BUS_IS_OPEN(bus->state))
741 mask &= ~SD_BUS_CREDS_AUGMENT;
743 do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
744 do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
746 /* Avoid allocating anything if we have no chance of returning useful data */
747 if (!bus->ucred_valid && !do_label && !do_groups)
754 if (bus->ucred_valid) {
755 if (pid_is_valid(bus->ucred.pid)) {
756 pid = c->pid = bus->ucred.pid;
757 c->mask |= SD_BUS_CREDS_PID & mask;
760 if (uid_is_valid(bus->ucred.uid)) {
761 c->euid = bus->ucred.uid;
762 c->mask |= SD_BUS_CREDS_EUID & mask;
765 if (gid_is_valid(bus->ucred.gid)) {
766 c->egid = bus->ucred.gid;
767 c->mask |= SD_BUS_CREDS_EGID & mask;
772 c->label = strdup(bus->label);
776 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
780 c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
781 if (!c->supplementary_gids)
784 c->n_supplementary_gids = bus->n_groups;
786 c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
789 r = bus_creds_add_more(c, mask, pid, 0);
798 #define append_eavesdrop(bus, m) \
800 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
803 int bus_add_match_internal(
811 if (!bus->bus_client)
814 e = append_eavesdrop(bus, match);
816 return sd_bus_call_method(
818 "org.freedesktop.DBus",
819 "/org/freedesktop/DBus",
820 "org.freedesktop.DBus",
827 int bus_add_match_internal_async(
829 sd_bus_slot **ret_slot,
831 sd_bus_message_handler_t callback,
838 if (!bus->bus_client)
841 e = append_eavesdrop(bus, match);
843 return sd_bus_call_method_async(
846 "org.freedesktop.DBus",
847 "/org/freedesktop/DBus",
848 "org.freedesktop.DBus",
856 int bus_remove_match_internal(
865 if (!bus->bus_client)
868 e = append_eavesdrop(bus, match);
870 /* Fire and forget */
872 return sd_bus_call_method_async(
875 "org.freedesktop.DBus",
876 "/org/freedesktop/DBus",
877 "org.freedesktop.DBus",
885 _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
886 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
890 assert_return(bus, -EINVAL);
891 assert_return(bus = bus_resolve(bus), -ENOPKG);
892 assert_return(name, -EINVAL);
893 assert_return(machine, -EINVAL);
894 assert_return(!bus_pid_changed(bus), -ECHILD);
895 assert_return(service_name_is_valid(name), -EINVAL);
897 if (!bus->bus_client)
900 if (!BUS_IS_OPEN(bus->state))
903 if (streq_ptr(name, bus->unique_name))
904 return sd_id128_get_machine(machine);
906 r = sd_bus_message_new_method_call(
911 "org.freedesktop.DBus.Peer",
916 r = sd_bus_message_set_auto_start(m, false);
920 r = sd_bus_call(bus, m, 0, NULL, &reply);
924 r = sd_bus_message_read(reply, "s", &mid);
928 return sd_id128_from_string(mid, machine);