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
);
446 _public_
int sd_bus_get_name_creds(
450 sd_bus_creds
**creds
) {
452 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
453 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
454 const char *unique
= NULL
;
458 assert_return(bus
, -EINVAL
);
459 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
460 assert_return(name
, -EINVAL
);
461 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
462 assert_return(mask
== 0 || creds
, -EINVAL
);
463 assert_return(!bus_pid_changed(bus
), -ECHILD
);
464 assert_return(service_name_is_valid(name
), -EINVAL
);
466 if (!bus
->bus_client
)
469 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
472 mask
&= ~SD_BUS_CREDS_AUGMENT
;
474 if (streq(name
, "org.freedesktop.DBus.Local"))
477 if (streq(name
, "org.freedesktop.DBus"))
478 return sd_bus_get_owner_creds(bus
, mask
, creds
);
480 if (!BUS_IS_OPEN(bus
->state
))
483 /* Only query the owner if the caller wants to know it or if
484 * the caller just wants to check whether a name exists */
485 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) || mask
== 0) {
486 r
= sd_bus_call_method(
488 "org.freedesktop.DBus",
489 "/org/freedesktop/DBus",
490 "org.freedesktop.DBus",
499 r
= sd_bus_message_read(reply_unique
, "s", &unique
);
505 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
506 bool need_pid
, need_uid
, need_selinux
, need_separate_calls
;
511 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) && unique
) {
512 c
->unique_name
= strdup(unique
);
516 c
->mask
|= SD_BUS_CREDS_UNIQUE_NAME
;
519 need_pid
= (mask
& SD_BUS_CREDS_PID
) ||
520 ((mask
& SD_BUS_CREDS_AUGMENT
) &&
521 (mask
& (SD_BUS_CREDS_UID
|SD_BUS_CREDS_SUID
|SD_BUS_CREDS_FSUID
|
522 SD_BUS_CREDS_GID
|SD_BUS_CREDS_EGID
|SD_BUS_CREDS_SGID
|SD_BUS_CREDS_FSGID
|
523 SD_BUS_CREDS_SUPPLEMENTARY_GIDS
|
524 SD_BUS_CREDS_COMM
|SD_BUS_CREDS_EXE
|SD_BUS_CREDS_CMDLINE
|
525 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
|
526 SD_BUS_CREDS_EFFECTIVE_CAPS
|SD_BUS_CREDS_PERMITTED_CAPS
|SD_BUS_CREDS_INHERITABLE_CAPS
|SD_BUS_CREDS_BOUNDING_CAPS
|
527 SD_BUS_CREDS_SELINUX_CONTEXT
|
528 SD_BUS_CREDS_AUDIT_SESSION_ID
|SD_BUS_CREDS_AUDIT_LOGIN_UID
)));
529 need_uid
= mask
& SD_BUS_CREDS_EUID
;
530 need_selinux
= mask
& SD_BUS_CREDS_SELINUX_CONTEXT
;
532 if (need_pid
+ need_uid
+ need_selinux
> 1) {
534 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
536 r
= sd_bus_call_method(
538 "org.freedesktop.DBus",
539 "/org/freedesktop/DBus",
540 "org.freedesktop.DBus",
541 "GetConnectionCredentials",
549 if (!sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
))
552 /* If we got an unknown method error, fall back to the invidual calls... */
553 need_separate_calls
= true;
554 sd_bus_error_free(&error
);
557 need_separate_calls
= false;
559 r
= sd_bus_message_enter_container(reply
, 'a', "{sv}");
566 r
= sd_bus_message_enter_container(reply
, 'e', "sv");
572 r
= sd_bus_message_read(reply
, "s", &m
);
576 if (need_uid
&& streq(m
, "UnixUserID")) {
579 r
= sd_bus_message_read(reply
, "v", "u", &u
);
584 c
->mask
|= SD_BUS_CREDS_EUID
;
586 } else if (need_pid
&& streq(m
, "ProcessID")) {
589 r
= sd_bus_message_read(reply
, "v", "u", &p
);
594 if (mask
& SD_BUS_CREDS_PID
) {
596 c
->mask
|= SD_BUS_CREDS_PID
;
599 } else if (need_selinux
&& streq(m
, "LinuxSecurityLabel")) {
600 const void *p
= NULL
;
603 r
= sd_bus_message_enter_container(reply
, 'v', "ay");
607 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
612 c
->label
= strndup(p
, sz
);
616 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
618 r
= sd_bus_message_exit_container(reply
);
622 r
= sd_bus_message_skip(reply
, "v");
627 r
= sd_bus_message_exit_container(reply
);
632 r
= sd_bus_message_exit_container(reply
);
636 if (need_pid
&& pid
== 0)
640 } else /* When we only need a single field, then let's use separate calls */
641 need_separate_calls
= true;
643 if (need_separate_calls
) {
647 r
= sd_bus_call_method(
649 "org.freedesktop.DBus",
650 "/org/freedesktop/DBus",
651 "org.freedesktop.DBus",
652 "GetConnectionUnixProcessID",
660 r
= sd_bus_message_read(reply
, "u", &u
);
665 if (mask
& SD_BUS_CREDS_PID
) {
667 c
->mask
|= SD_BUS_CREDS_PID
;
670 reply
= sd_bus_message_unref(reply
);
676 r
= sd_bus_call_method(
678 "org.freedesktop.DBus",
679 "/org/freedesktop/DBus",
680 "org.freedesktop.DBus",
681 "GetConnectionUnixUser",
685 unique
? unique
: name
);
689 r
= sd_bus_message_read(reply
, "u", &u
);
694 c
->mask
|= SD_BUS_CREDS_EUID
;
696 reply
= sd_bus_message_unref(reply
);
700 const void *p
= NULL
;
703 r
= sd_bus_call_method(
705 "org.freedesktop.DBus",
706 "/org/freedesktop/DBus",
707 "org.freedesktop.DBus",
708 "GetConnectionSELinuxSecurityContext",
712 unique
? unique
: name
);
714 if (!sd_bus_error_has_name(&error
, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
717 /* no data is fine */
719 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
723 c
->label
= strndup(p
, sz
);
727 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
732 r
= bus_creds_add_more(c
, mask
, pid
, 0);
745 _public_
int sd_bus_get_owner_creds(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
746 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
747 bool do_label
, do_groups
;
751 assert_return(bus
, -EINVAL
);
752 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
753 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
754 assert_return(ret
, -EINVAL
);
755 assert_return(!bus_pid_changed(bus
), -ECHILD
);
757 if (!BUS_IS_OPEN(bus
->state
))
761 mask
&= ~SD_BUS_CREDS_AUGMENT
;
763 do_label
= bus
->label
&& (mask
& SD_BUS_CREDS_SELINUX_CONTEXT
);
764 do_groups
= bus
->n_groups
!= (size_t) -1 && (mask
& SD_BUS_CREDS_SUPPLEMENTARY_GIDS
);
766 /* Avoid allocating anything if we have no chance of returning useful data */
767 if (!bus
->ucred_valid
&& !do_label
&& !do_groups
)
774 if (bus
->ucred_valid
) {
775 if (pid_is_valid(bus
->ucred
.pid
)) {
776 pid
= c
->pid
= bus
->ucred
.pid
;
777 c
->mask
|= SD_BUS_CREDS_PID
& mask
;
780 if (uid_is_valid(bus
->ucred
.uid
)) {
781 c
->euid
= bus
->ucred
.uid
;
782 c
->mask
|= SD_BUS_CREDS_EUID
& mask
;
785 if (gid_is_valid(bus
->ucred
.gid
)) {
786 c
->egid
= bus
->ucred
.gid
;
787 c
->mask
|= SD_BUS_CREDS_EGID
& mask
;
792 c
->label
= strdup(bus
->label
);
796 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
800 c
->supplementary_gids
= newdup(gid_t
, bus
->groups
, bus
->n_groups
);
801 if (!c
->supplementary_gids
)
804 c
->n_supplementary_gids
= bus
->n_groups
;
806 c
->mask
|= SD_BUS_CREDS_SUPPLEMENTARY_GIDS
;
809 r
= bus_creds_add_more(c
, mask
, pid
, 0);
818 #define append_eavesdrop(bus, m) \
820 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
823 int bus_add_match_internal(
831 if (!bus
->bus_client
)
834 e
= append_eavesdrop(bus
, match
);
836 return sd_bus_call_method(
838 "org.freedesktop.DBus",
839 "/org/freedesktop/DBus",
840 "org.freedesktop.DBus",
847 int bus_add_match_internal_async(
849 sd_bus_slot
**ret_slot
,
851 sd_bus_message_handler_t callback
,
858 if (!bus
->bus_client
)
861 e
= append_eavesdrop(bus
, match
);
863 return sd_bus_call_method_async(
866 "org.freedesktop.DBus",
867 "/org/freedesktop/DBus",
868 "org.freedesktop.DBus",
876 int bus_remove_match_internal(
885 if (!bus
->bus_client
)
888 e
= append_eavesdrop(bus
, match
);
890 /* Fire and forget */
892 return sd_bus_call_method_async(
895 "org.freedesktop.DBus",
896 "/org/freedesktop/DBus",
897 "org.freedesktop.DBus",
905 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
906 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
910 assert_return(bus
, -EINVAL
);
911 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
912 assert_return(name
, -EINVAL
);
913 assert_return(machine
, -EINVAL
);
914 assert_return(!bus_pid_changed(bus
), -ECHILD
);
915 assert_return(service_name_is_valid(name
), -EINVAL
);
917 if (!bus
->bus_client
)
920 if (!BUS_IS_OPEN(bus
->state
))
923 if (streq_ptr(name
, bus
->unique_name
))
924 return sd_id128_get_machine(machine
);
926 r
= sd_bus_message_new_method_call(
931 "org.freedesktop.DBus.Peer",
936 r
= sd_bus_message_set_auto_start(m
, false);
940 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
944 r
= sd_bus_message_read(reply
, "s", &mid
);
948 return sd_id128_from_string(mid
, machine
);