1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #if HAVE_VALGRIND_MEMCHECK_H
6 #include <valgrind/memcheck.h>
14 #include "alloc-util.h"
15 #include "bus-control.h"
16 #include "bus-internal.h"
17 #include "bus-message.h"
19 #include "capability-util.h"
20 #include "process-util.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
24 #include "user-util.h"
26 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
29 assert_return(bus, -EINVAL);
30 assert_return(bus = bus_resolve(bus), -ENOPKG);
31 assert_return(unique, -EINVAL);
32 assert_return(!bus_pid_changed(bus), -ECHILD);
37 r = bus_ensure_running(bus);
41 *unique = bus->unique_name;
45 static int validate_request_name_parameters(
49 uint32_t *ret_param) {
57 assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
58 assert_return(service_name_is_valid(name), -EINVAL);
59 assert_return(name[0] != ':', -EINVAL);
64 /* Don't allow requesting the special driver and local names */
65 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
68 if (!BUS_IS_OPEN(bus->state))
71 if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
72 param |= BUS_NAME_ALLOW_REPLACEMENT;
73 if (flags & SD_BUS_NAME_REPLACE_EXISTING)
74 param |= BUS_NAME_REPLACE_EXISTING;
75 if (!(flags & SD_BUS_NAME_QUEUE))
76 param |= BUS_NAME_DO_NOT_QUEUE;
83 _public_ int sd_bus_request_name(
88 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
89 uint32_t ret, param = 0;
92 assert_return(bus, -EINVAL);
93 assert_return(bus = bus_resolve(bus), -ENOPKG);
94 assert_return(name, -EINVAL);
95 assert_return(!bus_pid_changed(bus), -ECHILD);
97 r = validate_request_name_parameters(bus, name, flags, ¶m);
101 r = sd_bus_call_method(
103 "org.freedesktop.DBus",
104 "/org/freedesktop/DBus",
105 "org.freedesktop.DBus",
115 r = sd_bus_message_read(reply, "u", &ret);
121 case BUS_NAME_ALREADY_OWNER:
124 case BUS_NAME_EXISTS:
127 case BUS_NAME_IN_QUEUE:
130 case BUS_NAME_PRIMARY_OWNER:
137 static int default_request_name_handler(
140 sd_bus_error *ret_error) {
147 if (sd_bus_message_is_method_error(m, NULL)) {
148 log_debug_errno(sd_bus_message_get_errno(m),
149 "Unable to request name, failing connection: %s",
150 sd_bus_message_get_error(m)->message);
152 bus_enter_closing(sd_bus_message_get_bus(m));
156 r = sd_bus_message_read(m, "u", &ret);
162 case BUS_NAME_ALREADY_OWNER:
163 log_debug("Already owner of requested service name, ignoring.");
166 case BUS_NAME_IN_QUEUE:
167 log_debug("In queue for requested service name.");
170 case BUS_NAME_PRIMARY_OWNER:
171 log_debug("Successfully acquired requested service name.");
174 case BUS_NAME_EXISTS:
175 log_debug("Requested service name already owned, failing connection.");
176 bus_enter_closing(sd_bus_message_get_bus(m));
180 log_debug("Unexpected response from RequestName(), failing connection.");
181 bus_enter_closing(sd_bus_message_get_bus(m));
185 _public_ int sd_bus_request_name_async(
187 sd_bus_slot **ret_slot,
190 sd_bus_message_handler_t callback,
196 assert_return(bus, -EINVAL);
197 assert_return(bus = bus_resolve(bus), -ENOPKG);
198 assert_return(name, -EINVAL);
199 assert_return(!bus_pid_changed(bus), -ECHILD);
201 r = validate_request_name_parameters(bus, name, flags, ¶m);
205 return sd_bus_call_method_async(
208 "org.freedesktop.DBus",
209 "/org/freedesktop/DBus",
210 "org.freedesktop.DBus",
212 callback ?: default_request_name_handler,
219 static int validate_release_name_parameters(
226 assert_return(service_name_is_valid(name), -EINVAL);
227 assert_return(name[0] != ':', -EINVAL);
229 if (!bus->bus_client)
232 /* Don't allow releasing the special driver and local names */
233 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
236 if (!BUS_IS_OPEN(bus->state))
242 _public_ int sd_bus_release_name(
246 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
250 assert_return(bus, -EINVAL);
251 assert_return(bus = bus_resolve(bus), -ENOPKG);
252 assert_return(name, -EINVAL);
253 assert_return(!bus_pid_changed(bus), -ECHILD);
255 r = validate_release_name_parameters(bus, name);
259 r = sd_bus_call_method(
261 "org.freedesktop.DBus",
262 "/org/freedesktop/DBus",
263 "org.freedesktop.DBus",
272 r = sd_bus_message_read(reply, "u", &ret);
278 case BUS_NAME_NON_EXISTENT:
281 case BUS_NAME_NOT_OWNER:
284 case BUS_NAME_RELEASED:
291 static int default_release_name_handler(
294 sd_bus_error *ret_error) {
301 if (sd_bus_message_is_method_error(m, NULL)) {
302 log_debug_errno(sd_bus_message_get_errno(m),
303 "Unable to release name, failing connection: %s",
304 sd_bus_message_get_error(m)->message);
306 bus_enter_closing(sd_bus_message_get_bus(m));
310 r = sd_bus_message_read(m, "u", &ret);
316 case BUS_NAME_NON_EXISTENT:
317 log_debug("Name asked to release is not taken currently, ignoring.");
320 case BUS_NAME_NOT_OWNER:
321 log_debug("Name asked to release is owned by somebody else, ignoring.");
324 case BUS_NAME_RELEASED:
325 log_debug("Name successfully released.");
329 log_debug("Unexpected response from ReleaseName(), failing connection.");
330 bus_enter_closing(sd_bus_message_get_bus(m));
334 _public_ int sd_bus_release_name_async(
336 sd_bus_slot **ret_slot,
338 sd_bus_message_handler_t callback,
343 assert_return(bus, -EINVAL);
344 assert_return(bus = bus_resolve(bus), -ENOPKG);
345 assert_return(name, -EINVAL);
346 assert_return(!bus_pid_changed(bus), -ECHILD);
348 r = validate_release_name_parameters(bus, name);
352 return sd_bus_call_method_async(
355 "org.freedesktop.DBus",
356 "/org/freedesktop/DBus",
357 "org.freedesktop.DBus",
359 callback ?: default_release_name_handler,
365 _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
366 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
367 _cleanup_strv_free_ char **x = NULL, **y = NULL;
370 assert_return(bus, -EINVAL);
371 assert_return(bus = bus_resolve(bus), -ENOPKG);
372 assert_return(acquired || activatable, -EINVAL);
373 assert_return(!bus_pid_changed(bus), -ECHILD);
375 if (!bus->bus_client)
378 if (!BUS_IS_OPEN(bus->state))
382 r = sd_bus_call_method(
384 "org.freedesktop.DBus",
385 "/org/freedesktop/DBus",
386 "org.freedesktop.DBus",
394 r = sd_bus_message_read_strv(reply, &x);
398 reply = sd_bus_message_unref(reply);
402 r = sd_bus_call_method(
404 "org.freedesktop.DBus",
405 "/org/freedesktop/DBus",
406 "org.freedesktop.DBus",
407 "ListActivatableNames",
414 r = sd_bus_message_read_strv(reply, &y);
418 *activatable = TAKE_PTR(y);
422 *acquired = TAKE_PTR(x);
427 _public_ int sd_bus_get_name_creds(
431 sd_bus_creds **creds) {
433 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
434 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
435 const char *unique = NULL;
439 assert_return(bus, -EINVAL);
440 assert_return(bus = bus_resolve(bus), -ENOPKG);
441 assert_return(name, -EINVAL);
442 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
443 assert_return(mask == 0 || creds, -EINVAL);
444 assert_return(!bus_pid_changed(bus), -ECHILD);
445 assert_return(service_name_is_valid(name), -EINVAL);
447 if (!bus->bus_client)
450 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
453 mask &= ~SD_BUS_CREDS_AUGMENT;
455 if (streq(name, "org.freedesktop.DBus.Local"))
458 if (streq(name, "org.freedesktop.DBus"))
459 return sd_bus_get_owner_creds(bus, mask, creds);
461 if (!BUS_IS_OPEN(bus->state))
464 /* Only query the owner if the caller wants to know it or if
465 * the caller just wants to check whether a name exists */
466 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
467 r = sd_bus_call_method(
469 "org.freedesktop.DBus",
470 "/org/freedesktop/DBus",
471 "org.freedesktop.DBus",
480 r = sd_bus_message_read(reply_unique, "s", &unique);
486 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
487 bool need_pid, need_uid, need_selinux, need_separate_calls;
492 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
493 c->unique_name = strdup(unique);
497 c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
500 need_pid = (mask & SD_BUS_CREDS_PID) ||
501 ((mask & SD_BUS_CREDS_AUGMENT) &&
502 (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
503 SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
504 SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
505 SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
506 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|
507 SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
508 SD_BUS_CREDS_SELINUX_CONTEXT|
509 SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)));
510 need_uid = mask & SD_BUS_CREDS_EUID;
511 need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT;
513 if (need_pid + need_uid + need_selinux > 1) {
515 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
517 r = sd_bus_call_method(
519 "org.freedesktop.DBus",
520 "/org/freedesktop/DBus",
521 "org.freedesktop.DBus",
522 "GetConnectionCredentials",
530 if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
533 /* If we got an unknown method error, fall back to the invidual calls... */
534 need_separate_calls = true;
535 sd_bus_error_free(&error);
538 need_separate_calls = false;
540 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
547 r = sd_bus_message_enter_container(reply, 'e', "sv");
553 r = sd_bus_message_read(reply, "s", &m);
557 if (need_uid && streq(m, "UnixUserID")) {
560 r = sd_bus_message_read(reply, "v", "u", &u);
565 c->mask |= SD_BUS_CREDS_EUID;
567 } else if (need_pid && streq(m, "ProcessID")) {
570 r = sd_bus_message_read(reply, "v", "u", &p);
575 if (mask & SD_BUS_CREDS_PID) {
577 c->mask |= SD_BUS_CREDS_PID;
580 } else if (need_selinux && streq(m, "LinuxSecurityLabel")) {
581 const void *p = NULL;
584 r = sd_bus_message_enter_container(reply, 'v', "ay");
588 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
593 c->label = strndup(p, sz);
597 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
599 r = sd_bus_message_exit_container(reply);
603 r = sd_bus_message_skip(reply, "v");
608 r = sd_bus_message_exit_container(reply);
613 r = sd_bus_message_exit_container(reply);
617 if (need_pid && pid == 0)
621 } else /* When we only need a single field, then let's use separate calls */
622 need_separate_calls = true;
624 if (need_separate_calls) {
628 r = sd_bus_call_method(
630 "org.freedesktop.DBus",
631 "/org/freedesktop/DBus",
632 "org.freedesktop.DBus",
633 "GetConnectionUnixProcessID",
641 r = sd_bus_message_read(reply, "u", &u);
646 if (mask & SD_BUS_CREDS_PID) {
648 c->mask |= SD_BUS_CREDS_PID;
651 reply = sd_bus_message_unref(reply);
657 r = sd_bus_call_method(
659 "org.freedesktop.DBus",
660 "/org/freedesktop/DBus",
661 "org.freedesktop.DBus",
662 "GetConnectionUnixUser",
666 unique ? unique : name);
670 r = sd_bus_message_read(reply, "u", &u);
675 c->mask |= SD_BUS_CREDS_EUID;
677 reply = sd_bus_message_unref(reply);
681 const void *p = NULL;
684 r = sd_bus_call_method(
686 "org.freedesktop.DBus",
687 "/org/freedesktop/DBus",
688 "org.freedesktop.DBus",
689 "GetConnectionSELinuxSecurityContext",
693 unique ? unique : name);
695 if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
698 /* no data is fine */
700 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
704 c->label = strndup(p, sz);
708 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
713 r = bus_creds_add_more(c, mask, pid, 0);
719 *creds = TAKE_PTR(c);
724 _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
725 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
726 bool do_label, do_groups;
730 assert_return(bus, -EINVAL);
731 assert_return(bus = bus_resolve(bus), -ENOPKG);
732 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
733 assert_return(ret, -EINVAL);
734 assert_return(!bus_pid_changed(bus), -ECHILD);
736 if (!BUS_IS_OPEN(bus->state))
740 mask &= ~SD_BUS_CREDS_AUGMENT;
742 do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
743 do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
745 /* Avoid allocating anything if we have no chance of returning useful data */
746 if (!bus->ucred_valid && !do_label && !do_groups)
753 if (bus->ucred_valid) {
754 if (pid_is_valid(bus->ucred.pid)) {
755 pid = c->pid = bus->ucred.pid;
756 c->mask |= SD_BUS_CREDS_PID & mask;
759 if (uid_is_valid(bus->ucred.uid)) {
760 c->euid = bus->ucred.uid;
761 c->mask |= SD_BUS_CREDS_EUID & mask;
764 if (gid_is_valid(bus->ucred.gid)) {
765 c->egid = bus->ucred.gid;
766 c->mask |= SD_BUS_CREDS_EGID & mask;
771 c->label = strdup(bus->label);
775 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
779 c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
780 if (!c->supplementary_gids)
783 c->n_supplementary_gids = bus->n_groups;
785 c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
788 r = bus_creds_add_more(c, mask, pid, 0);
797 #define append_eavesdrop(bus, m) \
799 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
802 int bus_add_match_internal(
810 if (!bus->bus_client)
813 e = append_eavesdrop(bus, match);
815 return sd_bus_call_method(
817 "org.freedesktop.DBus",
818 "/org/freedesktop/DBus",
819 "org.freedesktop.DBus",
826 int bus_add_match_internal_async(
828 sd_bus_slot **ret_slot,
830 sd_bus_message_handler_t callback,
837 if (!bus->bus_client)
840 e = append_eavesdrop(bus, match);
842 return sd_bus_call_method_async(
845 "org.freedesktop.DBus",
846 "/org/freedesktop/DBus",
847 "org.freedesktop.DBus",
855 int bus_remove_match_internal(
864 if (!bus->bus_client)
867 e = append_eavesdrop(bus, match);
869 /* Fire and forget */
871 return sd_bus_call_method_async(
874 "org.freedesktop.DBus",
875 "/org/freedesktop/DBus",
876 "org.freedesktop.DBus",
884 _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
885 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
889 assert_return(bus, -EINVAL);
890 assert_return(bus = bus_resolve(bus), -ENOPKG);
891 assert_return(name, -EINVAL);
892 assert_return(machine, -EINVAL);
893 assert_return(!bus_pid_changed(bus), -ECHILD);
894 assert_return(service_name_is_valid(name), -EINVAL);
896 if (!bus->bus_client)
899 if (!BUS_IS_OPEN(bus->state))
902 if (streq_ptr(name, bus->unique_name))
903 return sd_id128_get_machine(machine);
905 r = sd_bus_message_new_method_call(
910 "org.freedesktop.DBus.Peer",
915 r = sd_bus_message_set_auto_start(m, false);
919 r = sd_bus_call(bus, m, 0, NULL, &reply);
923 r = sd_bus_message_read(reply, "s", &mid);
927 return sd_id128_from_string(mid, machine);