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 validate_request_name_parameters(
64 uint32_t *ret_param) {
72 assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
73 assert_return(service_name_is_valid(name), -EINVAL);
74 assert_return(name[0] != ':', -EINVAL);
79 /* Don't allow requesting the special driver and local names */
80 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
83 if (!BUS_IS_OPEN(bus->state))
86 if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
87 param |= BUS_NAME_ALLOW_REPLACEMENT;
88 if (flags & SD_BUS_NAME_REPLACE_EXISTING)
89 param |= BUS_NAME_REPLACE_EXISTING;
90 if (!(flags & SD_BUS_NAME_QUEUE))
91 param |= BUS_NAME_DO_NOT_QUEUE;
98 _public_ int sd_bus_request_name(
103 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
104 uint32_t ret, param = 0;
107 assert_return(bus, -EINVAL);
108 assert_return(name, -EINVAL);
109 assert_return(!bus_pid_changed(bus), -ECHILD);
111 r = validate_request_name_parameters(bus, name, flags, ¶m);
115 r = sd_bus_call_method(
117 "org.freedesktop.DBus",
118 "/org/freedesktop/DBus",
119 "org.freedesktop.DBus",
129 r = sd_bus_message_read(reply, "u", &ret);
135 case BUS_NAME_ALREADY_OWNER:
138 case BUS_NAME_EXISTS:
141 case BUS_NAME_IN_QUEUE:
144 case BUS_NAME_PRIMARY_OWNER:
151 static int default_request_name_handler(
154 sd_bus_error *ret_error) {
161 if (sd_bus_message_is_method_error(m, NULL)) {
162 log_debug_errno(sd_bus_message_get_errno(m),
163 "Unable to request name, failing connection: %s",
164 sd_bus_message_get_error(m)->message);
166 bus_enter_closing(sd_bus_message_get_bus(m));
170 r = sd_bus_message_read(m, "u", &ret);
176 case BUS_NAME_ALREADY_OWNER:
177 log_debug("Already owner of requested service name, ignoring.");
180 case BUS_NAME_IN_QUEUE:
181 log_debug("In queue for requested service name.");
184 case BUS_NAME_PRIMARY_OWNER:
185 log_debug("Successfully acquired requested service name.");
188 case BUS_NAME_EXISTS:
189 log_debug("Requested service name already owned, failing connection.");
190 bus_enter_closing(sd_bus_message_get_bus(m));
194 log_debug("Unexpected response from RequestName(), failing connection.");
195 bus_enter_closing(sd_bus_message_get_bus(m));
199 _public_ int sd_bus_request_name_async(
201 sd_bus_slot **ret_slot,
204 sd_bus_message_handler_t callback,
210 assert_return(bus, -EINVAL);
211 assert_return(name, -EINVAL);
212 assert_return(!bus_pid_changed(bus), -ECHILD);
214 r = validate_request_name_parameters(bus, name, flags, ¶m);
218 return sd_bus_call_method_async(
221 "org.freedesktop.DBus",
222 "/org/freedesktop/DBus",
223 "org.freedesktop.DBus",
225 callback ?: default_request_name_handler,
232 static int validate_release_name_parameters(
239 assert_return(service_name_is_valid(name), -EINVAL);
240 assert_return(name[0] != ':', -EINVAL);
242 if (!bus->bus_client)
245 /* Don't allow releasing the special driver and local names */
246 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
249 if (!BUS_IS_OPEN(bus->state))
255 _public_ int sd_bus_release_name(
259 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
263 assert_return(bus, -EINVAL);
264 assert_return(name, -EINVAL);
265 assert_return(!bus_pid_changed(bus), -ECHILD);
267 r = validate_release_name_parameters(bus, name);
271 r = sd_bus_call_method(
273 "org.freedesktop.DBus",
274 "/org/freedesktop/DBus",
275 "org.freedesktop.DBus",
284 r = sd_bus_message_read(reply, "u", &ret);
290 case BUS_NAME_NON_EXISTENT:
293 case BUS_NAME_NOT_OWNER:
296 case BUS_NAME_RELEASED:
303 static int default_release_name_handler(
306 sd_bus_error *ret_error) {
313 if (sd_bus_message_is_method_error(m, NULL)) {
314 log_debug_errno(sd_bus_message_get_errno(m),
315 "Unable to release name, failing connection: %s",
316 sd_bus_message_get_error(m)->message);
318 bus_enter_closing(sd_bus_message_get_bus(m));
322 r = sd_bus_message_read(m, "u", &ret);
328 case BUS_NAME_NON_EXISTENT:
329 log_debug("Name asked to release is not taken currently, ignoring.");
332 case BUS_NAME_NOT_OWNER:
333 log_debug("Name asked to release is owned by somebody else, ignoring.");
336 case BUS_NAME_RELEASED:
337 log_debug("Name successfully released.");
341 log_debug("Unexpected response from ReleaseName(), failing connection.");
342 bus_enter_closing(sd_bus_message_get_bus(m));
346 _public_ int sd_bus_release_name_async(
348 sd_bus_slot **ret_slot,
350 sd_bus_message_handler_t callback,
355 assert_return(bus, -EINVAL);
356 assert_return(name, -EINVAL);
357 assert_return(!bus_pid_changed(bus), -ECHILD);
359 r = validate_release_name_parameters(bus, name);
363 return sd_bus_call_method_async(
366 "org.freedesktop.DBus",
367 "/org/freedesktop/DBus",
368 "org.freedesktop.DBus",
370 callback ?: default_release_name_handler,
376 _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
377 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
378 _cleanup_strv_free_ char **x = NULL, **y = NULL;
381 assert_return(bus, -EINVAL);
382 assert_return(acquired || activatable, -EINVAL);
383 assert_return(!bus_pid_changed(bus), -ECHILD);
385 if (!bus->bus_client)
388 if (!BUS_IS_OPEN(bus->state))
392 r = sd_bus_call_method(
394 "org.freedesktop.DBus",
395 "/org/freedesktop/DBus",
396 "org.freedesktop.DBus",
404 r = sd_bus_message_read_strv(reply, &x);
408 reply = sd_bus_message_unref(reply);
412 r = sd_bus_call_method(
414 "org.freedesktop.DBus",
415 "/org/freedesktop/DBus",
416 "org.freedesktop.DBus",
417 "ListActivatableNames",
424 r = sd_bus_message_read_strv(reply, &y);
440 _public_ int sd_bus_get_name_creds(
444 sd_bus_creds **creds) {
446 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
447 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
448 const char *unique = NULL;
452 assert_return(bus, -EINVAL);
453 assert_return(name, -EINVAL);
454 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
455 assert_return(mask == 0 || creds, -EINVAL);
456 assert_return(!bus_pid_changed(bus), -ECHILD);
457 assert_return(service_name_is_valid(name), -EINVAL);
459 if (!bus->bus_client)
462 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
465 mask &= ~SD_BUS_CREDS_AUGMENT;
467 if (streq(name, "org.freedesktop.DBus.Local"))
470 if (streq(name, "org.freedesktop.DBus"))
471 return sd_bus_get_owner_creds(bus, mask, creds);
473 if (!BUS_IS_OPEN(bus->state))
476 /* Only query the owner if the caller wants to know it or if
477 * the caller just wants to check whether a name exists */
478 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
479 r = sd_bus_call_method(
481 "org.freedesktop.DBus",
482 "/org/freedesktop/DBus",
483 "org.freedesktop.DBus",
492 r = sd_bus_message_read(reply_unique, "s", &unique);
498 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
499 bool need_pid, need_uid, need_selinux, need_separate_calls;
504 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
505 c->unique_name = strdup(unique);
509 c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
512 need_pid = (mask & SD_BUS_CREDS_PID) ||
513 ((mask & SD_BUS_CREDS_AUGMENT) &&
514 (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
515 SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
516 SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
517 SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
518 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|
519 SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
520 SD_BUS_CREDS_SELINUX_CONTEXT|
521 SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)));
522 need_uid = mask & SD_BUS_CREDS_EUID;
523 need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT;
525 if (need_pid + need_uid + need_selinux > 1) {
527 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
529 r = sd_bus_call_method(
531 "org.freedesktop.DBus",
532 "/org/freedesktop/DBus",
533 "org.freedesktop.DBus",
534 "GetConnectionCredentials",
542 if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
545 /* If we got an unknown method error, fall back to the invidual calls... */
546 need_separate_calls = true;
547 sd_bus_error_free(&error);
550 need_separate_calls = false;
552 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
559 r = sd_bus_message_enter_container(reply, 'e', "sv");
565 r = sd_bus_message_read(reply, "s", &m);
569 if (need_uid && streq(m, "UnixUserID")) {
572 r = sd_bus_message_read(reply, "v", "u", &u);
577 c->mask |= SD_BUS_CREDS_EUID;
579 } else if (need_pid && streq(m, "ProcessID")) {
582 r = sd_bus_message_read(reply, "v", "u", &p);
587 if (mask & SD_BUS_CREDS_PID) {
589 c->mask |= SD_BUS_CREDS_PID;
592 } else if (need_selinux && streq(m, "LinuxSecurityLabel")) {
593 const void *p = NULL;
596 r = sd_bus_message_enter_container(reply, 'v', "ay");
600 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
605 c->label = strndup(p, sz);
609 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
611 r = sd_bus_message_exit_container(reply);
615 r = sd_bus_message_skip(reply, "v");
620 r = sd_bus_message_exit_container(reply);
625 r = sd_bus_message_exit_container(reply);
629 if (need_pid && pid == 0)
633 } else /* When we only need a single field, then let's use separate calls */
634 need_separate_calls = true;
636 if (need_separate_calls) {
640 r = sd_bus_call_method(
642 "org.freedesktop.DBus",
643 "/org/freedesktop/DBus",
644 "org.freedesktop.DBus",
645 "GetConnectionUnixProcessID",
653 r = sd_bus_message_read(reply, "u", &u);
658 if (mask & SD_BUS_CREDS_PID) {
660 c->mask |= SD_BUS_CREDS_PID;
663 reply = sd_bus_message_unref(reply);
669 r = sd_bus_call_method(
671 "org.freedesktop.DBus",
672 "/org/freedesktop/DBus",
673 "org.freedesktop.DBus",
674 "GetConnectionUnixUser",
678 unique ? unique : name);
682 r = sd_bus_message_read(reply, "u", &u);
687 c->mask |= SD_BUS_CREDS_EUID;
689 reply = sd_bus_message_unref(reply);
693 const void *p = NULL;
696 r = sd_bus_call_method(
698 "org.freedesktop.DBus",
699 "/org/freedesktop/DBus",
700 "org.freedesktop.DBus",
701 "GetConnectionSELinuxSecurityContext",
705 unique ? unique : name);
707 if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
710 /* no data is fine */
712 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
716 c->label = strndup(p, sz);
720 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
725 r = bus_creds_add_more(c, mask, pid, 0);
738 _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
739 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
740 bool do_label, do_groups;
744 assert_return(bus, -EINVAL);
745 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
746 assert_return(ret, -EINVAL);
747 assert_return(!bus_pid_changed(bus), -ECHILD);
749 if (!BUS_IS_OPEN(bus->state))
753 mask &= ~SD_BUS_CREDS_AUGMENT;
755 do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
756 do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
758 /* Avoid allocating anything if we have no chance of returning useful data */
759 if (!bus->ucred_valid && !do_label && !do_groups)
766 if (bus->ucred_valid) {
767 if (pid_is_valid(bus->ucred.pid)) {
768 pid = c->pid = bus->ucred.pid;
769 c->mask |= SD_BUS_CREDS_PID & mask;
772 if (uid_is_valid(bus->ucred.uid)) {
773 c->euid = bus->ucred.uid;
774 c->mask |= SD_BUS_CREDS_EUID & mask;
777 if (gid_is_valid(bus->ucred.gid)) {
778 c->egid = bus->ucred.gid;
779 c->mask |= SD_BUS_CREDS_EGID & mask;
784 c->label = strdup(bus->label);
788 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
792 c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
793 if (!c->supplementary_gids)
796 c->n_supplementary_gids = bus->n_groups;
798 c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
801 r = bus_creds_add_more(c, mask, pid, 0);
810 #define append_eavesdrop(bus, m) \
812 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
815 int bus_add_match_internal(
823 if (!bus->bus_client)
826 e = append_eavesdrop(bus, match);
828 return sd_bus_call_method(
830 "org.freedesktop.DBus",
831 "/org/freedesktop/DBus",
832 "org.freedesktop.DBus",
840 int bus_remove_match_internal(
849 if (!bus->bus_client)
852 e = append_eavesdrop(bus, match);
854 /* Fire and forget */
856 return sd_bus_call_method_async(
859 "org.freedesktop.DBus",
860 "/org/freedesktop/DBus",
861 "org.freedesktop.DBus",
869 _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
870 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
874 assert_return(bus, -EINVAL);
875 assert_return(name, -EINVAL);
876 assert_return(machine, -EINVAL);
877 assert_return(!bus_pid_changed(bus), -ECHILD);
878 assert_return(service_name_is_valid(name), -EINVAL);
880 if (!bus->bus_client)
883 if (!BUS_IS_OPEN(bus->state))
886 if (streq_ptr(name, bus->unique_name))
887 return sd_id128_get_machine(machine);
889 r = sd_bus_message_new_method_call(
894 "org.freedesktop.DBus.Peer",
899 r = sd_bus_message_set_auto_start(m, false);
903 r = sd_bus_call(bus, m, 0, NULL, &reply);
907 r = sd_bus_message_read(reply, "s", &mid);
911 return sd_id128_from_string(mid, machine);