1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-26 14:04:03 +03:00

Merge pull request #27573 from poettering/sd-bus-description

sd-bus: pass bus description (and comm name) to per via socket address binding on AF_UNIX
This commit is contained in:
Lennart Poettering 2023-05-16 06:46:29 -07:00 committed by GitHub
commit 3907b25638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 346 additions and 112 deletions

View File

@ -77,6 +77,8 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
if (r < 0)
return log_error_errno(r, "Failed to allocate bus: %m");
(void) sd_bus_set_description(bus, "busctl");
if (set_monitor) {
r = sd_bus_set_monitor(bus, true);
if (r < 0)

View File

@ -684,7 +684,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
nfd = -EBADF;
TAKE_FD(nfd);
r = bus_check_peercred(bus);
if (r < 0) {
@ -703,7 +703,8 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
r = sd_bus_negotiate_creds(bus, 1,
SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|
SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS|
SD_BUS_CREDS_SELINUX_CONTEXT);
SD_BUS_CREDS_SELINUX_CONTEXT|
SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION);
if (r < 0) {
log_warning_errno(r, "Failed to enable credentials for new connection: %m");
return 0;
@ -721,6 +722,23 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
if (DEBUG_LOGGING) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
const char *comm = NULL, *description = NULL;
pid_t pid = 0;
r = sd_bus_get_owner_creds(bus, SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION, &c);
if (r < 0)
log_warning_errno(r, "Failed to get peer creds, ignoring: %m");
else {
(void) sd_bus_creds_get_pid(c, &pid);
(void) sd_bus_creds_get_comm(c, &comm);
(void) sd_bus_creds_get_description(c, &description);
}
log_debug("Accepting direct incoming connection from " PID_FMT " (%s) [%s]", pid, strna(comm), strna(description));
}
r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0) {
log_warning_errno(r, "Failed to attach new connection bus to event loop: %m");

View File

@ -210,6 +210,10 @@ tests += [
'sources' : files('sd-bus/test-bus-objects.c'),
'dependencies' : threads,
},
{
'sources' : files('sd-bus/test-bus-peersockaddr.c'),
'dependencies' : threads,
},
{
'sources' : files('sd-bus/test-bus-queue-ref-cycle.c'),
'dependencies' : threads,

View File

@ -722,9 +722,51 @@ _public_ int sd_bus_get_name_creds(
return 0;
}
static int parse_sockaddr_string(const char *t, char **ret_comm, char **ret_description) {
_cleanup_free_ char *comm = NULL, *description = NULL;
const char *e, *sl;
assert(t);
assert(ret_comm);
assert(ret_description);
e = strstrafter(t, "/bus/");
if (!e) {
log_debug("Didn't find /bus/ substring in peer socket address, ignoring.");
goto not_found;
}
sl = strchr(e, '/');
if (!sl) {
log_debug("Didn't find / substring after /bus/ in peer socket address, ignoring.");
goto not_found;
}
if (sl - e > 0) {
comm = strndup(e, sl - e);
if (!comm)
return -ENOMEM;
}
sl++;
if (!isempty(sl)) {
description = strdup(sl);
if (!description)
return -ENOMEM;
}
*ret_comm = TAKE_PTR(comm);
*ret_description = TAKE_PTR(description);
return 0;
not_found:
*ret_comm = *ret_description = NULL;
return 0;
}
_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
bool do_label, do_groups;
bool do_label, do_groups, do_sockaddr_peer;
pid_t pid = 0;
int r;
@ -742,9 +784,12 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
do_groups = bus->n_groups != SIZE_MAX && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
do_sockaddr_peer = bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1 &&
bus->sockaddr_peer.sa.sa_family == AF_UNIX &&
bus->sockaddr_peer.un.sun_path[0] == 0;
/* Avoid allocating anything if we have no chance of returning useful data */
if (!bus->ucred_valid && !do_label && !do_groups)
if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer)
return -ENODATA;
c = bus_creds_new();
@ -786,6 +831,35 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
}
if (do_sockaddr_peer) {
_cleanup_free_ char *t = NULL;
assert(bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1);
assert(bus->sockaddr_peer.sa.sa_family == AF_UNIX);
assert(bus->sockaddr_peer.un.sun_path[0] == 0);
/* So this is an abstract namespace socket, good. Now let's find the data we are interested in */
r = make_cstring(bus->sockaddr_peer.un.sun_path + 1,
bus->sockaddr_size_peer - offsetof(struct sockaddr_un, sun_path) - 1,
MAKE_CSTRING_ALLOW_TRAILING_NUL,
&t);
if (r == -ENOMEM)
return r;
if (r < 0)
log_debug_errno(r, "Can't extract string from peer socket address, ignoring: %m");
else {
r = parse_sockaddr_string(t, &c->comm, &c->description);
if (r < 0)
return r;
if (c->comm)
c->mask |= SD_BUS_CREDS_COMM & mask;
if (c->description)
c->mask |= SD_BUS_CREDS_DESCRIPTION & mask;
}
}
r = bus_creds_add_more(c, mask, pid, 0);
if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */
return r;

View File

@ -267,6 +267,8 @@ struct sd_bus {
char *label;
gid_t *groups;
size_t n_groups;
union sockaddr_union sockaddr_peer;
socklen_t sockaddr_size_peer;
uint64_t creds_mask;

View File

@ -22,6 +22,7 @@
#include "memory-util.h"
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
#include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h"
@ -651,6 +652,17 @@ static void bus_get_peercred(sd_bus *b) {
b->n_groups = (size_t) r;
else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
log_debug_errno(r, "Failed to determine peer's group list: %m");
/* Let's query the peers socket address, it might carry information such as the peer's comm or
* description string */
zero(b->sockaddr_peer);
b->sockaddr_size_peer = 0;
socklen_t l = sizeof(b->sockaddr_peer) - 1; /* Leave space for a NUL */
if (getpeername(b->input_fd, &b->sockaddr_peer.sa, &l) < 0)
log_debug_errno(errno, "Failed to get peer's socket address, ignoring: %m");
else
b->sockaddr_size_peer = l;
}
static int bus_socket_start_auth_client(sd_bus *b) {
@ -889,6 +901,50 @@ fail:
return r;
}
static int bind_description(sd_bus *b, int fd, int family) {
_cleanup_free_ char *bind_name = NULL, *comm = NULL;
union sockaddr_union bsa;
const char *d = NULL;
int r;
assert(b);
assert(fd >= 0);
/* If this is an AF_UNIX socket, let's set our client's socket address to carry the description
* string for this bus connection. This is useful for debugging things, as the connection name is
* visible in various socket-related tools, and can even be queried by the server side. */
if (family != AF_UNIX)
return 0;
(void) sd_bus_get_description(b, &d);
/* Generate a recognizable source address in the abstract namespace. We'll include:
* - a random 64bit value (to avoid collisions)
* - our "comm" process name (suppressed if contains "/" to avoid parsing issues)
* - the description string of the bus connection. */
(void) get_process_comm(0, &comm);
if (comm && strchr(comm, '/'))
comm = mfree(comm);
if (!d && !comm) /* skip if we don't have either field, rely on kernel autobind instead */
return 0;
if (asprintf(&bind_name, "@%" PRIx64 "/bus/%s/%s", random_u64(), strempty(comm), strempty(d)) < 0)
return -ENOMEM;
strshorten(bind_name, sizeof_field(struct sockaddr_un, sun_path));
r = sockaddr_un_set_path(&bsa.un, bind_name);
if (r < 0)
return r;
if (bind(fd, &bsa.sa, r) < 0)
return -errno;
return 0;
}
int bus_socket_connect(sd_bus *b) {
bool inotify_done = false;
int r;
@ -911,6 +967,10 @@ int bus_socket_connect(sd_bus *b) {
if (b->input_fd < 0)
return -errno;
r = bind_description(b, b->input_fd, b->sockaddr.sa.sa_family);
if (r < 0)
return r;
b->input_fd = fd_move_above_stdio(b->input_fd);
b->output_fd = b->input_fd;

View File

@ -46,31 +46,25 @@ static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_
return 0;
}
static int server_init(sd_bus **_bus) {
sd_bus *bus = NULL;
static int server_init(sd_bus **ret_bus) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
const char *unique, *desc;
sd_id128_t id;
int r;
const char *unique, *desc;
assert_se(_bus);
assert_se(ret_bus);
r = sd_bus_open_user_with_description(&bus, "my bus!");
if (r < 0) {
log_error_errno(r, "Failed to connect to user bus: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to connect to user bus: %m");
r = sd_bus_get_bus_id(bus, &id);
if (r < 0) {
log_error_errno(r, "Failed to get server ID: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to get server ID: %m");
r = sd_bus_get_unique_name(bus, &unique);
if (r < 0) {
log_error_errno(r, "Failed to get unique name: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to get unique name: %m");
assert_se(sd_bus_get_description(bus, &desc) >= 0);
assert_se(streq(desc, "my bus!"));
@ -80,48 +74,35 @@ static int server_init(sd_bus **_bus) {
log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
if (r < 0) {
log_error_errno(r, "Failed to acquire name: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to acquire name: %m");
r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL);
if (r < 0) {
log_error_errno(r, "Failed to add object: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to add object: %m");
r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL);
if (r < 0) {
log_error_errno(r, "Failed to request match: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "NotifyTo", match_callback, NULL);
if (r < 0) {
log_error_errno(r, "Failed to request match: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
if (r < 0) {
log_error_errno(r, "Failed to add match: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
bus_match_dump(stdout, &bus->match_callbacks, 0);
*_bus = bus;
*ret_bus = TAKE_PTR(bus);
return 0;
fail:
sd_bus_unref(bus);
return r;
}
static int server(sd_bus *bus) {
int r;
static int server(sd_bus *_bus) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = ASSERT_PTR(_bus);
bool client1_gone = false, client2_gone = false;
int r;
while (!client1_gone || !client2_gone) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@ -129,30 +110,26 @@ static int server(sd_bus *bus) {
const char *label = NULL;
r = sd_bus_process(bus, &m);
if (r < 0) {
log_error_errno(r, "Failed to process requests: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to process requests: %m");
if (r == 0) {
r = sd_bus_wait(bus, UINT64_MAX);
if (r < 0) {
log_error_errno(r, "Failed to wait: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to wait: %m");
continue;
}
if (!m)
continue;
sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
(void) sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
(void) sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
log_info("Got message! member=%s pid="PID_FMT" label=%s",
strna(sd_bus_message_get_member(m)),
pid,
strna(label));
/* sd_bus_message_dump(m); */
/* sd_bus_message_rewind(m, true); */
@ -161,40 +138,31 @@ static int server(sd_bus *bus) {
_cleanup_free_ char *lowercase = NULL;
r = sd_bus_message_read(m, "s", &hello);
if (r < 0) {
log_error_errno(r, "Failed to get parameter: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to get parameter: %m");
lowercase = strdup(hello);
if (!lowercase) {
r = log_oom();
goto fail;
}
if (!lowercase)
return log_oom();
ascii_strlower(lowercase);
r = sd_bus_reply_method_return(m, "s", lowercase);
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
r = sd_bus_reply_method_return(m, NULL);
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
client1_gone = true;
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
r = sd_bus_reply_method_return(m, NULL);
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
client2_gone = true;
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
@ -202,56 +170,40 @@ static int server(sd_bus *bus) {
sleep(1);
r = sd_bus_reply_method_return(m, NULL);
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
int fd;
static const char x = 'X';
r = sd_bus_message_read(m, "h", &fd);
if (r < 0) {
log_error_errno(r, "Failed to get parameter: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to get parameter: %m");
log_info("Received fd=%d", fd);
if (write(fd, &x, 1) < 0) {
log_error_errno(errno, "Failed to write to fd: %m");
r = log_error_errno(errno, "Failed to write to fd: %m");
safe_close(fd);
goto fail;
return r;
}
r = sd_bus_reply_method_return(m, NULL);
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
} else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
r = sd_bus_reply_method_error(
m,
&SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
if (r < 0) {
log_error_errno(r, "Failed to send reply: %m");
goto fail;
}
if (r < 0)
return log_error_errno(r, "Failed to send reply: %m");
}
}
r = 0;
fail:
if (bus) {
sd_bus_flush(bus);
sd_bus_unref(bus);
}
return r;
return 0;
}
static void* client1(void *p) {

View File

@ -0,0 +1,127 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <pthread.h>
#include <unistd.h>
#include "sd-bus.h"
#include "fd-util.h"
#include "process-util.h"
#include "socket-util.h"
#include "tests.h"
static void *server(void *p) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_close_ int listen_fd = PTR_TO_INT(p), fd = -EBADF;
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
_cleanup_free_ char *our_comm = NULL;
sd_id128_t id;
int r;
assert_se(sd_id128_randomize(&id) >= 0);
fd = accept4(listen_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
assert_se(fd >= 0);
assert_se(sd_bus_new(&bus) >= 0);
assert_se(sd_bus_set_fd(bus, fd, fd) >= 0);
TAKE_FD(fd);
assert_se(sd_bus_set_server(bus, true, id) >= 0);
assert_se(sd_bus_negotiate_creds(bus, 1, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION) >= 0);
assert_se(sd_bus_start(bus) >= 0);
assert_se(sd_bus_get_owner_creds(bus, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION, &c) >= 0);
uid_t u;
assert_se(sd_bus_creds_get_euid(c, &u) >= 0);
assert_se(u == getuid());
gid_t g;
assert_se(sd_bus_creds_get_egid(c, &g) >= 0);
assert_se(g == getgid());
pid_t pid;
assert_se(sd_bus_creds_get_pid(c, &pid) >= 0);
assert_se(pid == getpid_cached());
const char *comm;
assert_se(sd_bus_creds_get_comm(c, &comm) >= 0);
assert_se(get_process_comm(0, &our_comm) >= 0);
assert_se(streq_ptr(comm, our_comm));
const char *description;
assert_se(sd_bus_creds_get_description(c, &description) >= 0);
assert_se(streq_ptr(description, "wuffwuff"));
for (;;) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
r = sd_bus_process(bus, &m);
assert_se(r >= 0);
if (r == 0) {
assert_se(sd_bus_wait(bus, UINT64_MAX) >= 0);
continue;
}
if (sd_bus_message_is_method_call(m, "foo.foo", "Foo") > 0) {
assert_se(sd_bus_reply_method_return(m, "s", "bar") >= 0);
break;
}
}
return NULL;
}
static void* client(void *p) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *z;
assert_se(sd_bus_new(&bus) >= 0);
assert_se(sd_bus_set_description(bus, "wuffwuff") >= 0);
assert_se(sd_bus_set_address(bus, p) >= 0);
assert_se(sd_bus_start(bus) >= 0);
assert_se(sd_bus_call_method(bus, "foo.foo", "/foo", "foo.foo", "Foo", NULL, &reply, "s", "foo") >= 0);
assert_se(sd_bus_message_read(reply, "s", &z) >= 0);
assert_se(streq_ptr(z, "bar"));
return NULL;
}
TEST(description) {
_cleanup_free_ char *a = NULL;
_cleanup_close_ int fd = -EBADF;
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
socklen_t salen;
pthread_t s, c;
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
assert_se(fd >= 0);
assert_se(bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path)) >= 0); /* force auto-bind */
assert_se(listen(fd, 1) >= 0);
salen = sizeof(sa);
assert_se(getsockname(fd, &sa.sa, &salen) >= 0);
assert_se(salen >= offsetof(struct sockaddr_un, sun_path));
assert_se(sa.un.sun_path[0] == 0);
assert_se(asprintf(&a, "unix:abstract=%s", sa.un.sun_path + 1) >= 0);
assert_se(pthread_create(&s, NULL, server, INT_TO_PTR(fd)) == 0);
TAKE_FD(fd);
assert_se(pthread_create(&c, NULL, client, a) == 0);
assert_se(pthread_join(s, NULL) == 0);
assert_se(pthread_join(c, NULL) == 0);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -22,8 +22,8 @@ struct context {
};
static void *server(void *p) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
struct context *c = p;
sd_bus *bus = NULL;
sd_id128_t id;
bool quit = false;
int r;
@ -97,11 +97,6 @@ static void *server(void *p) {
r = 0;
fail:
if (bus) {
sd_bus_flush(bus);
sd_bus_unref(bus);
}
return INT_TO_PTR(r);
}