From 78e9bb837c258ac0ec7712b3d612cc2f407e731e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 05:50:42 +0200 Subject: Merging upstream version 256. Signed-off-by: Daniel Baumann --- src/libsystemd/sd-bus/bus-common-errors.c | 3 + src/libsystemd/sd-bus/bus-common-errors.h | 3 + src/libsystemd/sd-bus/bus-container.c | 2 +- src/libsystemd/sd-bus/bus-control.c | 139 +++++++++++++++++++----- src/libsystemd/sd-bus/bus-convenience.c | 38 +++---- src/libsystemd/sd-bus/bus-creds.c | 149 +++++++++++++++++++------- src/libsystemd/sd-bus/bus-creds.h | 18 +++- src/libsystemd/sd-bus/bus-dump.c | 2 + src/libsystemd/sd-bus/bus-internal.h | 4 + src/libsystemd/sd-bus/bus-message.c | 4 +- src/libsystemd/sd-bus/bus-objects.c | 2 +- src/libsystemd/sd-bus/bus-socket.c | 112 +++++++++++++++++-- src/libsystemd/sd-bus/bus-track.c | 2 +- src/libsystemd/sd-bus/sd-bus.c | 102 ++++++++++++------ src/libsystemd/sd-bus/test-bus-chat.c | 3 +- src/libsystemd/sd-bus/test-bus-cleanup.c | 2 +- src/libsystemd/sd-bus/test-bus-creds.c | 24 ++++- src/libsystemd/sd-bus/test-bus-error.c | 4 +- src/libsystemd/sd-bus/test-bus-marshal.c | 4 +- src/libsystemd/sd-bus/test-bus-objects.c | 3 +- src/libsystemd/sd-bus/test-bus-peersockaddr.c | 55 +++++++++- 21 files changed, 524 insertions(+), 151 deletions(-) (limited to 'src/libsystemd/sd-bus') diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index df26fd7..de12ec5 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -105,11 +105,13 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_HARDWARE_SERIAL, EOPNOTSUPP), SD_BUS_ERROR_MAP(BUS_ERROR_FILE_IS_PROTECTED, EACCES), SD_BUS_ERROR_MAP(BUS_ERROR_READ_ONLY_FILESYSTEM, EROFS), SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP), SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_ALREADY_RELOADING, EBUSY), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_HOME, EEXIST), SD_BUS_ERROR_MAP(BUS_ERROR_UID_IN_USE, EEXIST), @@ -146,6 +148,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED), SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE), SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY), + SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_REFERENCED, EBADR), SD_BUS_ERROR_MAP_END }; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 3a0eef4..94dc85d 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -106,11 +106,13 @@ #define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" #define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID" +#define BUS_ERROR_NO_HARDWARE_SERIAL "org.freedesktop.hostname1.NoHardwareSerial" #define BUS_ERROR_FILE_IS_PROTECTED "org.freedesktop.hostname1.FileIsProtected" #define BUS_ERROR_READ_ONLY_FILESYSTEM "org.freedesktop.hostname1.ReadOnlyFilesystem" #define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive" #define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface" +#define BUS_ERROR_NETWORK_ALREADY_RELOADING "org.freedesktop.network1.AlreadyReloading" #define BUS_ERROR_NO_SUCH_HOME "org.freedesktop.home1.NoSuchHome" #define BUS_ERROR_UID_IN_USE "org.freedesktop.home1.UIDInUse" @@ -151,5 +153,6 @@ #define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate" #define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse" #define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded" +#define BUS_ERROR_HOME_NOT_REFERENCED "org.freedesktop.home1.HomeNotReferenced" BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 4146a6e..2eca82b 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -34,7 +34,7 @@ int bus_container_connect_socket(sd_bus *b) { log_debug("sd-bus: connecting bus%s%s to namespace of PID "PID_FMT"...", b->description ? " " : "", strempty(b->description), b->nspid); - r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd); if (r < 0) return log_debug_errno(r, "Failed to open namespace of PID "PID_FMT": %m", b->nspid); diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 1355e41..c420584 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -14,6 +14,7 @@ #include "bus-internal.h" #include "bus-message.h" #include "capability-util.h" +#include "fd-util.h" #include "process-util.h" #include "stdio-util.h" #include "string-util.h" @@ -430,7 +431,6 @@ _public_ int sd_bus_get_name_creds( _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL; _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; const char *unique; - pid_t pid = 0; int r; assert_return(bus, -EINVAL); @@ -483,8 +483,9 @@ _public_ int sd_bus_get_name_creds( } if (mask != 0) { + bool need_pid, need_uid, need_gids, need_selinux, need_separate_calls, need_pidfd, need_augment; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - bool need_pid, need_uid, need_selinux, need_separate_calls; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; c = bus_creds_new(); if (!c) @@ -498,20 +499,25 @@ _public_ int sd_bus_get_name_creds( c->mask |= SD_BUS_CREDS_UNIQUE_NAME; } - need_pid = (mask & SD_BUS_CREDS_PID) || - ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - 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| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))); + need_augment = + (mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + 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| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID| + SD_BUS_CREDS_PIDFD)); + + need_pid = (mask & SD_BUS_CREDS_PID) || need_augment; need_uid = mask & SD_BUS_CREDS_EUID; + need_gids = mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS; need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT; + need_pidfd = (mask & SD_BUS_CREDS_PIDFD) || need_augment; - if (need_pid + need_uid + need_selinux > 1) { + if (need_pid + need_uid + need_selinux + need_pidfd + need_gids > 1) { /* If we need more than one of the credentials, then use GetConnectionCredentials() */ @@ -572,7 +578,9 @@ _public_ int sd_bus_get_name_creds( if (r < 0) return r; - pid = p; + if (!pidref_is_set(&pidref)) + pidref = PIDREF_MAKE_FROM_PID(p); + if (mask & SD_BUS_CREDS_PID) { c->pid = p; c->mask |= SD_BUS_CREDS_PID; @@ -599,6 +607,69 @@ _public_ int sd_bus_get_name_creds( r = sd_bus_message_exit_container(reply); if (r < 0) return r; + } else if (need_pidfd && streq(m, "ProcessFD")) { + int fd; + + r = sd_bus_message_read(reply, "v", "h", &fd); + if (r < 0) + return r; + + pidref_done(&pidref); + r = pidref_set_pidfd(&pidref, fd); + if (r < 0) + return r; + + if (mask & SD_BUS_CREDS_PIDFD) { + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) + return -errno; + + close_and_replace(c->pidfd, fd); + c->mask |= SD_BUS_CREDS_PIDFD; + } + } else if (need_gids && streq(m, "UnixGroupIDs")) { + + /* Note that D-Bus actually only gives us a combined list of + * primary gid and supplementary gids. And we don't know + * which one the primary one is. We'll take the whole shebang + * hence and use it as the supplementary group list, and not + * initialize the primary gid field. This is slightly + * incorrect of course, but only slightly, as in effect if + * the primary gid is also listed in the supplementary gid + * it has zero effect. */ + + r = sd_bus_message_enter_container(reply, 'v', "au"); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'a', "u"); + if (r < 0) + return r; + + for (;;) { + uint32_t u; + + r = sd_bus_message_read(reply, "u", &u); + if (r < 0) + return r; + if (r == 0) + break; + + if (!GREEDY_REALLOC(c->supplementary_gids, c->n_supplementary_gids+1)) + return -ENOMEM; + + c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) u; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; } else { r = sd_bus_message_skip(reply, "v"); if (r < 0) @@ -614,7 +685,7 @@ _public_ int sd_bus_get_name_creds( if (r < 0) return r; - if (need_pid && pid == 0) + if (need_pid && !pidref_is_set(&pidref)) return -EPROTO; } @@ -642,7 +713,9 @@ _public_ int sd_bus_get_name_creds( if (r < 0) return r; - pid = u; + if (!pidref_is_set(&pidref)) + pidref = PIDREF_MAKE_FROM_PID(u); + if (mask & SD_BUS_CREDS_PID) { c->pid = u; c->mask |= SD_BUS_CREDS_PID; @@ -710,9 +783,11 @@ _public_ int sd_bus_get_name_creds( } } - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */ - return r; + if (pidref_is_set(&pidref)) { + r = bus_creds_add_more(c, mask, &pidref, 0); + if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */ + return r; + } } if (creds) @@ -765,8 +840,8 @@ not_found: _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, do_sockaddr_peer; - pid_t pid = 0; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + bool do_label, do_groups, do_sockaddr_peer, do_pidfd; int r; assert_return(bus, -EINVAL); @@ -786,9 +861,10 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r 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; + do_pidfd = bus->pidfd >= 0 && (mask & SD_BUS_CREDS_PIDFD); /* Avoid allocating anything if we have no chance of returning useful data */ - if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer) + if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer && !do_pidfd) return -ENODATA; c = bus_creds_new(); @@ -797,8 +873,10 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r if (bus->ucred_valid) { if (pid_is_valid(bus->ucred.pid)) { - pid = c->pid = bus->ucred.pid; + c->pid = bus->ucred.pid; c->mask |= SD_BUS_CREDS_PID & mask; + + pidref = PIDREF_MAKE_FROM_PID(c->pid); } if (uid_is_valid(bus->ucred.uid)) { @@ -859,7 +937,20 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r } } - r = bus_creds_add_more(c, mask, pid, 0); + if (do_pidfd) { + c->pidfd = fcntl(bus->pidfd, F_DUPFD_CLOEXEC, 3); + if (c->pidfd < 0) + return -errno; + + pidref_done(&pidref); + r = pidref_set_pidfd(&pidref, bus->pidfd); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_PIDFD; + } + + r = bus_creds_add_more(c, mask, &pidref, 0); if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */ return r; diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 989e577..14d8073 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -640,8 +640,8 @@ _public_ int sd_bus_set_property( } _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **ret) { + uint64_t missing; sd_bus_creds *c; - int r; assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); @@ -653,36 +653,22 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b return -ENOTCONN; c = sd_bus_message_get_creds(call); - - /* All data we need? */ - if (c && (mask & ~SD_BUS_CREDS_AUGMENT & ~c->mask) == 0) { + if (c) + missing = mask & ~SD_BUS_CREDS_AUGMENT & ~c->mask; + else + missing = mask & ~SD_BUS_CREDS_AUGMENT; + if (missing == 0) { /* All data we need? */ *ret = sd_bus_creds_ref(c); return 0; } - /* No data passed? Or not enough data passed to retrieve the missing bits? */ - if (!c || !(c->mask & SD_BUS_CREDS_PID)) { - /* We couldn't read anything from the call, let's try - * to get it from the sender or peer. */ - - if (call->sender) - /* There's a sender, but the creds are missing. */ - return sd_bus_get_name_creds(call->bus, call->sender, mask, ret); - else - /* There's no sender. For direct connections - * the credentials of the AF_UNIX peer matter, - * which may be queried via sd_bus_get_owner_creds(). */ - return sd_bus_get_owner_creds(call->bus, mask, ret); - } - - r = bus_creds_extend_by_pid(c, mask, ret); - if (r == -ESRCH) { - /* Process doesn't exist anymore? propagate the few things we have */ - *ret = sd_bus_creds_ref(c); - return 0; - } + /* There's a sender, use that */ + if (call->sender && call->bus->bus_client) + return sd_bus_get_name_creds(call->bus, call->sender, mask, ret); - return r; + /* There's no sender. For direct connections the credentials of the AF_UNIX peer matter, which may be + * queried via sd_bus_get_owner_creds(). */ + return sd_bus_get_owner_creds(call->bus, mask, ret); } _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index c6d8caa..adbdeaa 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -53,6 +53,8 @@ void bus_creds_done(sd_bus_creds *c) { * below. */ strv_free(c->cmdline_array); + + safe_close(c->pidfd); } _public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { @@ -129,46 +131,72 @@ _public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { sd_bus_creds* bus_creds_new(void) { sd_bus_creds *c; - c = new0(sd_bus_creds, 1); + c = new(sd_bus_creds, 1); if (!c) return NULL; - c->allocated = true; - c->n_ref = 1; + *c = (sd_bus_creds) { + .allocated = true, + .n_ref = 1, + SD_BUS_CREDS_INIT_FIELDS, + }; + return c; } -_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) { +static int bus_creds_new_from_pidref(sd_bus_creds **ret, PidRef *pidref, uint64_t mask) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; int r; - assert_return(pid >= 0, -EINVAL); assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); assert_return(ret, -EINVAL); - if (pid == 0) - pid = getpid_cached(); - c = bus_creds_new(); if (!c) return -ENOMEM; - r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0); + r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pidref, 0); if (r < 0) return r; - /* Check if the process existed at all, in case we haven't - * figured that out already */ - r = pid_is_alive(pid); + r = pidref_verify(pidref); if (r < 0) return r; - if (r == 0) - return -ESRCH; *ret = TAKE_PTR(c); return 0; } +_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + r = pidref_set_pid(&pidref, pid); + if (r < 0) + return r; + + return bus_creds_new_from_pidref(ret, &pidref, mask); +} + +_public_ int sd_bus_creds_new_from_pidfd(sd_bus_creds **ret, int pidfd, uint64_t mask) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + int r; + + assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + assert_return(pidfd >= 0, -EBADF); + + r = pidref_set_pidfd(&pidref, pidfd); + if (r < 0) + return r; + + return bus_creds_new_from_pidref(ret, &pidref, mask); +} + _public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) { assert_return(c, -EINVAL); assert_return(uid, -EINVAL); @@ -280,6 +308,23 @@ _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) { return 0; } +_public_ int sd_bus_creds_get_pidfd_dup(sd_bus_creds *c, int *ret_fd) { + _cleanup_close_ int copy = -EBADF; + + assert_return(c, -EINVAL); + assert_return(ret_fd, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PIDFD)) + return -ENODATA; + + copy = fcntl(c->pidfd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; + + *ret_fd = TAKE_FD(copy); + return 0; +} + _public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) { assert_return(c, -EINVAL); assert_return(ppid, -EINVAL); @@ -731,7 +776,7 @@ static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) { return -ENOMEM; } - for (i = 0; i < sz; i ++) { + for (i = 0; i < sz; i++) { uint32_t v = 0; for (j = 0; j < 8; ++j) { @@ -750,7 +795,8 @@ static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) { return 0; } -int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { +int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, PidRef *pidref, pid_t tid) { + _cleanup_(pidref_done) PidRef pidref_buf = PIDREF_NULL; uint64_t missing; int r; @@ -761,12 +807,26 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { return 0; /* Try to retrieve PID from creds if it wasn't passed to us */ - if (pid > 0) { - c->pid = pid; + if (pidref_is_set(pidref)) { + if ((c->mask & SD_BUS_CREDS_PID) && c->pid != pidref->pid) /* Insist that things match if already set */ + return -EBUSY; + + c->pid = pidref->pid; c->mask |= SD_BUS_CREDS_PID; - } else if (c->mask & SD_BUS_CREDS_PID) - pid = c->pid; - else + } else if (c->mask & SD_BUS_CREDS_PIDFD) { + r = pidref_set_pidfd(&pidref_buf, c->pidfd); + if (r < 0) + return r; + + pidref = &pidref_buf; + + } else if (c->mask & SD_BUS_CREDS_PID) { + r = pidref_set_pid(&pidref_buf, c->pid); + if (r < 0) + return r; + + pidref = &pidref_buf; + } else /* Without pid we cannot do much... */ return 0; @@ -784,6 +844,14 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { c->mask |= SD_BUS_CREDS_TID; } + if ((missing & SD_BUS_CREDS_PIDFD) && pidref->fd >= 0) { + c->pidfd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3); + if (c->pidfd < 0) + return -errno; + + c->mask |= SD_BUS_CREDS_PIDFD; + } + if (missing & (SD_BUS_CREDS_PPID | SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | @@ -794,13 +862,13 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { _cleanup_fclose_ FILE *f = NULL; const char *p; - p = procfs_file_alloca(pid, "status"); + p = procfs_file_alloca(pidref->pid, "status"); f = fopen(p, "re"); if (!f) { if (errno == ENOENT) return -ESRCH; - else if (!ERRNO_IS_PRIVILEGE(errno)) + if (!ERRNO_IS_PRIVILEGE(errno)) return -errno; } else { @@ -958,7 +1026,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) { const char *p; - p = procfs_file_alloca(pid, "attr/current"); + p = procfs_file_alloca(pidref->pid, "attr/current"); r = read_one_line_file(p, &c->label); if (r < 0) { if (!IN_SET(r, -ENOENT, -EINVAL, -EPERM, -EACCES)) @@ -968,7 +1036,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { } if (missing & SD_BUS_CREDS_COMM) { - r = pid_get_comm(pid, &c->comm); + r = pid_get_comm(pidref->pid, &c->comm); if (r < 0) { if (!ERRNO_IS_PRIVILEGE(r)) return r; @@ -977,7 +1045,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { } if (missing & SD_BUS_CREDS_EXE) { - r = get_process_exe(pid, &c->exe); + r = get_process_exe(pidref->pid, &c->exe); if (r == -ESRCH) { /* Unfortunately we cannot really distinguish * the case here where the process does not @@ -998,7 +1066,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_CMDLINE) { const char *p; - p = procfs_file_alloca(pid, "cmdline"); + p = procfs_file_alloca(pidref->pid, "cmdline"); r = read_full_virtual_file(p, &c->cmdline, &c->cmdline_size); if (r == -ENOENT) return -ESRCH; @@ -1016,7 +1084,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { _cleanup_free_ char *p = NULL; - if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0) + if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pidref->pid, tid) < 0) return -ENOMEM; r = read_one_line_file(p, &c->tid_comm); @@ -1032,7 +1100,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { if (!c->cgroup) { - r = cg_pid_get_path(NULL, pid, &c->cgroup); + r = cg_pid_get_path(NULL, pidref->pid, &c->cgroup); if (r < 0) { if (!ERRNO_IS_PRIVILEGE(r)) return r; @@ -1050,7 +1118,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { } if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { - r = audit_session_from_pid(pid, &c->audit_session_id); + r = audit_session_from_pid(pidref->pid, &c->audit_session_id); if (r == -ENODATA) { /* ENODATA means: no audit session id assigned */ c->audit_session_id = AUDIT_SESSION_INVALID; @@ -1063,7 +1131,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { } if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { - r = audit_loginuid_from_pid(pid, &c->audit_login_uid); + r = audit_loginuid_from_pid(pidref->pid, &c->audit_login_uid); if (r == -ENODATA) { /* ENODATA means: no audit login uid assigned */ c->audit_login_uid = UID_INVALID; @@ -1076,7 +1144,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { } if (missing & SD_BUS_CREDS_TTY) { - r = get_ctty(pid, NULL, &c->tty); + r = get_ctty(pidref->pid, NULL, &c->tty); if (r == -ENXIO) { /* ENXIO means: process has no controlling TTY */ c->tty = NULL; @@ -1088,16 +1156,12 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { c->mask |= SD_BUS_CREDS_TTY; } - /* In case only the exe path was to be read we cannot distinguish the case where the exe path was - * unreadable because the process was a kernel thread, or when the process didn't exist at - * all. Hence, let's do a final check, to be sure. */ - r = pid_is_alive(pid); + r = pidref_verify(pidref); if (r < 0) return r; - if (r == 0) - return -ESRCH; - if (tid > 0 && tid != pid && pid_is_unwaited(tid) == 0) + /* Validate tid is still valid, too */ + if (tid > 0 && tid != pidref->pid && pid_is_unwaited(tid) == 0) return -ESRCH; c->augmented = missing & c->mask; @@ -1131,6 +1195,13 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->mask |= SD_BUS_CREDS_PID; } + if (c->mask & mask & SD_BUS_CREDS_PIDFD) { + n->pidfd = fcntl(c->pidfd, F_DUPFD_CLOEXEC, 3); + if (n->pidfd < 0) + return -errno; + n->mask |= SD_BUS_CREDS_PIDFD; + } + if (c->mask & mask & SD_BUS_CREDS_TID) { n->tid = c->tid; n->mask |= SD_BUS_CREDS_TID; diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h index 7806d9e..f45de1c 100644 --- a/src/libsystemd/sd-bus/bus-creds.h +++ b/src/libsystemd/sd-bus/bus-creds.h @@ -5,6 +5,9 @@ #include "sd-bus.h" +#include "pidref.h" +#include "user-util.h" + struct sd_bus_creds { bool allocated; unsigned n_ref; @@ -27,6 +30,7 @@ struct sd_bus_creds { pid_t ppid; pid_t pid; pid_t tid; + int pidfd; char *comm; char *tid_comm; @@ -63,10 +67,22 @@ struct sd_bus_creds { char *description, *unescaped_description; }; +#define SD_BUS_CREDS_INIT_FIELDS \ + .uid = UID_INVALID, \ + .euid = UID_INVALID, \ + .suid = UID_INVALID, \ + .fsuid = UID_INVALID, \ + .gid = GID_INVALID, \ + .egid = GID_INVALID, \ + .sgid = GID_INVALID, \ + .fsgid = GID_INVALID, \ + .pidfd = -EBADF, \ + .audit_login_uid = UID_INVALID + sd_bus_creds* bus_creds_new(void); void bus_creds_done(sd_bus_creds *c); -int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid); +int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, PidRef *pidref, pid_t tid); int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret); diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 6d24f3b..aa46fec 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -355,6 +355,8 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { if (c->mask & SD_BUS_CREDS_PID) fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); + if (c->mask & SD_BUS_CREDS_PIDFD) + fprintf(f, "%sPIDFD=%syes%s", prefix, color, suffix); if (c->mask & SD_BUS_CREDS_TID) fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); if (c->mask & SD_BUS_CREDS_PPID) { diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 098a518..e0f4746 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -254,6 +254,9 @@ struct sd_bus { char *address; unsigned address_index; + uid_t connect_as_uid; + gid_t connect_as_gid; + int last_connect_error; enum bus_auth auth; @@ -269,6 +272,7 @@ struct sd_bus { size_t n_groups; union sockaddr_union sockaddr_peer; socklen_t sockaddr_size_peer; + int pidfd; uint64_t creds_mask; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index ab8b068..296f450 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -373,6 +373,7 @@ static int message_from_header( if (!m) return -ENOMEM; + m->creds = (sd_bus_creds) { SD_BUS_CREDS_INIT_FIELDS }; m->sealed = true; m->header = buffer; @@ -469,6 +470,7 @@ _public_ int sd_bus_message_new( return -ENOMEM; t->n_ref = 1; + t->creds = (sd_bus_creds) { SD_BUS_CREDS_INIT_FIELDS }; t->bus = sd_bus_ref(bus); t->header = (struct bus_header*) ((uint8_t*) t + ALIGN(sizeof(struct sd_bus_message))); t->header->endian = BUS_NATIVE_ENDIAN; @@ -627,7 +629,7 @@ static int message_new_reply( return r; } - t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED); + t->dont_send = FLAGS_SET(call->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED); t->enforced_reply_signature = call->enforced_reply_signature; /* let's copy the sensitive flag over. Let's do that as a safety precaution to keep a transaction diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index c25c40f..e528987 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1701,7 +1701,7 @@ static bool names_are_valid(const char *signature, const char **names, names_fla if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0') *flags |= NAMES_PRESENT; - for (;*flags & NAMES_PRESENT;) { + while (*flags & NAMES_PRESENT) { size_t l; if (!*signature) diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 5ade8e9..07179e0 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -217,7 +217,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) { /* And possibly check the third line, too */ if (b->accept_fd) { l = lines[i++]; - b->can_fds = !!memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD"); + b->can_fds = memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD"); } assert(i == n); @@ -266,7 +266,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { if (l % 2 != 0) return 0; - r = unhexmem(p, l, (void **) &token, &len); + r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len); if (r < 0) return 0; @@ -298,7 +298,7 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) { if (l % 2 != 0) return 0; - r = unhexmem(p, l, (void**) &token, &len); + r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len); if (r < 0) return 0; @@ -503,11 +503,38 @@ static int bus_socket_write_auth(sd_bus *b) { if (b->prefer_writev) k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); else { + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {}; + struct msghdr mh = { .msg_iov = b->auth_iovec + b->auth_index, .msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index, }; + if (uid_is_valid(b->connect_as_uid) || gid_is_valid(b->connect_as_gid)) { + + /* If we shall connect under some specific UID/GID, then synthesize an + * SCM_CREDENTIALS record accordingly. After all we want to adopt this UID/GID both + * for SO_PEERCRED (where we have to fork()) and SCM_CREDENTIALS (where we can just + * fake it via sendmsg()) */ + + struct ucred ucred = { + .pid = getpid_cached(), + .uid = uid_is_valid(b->connect_as_uid) ? b->connect_as_uid : getuid(), + .gid = gid_is_valid(b->connect_as_gid) ? b->connect_as_gid : getgid(), + }; + + mh.msg_control = &control; + mh.msg_controllen = sizeof(control); + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh); + *cmsg = (struct cmsghdr) { + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_CREDENTIALS, + .cmsg_len = CMSG_LEN(sizeof(struct ucred)), + }; + + memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred)); + } + k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); if (k < 0 && errno == ENOTSOCK) { b->prefer_writev = true; @@ -643,14 +670,20 @@ static void bus_get_peercred(sd_bus *b) { /* Get the SELinux context of the peer */ r = getpeersec(b->input_fd, &b->label); if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) - log_debug_errno(r, "Failed to determine peer security context: %m"); + log_debug_errno(r, "Failed to determine peer security context, ignoring: %m"); /* Get the list of auxiliary groups of the peer */ r = getpeergroups(b->input_fd, &b->groups); if (r >= 0) 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"); + log_debug_errno(r, "Failed to determine peer's group list, ignoring: %m"); + + r = getpeerpidfd(b->input_fd); + if (r < 0) + log_debug_errno(r, "Failed to determine peer pidfd, ignoring: %m"); + else + close_and_replace(b->pidfd, r); /* Let's query the peers socket address, it might carry information such as the peer's comm or * description string */ @@ -943,6 +976,66 @@ static int bind_description(sd_bus *b, int fd, int family) { return 0; } +static int connect_as(int fd, const struct sockaddr *sa, socklen_t salen, uid_t uid, gid_t gid) { + _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR; + ssize_t n; + int r; + + /* Shortcut if we are not supposed to drop privileges */ + if (!uid_is_valid(uid) && !gid_is_valid(gid)) + return RET_NERRNO(connect(fd, sa, salen)); + + /* This changes identity to the specified uid/gid and issues connect() as that. This is useful to + * make sure SO_PEERCRED reports the selected UID/GID rather than the usual one of the caller. */ + + if (pipe2(pfd, O_CLOEXEC) < 0) + return -errno; + + r = safe_fork("(sd-setresuid)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, /* ret_pid= */ NULL); + if (r < 0) + return r; + if (r == 0) { + /* child */ + + pfd[0] = safe_close(pfd[0]); + + r = RET_NERRNO(setgroups(0, NULL)); + if (r < 0) + goto child_finish; + + if (gid_is_valid(gid)) { + r = RET_NERRNO(setresgid(gid, gid, gid)); + if (r < 0) + goto child_finish; + } + + if (uid_is_valid(uid)) { + r = RET_NERRNO(setresuid(uid, uid, uid)); + if (r < 0) + goto child_finish; + } + + r = RET_NERRNO(connect(fd, sa, salen)); + if (r < 0) + goto child_finish; + + r = 0; + + child_finish: + n = write(pfd[1], &r, sizeof(r)); + if (n != sizeof(r)) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + n = read(pfd[0], &r, sizeof(r)); + if (n != sizeof(r)) + return -EIO; + + return r; +} + int bus_socket_connect(sd_bus *b) { bool inotify_done = false; int r; @@ -974,8 +1067,9 @@ int bus_socket_connect(sd_bus *b) { b->output_fd = b->input_fd; bus_socket_setup(b); - if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) { - if (errno == EINPROGRESS) { + r = connect_as(b->input_fd, &b->sockaddr.sa, b->sockaddr_size, b->connect_as_uid, b->connect_as_gid); + if (r < 0) { + if (r == -EINPROGRESS) { /* If we have any inotify watches open, close them now, we don't need them anymore, as * we have successfully initiated a connection */ @@ -988,7 +1082,7 @@ int bus_socket_connect(sd_bus *b) { return 1; } - if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */ + if (IN_SET(r, -ENOENT, -ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */ b->watch_bind && b->sockaddr.sa.sa_family == AF_UNIX && b->sockaddr.un.sun_path[0] != 0) { @@ -1016,7 +1110,7 @@ int bus_socket_connect(sd_bus *b) { inotify_done = true; } else - return -errno; + return r; } else break; } diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index f9c59a1..6f6fa2d 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -69,7 +69,7 @@ static void bus_track_add_to_queue(sd_bus_track *track) { return; /* still referenced? */ - if (hashmap_size(track->names) > 0) + if (!hashmap_isempty(track->names)) return; /* Nothing to call? */ diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 8befc97..1a642cb 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -30,6 +30,7 @@ #include "constants.h" #include "errno-util.h" #include "fd-util.h" +#include "format-util.h" #include "glyph-util.h" #include "hexdecoct.h" #include "hostname-util.h" @@ -151,6 +152,14 @@ void bus_close_inotify_fd(sd_bus *b) { b->n_inotify_watches = 0; } +static void bus_close_fds(sd_bus *b) { + assert(b); + + bus_close_io_fds(b); + bus_close_inotify_fd(b); + b->pidfd = safe_close(b->pidfd); +} + static void bus_reset_queues(sd_bus *b) { assert(b); @@ -191,8 +200,7 @@ static sd_bus* bus_free(sd_bus *b) { if (b->default_bus_ptr) *b->default_bus_ptr = NULL; - bus_close_io_fds(b); - bus_close_inotify_fd(b); + bus_close_fds(b); free(b->label); free(b->groups); @@ -256,7 +264,10 @@ _public_ int sd_bus_new(sd_bus **ret) { .n_groups = SIZE_MAX, .close_on_exit = true, .ucred = UCRED_INVALID, + .pidfd = -EBADF, .runtime_scope = _RUNTIME_SCOPE_INVALID, + .connect_as_uid = UID_INVALID, + .connect_as_gid = GID_INVALID, }; /* We guarantee that wqueue always has space for at least one entry */ @@ -321,7 +332,7 @@ _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { assert_return(!bus->patch_sender, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->bus_client = !!b; + bus->bus_client = b; return 0; } @@ -331,7 +342,7 @@ _public_ int sd_bus_set_monitor(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->is_monitor = !!b; + bus->is_monitor = b; return 0; } @@ -341,7 +352,7 @@ _public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->accept_fd = !!b; + bus->accept_fd = b; return 0; } @@ -353,7 +364,7 @@ _public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { /* This is not actually supported by any of our transports these days, but we do honour it for synthetic * replies, and maybe one day classic D-Bus learns this too */ - bus->attach_timestamp = !!b; + bus->attach_timestamp = b; return 0; } @@ -380,7 +391,7 @@ _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->is_server = !!b; + bus->is_server = b; bus->server_id = server_id; return 0; } @@ -391,7 +402,7 @@ _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->anonymous_auth = !!b; + bus->anonymous_auth = b; return 0; } @@ -401,7 +412,7 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->trusted = !!b; + bus->trusted = b; return 0; } @@ -419,7 +430,7 @@ _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->allow_interactive_authorization = !!b; + bus->allow_interactive_authorization = b; return 0; } @@ -437,7 +448,7 @@ _public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->watch_bind = !!b; + bus->watch_bind = b; return 0; } @@ -455,7 +466,7 @@ _public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_origin_changed(bus), -ECHILD); - bus->connected_signal = !!b; + bus->connected_signal = b; return 0; } @@ -640,7 +651,7 @@ int bus_start_running(sd_bus *bus) { static int parse_address_key(const char **p, const char *key, char **value) { _cleanup_free_ char *r = NULL; - size_t l, n = 0; + size_t n = 0; const char *a; assert(p); @@ -648,17 +659,14 @@ static int parse_address_key(const char **p, const char *key, char **value) { assert(value); if (key) { - l = strlen(key); - if (strncmp(*p, key, l) != 0) - return 0; - - if ((*p)[l] != '=') + a = startswith(*p, key); + if (!a || *a != '=') return 0; if (*value) return -EINVAL; - a = *p + l + 1; + a++; } else a = *p; @@ -717,7 +725,7 @@ static void skip_address_key(const char **p) { } static int parse_unix_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *path = NULL, *abstract = NULL; + _cleanup_free_ char *path = NULL, *abstract = NULL, *uids = NULL, *gids = NULL; size_t l; int r; @@ -745,6 +753,18 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) { else if (r > 0) continue; + r = parse_address_key(p, "uid", &uids); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "gid", &gids); + if (r < 0) + return r; + else if (r > 0) + continue; + skip_address_key(p); } @@ -781,6 +801,17 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) { b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l; } + if (uids) { + r = parse_uid(uids, &b->connect_as_uid); + if (r < 0) + return r; + } + if (gids) { + r = parse_gid(gids, &b->connect_as_gid); + if (r < 0) + return r; + } + b->is_local = true; return 0; @@ -1102,8 +1133,7 @@ static int bus_start_address(sd_bus *b) { assert(b); for (;;) { - bus_close_io_fds(b); - bus_close_inotify_fd(b); + bus_close_fds(b); bus_kill_exec(b); @@ -1486,9 +1516,15 @@ interpret_port_as_machine_old_syntax: return -ENOMEM; } - a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p), - ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e, - ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c); + const char *ssh = secure_getenv("SYSTEMD_SSH") ?: "ssh"; + _cleanup_free_ char *ssh_escaped = bus_address_escape(ssh); + if (!ssh_escaped) + return -ENOMEM; + + a = strjoin("unixexec:path=", ssh_escaped, ",argv1=-xT", + p ? ",argv2=-p,argv3=" : "", strempty(p), + ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e, + ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c); if (!a) return -ENOMEM; @@ -1668,10 +1704,7 @@ static int user_and_machine_equivalent(const char *user_and_machine) { return true; /* Otherwise, we have to figure out our user id and name, and compare things with that. */ - char buf[DECIMAL_STR_MAX(uid_t)]; - xsprintf(buf, UID_FMT, uid); - - f = startswith(user_and_machine, buf); + f = startswith(user_and_machine, FORMAT_UID(uid)); if (!f) { un = getusername_malloc(); if (!un) @@ -1775,8 +1808,7 @@ _public_ void sd_bus_close(sd_bus *bus) { * the bus object and the bus may be freed */ bus_reset_queues(bus); - bus_close_io_fds(bus); - bus_close_inotify_fd(bus); + bus_close_fds(bus); } _public_ sd_bus *sd_bus_close_unref(sd_bus *bus) { @@ -4123,13 +4155,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template for (template_pos = path_template; *template_pos; ) { const char *sep; - size_t length; + size_t length, path_length; char *label; /* verify everything until the next '%' matches verbatim */ sep = strchrnul(template_pos, '%'); length = sep - template_pos; - if (strncmp(path_pos, template_pos, length)) + if (!strneq(path_pos, template_pos, length)) return 0; path_pos += length; @@ -4150,8 +4182,8 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template /* verify the suffixes match */ sep = strchrnul(path_pos, '/'); - if (sep - path_pos < (ssize_t)length || - strncmp(sep - length, template_pos, length)) + path_length = sep - path_pos; + if (length > path_length || !strneq(sep - length, template_pos, length)) return 0; template_pos += length; /* skip over matched label */ diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index da1340f..d06853b 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -432,8 +432,7 @@ static void* client2(void *p) { if (r < 0) log_debug("Failed to issue method call: %s", bus_error_message(&error, r)); else { - log_error("Slow call unexpectedly succeed."); - r = -ENOANO; + r = log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Slow call unexpectedly succeeded."); goto finish; } diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c index 3e14627..b569986 100644 --- a/src/libsystemd/sd-bus/test-bus-cleanup.c +++ b/src/libsystemd/sd-bus/test-bus-cleanup.c @@ -30,7 +30,7 @@ static void test_bus_fork(void) { r = safe_fork("(bus-fork-test)", FORK_WAIT|FORK_LOG, NULL); if (r == 0) { assert_se(bus); - assert_se(sd_bus_is_ready(bus) == -ECHILD); + ASSERT_RETURN_EXPECTED_SE(sd_bus_is_ready(bus) == -ECHILD); assert_se(sd_bus_flush_close_unref(bus) == NULL); assert_se(sd_bus_close_unref(bus) == NULL); assert_se(sd_bus_unref(bus) == NULL); diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 13801be..7eb7a38 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -4,6 +4,7 @@ #include "bus-dump.h" #include "cgroup-util.h" +#include "errno-util.h" #include "tests.h" int main(int argc, char *argv[]) { @@ -12,7 +13,7 @@ int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); - if (cg_unified() == -ENOMEDIUM) + if (IN_SET(cg_unified(), -ENOMEDIUM, -ENOENT)) return log_tests_skipped("/sys/fs/cgroup/ not available"); r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); @@ -24,11 +25,30 @@ int main(int argc, char *argv[]) { creds = sd_bus_creds_unref(creds); r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL); - if (r != -EACCES) { + if (!ERRNO_IS_NEG_PRIVILEGE(r)) { assert_se(r >= 0); putchar('\n'); bus_creds_dump(creds, NULL, true); } + creds = sd_bus_creds_unref(creds); + + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + r = sd_bus_default_system(&bus); + if (r < 0) + log_warning_errno(r, "Unable to connect to system bus, skipping rest of test."); + else { + const char *unique; + + assert_se(sd_bus_get_unique_name(bus, &unique) >= 0); + + r = sd_bus_get_name_creds(bus, unique, _SD_BUS_CREDS_ALL, &creds); + log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG, r, "sd_bus_get_name_creds: %m"); + assert_se(r >= 0); + + putchar('\n'); + bus_creds_dump(creds, NULL, true); + } + return 0; } diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index a55f3f9..91045c0 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -213,8 +213,8 @@ TEST(errno_mapping_custom) { assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); - assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL); - assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); + ASSERT_RETURN_EXPECTED_SE(sd_bus_error_add_map(test_errors_bad1) == -EINVAL); + ASSERT_RETURN_EXPECTED_SE(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); } TEST(sd_bus_error_set_errnof) { diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index 0044d33..92da627 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -42,8 +42,8 @@ static void test_bus_path_encode(void) { assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL); assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo")); - assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0); - assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0); + ASSERT_RETURN_EXPECTED_SE(sd_bus_path_encode("xxxx", "waldo", &c) < 0); + ASSERT_RETURN_EXPECTED_SE(sd_bus_path_encode("/foo/", "waldo", &c) < 0); assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_")); assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, "")); diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index ccdd0d5..2847ba8 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -494,10 +494,9 @@ static int client(struct context *c) { } assert_se(sd_bus_message_exit_container(reply) >= 0); - if (streq(path, "/value/a")) { + if (streq(path, "/value/a")) /* ObjectManager must be here */ assert_se(found_object_manager_interface); - } } else assert_se(sd_bus_message_skip(reply, "a{sa{sv}}") >= 0); diff --git a/src/libsystemd/sd-bus/test-bus-peersockaddr.c b/src/libsystemd/sd-bus/test-bus-peersockaddr.c index 79556e8..a7bba17 100644 --- a/src/libsystemd/sd-bus/test-bus-peersockaddr.c +++ b/src/libsystemd/sd-bus/test-bus-peersockaddr.c @@ -5,10 +5,39 @@ #include "sd-bus.h" +#include "bus-dump.h" +#include "bus-util.h" #include "fd-util.h" #include "process-util.h" #include "socket-util.h" +#include "sort-util.h" #include "tests.h" +#include "user-util.h" + +static bool gid_list_contained(const gid_t *a, size_t n, const gid_t *b, size_t m) { + assert_se(a || n == 0); + assert_se(b || m == 0); + + /* Checks if every entry in a[] is also in b[] */ + + for (size_t i = 0; i < n; i++) { + size_t j; + + for (j = 0; j < m; j++) + if (a[i] == b[j]) + break; + + if (j >= m) + return false; + } + + return true; +} + +static bool gid_list_same(const gid_t *a, size_t n, const gid_t *b, size_t m) { + return gid_list_contained(a, n, b, m) && + gid_list_contained(b, m, a, n); +} static void *server(void *p) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -27,11 +56,13 @@ static void *server(void *p) { 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_negotiate_creds(bus, 1, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_PIDFD|SD_BUS_CREDS_SUPPLEMENTARY_GIDS) >= 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); + 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|SD_BUS_CREDS_PIDFD|SD_BUS_CREDS_SUPPLEMENTARY_GIDS, &c) >= 0); + + bus_creds_dump(c, /* f= */ NULL, /* terse= */ false); uid_t u; assert_se(sd_bus_creds_get_euid(c, &u) >= 0); @@ -45,6 +76,26 @@ static void *server(void *p) { assert_se(sd_bus_creds_get_pid(c, &pid) >= 0); assert_se(pid == getpid_cached()); + int pidfd = -EBADF; + if (sd_bus_creds_get_pidfd_dup(c, &pidfd) >= 0) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + + assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0); + assert_se(pidref.pid == getpid_cached()); + } + + const gid_t *gl = NULL; + int n; + n = sd_bus_creds_get_supplementary_gids(c, &gl); + + if (n >= 0) { + _cleanup_free_ gid_t *gg = NULL; + r = getgroups_alloc(&gg); + assert_se(r >= 0); + + assert_se(gid_list_same(gl, n, gg, r)); + } + const char *comm; assert_se(sd_bus_creds_get_comm(c, &comm) >= 0); assert_se(pid_get_comm(0, &our_comm) >= 0); -- cgit v1.2.3