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
);