1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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"
16 #include "capability-util.h"
17 #include "process-util.h"
18 #include "stdio-util.h"
19 #include "string-util.h"
21 #include "user-util.h"
23 _public_
int sd_bus_get_unique_name(sd_bus
*bus
, const char **unique
) {
26 assert_return(bus
, -EINVAL
);
27 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
28 assert_return(unique
, -EINVAL
);
29 assert_return(!bus_pid_changed(bus
), -ECHILD
);
34 r
= bus_ensure_running(bus
);
38 *unique
= bus
->unique_name
;
42 static int validate_request_name_parameters(
46 uint32_t *ret_param
) {
54 assert_return(!(flags
& ~(SD_BUS_NAME_ALLOW_REPLACEMENT
|SD_BUS_NAME_REPLACE_EXISTING
|SD_BUS_NAME_QUEUE
)), -EINVAL
);
55 assert_return(service_name_is_valid(name
), -EINVAL
);
56 assert_return(name
[0] != ':', -EINVAL
);
61 /* Don't allow requesting the special driver and local names */
62 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
65 if (!BUS_IS_OPEN(bus
->state
))
68 if (flags
& SD_BUS_NAME_ALLOW_REPLACEMENT
)
69 param
|= BUS_NAME_ALLOW_REPLACEMENT
;
70 if (flags
& SD_BUS_NAME_REPLACE_EXISTING
)
71 param
|= BUS_NAME_REPLACE_EXISTING
;
72 if (!(flags
& SD_BUS_NAME_QUEUE
))
73 param
|= BUS_NAME_DO_NOT_QUEUE
;
80 _public_
int sd_bus_request_name(
85 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
86 uint32_t ret
, param
= 0;
89 assert_return(bus
, -EINVAL
);
90 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
91 assert_return(name
, -EINVAL
);
92 assert_return(!bus_pid_changed(bus
), -ECHILD
);
94 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
98 r
= sd_bus_call_method(
100 "org.freedesktop.DBus",
101 "/org/freedesktop/DBus",
102 "org.freedesktop.DBus",
112 r
= sd_bus_message_read(reply
, "u", &ret
);
118 case BUS_NAME_ALREADY_OWNER
:
121 case BUS_NAME_EXISTS
:
124 case BUS_NAME_IN_QUEUE
:
127 case BUS_NAME_PRIMARY_OWNER
:
134 static int default_request_name_handler(
137 sd_bus_error
*ret_error
) {
144 if (sd_bus_message_is_method_error(m
, NULL
)) {
145 log_debug_errno(sd_bus_message_get_errno(m
),
146 "Unable to request name, failing connection: %s",
147 sd_bus_message_get_error(m
)->message
);
149 bus_enter_closing(sd_bus_message_get_bus(m
));
153 r
= sd_bus_message_read(m
, "u", &ret
);
159 case BUS_NAME_ALREADY_OWNER
:
160 log_debug("Already owner of requested service name, ignoring.");
163 case BUS_NAME_IN_QUEUE
:
164 log_debug("In queue for requested service name.");
167 case BUS_NAME_PRIMARY_OWNER
:
168 log_debug("Successfully acquired requested service name.");
171 case BUS_NAME_EXISTS
:
172 log_debug("Requested service name already owned, failing connection.");
173 bus_enter_closing(sd_bus_message_get_bus(m
));
177 log_debug("Unexpected response from RequestName(), failing connection.");
178 bus_enter_closing(sd_bus_message_get_bus(m
));
182 _public_
int sd_bus_request_name_async(
184 sd_bus_slot
**ret_slot
,
187 sd_bus_message_handler_t callback
,
193 assert_return(bus
, -EINVAL
);
194 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
195 assert_return(name
, -EINVAL
);
196 assert_return(!bus_pid_changed(bus
), -ECHILD
);
198 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
202 return sd_bus_call_method_async(
205 "org.freedesktop.DBus",
206 "/org/freedesktop/DBus",
207 "org.freedesktop.DBus",
209 callback
?: default_request_name_handler
,
216 static int validate_release_name_parameters(
223 assert_return(service_name_is_valid(name
), -EINVAL
);
224 assert_return(name
[0] != ':', -EINVAL
);
226 if (!bus
->bus_client
)
229 /* Don't allow releasing the special driver and local names */
230 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
233 if (!BUS_IS_OPEN(bus
->state
))
239 _public_
int sd_bus_release_name(
243 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
247 assert_return(bus
, -EINVAL
);
248 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
249 assert_return(name
, -EINVAL
);
250 assert_return(!bus_pid_changed(bus
), -ECHILD
);
252 r
= validate_release_name_parameters(bus
, name
);
256 r
= sd_bus_call_method(
258 "org.freedesktop.DBus",
259 "/org/freedesktop/DBus",
260 "org.freedesktop.DBus",
269 r
= sd_bus_message_read(reply
, "u", &ret
);
275 case BUS_NAME_NON_EXISTENT
:
278 case BUS_NAME_NOT_OWNER
:
281 case BUS_NAME_RELEASED
:
288 static int default_release_name_handler(
291 sd_bus_error
*ret_error
) {
298 if (sd_bus_message_is_method_error(m
, NULL
)) {
299 log_debug_errno(sd_bus_message_get_errno(m
),
300 "Unable to release name, failing connection: %s",
301 sd_bus_message_get_error(m
)->message
);
303 bus_enter_closing(sd_bus_message_get_bus(m
));
307 r
= sd_bus_message_read(m
, "u", &ret
);
313 case BUS_NAME_NON_EXISTENT
:
314 log_debug("Name asked to release is not taken currently, ignoring.");
317 case BUS_NAME_NOT_OWNER
:
318 log_debug("Name asked to release is owned by somebody else, ignoring.");
321 case BUS_NAME_RELEASED
:
322 log_debug("Name successfully released.");
326 log_debug("Unexpected response from ReleaseName(), failing connection.");
327 bus_enter_closing(sd_bus_message_get_bus(m
));
331 _public_
int sd_bus_release_name_async(
333 sd_bus_slot
**ret_slot
,
335 sd_bus_message_handler_t callback
,
340 assert_return(bus
, -EINVAL
);
341 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
342 assert_return(name
, -EINVAL
);
343 assert_return(!bus_pid_changed(bus
), -ECHILD
);
345 r
= validate_release_name_parameters(bus
, name
);
349 return sd_bus_call_method_async(
352 "org.freedesktop.DBus",
353 "/org/freedesktop/DBus",
354 "org.freedesktop.DBus",
356 callback
?: default_release_name_handler
,
362 _public_
int sd_bus_list_names(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
363 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
364 _cleanup_strv_free_
char **x
= NULL
, **y
= NULL
;
367 assert_return(bus
, -EINVAL
);
368 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
369 assert_return(acquired
|| activatable
, -EINVAL
);
370 assert_return(!bus_pid_changed(bus
), -ECHILD
);
372 if (!bus
->bus_client
)
375 if (!BUS_IS_OPEN(bus
->state
))
379 r
= sd_bus_call_method(
381 "org.freedesktop.DBus",
382 "/org/freedesktop/DBus",
383 "org.freedesktop.DBus",
391 r
= sd_bus_message_read_strv(reply
, &x
);
395 reply
= sd_bus_message_unref(reply
);
399 r
= sd_bus_call_method(
401 "org.freedesktop.DBus",
402 "/org/freedesktop/DBus",
403 "org.freedesktop.DBus",
404 "ListActivatableNames",
411 r
= sd_bus_message_read_strv(reply
, &y
);
415 *activatable
= TAKE_PTR(y
);
419 *acquired
= TAKE_PTR(x
);
424 _public_
int sd_bus_get_name_creds(
428 sd_bus_creds
**creds
) {
430 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
431 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
436 assert_return(bus
, -EINVAL
);
437 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
438 assert_return(name
, -EINVAL
);
439 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
440 assert_return(mask
== 0 || creds
, -EINVAL
);
441 assert_return(!bus_pid_changed(bus
), -ECHILD
);
442 assert_return(service_name_is_valid(name
), -EINVAL
);
444 if (!bus
->bus_client
)
447 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
450 mask
&= ~SD_BUS_CREDS_AUGMENT
;
452 if (streq(name
, "org.freedesktop.DBus.Local"))
455 if (streq(name
, "org.freedesktop.DBus"))
456 return sd_bus_get_owner_creds(bus
, mask
, creds
);
458 if (!BUS_IS_OPEN(bus
->state
))
461 /* If the name is unique anyway, we can use it directly */
462 unique
= name
[0] == ':' ? name
: NULL
;
464 /* Only query the owner if the caller wants to know it and the name is not unique anyway, or if the caller just
465 * wants to check whether a name exists */
466 if ((FLAGS_SET(mask
, SD_BUS_CREDS_UNIQUE_NAME
) && !unique
) || 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
;
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 individual 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",
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",
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
= memdup_suffix0(p
, sz
);
709 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
714 r
= bus_creds_add_more(c
, mask
, pid
, 0);
715 if (r
< 0 && r
!= -ESRCH
) /* Return the error, but ignore ESRCH which just means the process is already gone */
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);
790 if (r
< 0 && r
!= -ESRCH
) /* If the process vanished, then don't complain, just return what we got */
798 #define append_eavesdrop(bus, m) \
800 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
803 int bus_add_match_internal(
806 uint64_t *ret_counter
) {
808 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
814 if (!bus
->bus_client
)
817 e
= append_eavesdrop(bus
, match
);
819 r
= sd_bus_call_method(
821 "org.freedesktop.DBus",
822 "/org/freedesktop/DBus",
823 "org.freedesktop.DBus",
832 /* If the caller asked for it, return the read counter of the reply */
834 *ret_counter
= reply
->read_counter
;
839 int bus_add_match_internal_async(
841 sd_bus_slot
**ret_slot
,
843 sd_bus_message_handler_t callback
,
850 if (!bus
->bus_client
)
853 e
= append_eavesdrop(bus
, match
);
855 return sd_bus_call_method_async(
858 "org.freedesktop.DBus",
859 "/org/freedesktop/DBus",
860 "org.freedesktop.DBus",
868 int bus_remove_match_internal(
877 if (!bus
->bus_client
)
880 e
= append_eavesdrop(bus
, match
);
882 /* Fire and forget */
884 return sd_bus_call_method_async(
887 "org.freedesktop.DBus",
888 "/org/freedesktop/DBus",
889 "org.freedesktop.DBus",
897 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
898 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
902 assert_return(bus
, -EINVAL
);
903 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
904 assert_return(name
, -EINVAL
);
905 assert_return(machine
, -EINVAL
);
906 assert_return(!bus_pid_changed(bus
), -ECHILD
);
907 assert_return(service_name_is_valid(name
), -EINVAL
);
909 if (!bus
->bus_client
)
912 if (!BUS_IS_OPEN(bus
->state
))
915 if (streq_ptr(name
, bus
->unique_name
))
916 return sd_id128_get_machine(machine
);
918 r
= sd_bus_message_new_method_call(
923 "org.freedesktop.DBus.Peer",
928 r
= sd_bus_message_set_auto_start(m
, false);
932 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
936 r
= sd_bus_message_read(reply
, "s", &mid
);
940 return sd_id128_from_string(mid
, machine
);