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;
558 bool do_label, do_groups;
563 do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
564 do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
566 /* Avoid allocating anything if we have no chance of returning useful data */
567 if (!bus->ucred_valid && !do_label && !do_groups)
574 if (bus->ucred_valid) {
575 if (pid_is_valid(bus->ucred.pid)) {
576 pid = c->pid = bus->ucred.pid;
577 c->mask |= SD_BUS_CREDS_PID & mask;
580 if (uid_is_valid(bus->ucred.uid)) {
581 c->euid = bus->ucred.uid;
582 c->mask |= SD_BUS_CREDS_EUID & mask;
585 if (gid_is_valid(bus->ucred.gid)) {
586 c->egid = bus->ucred.gid;
587 c->mask |= SD_BUS_CREDS_EGID & mask;
592 c->label = strdup(bus->label);
596 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
600 c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
601 if (!c->supplementary_gids)
604 c->n_supplementary_gids = bus->n_groups;
606 c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
609 r = bus_creds_add_more(c, mask, pid, 0);
618 _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
619 assert_return(bus, -EINVAL);
620 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
621 assert_return(ret, -EINVAL);
622 assert_return(!bus_pid_changed(bus), -ECHILD);
624 if (!BUS_IS_OPEN(bus->state))
628 mask &= ~SD_BUS_CREDS_AUGMENT;
630 return bus_get_owner_creds_dbus1(bus, mask, ret);
633 #define internal_match(bus, m) \
635 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
638 static int bus_add_match_internal_dbus1(
647 e = internal_match(bus, match);
649 return sd_bus_call_method(
651 "org.freedesktop.DBus",
652 "/org/freedesktop/DBus",
653 "org.freedesktop.DBus",
661 int bus_add_match_internal(
664 struct bus_match_component *components,
665 unsigned n_components) {
669 if (!bus->bus_client)
672 return bus_add_match_internal_dbus1(bus, match);
675 static int bus_remove_match_internal_dbus1(
684 e = internal_match(bus, match);
686 return sd_bus_call_method(
688 "org.freedesktop.DBus",
689 "/org/freedesktop/DBus",
690 "org.freedesktop.DBus",
698 int bus_remove_match_internal(
704 if (!bus->bus_client)
707 return bus_remove_match_internal_dbus1(bus, match);
710 _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
711 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
715 assert_return(bus, -EINVAL);
716 assert_return(name, -EINVAL);
717 assert_return(machine, -EINVAL);
718 assert_return(!bus_pid_changed(bus), -ECHILD);
719 assert_return(service_name_is_valid(name), -EINVAL);
721 if (!bus->bus_client)
724 if (!BUS_IS_OPEN(bus->state))
727 if (streq_ptr(name, bus->unique_name))
728 return sd_id128_get_machine(machine);
730 r = sd_bus_message_new_method_call(
735 "org.freedesktop.DBus.Peer",
740 r = sd_bus_message_set_auto_start(m, false);
744 r = sd_bus_call(bus, m, 0, NULL, &reply);
748 r = sd_bus_message_read(reply, "s", &mid);
752 return sd_id128_from_string(mid, machine);