1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #if HAVE_VALGRIND_MEMCHECK_H
4 #include <valgrind/memcheck.h>
12 #include "alloc-util.h"
13 #include "bus-control.h"
14 #include "bus-internal.h"
15 #include "bus-message.h"
17 #include "capability-util.h"
18 #include "process-util.h"
19 #include "stdio-util.h"
20 #include "string-util.h"
22 #include "user-util.h"
24 _public_
int sd_bus_get_unique_name(sd_bus
*bus
, const char **unique
) {
27 assert_return(bus
, -EINVAL
);
28 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
29 assert_return(unique
, -EINVAL
);
30 assert_return(!bus_pid_changed(bus
), -ECHILD
);
35 r
= bus_ensure_running(bus
);
39 *unique
= bus
->unique_name
;
43 static int validate_request_name_parameters(
47 uint32_t *ret_param
) {
55 assert_return(!(flags
& ~(SD_BUS_NAME_ALLOW_REPLACEMENT
|SD_BUS_NAME_REPLACE_EXISTING
|SD_BUS_NAME_QUEUE
)), -EINVAL
);
56 assert_return(service_name_is_valid(name
), -EINVAL
);
57 assert_return(name
[0] != ':', -EINVAL
);
62 /* Don't allow requesting the special driver and local names */
63 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
66 if (!BUS_IS_OPEN(bus
->state
))
69 if (flags
& SD_BUS_NAME_ALLOW_REPLACEMENT
)
70 param
|= BUS_NAME_ALLOW_REPLACEMENT
;
71 if (flags
& SD_BUS_NAME_REPLACE_EXISTING
)
72 param
|= BUS_NAME_REPLACE_EXISTING
;
73 if (!(flags
& SD_BUS_NAME_QUEUE
))
74 param
|= BUS_NAME_DO_NOT_QUEUE
;
81 _public_
int sd_bus_request_name(
86 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
87 uint32_t ret
, param
= 0;
90 assert_return(bus
, -EINVAL
);
91 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
92 assert_return(name
, -EINVAL
);
93 assert_return(!bus_pid_changed(bus
), -ECHILD
);
95 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
99 r
= sd_bus_call_method(
101 "org.freedesktop.DBus",
102 "/org/freedesktop/DBus",
103 "org.freedesktop.DBus",
113 r
= sd_bus_message_read(reply
, "u", &ret
);
119 case BUS_NAME_ALREADY_OWNER
:
122 case BUS_NAME_EXISTS
:
125 case BUS_NAME_IN_QUEUE
:
128 case BUS_NAME_PRIMARY_OWNER
:
135 static int default_request_name_handler(
138 sd_bus_error
*ret_error
) {
145 if (sd_bus_message_is_method_error(m
, NULL
)) {
146 log_debug_errno(sd_bus_message_get_errno(m
),
147 "Unable to request name, failing connection: %s",
148 sd_bus_message_get_error(m
)->message
);
150 bus_enter_closing(sd_bus_message_get_bus(m
));
154 r
= sd_bus_message_read(m
, "u", &ret
);
160 case BUS_NAME_ALREADY_OWNER
:
161 log_debug("Already owner of requested service name, ignoring.");
164 case BUS_NAME_IN_QUEUE
:
165 log_debug("In queue for requested service name.");
168 case BUS_NAME_PRIMARY_OWNER
:
169 log_debug("Successfully acquired requested service name.");
172 case BUS_NAME_EXISTS
:
173 log_debug("Requested service name already owned, failing connection.");
174 bus_enter_closing(sd_bus_message_get_bus(m
));
178 log_debug("Unexpected response from RequestName(), failing connection.");
179 bus_enter_closing(sd_bus_message_get_bus(m
));
183 _public_
int sd_bus_request_name_async(
185 sd_bus_slot
**ret_slot
,
188 sd_bus_message_handler_t callback
,
194 assert_return(bus
, -EINVAL
);
195 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
196 assert_return(name
, -EINVAL
);
197 assert_return(!bus_pid_changed(bus
), -ECHILD
);
199 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
203 return sd_bus_call_method_async(
206 "org.freedesktop.DBus",
207 "/org/freedesktop/DBus",
208 "org.freedesktop.DBus",
210 callback
?: default_request_name_handler
,
217 static int validate_release_name_parameters(
224 assert_return(service_name_is_valid(name
), -EINVAL
);
225 assert_return(name
[0] != ':', -EINVAL
);
227 if (!bus
->bus_client
)
230 /* Don't allow releasing the special driver and local names */
231 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
234 if (!BUS_IS_OPEN(bus
->state
))
240 _public_
int sd_bus_release_name(
244 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
248 assert_return(bus
, -EINVAL
);
249 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
250 assert_return(name
, -EINVAL
);
251 assert_return(!bus_pid_changed(bus
), -ECHILD
);
253 r
= validate_release_name_parameters(bus
, name
);
257 r
= sd_bus_call_method(
259 "org.freedesktop.DBus",
260 "/org/freedesktop/DBus",
261 "org.freedesktop.DBus",
270 r
= sd_bus_message_read(reply
, "u", &ret
);
276 case BUS_NAME_NON_EXISTENT
:
279 case BUS_NAME_NOT_OWNER
:
282 case BUS_NAME_RELEASED
:
289 static int default_release_name_handler(
292 sd_bus_error
*ret_error
) {
299 if (sd_bus_message_is_method_error(m
, NULL
)) {
300 log_debug_errno(sd_bus_message_get_errno(m
),
301 "Unable to release name, failing connection: %s",
302 sd_bus_message_get_error(m
)->message
);
304 bus_enter_closing(sd_bus_message_get_bus(m
));
308 r
= sd_bus_message_read(m
, "u", &ret
);
314 case BUS_NAME_NON_EXISTENT
:
315 log_debug("Name asked to release is not taken currently, ignoring.");
318 case BUS_NAME_NOT_OWNER
:
319 log_debug("Name asked to release is owned by somebody else, ignoring.");
322 case BUS_NAME_RELEASED
:
323 log_debug("Name successfully released.");
327 log_debug("Unexpected response from ReleaseName(), failing connection.");
328 bus_enter_closing(sd_bus_message_get_bus(m
));
332 _public_
int sd_bus_release_name_async(
334 sd_bus_slot
**ret_slot
,
336 sd_bus_message_handler_t callback
,
341 assert_return(bus
, -EINVAL
);
342 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
343 assert_return(name
, -EINVAL
);
344 assert_return(!bus_pid_changed(bus
), -ECHILD
);
346 r
= validate_release_name_parameters(bus
, name
);
350 return sd_bus_call_method_async(
353 "org.freedesktop.DBus",
354 "/org/freedesktop/DBus",
355 "org.freedesktop.DBus",
357 callback
?: default_release_name_handler
,
363 _public_
int sd_bus_list_names(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
364 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
365 _cleanup_strv_free_
char **x
= NULL
, **y
= NULL
;
368 assert_return(bus
, -EINVAL
);
369 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
370 assert_return(acquired
|| activatable
, -EINVAL
);
371 assert_return(!bus_pid_changed(bus
), -ECHILD
);
373 if (!bus
->bus_client
)
376 if (!BUS_IS_OPEN(bus
->state
))
380 r
= sd_bus_call_method(
382 "org.freedesktop.DBus",
383 "/org/freedesktop/DBus",
384 "org.freedesktop.DBus",
392 r
= sd_bus_message_read_strv(reply
, &x
);
396 reply
= sd_bus_message_unref(reply
);
400 r
= sd_bus_call_method(
402 "org.freedesktop.DBus",
403 "/org/freedesktop/DBus",
404 "org.freedesktop.DBus",
405 "ListActivatableNames",
412 r
= sd_bus_message_read_strv(reply
, &y
);
416 *activatable
= TAKE_PTR(y
);
420 *acquired
= TAKE_PTR(x
);
425 _public_
int sd_bus_get_name_creds(
429 sd_bus_creds
**creds
) {
431 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
432 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
433 const char *unique
= NULL
;
437 assert_return(bus
, -EINVAL
);
438 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
439 assert_return(name
, -EINVAL
);
440 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
441 assert_return(mask
== 0 || creds
, -EINVAL
);
442 assert_return(!bus_pid_changed(bus
), -ECHILD
);
443 assert_return(service_name_is_valid(name
), -EINVAL
);
445 if (!bus
->bus_client
)
448 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
451 mask
&= ~SD_BUS_CREDS_AUGMENT
;
453 if (streq(name
, "org.freedesktop.DBus.Local"))
456 if (streq(name
, "org.freedesktop.DBus"))
457 return sd_bus_get_owner_creds(bus
, mask
, creds
);
459 if (!BUS_IS_OPEN(bus
->state
))
462 /* Only query the owner if the caller wants to know it or if
463 * the caller just wants to check whether a name exists */
464 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) || mask
== 0) {
465 r
= sd_bus_call_method(
467 "org.freedesktop.DBus",
468 "/org/freedesktop/DBus",
469 "org.freedesktop.DBus",
478 r
= sd_bus_message_read(reply_unique
, "s", &unique
);
484 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
485 bool need_pid
, need_uid
, need_selinux
, need_separate_calls
;
490 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) && unique
) {
491 c
->unique_name
= strdup(unique
);
495 c
->mask
|= SD_BUS_CREDS_UNIQUE_NAME
;
498 need_pid
= (mask
& SD_BUS_CREDS_PID
) ||
499 ((mask
& SD_BUS_CREDS_AUGMENT
) &&
500 (mask
& (SD_BUS_CREDS_UID
|SD_BUS_CREDS_SUID
|SD_BUS_CREDS_FSUID
|
501 SD_BUS_CREDS_GID
|SD_BUS_CREDS_EGID
|SD_BUS_CREDS_SGID
|SD_BUS_CREDS_FSGID
|
502 SD_BUS_CREDS_SUPPLEMENTARY_GIDS
|
503 SD_BUS_CREDS_COMM
|SD_BUS_CREDS_EXE
|SD_BUS_CREDS_CMDLINE
|
504 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
|
505 SD_BUS_CREDS_EFFECTIVE_CAPS
|SD_BUS_CREDS_PERMITTED_CAPS
|SD_BUS_CREDS_INHERITABLE_CAPS
|SD_BUS_CREDS_BOUNDING_CAPS
|
506 SD_BUS_CREDS_SELINUX_CONTEXT
|
507 SD_BUS_CREDS_AUDIT_SESSION_ID
|SD_BUS_CREDS_AUDIT_LOGIN_UID
)));
508 need_uid
= mask
& SD_BUS_CREDS_EUID
;
509 need_selinux
= mask
& SD_BUS_CREDS_SELINUX_CONTEXT
;
511 if (need_pid
+ need_uid
+ need_selinux
> 1) {
513 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
515 r
= sd_bus_call_method(
517 "org.freedesktop.DBus",
518 "/org/freedesktop/DBus",
519 "org.freedesktop.DBus",
520 "GetConnectionCredentials",
528 if (!sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
))
531 /* If we got an unknown method error, fall back to the invidual calls... */
532 need_separate_calls
= true;
533 sd_bus_error_free(&error
);
536 need_separate_calls
= false;
538 r
= sd_bus_message_enter_container(reply
, 'a', "{sv}");
545 r
= sd_bus_message_enter_container(reply
, 'e', "sv");
551 r
= sd_bus_message_read(reply
, "s", &m
);
555 if (need_uid
&& streq(m
, "UnixUserID")) {
558 r
= sd_bus_message_read(reply
, "v", "u", &u
);
563 c
->mask
|= SD_BUS_CREDS_EUID
;
565 } else if (need_pid
&& streq(m
, "ProcessID")) {
568 r
= sd_bus_message_read(reply
, "v", "u", &p
);
573 if (mask
& SD_BUS_CREDS_PID
) {
575 c
->mask
|= SD_BUS_CREDS_PID
;
578 } else if (need_selinux
&& streq(m
, "LinuxSecurityLabel")) {
579 const void *p
= NULL
;
582 r
= sd_bus_message_enter_container(reply
, 'v', "ay");
586 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
591 c
->label
= strndup(p
, sz
);
595 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
597 r
= sd_bus_message_exit_container(reply
);
601 r
= sd_bus_message_skip(reply
, "v");
606 r
= sd_bus_message_exit_container(reply
);
611 r
= sd_bus_message_exit_container(reply
);
615 if (need_pid
&& pid
== 0)
619 } else /* When we only need a single field, then let's use separate calls */
620 need_separate_calls
= true;
622 if (need_separate_calls
) {
626 r
= sd_bus_call_method(
628 "org.freedesktop.DBus",
629 "/org/freedesktop/DBus",
630 "org.freedesktop.DBus",
631 "GetConnectionUnixProcessID",
639 r
= sd_bus_message_read(reply
, "u", &u
);
644 if (mask
& SD_BUS_CREDS_PID
) {
646 c
->mask
|= SD_BUS_CREDS_PID
;
649 reply
= sd_bus_message_unref(reply
);
655 r
= sd_bus_call_method(
657 "org.freedesktop.DBus",
658 "/org/freedesktop/DBus",
659 "org.freedesktop.DBus",
660 "GetConnectionUnixUser",
664 unique
? unique
: name
);
668 r
= sd_bus_message_read(reply
, "u", &u
);
673 c
->mask
|= SD_BUS_CREDS_EUID
;
675 reply
= sd_bus_message_unref(reply
);
679 const void *p
= NULL
;
682 r
= sd_bus_call_method(
684 "org.freedesktop.DBus",
685 "/org/freedesktop/DBus",
686 "org.freedesktop.DBus",
687 "GetConnectionSELinuxSecurityContext",
691 unique
? unique
: name
);
693 if (!sd_bus_error_has_name(&error
, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
696 /* no data is fine */
698 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
702 c
->label
= strndup(p
, sz
);
706 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
711 r
= bus_creds_add_more(c
, mask
, pid
, 0);
717 *creds
= TAKE_PTR(c
);
722 _public_
int sd_bus_get_owner_creds(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
723 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
724 bool do_label
, do_groups
;
728 assert_return(bus
, -EINVAL
);
729 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
730 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
731 assert_return(ret
, -EINVAL
);
732 assert_return(!bus_pid_changed(bus
), -ECHILD
);
734 if (!BUS_IS_OPEN(bus
->state
))
738 mask
&= ~SD_BUS_CREDS_AUGMENT
;
740 do_label
= bus
->label
&& (mask
& SD_BUS_CREDS_SELINUX_CONTEXT
);
741 do_groups
= bus
->n_groups
!= (size_t) -1 && (mask
& SD_BUS_CREDS_SUPPLEMENTARY_GIDS
);
743 /* Avoid allocating anything if we have no chance of returning useful data */
744 if (!bus
->ucred_valid
&& !do_label
&& !do_groups
)
751 if (bus
->ucred_valid
) {
752 if (pid_is_valid(bus
->ucred
.pid
)) {
753 pid
= c
->pid
= bus
->ucred
.pid
;
754 c
->mask
|= SD_BUS_CREDS_PID
& mask
;
757 if (uid_is_valid(bus
->ucred
.uid
)) {
758 c
->euid
= bus
->ucred
.uid
;
759 c
->mask
|= SD_BUS_CREDS_EUID
& mask
;
762 if (gid_is_valid(bus
->ucred
.gid
)) {
763 c
->egid
= bus
->ucred
.gid
;
764 c
->mask
|= SD_BUS_CREDS_EGID
& mask
;
769 c
->label
= strdup(bus
->label
);
773 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
777 c
->supplementary_gids
= newdup(gid_t
, bus
->groups
, bus
->n_groups
);
778 if (!c
->supplementary_gids
)
781 c
->n_supplementary_gids
= bus
->n_groups
;
783 c
->mask
|= SD_BUS_CREDS_SUPPLEMENTARY_GIDS
;
786 r
= bus_creds_add_more(c
, mask
, pid
, 0);
795 #define append_eavesdrop(bus, m) \
797 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
800 int bus_add_match_internal(
808 if (!bus
->bus_client
)
811 e
= append_eavesdrop(bus
, match
);
813 return sd_bus_call_method(
815 "org.freedesktop.DBus",
816 "/org/freedesktop/DBus",
817 "org.freedesktop.DBus",
824 int bus_add_match_internal_async(
826 sd_bus_slot
**ret_slot
,
828 sd_bus_message_handler_t callback
,
835 if (!bus
->bus_client
)
838 e
= append_eavesdrop(bus
, match
);
840 return sd_bus_call_method_async(
843 "org.freedesktop.DBus",
844 "/org/freedesktop/DBus",
845 "org.freedesktop.DBus",
853 int bus_remove_match_internal(
862 if (!bus
->bus_client
)
865 e
= append_eavesdrop(bus
, match
);
867 /* Fire and forget */
869 return sd_bus_call_method_async(
872 "org.freedesktop.DBus",
873 "/org/freedesktop/DBus",
874 "org.freedesktop.DBus",
882 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
883 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
887 assert_return(bus
, -EINVAL
);
888 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
889 assert_return(name
, -EINVAL
);
890 assert_return(machine
, -EINVAL
);
891 assert_return(!bus_pid_changed(bus
), -ECHILD
);
892 assert_return(service_name_is_valid(name
), -EINVAL
);
894 if (!bus
->bus_client
)
897 if (!BUS_IS_OPEN(bus
->state
))
900 if (streq_ptr(name
, bus
->unique_name
))
901 return sd_id128_get_machine(machine
);
903 r
= sd_bus_message_new_method_call(
908 "org.freedesktop.DBus.Peer",
913 r
= sd_bus_message_set_auto_start(m
, false);
917 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
921 r
= sd_bus_message_read(reply
, "s", &mid
);
925 return sd_id128_from_string(mid
, machine
);