1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
8 #if HAVE_VALGRIND_MEMCHECK_H
9 #include <valgrind/memcheck.h>
17 #include "alloc-util.h"
18 #include "bus-control.h"
19 #include "bus-internal.h"
20 #include "bus-message.h"
22 #include "capability-util.h"
23 #include "process-util.h"
24 #include "stdio-util.h"
25 #include "string-util.h"
27 #include "user-util.h"
29 _public_
int sd_bus_get_unique_name(sd_bus
*bus
, const char **unique
) {
32 assert_return(bus
, -EINVAL
);
33 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
34 assert_return(unique
, -EINVAL
);
35 assert_return(!bus_pid_changed(bus
), -ECHILD
);
40 r
= bus_ensure_running(bus
);
44 *unique
= bus
->unique_name
;
48 static int validate_request_name_parameters(
52 uint32_t *ret_param
) {
60 assert_return(!(flags
& ~(SD_BUS_NAME_ALLOW_REPLACEMENT
|SD_BUS_NAME_REPLACE_EXISTING
|SD_BUS_NAME_QUEUE
)), -EINVAL
);
61 assert_return(service_name_is_valid(name
), -EINVAL
);
62 assert_return(name
[0] != ':', -EINVAL
);
67 /* Don't allow requesting the special driver and local names */
68 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
71 if (!BUS_IS_OPEN(bus
->state
))
74 if (flags
& SD_BUS_NAME_ALLOW_REPLACEMENT
)
75 param
|= BUS_NAME_ALLOW_REPLACEMENT
;
76 if (flags
& SD_BUS_NAME_REPLACE_EXISTING
)
77 param
|= BUS_NAME_REPLACE_EXISTING
;
78 if (!(flags
& SD_BUS_NAME_QUEUE
))
79 param
|= BUS_NAME_DO_NOT_QUEUE
;
86 _public_
int sd_bus_request_name(
91 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
92 uint32_t ret
, param
= 0;
95 assert_return(bus
, -EINVAL
);
96 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
97 assert_return(name
, -EINVAL
);
98 assert_return(!bus_pid_changed(bus
), -ECHILD
);
100 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
104 r
= sd_bus_call_method(
106 "org.freedesktop.DBus",
107 "/org/freedesktop/DBus",
108 "org.freedesktop.DBus",
118 r
= sd_bus_message_read(reply
, "u", &ret
);
124 case BUS_NAME_ALREADY_OWNER
:
127 case BUS_NAME_EXISTS
:
130 case BUS_NAME_IN_QUEUE
:
133 case BUS_NAME_PRIMARY_OWNER
:
140 static int default_request_name_handler(
143 sd_bus_error
*ret_error
) {
150 if (sd_bus_message_is_method_error(m
, NULL
)) {
151 log_debug_errno(sd_bus_message_get_errno(m
),
152 "Unable to request name, failing connection: %s",
153 sd_bus_message_get_error(m
)->message
);
155 bus_enter_closing(sd_bus_message_get_bus(m
));
159 r
= sd_bus_message_read(m
, "u", &ret
);
165 case BUS_NAME_ALREADY_OWNER
:
166 log_debug("Already owner of requested service name, ignoring.");
169 case BUS_NAME_IN_QUEUE
:
170 log_debug("In queue for requested service name.");
173 case BUS_NAME_PRIMARY_OWNER
:
174 log_debug("Successfully acquired requested service name.");
177 case BUS_NAME_EXISTS
:
178 log_debug("Requested service name already owned, failing connection.");
179 bus_enter_closing(sd_bus_message_get_bus(m
));
183 log_debug("Unexpected response from RequestName(), failing connection.");
184 bus_enter_closing(sd_bus_message_get_bus(m
));
188 _public_
int sd_bus_request_name_async(
190 sd_bus_slot
**ret_slot
,
193 sd_bus_message_handler_t callback
,
199 assert_return(bus
, -EINVAL
);
200 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
201 assert_return(name
, -EINVAL
);
202 assert_return(!bus_pid_changed(bus
), -ECHILD
);
204 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
208 return sd_bus_call_method_async(
211 "org.freedesktop.DBus",
212 "/org/freedesktop/DBus",
213 "org.freedesktop.DBus",
215 callback
?: default_request_name_handler
,
222 static int validate_release_name_parameters(
229 assert_return(service_name_is_valid(name
), -EINVAL
);
230 assert_return(name
[0] != ':', -EINVAL
);
232 if (!bus
->bus_client
)
235 /* Don't allow releasing the special driver and local names */
236 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
239 if (!BUS_IS_OPEN(bus
->state
))
245 _public_
int sd_bus_release_name(
249 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
253 assert_return(bus
, -EINVAL
);
254 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
255 assert_return(name
, -EINVAL
);
256 assert_return(!bus_pid_changed(bus
), -ECHILD
);
258 r
= validate_release_name_parameters(bus
, name
);
262 r
= sd_bus_call_method(
264 "org.freedesktop.DBus",
265 "/org/freedesktop/DBus",
266 "org.freedesktop.DBus",
275 r
= sd_bus_message_read(reply
, "u", &ret
);
281 case BUS_NAME_NON_EXISTENT
:
284 case BUS_NAME_NOT_OWNER
:
287 case BUS_NAME_RELEASED
:
294 static int default_release_name_handler(
297 sd_bus_error
*ret_error
) {
304 if (sd_bus_message_is_method_error(m
, NULL
)) {
305 log_debug_errno(sd_bus_message_get_errno(m
),
306 "Unable to release name, failing connection: %s",
307 sd_bus_message_get_error(m
)->message
);
309 bus_enter_closing(sd_bus_message_get_bus(m
));
313 r
= sd_bus_message_read(m
, "u", &ret
);
319 case BUS_NAME_NON_EXISTENT
:
320 log_debug("Name asked to release is not taken currently, ignoring.");
323 case BUS_NAME_NOT_OWNER
:
324 log_debug("Name asked to release is owned by somebody else, ignoring.");
327 case BUS_NAME_RELEASED
:
328 log_debug("Name successfully released.");
332 log_debug("Unexpected response from ReleaseName(), failing connection.");
333 bus_enter_closing(sd_bus_message_get_bus(m
));
337 _public_
int sd_bus_release_name_async(
339 sd_bus_slot
**ret_slot
,
341 sd_bus_message_handler_t callback
,
346 assert_return(bus
, -EINVAL
);
347 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
348 assert_return(name
, -EINVAL
);
349 assert_return(!bus_pid_changed(bus
), -ECHILD
);
351 r
= validate_release_name_parameters(bus
, name
);
355 return sd_bus_call_method_async(
358 "org.freedesktop.DBus",
359 "/org/freedesktop/DBus",
360 "org.freedesktop.DBus",
362 callback
?: default_release_name_handler
,
368 _public_
int sd_bus_list_names(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
369 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
370 _cleanup_strv_free_
char **x
= NULL
, **y
= NULL
;
373 assert_return(bus
, -EINVAL
);
374 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
375 assert_return(acquired
|| activatable
, -EINVAL
);
376 assert_return(!bus_pid_changed(bus
), -ECHILD
);
378 if (!bus
->bus_client
)
381 if (!BUS_IS_OPEN(bus
->state
))
385 r
= sd_bus_call_method(
387 "org.freedesktop.DBus",
388 "/org/freedesktop/DBus",
389 "org.freedesktop.DBus",
397 r
= sd_bus_message_read_strv(reply
, &x
);
401 reply
= sd_bus_message_unref(reply
);
405 r
= sd_bus_call_method(
407 "org.freedesktop.DBus",
408 "/org/freedesktop/DBus",
409 "org.freedesktop.DBus",
410 "ListActivatableNames",
417 r
= sd_bus_message_read_strv(reply
, &y
);
421 *activatable
= TAKE_PTR(y
);
425 *acquired
= TAKE_PTR(x
);
430 _public_
int sd_bus_get_name_creds(
434 sd_bus_creds
**creds
) {
436 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
437 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
438 const char *unique
= NULL
;
442 assert_return(bus
, -EINVAL
);
443 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
444 assert_return(name
, -EINVAL
);
445 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
446 assert_return(mask
== 0 || creds
, -EINVAL
);
447 assert_return(!bus_pid_changed(bus
), -ECHILD
);
448 assert_return(service_name_is_valid(name
), -EINVAL
);
450 if (!bus
->bus_client
)
453 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
456 mask
&= ~SD_BUS_CREDS_AUGMENT
;
458 if (streq(name
, "org.freedesktop.DBus.Local"))
461 if (streq(name
, "org.freedesktop.DBus"))
462 return sd_bus_get_owner_creds(bus
, mask
, creds
);
464 if (!BUS_IS_OPEN(bus
->state
))
467 /* Only query the owner if the caller wants to know it or if
468 * the caller just wants to check whether a name exists */
469 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) || mask
== 0) {
470 r
= sd_bus_call_method(
472 "org.freedesktop.DBus",
473 "/org/freedesktop/DBus",
474 "org.freedesktop.DBus",
483 r
= sd_bus_message_read(reply_unique
, "s", &unique
);
489 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
490 bool need_pid
, need_uid
, need_selinux
, need_separate_calls
;
495 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) && unique
) {
496 c
->unique_name
= strdup(unique
);
500 c
->mask
|= SD_BUS_CREDS_UNIQUE_NAME
;
503 need_pid
= (mask
& SD_BUS_CREDS_PID
) ||
504 ((mask
& SD_BUS_CREDS_AUGMENT
) &&
505 (mask
& (SD_BUS_CREDS_UID
|SD_BUS_CREDS_SUID
|SD_BUS_CREDS_FSUID
|
506 SD_BUS_CREDS_GID
|SD_BUS_CREDS_EGID
|SD_BUS_CREDS_SGID
|SD_BUS_CREDS_FSGID
|
507 SD_BUS_CREDS_SUPPLEMENTARY_GIDS
|
508 SD_BUS_CREDS_COMM
|SD_BUS_CREDS_EXE
|SD_BUS_CREDS_CMDLINE
|
509 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
|
510 SD_BUS_CREDS_EFFECTIVE_CAPS
|SD_BUS_CREDS_PERMITTED_CAPS
|SD_BUS_CREDS_INHERITABLE_CAPS
|SD_BUS_CREDS_BOUNDING_CAPS
|
511 SD_BUS_CREDS_SELINUX_CONTEXT
|
512 SD_BUS_CREDS_AUDIT_SESSION_ID
|SD_BUS_CREDS_AUDIT_LOGIN_UID
)));
513 need_uid
= mask
& SD_BUS_CREDS_EUID
;
514 need_selinux
= mask
& SD_BUS_CREDS_SELINUX_CONTEXT
;
516 if (need_pid
+ need_uid
+ need_selinux
> 1) {
518 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
520 r
= sd_bus_call_method(
522 "org.freedesktop.DBus",
523 "/org/freedesktop/DBus",
524 "org.freedesktop.DBus",
525 "GetConnectionCredentials",
533 if (!sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
))
536 /* If we got an unknown method error, fall back to the invidual calls... */
537 need_separate_calls
= true;
538 sd_bus_error_free(&error
);
541 need_separate_calls
= false;
543 r
= sd_bus_message_enter_container(reply
, 'a', "{sv}");
550 r
= sd_bus_message_enter_container(reply
, 'e', "sv");
556 r
= sd_bus_message_read(reply
, "s", &m
);
560 if (need_uid
&& streq(m
, "UnixUserID")) {
563 r
= sd_bus_message_read(reply
, "v", "u", &u
);
568 c
->mask
|= SD_BUS_CREDS_EUID
;
570 } else if (need_pid
&& streq(m
, "ProcessID")) {
573 r
= sd_bus_message_read(reply
, "v", "u", &p
);
578 if (mask
& SD_BUS_CREDS_PID
) {
580 c
->mask
|= SD_BUS_CREDS_PID
;
583 } else if (need_selinux
&& streq(m
, "LinuxSecurityLabel")) {
584 const void *p
= NULL
;
587 r
= sd_bus_message_enter_container(reply
, 'v', "ay");
591 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
596 c
->label
= strndup(p
, sz
);
600 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
602 r
= sd_bus_message_exit_container(reply
);
606 r
= sd_bus_message_skip(reply
, "v");
611 r
= sd_bus_message_exit_container(reply
);
616 r
= sd_bus_message_exit_container(reply
);
620 if (need_pid
&& pid
== 0)
624 } else /* When we only need a single field, then let's use separate calls */
625 need_separate_calls
= true;
627 if (need_separate_calls
) {
631 r
= sd_bus_call_method(
633 "org.freedesktop.DBus",
634 "/org/freedesktop/DBus",
635 "org.freedesktop.DBus",
636 "GetConnectionUnixProcessID",
644 r
= sd_bus_message_read(reply
, "u", &u
);
649 if (mask
& SD_BUS_CREDS_PID
) {
651 c
->mask
|= SD_BUS_CREDS_PID
;
654 reply
= sd_bus_message_unref(reply
);
660 r
= sd_bus_call_method(
662 "org.freedesktop.DBus",
663 "/org/freedesktop/DBus",
664 "org.freedesktop.DBus",
665 "GetConnectionUnixUser",
669 unique
? unique
: name
);
673 r
= sd_bus_message_read(reply
, "u", &u
);
678 c
->mask
|= SD_BUS_CREDS_EUID
;
680 reply
= sd_bus_message_unref(reply
);
684 const void *p
= NULL
;
687 r
= sd_bus_call_method(
689 "org.freedesktop.DBus",
690 "/org/freedesktop/DBus",
691 "org.freedesktop.DBus",
692 "GetConnectionSELinuxSecurityContext",
696 unique
? unique
: name
);
698 if (!sd_bus_error_has_name(&error
, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
701 /* no data is fine */
703 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
707 c
->label
= strndup(p
, sz
);
711 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
716 r
= bus_creds_add_more(c
, mask
, pid
, 0);
722 *creds
= TAKE_PTR(c
);
727 _public_
int sd_bus_get_owner_creds(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
728 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
729 bool do_label
, do_groups
;
733 assert_return(bus
, -EINVAL
);
734 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
735 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
736 assert_return(ret
, -EINVAL
);
737 assert_return(!bus_pid_changed(bus
), -ECHILD
);
739 if (!BUS_IS_OPEN(bus
->state
))
743 mask
&= ~SD_BUS_CREDS_AUGMENT
;
745 do_label
= bus
->label
&& (mask
& SD_BUS_CREDS_SELINUX_CONTEXT
);
746 do_groups
= bus
->n_groups
!= (size_t) -1 && (mask
& SD_BUS_CREDS_SUPPLEMENTARY_GIDS
);
748 /* Avoid allocating anything if we have no chance of returning useful data */
749 if (!bus
->ucred_valid
&& !do_label
&& !do_groups
)
756 if (bus
->ucred_valid
) {
757 if (pid_is_valid(bus
->ucred
.pid
)) {
758 pid
= c
->pid
= bus
->ucred
.pid
;
759 c
->mask
|= SD_BUS_CREDS_PID
& mask
;
762 if (uid_is_valid(bus
->ucred
.uid
)) {
763 c
->euid
= bus
->ucred
.uid
;
764 c
->mask
|= SD_BUS_CREDS_EUID
& mask
;
767 if (gid_is_valid(bus
->ucred
.gid
)) {
768 c
->egid
= bus
->ucred
.gid
;
769 c
->mask
|= SD_BUS_CREDS_EGID
& mask
;
774 c
->label
= strdup(bus
->label
);
778 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
782 c
->supplementary_gids
= newdup(gid_t
, bus
->groups
, bus
->n_groups
);
783 if (!c
->supplementary_gids
)
786 c
->n_supplementary_gids
= bus
->n_groups
;
788 c
->mask
|= SD_BUS_CREDS_SUPPLEMENTARY_GIDS
;
791 r
= bus_creds_add_more(c
, mask
, pid
, 0);
800 #define append_eavesdrop(bus, m) \
802 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
805 int bus_add_match_internal(
813 if (!bus
->bus_client
)
816 e
= append_eavesdrop(bus
, match
);
818 return sd_bus_call_method(
820 "org.freedesktop.DBus",
821 "/org/freedesktop/DBus",
822 "org.freedesktop.DBus",
829 int bus_add_match_internal_async(
831 sd_bus_slot
**ret_slot
,
833 sd_bus_message_handler_t callback
,
840 if (!bus
->bus_client
)
843 e
= append_eavesdrop(bus
, match
);
845 return sd_bus_call_method_async(
848 "org.freedesktop.DBus",
849 "/org/freedesktop/DBus",
850 "org.freedesktop.DBus",
858 int bus_remove_match_internal(
867 if (!bus
->bus_client
)
870 e
= append_eavesdrop(bus
, match
);
872 /* Fire and forget */
874 return sd_bus_call_method_async(
877 "org.freedesktop.DBus",
878 "/org/freedesktop/DBus",
879 "org.freedesktop.DBus",
887 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
888 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
892 assert_return(bus
, -EINVAL
);
893 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
894 assert_return(name
, -EINVAL
);
895 assert_return(machine
, -EINVAL
);
896 assert_return(!bus_pid_changed(bus
), -ECHILD
);
897 assert_return(service_name_is_valid(name
), -EINVAL
);
899 if (!bus
->bus_client
)
902 if (!BUS_IS_OPEN(bus
->state
))
905 if (streq_ptr(name
, bus
->unique_name
))
906 return sd_id128_get_machine(machine
);
908 r
= sd_bus_message_new_method_call(
913 "org.freedesktop.DBus.Peer",
918 r
= sd_bus_message_set_auto_start(m
, false);
922 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
926 r
= sd_bus_message_read(reply
, "s", &mid
);
930 return sd_id128_from_string(mid
, machine
);