1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #if HAVE_VALGRIND_MEMCHECK_H
22 #include <valgrind/memcheck.h>
30 #include "alloc-util.h"
31 #include "bus-control.h"
32 #include "bus-internal.h"
33 #include "bus-message.h"
35 #include "capability-util.h"
36 #include "process-util.h"
37 #include "stdio-util.h"
38 #include "string-util.h"
40 #include "user-util.h"
42 _public_
int sd_bus_get_unique_name(sd_bus
*bus
, const char **unique
) {
45 assert_return(bus
, -EINVAL
);
46 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
47 assert_return(unique
, -EINVAL
);
48 assert_return(!bus_pid_changed(bus
), -ECHILD
);
53 r
= bus_ensure_running(bus
);
57 *unique
= bus
->unique_name
;
61 static int validate_request_name_parameters(
65 uint32_t *ret_param
) {
73 assert_return(!(flags
& ~(SD_BUS_NAME_ALLOW_REPLACEMENT
|SD_BUS_NAME_REPLACE_EXISTING
|SD_BUS_NAME_QUEUE
)), -EINVAL
);
74 assert_return(service_name_is_valid(name
), -EINVAL
);
75 assert_return(name
[0] != ':', -EINVAL
);
80 /* Don't allow requesting the special driver and local names */
81 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
84 if (!BUS_IS_OPEN(bus
->state
))
87 if (flags
& SD_BUS_NAME_ALLOW_REPLACEMENT
)
88 param
|= BUS_NAME_ALLOW_REPLACEMENT
;
89 if (flags
& SD_BUS_NAME_REPLACE_EXISTING
)
90 param
|= BUS_NAME_REPLACE_EXISTING
;
91 if (!(flags
& SD_BUS_NAME_QUEUE
))
92 param
|= BUS_NAME_DO_NOT_QUEUE
;
99 _public_
int sd_bus_request_name(
104 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
105 uint32_t ret
, param
= 0;
108 assert_return(bus
, -EINVAL
);
109 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
110 assert_return(name
, -EINVAL
);
111 assert_return(!bus_pid_changed(bus
), -ECHILD
);
113 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
117 r
= sd_bus_call_method(
119 "org.freedesktop.DBus",
120 "/org/freedesktop/DBus",
121 "org.freedesktop.DBus",
131 r
= sd_bus_message_read(reply
, "u", &ret
);
137 case BUS_NAME_ALREADY_OWNER
:
140 case BUS_NAME_EXISTS
:
143 case BUS_NAME_IN_QUEUE
:
146 case BUS_NAME_PRIMARY_OWNER
:
153 static int default_request_name_handler(
156 sd_bus_error
*ret_error
) {
163 if (sd_bus_message_is_method_error(m
, NULL
)) {
164 log_debug_errno(sd_bus_message_get_errno(m
),
165 "Unable to request name, failing connection: %s",
166 sd_bus_message_get_error(m
)->message
);
168 bus_enter_closing(sd_bus_message_get_bus(m
));
172 r
= sd_bus_message_read(m
, "u", &ret
);
178 case BUS_NAME_ALREADY_OWNER
:
179 log_debug("Already owner of requested service name, ignoring.");
182 case BUS_NAME_IN_QUEUE
:
183 log_debug("In queue for requested service name.");
186 case BUS_NAME_PRIMARY_OWNER
:
187 log_debug("Successfully acquired requested service name.");
190 case BUS_NAME_EXISTS
:
191 log_debug("Requested service name already owned, failing connection.");
192 bus_enter_closing(sd_bus_message_get_bus(m
));
196 log_debug("Unexpected response from RequestName(), failing connection.");
197 bus_enter_closing(sd_bus_message_get_bus(m
));
201 _public_
int sd_bus_request_name_async(
203 sd_bus_slot
**ret_slot
,
206 sd_bus_message_handler_t callback
,
212 assert_return(bus
, -EINVAL
);
213 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
214 assert_return(name
, -EINVAL
);
215 assert_return(!bus_pid_changed(bus
), -ECHILD
);
217 r
= validate_request_name_parameters(bus
, name
, flags
, ¶m
);
221 return sd_bus_call_method_async(
224 "org.freedesktop.DBus",
225 "/org/freedesktop/DBus",
226 "org.freedesktop.DBus",
228 callback
?: default_request_name_handler
,
235 static int validate_release_name_parameters(
242 assert_return(service_name_is_valid(name
), -EINVAL
);
243 assert_return(name
[0] != ':', -EINVAL
);
245 if (!bus
->bus_client
)
248 /* Don't allow releasing the special driver and local names */
249 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
252 if (!BUS_IS_OPEN(bus
->state
))
258 _public_
int sd_bus_release_name(
262 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
266 assert_return(bus
, -EINVAL
);
267 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
268 assert_return(name
, -EINVAL
);
269 assert_return(!bus_pid_changed(bus
), -ECHILD
);
271 r
= validate_release_name_parameters(bus
, name
);
275 r
= sd_bus_call_method(
277 "org.freedesktop.DBus",
278 "/org/freedesktop/DBus",
279 "org.freedesktop.DBus",
288 r
= sd_bus_message_read(reply
, "u", &ret
);
294 case BUS_NAME_NON_EXISTENT
:
297 case BUS_NAME_NOT_OWNER
:
300 case BUS_NAME_RELEASED
:
307 static int default_release_name_handler(
310 sd_bus_error
*ret_error
) {
317 if (sd_bus_message_is_method_error(m
, NULL
)) {
318 log_debug_errno(sd_bus_message_get_errno(m
),
319 "Unable to release name, failing connection: %s",
320 sd_bus_message_get_error(m
)->message
);
322 bus_enter_closing(sd_bus_message_get_bus(m
));
326 r
= sd_bus_message_read(m
, "u", &ret
);
332 case BUS_NAME_NON_EXISTENT
:
333 log_debug("Name asked to release is not taken currently, ignoring.");
336 case BUS_NAME_NOT_OWNER
:
337 log_debug("Name asked to release is owned by somebody else, ignoring.");
340 case BUS_NAME_RELEASED
:
341 log_debug("Name successfully released.");
345 log_debug("Unexpected response from ReleaseName(), failing connection.");
346 bus_enter_closing(sd_bus_message_get_bus(m
));
350 _public_
int sd_bus_release_name_async(
352 sd_bus_slot
**ret_slot
,
354 sd_bus_message_handler_t callback
,
359 assert_return(bus
, -EINVAL
);
360 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
361 assert_return(name
, -EINVAL
);
362 assert_return(!bus_pid_changed(bus
), -ECHILD
);
364 r
= validate_release_name_parameters(bus
, name
);
368 return sd_bus_call_method_async(
371 "org.freedesktop.DBus",
372 "/org/freedesktop/DBus",
373 "org.freedesktop.DBus",
375 callback
?: default_release_name_handler
,
381 _public_
int sd_bus_list_names(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
382 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
383 _cleanup_strv_free_
char **x
= NULL
, **y
= NULL
;
386 assert_return(bus
, -EINVAL
);
387 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
388 assert_return(acquired
|| activatable
, -EINVAL
);
389 assert_return(!bus_pid_changed(bus
), -ECHILD
);
391 if (!bus
->bus_client
)
394 if (!BUS_IS_OPEN(bus
->state
))
398 r
= sd_bus_call_method(
400 "org.freedesktop.DBus",
401 "/org/freedesktop/DBus",
402 "org.freedesktop.DBus",
410 r
= sd_bus_message_read_strv(reply
, &x
);
414 reply
= sd_bus_message_unref(reply
);
418 r
= sd_bus_call_method(
420 "org.freedesktop.DBus",
421 "/org/freedesktop/DBus",
422 "org.freedesktop.DBus",
423 "ListActivatableNames",
430 r
= sd_bus_message_read_strv(reply
, &y
);
434 *activatable
= TAKE_PTR(y
);
438 *acquired
= TAKE_PTR(x
);
443 _public_
int sd_bus_get_name_creds(
447 sd_bus_creds
**creds
) {
449 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
450 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
451 const char *unique
= NULL
;
455 assert_return(bus
, -EINVAL
);
456 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
457 assert_return(name
, -EINVAL
);
458 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
459 assert_return(mask
== 0 || creds
, -EINVAL
);
460 assert_return(!bus_pid_changed(bus
), -ECHILD
);
461 assert_return(service_name_is_valid(name
), -EINVAL
);
463 if (!bus
->bus_client
)
466 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
469 mask
&= ~SD_BUS_CREDS_AUGMENT
;
471 if (streq(name
, "org.freedesktop.DBus.Local"))
474 if (streq(name
, "org.freedesktop.DBus"))
475 return sd_bus_get_owner_creds(bus
, mask
, creds
);
477 if (!BUS_IS_OPEN(bus
->state
))
480 /* Only query the owner if the caller wants to know it or if
481 * the caller just wants to check whether a name exists */
482 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) || mask
== 0) {
483 r
= sd_bus_call_method(
485 "org.freedesktop.DBus",
486 "/org/freedesktop/DBus",
487 "org.freedesktop.DBus",
496 r
= sd_bus_message_read(reply_unique
, "s", &unique
);
502 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
503 bool need_pid
, need_uid
, need_selinux
, need_separate_calls
;
508 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) && unique
) {
509 c
->unique_name
= strdup(unique
);
513 c
->mask
|= SD_BUS_CREDS_UNIQUE_NAME
;
516 need_pid
= (mask
& SD_BUS_CREDS_PID
) ||
517 ((mask
& SD_BUS_CREDS_AUGMENT
) &&
518 (mask
& (SD_BUS_CREDS_UID
|SD_BUS_CREDS_SUID
|SD_BUS_CREDS_FSUID
|
519 SD_BUS_CREDS_GID
|SD_BUS_CREDS_EGID
|SD_BUS_CREDS_SGID
|SD_BUS_CREDS_FSGID
|
520 SD_BUS_CREDS_SUPPLEMENTARY_GIDS
|
521 SD_BUS_CREDS_COMM
|SD_BUS_CREDS_EXE
|SD_BUS_CREDS_CMDLINE
|
522 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
|
523 SD_BUS_CREDS_EFFECTIVE_CAPS
|SD_BUS_CREDS_PERMITTED_CAPS
|SD_BUS_CREDS_INHERITABLE_CAPS
|SD_BUS_CREDS_BOUNDING_CAPS
|
524 SD_BUS_CREDS_SELINUX_CONTEXT
|
525 SD_BUS_CREDS_AUDIT_SESSION_ID
|SD_BUS_CREDS_AUDIT_LOGIN_UID
)));
526 need_uid
= mask
& SD_BUS_CREDS_EUID
;
527 need_selinux
= mask
& SD_BUS_CREDS_SELINUX_CONTEXT
;
529 if (need_pid
+ need_uid
+ need_selinux
> 1) {
531 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
533 r
= sd_bus_call_method(
535 "org.freedesktop.DBus",
536 "/org/freedesktop/DBus",
537 "org.freedesktop.DBus",
538 "GetConnectionCredentials",
546 if (!sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
))
549 /* If we got an unknown method error, fall back to the invidual calls... */
550 need_separate_calls
= true;
551 sd_bus_error_free(&error
);
554 need_separate_calls
= false;
556 r
= sd_bus_message_enter_container(reply
, 'a', "{sv}");
563 r
= sd_bus_message_enter_container(reply
, 'e', "sv");
569 r
= sd_bus_message_read(reply
, "s", &m
);
573 if (need_uid
&& streq(m
, "UnixUserID")) {
576 r
= sd_bus_message_read(reply
, "v", "u", &u
);
581 c
->mask
|= SD_BUS_CREDS_EUID
;
583 } else if (need_pid
&& streq(m
, "ProcessID")) {
586 r
= sd_bus_message_read(reply
, "v", "u", &p
);
591 if (mask
& SD_BUS_CREDS_PID
) {
593 c
->mask
|= SD_BUS_CREDS_PID
;
596 } else if (need_selinux
&& streq(m
, "LinuxSecurityLabel")) {
597 const void *p
= NULL
;
600 r
= sd_bus_message_enter_container(reply
, 'v', "ay");
604 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
609 c
->label
= strndup(p
, sz
);
613 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
615 r
= sd_bus_message_exit_container(reply
);
619 r
= sd_bus_message_skip(reply
, "v");
624 r
= sd_bus_message_exit_container(reply
);
629 r
= sd_bus_message_exit_container(reply
);
633 if (need_pid
&& pid
== 0)
637 } else /* When we only need a single field, then let's use separate calls */
638 need_separate_calls
= true;
640 if (need_separate_calls
) {
644 r
= sd_bus_call_method(
646 "org.freedesktop.DBus",
647 "/org/freedesktop/DBus",
648 "org.freedesktop.DBus",
649 "GetConnectionUnixProcessID",
657 r
= sd_bus_message_read(reply
, "u", &u
);
662 if (mask
& SD_BUS_CREDS_PID
) {
664 c
->mask
|= SD_BUS_CREDS_PID
;
667 reply
= sd_bus_message_unref(reply
);
673 r
= sd_bus_call_method(
675 "org.freedesktop.DBus",
676 "/org/freedesktop/DBus",
677 "org.freedesktop.DBus",
678 "GetConnectionUnixUser",
682 unique
? unique
: name
);
686 r
= sd_bus_message_read(reply
, "u", &u
);
691 c
->mask
|= SD_BUS_CREDS_EUID
;
693 reply
= sd_bus_message_unref(reply
);
697 const void *p
= NULL
;
700 r
= sd_bus_call_method(
702 "org.freedesktop.DBus",
703 "/org/freedesktop/DBus",
704 "org.freedesktop.DBus",
705 "GetConnectionSELinuxSecurityContext",
709 unique
? unique
: name
);
711 if (!sd_bus_error_has_name(&error
, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
714 /* no data is fine */
716 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
720 c
->label
= strndup(p
, sz
);
724 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
729 r
= bus_creds_add_more(c
, mask
, pid
, 0);
735 *creds
= TAKE_PTR(c
);
740 _public_
int sd_bus_get_owner_creds(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
741 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
742 bool do_label
, do_groups
;
746 assert_return(bus
, -EINVAL
);
747 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
748 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
749 assert_return(ret
, -EINVAL
);
750 assert_return(!bus_pid_changed(bus
), -ECHILD
);
752 if (!BUS_IS_OPEN(bus
->state
))
756 mask
&= ~SD_BUS_CREDS_AUGMENT
;
758 do_label
= bus
->label
&& (mask
& SD_BUS_CREDS_SELINUX_CONTEXT
);
759 do_groups
= bus
->n_groups
!= (size_t) -1 && (mask
& SD_BUS_CREDS_SUPPLEMENTARY_GIDS
);
761 /* Avoid allocating anything if we have no chance of returning useful data */
762 if (!bus
->ucred_valid
&& !do_label
&& !do_groups
)
769 if (bus
->ucred_valid
) {
770 if (pid_is_valid(bus
->ucred
.pid
)) {
771 pid
= c
->pid
= bus
->ucred
.pid
;
772 c
->mask
|= SD_BUS_CREDS_PID
& mask
;
775 if (uid_is_valid(bus
->ucred
.uid
)) {
776 c
->euid
= bus
->ucred
.uid
;
777 c
->mask
|= SD_BUS_CREDS_EUID
& mask
;
780 if (gid_is_valid(bus
->ucred
.gid
)) {
781 c
->egid
= bus
->ucred
.gid
;
782 c
->mask
|= SD_BUS_CREDS_EGID
& mask
;
787 c
->label
= strdup(bus
->label
);
791 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
795 c
->supplementary_gids
= newdup(gid_t
, bus
->groups
, bus
->n_groups
);
796 if (!c
->supplementary_gids
)
799 c
->n_supplementary_gids
= bus
->n_groups
;
801 c
->mask
|= SD_BUS_CREDS_SUPPLEMENTARY_GIDS
;
804 r
= bus_creds_add_more(c
, mask
, pid
, 0);
813 #define append_eavesdrop(bus, m) \
815 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
818 int bus_add_match_internal(
826 if (!bus
->bus_client
)
829 e
= append_eavesdrop(bus
, match
);
831 return sd_bus_call_method(
833 "org.freedesktop.DBus",
834 "/org/freedesktop/DBus",
835 "org.freedesktop.DBus",
842 int bus_add_match_internal_async(
844 sd_bus_slot
**ret_slot
,
846 sd_bus_message_handler_t callback
,
853 if (!bus
->bus_client
)
856 e
= append_eavesdrop(bus
, match
);
858 return sd_bus_call_method_async(
861 "org.freedesktop.DBus",
862 "/org/freedesktop/DBus",
863 "org.freedesktop.DBus",
871 int bus_remove_match_internal(
880 if (!bus
->bus_client
)
883 e
= append_eavesdrop(bus
, match
);
885 /* Fire and forget */
887 return sd_bus_call_method_async(
890 "org.freedesktop.DBus",
891 "/org/freedesktop/DBus",
892 "org.freedesktop.DBus",
900 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
901 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
905 assert_return(bus
, -EINVAL
);
906 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
907 assert_return(name
, -EINVAL
);
908 assert_return(machine
, -EINVAL
);
909 assert_return(!bus_pid_changed(bus
), -ECHILD
);
910 assert_return(service_name_is_valid(name
), -EINVAL
);
912 if (!bus
->bus_client
)
915 if (!BUS_IS_OPEN(bus
->state
))
918 if (streq_ptr(name
, bus
->unique_name
))
919 return sd_id128_get_machine(machine
);
921 r
= sd_bus_message_new_method_call(
926 "org.freedesktop.DBus.Peer",
931 r
= sd_bus_message_set_auto_start(m
, false);
935 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
939 r
= sd_bus_message_read(reply
, "s", &mid
);
943 return sd_id128_from_string(mid
, machine
);