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-bloom.h"
32 #include "bus-control.h"
33 #include "bus-internal.h"
34 #include "bus-message.h"
36 #include "capability-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(unique
, -EINVAL
);
47 assert_return(!bus_pid_changed(bus
), -ECHILD
);
52 r
= bus_ensure_running(bus
);
56 *unique
= bus
->unique_name
;
60 static int bus_request_name_dbus1(sd_bus
*bus
, const char *name
, uint64_t flags
) {
61 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
62 uint32_t ret
, param
= 0;
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
;
75 r
= sd_bus_call_method(
77 "org.freedesktop.DBus",
78 "/org/freedesktop/DBus",
79 "org.freedesktop.DBus",
89 r
= sd_bus_message_read(reply
, "u", &ret
);
93 if (ret
== BUS_NAME_ALREADY_OWNER
)
95 else if (ret
== BUS_NAME_EXISTS
)
97 else if (ret
== BUS_NAME_IN_QUEUE
)
99 else if (ret
== BUS_NAME_PRIMARY_OWNER
)
105 _public_
int sd_bus_request_name(sd_bus
*bus
, const char *name
, uint64_t flags
) {
106 assert_return(bus
, -EINVAL
);
107 assert_return(name
, -EINVAL
);
108 assert_return(!bus_pid_changed(bus
), -ECHILD
);
109 assert_return(!(flags
& ~(SD_BUS_NAME_ALLOW_REPLACEMENT
|SD_BUS_NAME_REPLACE_EXISTING
|SD_BUS_NAME_QUEUE
)), -EINVAL
);
110 assert_return(service_name_is_valid(name
), -EINVAL
);
111 assert_return(name
[0] != ':', -EINVAL
);
113 if (!bus
->bus_client
)
116 /* Don't allow requesting the special driver and local names */
117 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
120 if (!BUS_IS_OPEN(bus
->state
))
123 return bus_request_name_dbus1(bus
, name
, flags
);
126 static int bus_release_name_dbus1(sd_bus
*bus
, const char *name
) {
127 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
134 r
= sd_bus_call_method(
136 "org.freedesktop.DBus",
137 "/org/freedesktop/DBus",
138 "org.freedesktop.DBus",
147 r
= sd_bus_message_read(reply
, "u", &ret
);
150 if (ret
== BUS_NAME_NON_EXISTENT
)
152 if (ret
== BUS_NAME_NOT_OWNER
)
154 if (ret
== BUS_NAME_RELEASED
)
160 _public_
int sd_bus_release_name(sd_bus
*bus
, const char *name
) {
161 assert_return(bus
, -EINVAL
);
162 assert_return(name
, -EINVAL
);
163 assert_return(!bus_pid_changed(bus
), -ECHILD
);
164 assert_return(service_name_is_valid(name
), -EINVAL
);
165 assert_return(name
[0] != ':', -EINVAL
);
167 if (!bus
->bus_client
)
170 /* Don't allow releasing the special driver and local names */
171 if (STR_IN_SET(name
, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
174 if (!BUS_IS_OPEN(bus
->state
))
177 return bus_release_name_dbus1(bus
, name
);
180 static int bus_list_names_dbus1(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
181 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
182 _cleanup_strv_free_
char **x
= NULL
, **y
= NULL
;
186 r
= sd_bus_call_method(
188 "org.freedesktop.DBus",
189 "/org/freedesktop/DBus",
190 "org.freedesktop.DBus",
198 r
= sd_bus_message_read_strv(reply
, &x
);
202 reply
= sd_bus_message_unref(reply
);
206 r
= sd_bus_call_method(
208 "org.freedesktop.DBus",
209 "/org/freedesktop/DBus",
210 "org.freedesktop.DBus",
211 "ListActivatableNames",
218 r
= sd_bus_message_read_strv(reply
, &y
);
234 _public_
int sd_bus_list_names(sd_bus
*bus
, char ***acquired
, char ***activatable
) {
235 assert_return(bus
, -EINVAL
);
236 assert_return(acquired
|| activatable
, -EINVAL
);
237 assert_return(!bus_pid_changed(bus
), -ECHILD
);
239 if (!bus
->bus_client
)
242 if (!BUS_IS_OPEN(bus
->state
))
245 return bus_list_names_dbus1(bus
, acquired
, activatable
);
248 static int bus_get_name_creds_dbus1(
252 sd_bus_creds
**creds
) {
254 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply_unique
= NULL
, *reply
= NULL
;
255 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
256 const char *unique
= NULL
;
260 /* Only query the owner if the caller wants to know it or if
261 * the caller just wants to check whether a name exists */
262 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) || mask
== 0) {
263 r
= sd_bus_call_method(
265 "org.freedesktop.DBus",
266 "/org/freedesktop/DBus",
267 "org.freedesktop.DBus",
276 r
= sd_bus_message_read(reply_unique
, "s", &unique
);
282 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
283 bool need_pid
, need_uid
, need_selinux
, need_separate_calls
;
288 if ((mask
& SD_BUS_CREDS_UNIQUE_NAME
) && unique
) {
289 c
->unique_name
= strdup(unique
);
293 c
->mask
|= SD_BUS_CREDS_UNIQUE_NAME
;
296 need_pid
= (mask
& SD_BUS_CREDS_PID
) ||
297 ((mask
& SD_BUS_CREDS_AUGMENT
) &&
298 (mask
& (SD_BUS_CREDS_UID
|SD_BUS_CREDS_SUID
|SD_BUS_CREDS_FSUID
|
299 SD_BUS_CREDS_GID
|SD_BUS_CREDS_EGID
|SD_BUS_CREDS_SGID
|SD_BUS_CREDS_FSGID
|
300 SD_BUS_CREDS_SUPPLEMENTARY_GIDS
|
301 SD_BUS_CREDS_COMM
|SD_BUS_CREDS_EXE
|SD_BUS_CREDS_CMDLINE
|
302 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
|
303 SD_BUS_CREDS_EFFECTIVE_CAPS
|SD_BUS_CREDS_PERMITTED_CAPS
|SD_BUS_CREDS_INHERITABLE_CAPS
|SD_BUS_CREDS_BOUNDING_CAPS
|
304 SD_BUS_CREDS_SELINUX_CONTEXT
|
305 SD_BUS_CREDS_AUDIT_SESSION_ID
|SD_BUS_CREDS_AUDIT_LOGIN_UID
)));
306 need_uid
= mask
& SD_BUS_CREDS_EUID
;
307 need_selinux
= mask
& SD_BUS_CREDS_SELINUX_CONTEXT
;
309 if (need_pid
+ need_uid
+ need_selinux
> 1) {
311 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
313 r
= sd_bus_call_method(
315 "org.freedesktop.DBus",
316 "/org/freedesktop/DBus",
317 "org.freedesktop.DBus",
318 "GetConnectionCredentials",
326 if (!sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
))
329 /* If we got an unknown method error, fall back to the invidual calls... */
330 need_separate_calls
= true;
331 sd_bus_error_free(&error
);
334 need_separate_calls
= false;
336 r
= sd_bus_message_enter_container(reply
, 'a', "{sv}");
343 r
= sd_bus_message_enter_container(reply
, 'e', "sv");
349 r
= sd_bus_message_read(reply
, "s", &m
);
353 if (need_uid
&& streq(m
, "UnixUserID")) {
356 r
= sd_bus_message_read(reply
, "v", "u", &u
);
361 c
->mask
|= SD_BUS_CREDS_EUID
;
363 } else if (need_pid
&& streq(m
, "ProcessID")) {
366 r
= sd_bus_message_read(reply
, "v", "u", &p
);
371 if (mask
& SD_BUS_CREDS_PID
) {
373 c
->mask
|= SD_BUS_CREDS_PID
;
376 } else if (need_selinux
&& streq(m
, "LinuxSecurityLabel")) {
377 const void *p
= NULL
;
380 r
= sd_bus_message_enter_container(reply
, 'v', "ay");
384 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
389 c
->label
= strndup(p
, sz
);
393 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
395 r
= sd_bus_message_exit_container(reply
);
399 r
= sd_bus_message_skip(reply
, "v");
404 r
= sd_bus_message_exit_container(reply
);
409 r
= sd_bus_message_exit_container(reply
);
413 if (need_pid
&& pid
== 0)
417 } else /* When we only need a single field, then let's use separate calls */
418 need_separate_calls
= true;
420 if (need_separate_calls
) {
424 r
= sd_bus_call_method(
426 "org.freedesktop.DBus",
427 "/org/freedesktop/DBus",
428 "org.freedesktop.DBus",
429 "GetConnectionUnixProcessID",
437 r
= sd_bus_message_read(reply
, "u", &u
);
442 if (mask
& SD_BUS_CREDS_PID
) {
444 c
->mask
|= SD_BUS_CREDS_PID
;
447 reply
= sd_bus_message_unref(reply
);
453 r
= sd_bus_call_method(
455 "org.freedesktop.DBus",
456 "/org/freedesktop/DBus",
457 "org.freedesktop.DBus",
458 "GetConnectionUnixUser",
462 unique
? unique
: name
);
466 r
= sd_bus_message_read(reply
, "u", &u
);
471 c
->mask
|= SD_BUS_CREDS_EUID
;
473 reply
= sd_bus_message_unref(reply
);
477 const void *p
= NULL
;
480 r
= sd_bus_call_method(
482 "org.freedesktop.DBus",
483 "/org/freedesktop/DBus",
484 "org.freedesktop.DBus",
485 "GetConnectionSELinuxSecurityContext",
489 unique
? unique
: name
);
491 if (!sd_bus_error_has_name(&error
, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
494 /* no data is fine */
496 r
= sd_bus_message_read_array(reply
, 'y', &p
, &sz
);
500 c
->label
= strndup(p
, sz
);
504 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
509 r
= bus_creds_add_more(c
, mask
, pid
, 0);
522 _public_
int sd_bus_get_name_creds(
526 sd_bus_creds
**creds
) {
528 assert_return(bus
, -EINVAL
);
529 assert_return(name
, -EINVAL
);
530 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
531 assert_return(mask
== 0 || creds
, -EINVAL
);
532 assert_return(!bus_pid_changed(bus
), -ECHILD
);
533 assert_return(service_name_is_valid(name
), -EINVAL
);
535 if (!bus
->bus_client
)
538 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
541 mask
&= ~SD_BUS_CREDS_AUGMENT
;
543 if (streq(name
, "org.freedesktop.DBus.Local"))
546 if (streq(name
, "org.freedesktop.DBus"))
547 return sd_bus_get_owner_creds(bus
, mask
, creds
);
549 if (!BUS_IS_OPEN(bus
->state
))
552 return bus_get_name_creds_dbus1(bus
, name
, mask
, creds
);
555 static int bus_get_owner_creds_dbus1(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
556 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*c
= NULL
;
563 do_label
= bus
->label
&& (mask
& SD_BUS_CREDS_SELINUX_CONTEXT
);
565 /* Avoid allocating anything if we have no chance of returning useful data */
566 if (!bus
->ucred_valid
&& !do_label
)
573 if (bus
->ucred_valid
) {
574 if (bus
->ucred
.pid
> 0) {
575 pid
= c
->pid
= bus
->ucred
.pid
;
576 c
->mask
|= SD_BUS_CREDS_PID
& mask
;
579 if (bus
->ucred
.uid
!= UID_INVALID
) {
580 c
->euid
= bus
->ucred
.uid
;
581 c
->mask
|= SD_BUS_CREDS_EUID
& mask
;
584 if (bus
->ucred
.gid
!= GID_INVALID
) {
585 c
->egid
= bus
->ucred
.gid
;
586 c
->mask
|= SD_BUS_CREDS_EGID
& mask
;
591 c
->label
= strdup(bus
->label
);
595 c
->mask
|= SD_BUS_CREDS_SELINUX_CONTEXT
;
598 r
= bus_creds_add_more(c
, mask
, pid
, 0);
607 _public_
int sd_bus_get_owner_creds(sd_bus
*bus
, uint64_t mask
, sd_bus_creds
**ret
) {
608 assert_return(bus
, -EINVAL
);
609 assert_return((mask
& ~SD_BUS_CREDS_AUGMENT
) <= _SD_BUS_CREDS_ALL
, -EOPNOTSUPP
);
610 assert_return(ret
, -EINVAL
);
611 assert_return(!bus_pid_changed(bus
), -ECHILD
);
613 if (!BUS_IS_OPEN(bus
->state
))
617 mask
&= ~SD_BUS_CREDS_AUGMENT
;
619 return bus_get_owner_creds_dbus1(bus
, mask
, ret
);
622 #define internal_match(bus, m) \
623 ((bus)->hello_flags & KDBUS_HELLO_MONITOR \
624 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
627 static int bus_add_match_internal_dbus1(
636 e
= internal_match(bus
, match
);
638 return sd_bus_call_method(
640 "org.freedesktop.DBus",
641 "/org/freedesktop/DBus",
642 "org.freedesktop.DBus",
650 int bus_add_match_internal(
653 struct bus_match_component
*components
,
654 unsigned n_components
) {
658 if (!bus
->bus_client
)
661 return bus_add_match_internal_dbus1(bus
, match
);
664 static int bus_remove_match_internal_dbus1(
673 e
= internal_match(bus
, match
);
675 return sd_bus_call_method(
677 "org.freedesktop.DBus",
678 "/org/freedesktop/DBus",
679 "org.freedesktop.DBus",
687 int bus_remove_match_internal(
693 if (!bus
->bus_client
)
696 return bus_remove_match_internal_dbus1(bus
, match
);
699 _public_
int sd_bus_get_name_machine_id(sd_bus
*bus
, const char *name
, sd_id128_t
*machine
) {
700 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
704 assert_return(bus
, -EINVAL
);
705 assert_return(name
, -EINVAL
);
706 assert_return(machine
, -EINVAL
);
707 assert_return(!bus_pid_changed(bus
), -ECHILD
);
708 assert_return(service_name_is_valid(name
), -EINVAL
);
710 if (!bus
->bus_client
)
713 if (!BUS_IS_OPEN(bus
->state
))
716 if (streq_ptr(name
, bus
->unique_name
))
717 return sd_id128_get_machine(machine
);
719 r
= sd_bus_message_new_method_call(
724 "org.freedesktop.DBus.Peer",
729 r
= sd_bus_message_set_auto_start(m
, false);
733 r
= sd_bus_call(bus
, m
, 0, NULL
, &reply
);
737 r
= sd_bus_message_read(reply
, "s", &mid
);
741 return sd_id128_from_string(mid
, machine
);