From 26fde72265073b26498ce55596c0eda1bc3113b4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 16 Sep 2024 20:27:56 +0200 Subject: Adding upstream version 256.6. Signed-off-by: Daniel Baumann --- src/basic/audit-util.c | 2 +- src/basic/missing_loop.h | 4 ++ src/basic/process-util.c | 26 +++++++-- src/basic/virt.c | 7 +++ src/boot/bootctl-install.c | 2 +- src/core/namespace.c | 22 +++++--- src/home/homectl.c | 2 + src/libsystemd-network/sd-lldp-tx.c | 1 + src/libsystemd-network/test-dhcp-server.c | 6 ++- src/libsystemd/sd-netlink/test-netlink.c | 16 ++++-- src/login/logind-dbus.c | 12 +++++ src/login/logind-dbus.h | 1 + src/login/logind-session.c | 8 +-- src/network/networkd-lldp-tx.c | 30 +++++++++++ src/network/networkd-lldp-tx.h | 1 + src/network/networkd-sysctl.c | 63 +++++++++++++++++++++- src/nspawn/nspawn.c | 10 ++-- src/partition/repart.c | 12 ++--- src/resolve/resolved-dns-packet.h | 1 + src/resolve/resolved-dns-stub.c | 10 +++- src/shared/ask-password-api.c | 29 +++++++--- src/shared/copy.c | 8 ++- src/shared/copy.h | 39 +++++++------- src/shared/dissect-image.c | 1 + src/shared/tests.c | 19 +++++++ src/shared/tests.h | 34 ++++++++++++ src/test/test-acl-util.c | 2 +- src/test/test-capability.c | 7 ++- src/test/test-chase.c | 4 +- src/test/test-chown-rec.c | 4 +- src/test/test-condition.c | 7 +++ src/test/test-fs-util.c | 4 +- src/test/test-macro.c | 12 +++++ src/test/test-rm-rf.c | 3 ++ src/test/test-socket-util.c | 2 +- .../tty-ask-password-agent.c | 4 +- src/udev/udevadm-test-builtin.c | 1 + src/udev/udevadm-test.c | 1 + src/ukify/test/test_ukify.py | 7 +++ 39 files changed, 344 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index bf96e08..7f86f84 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -99,7 +99,7 @@ static int try_audit_request(int fd) { n = recvmsg_safe(fd, &mh, 0); if (n < 0) - return -errno; + return n; if (n != NLMSG_LENGTH(sizeof(struct nlmsgerr))) return -EIO; diff --git a/src/basic/missing_loop.h b/src/basic/missing_loop.h index b88501d..f83a14c 100644 --- a/src/basic/missing_loop.h +++ b/src/basic/missing_loop.h @@ -29,3 +29,7 @@ assert_cc(LOOP_SET_DIRECT_IO == 0x4C08); #ifndef LOOP_SET_STATUS_SETTABLE_FLAGS # define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN) #endif + +#ifndef LOOP_SET_BLOCK_SIZE +# define LOOP_SET_BLOCK_SIZE 0x4C09 +#endif diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c9d968d..de5a146 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2066,9 +2066,10 @@ int posix_spawn_wrapper( _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr; #if HAVE_PIDFD_SPAWN + static bool setcgroup_supported = true; _cleanup_close_ int cgroup_fd = -EBADF; - if (cgroup) { + if (cgroup && setcgroup_supported) { _cleanup_free_ char *resolved_cgroup = NULL; r = cg_get_path_and_check( @@ -2102,6 +2103,19 @@ int posix_spawn_wrapper( _cleanup_close_ int pidfd = -EBADF; r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); + if (r == E2BIG && FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP)) { + /* Some kernels (e.g., 5.4) support clone3 but they do not support CLONE_INTO_CGROUP. + * Retry pidfd_spawn() after removing the flag. */ + flags &= ~POSIX_SPAWN_SETCGROUP; + r = posix_spawnattr_setflags(&attr, flags); + if (r != 0) + return -r; + r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); + /* if pidfd_spawn was successful after removing SPAWN_CGROUP, + * mark setcgroup_supported as false so that we do not retry every time */ + if (r == 0) + setcgroup_supported = false; + } if (r == 0) { r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd)); if (r < 0) @@ -2120,10 +2134,12 @@ int posix_spawn_wrapper( /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the * flags to remove the cgroup one, which is what redirects to clone3() */ - flags &= ~POSIX_SPAWN_SETCGROUP; - r = posix_spawnattr_setflags(&attr, flags); - if (r != 0) - return -r; + if (FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP)) { + flags &= ~POSIX_SPAWN_SETCGROUP; + r = posix_spawnattr_setflags(&attr, flags); + if (r != 0) + return -r; + } #endif pid_t pid; diff --git a/src/basic/virt.c b/src/basic/virt.c index 0970350..2cb3a08 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -896,6 +896,13 @@ int running_in_chroot(void) { * mount /proc, so all other programs can assume that if /proc is *not* available, we're in some * chroot. */ + r = getenv_bool("SYSTEMD_IN_CHROOT"); + if (r >= 0) + return r > 0; + if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_IN_CHROOT, ignoring: %m"); + + /* Deprecated but kept for backwards compatibility. */ if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) return 0; diff --git a/src/boot/bootctl-install.c b/src/boot/bootctl-install.c index dc46d30..fd26b43 100644 --- a/src/boot/bootctl-install.c +++ b/src/boot/bootctl-install.c @@ -45,7 +45,7 @@ static int load_etc_machine_info(void) { _cleanup_free_ char *p = NULL, *s = NULL, *layout = NULL; int r; - p = path_join(arg_root, "etc/machine-info"); + p = path_join(arg_root, "/etc/machine-info"); if (!p) return log_oom(); diff --git a/src/core/namespace.c b/src/core/namespace.c index a9b98bc..b92bb01 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1565,12 +1565,24 @@ static int apply_one_mount( if (r < 0) return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m)); - r = load_extension_release_pairs(mount_entry_source(m), IMAGE_SYSEXT, extension_name, /* relax_extension_release_check= */ false, &extension_release); + r = load_extension_release_pairs( + mount_entry_source(m), + IMAGE_SYSEXT, + extension_name, + /* relax_extension_release_check= */ false, + &extension_release); if (r == -ENOENT) { - r = load_extension_release_pairs(mount_entry_source(m), IMAGE_CONFEXT, extension_name, /* relax_extension_release_check= */ false, &extension_release); + r = load_extension_release_pairs( + mount_entry_source(m), + IMAGE_CONFEXT, + extension_name, + /* relax_extension_release_check= */ false, + &extension_release); if (r >= 0) class = IMAGE_CONFEXT; } + if (r == -ENOENT && m->ignore) + return 0; if (r < 0) return log_debug_errno(r, "Failed to acquire 'extension-release' data of extension tree %s: %m", mount_entry_source(m)); @@ -1585,12 +1597,6 @@ static int apply_one_mount( if (isempty(host_os_release_id)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s'.", empty_to_root(root_directory)); - r = load_extension_release_pairs(mount_entry_source(m), class, extension_name, /* relax_extension_release_check= */ false, &extension_release); - if (r == -ENOENT && m->ignore) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name); - r = extension_release_validate( extension_name, host_os_release_id, diff --git a/src/home/homectl.c b/src/home/homectl.c index d9321a2..5c454f7 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -1243,6 +1243,8 @@ static int acquire_new_password( if (r < 0) return log_error_errno(r, "Failed to acquire password: %m"); + assert(!strv_isempty(first)); + question = mfree(question); if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0) return log_oom(); diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index 2b822af..01c476e 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -590,6 +590,7 @@ int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) { return 1; } + int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) { usec_t delay; int r; diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index ecd030e..228b166 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -127,6 +127,7 @@ static void test_message_handler(void) { .s_addr = htobe32(INADDR_LOOPBACK + 42), }; static uint8_t static_lease_client_id[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' }; + int r; log_debug("/* %s */", __func__); @@ -137,7 +138,10 @@ static void test_message_handler(void) { assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); assert_se(sd_dhcp_server_start(server) >= 0); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_OFFER); + r = dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL); + if (r == -ENETDOWN) + return (void) log_tests_skipped("Network is not available"); + assert_se(r == DHCP_OFFER); test.end = 0; /* TODO, shouldn't this fail? */ diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index cf19c48..c58cfc0 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -92,18 +92,24 @@ TEST(message_address) { struct in_addr in_data; struct ifa_cacheinfo cache; const char *label; + int r; assert_se(sd_netlink_open(&rtnl) >= 0); ifindex = (int) if_nametoindex("lo"); assert_se(sd_rtnl_message_new_addr(rtnl, &message, RTM_GETADDR, ifindex, AF_INET) >= 0); assert_se(sd_netlink_message_set_request_dump(message, true) >= 0); - assert_se(sd_netlink_call(rtnl, message, 0, &reply) == 1); - assert_se(sd_netlink_message_read_in_addr(reply, IFA_LOCAL, &in_data) >= 0); - assert_se(sd_netlink_message_read_in_addr(reply, IFA_ADDRESS, &in_data) >= 0); - assert_se(sd_netlink_message_read_string(reply, IFA_LABEL, &label) >= 0); - assert_se(sd_netlink_message_read_cache_info(reply, IFA_CACHEINFO, &cache) == 0); + r = sd_netlink_call(rtnl, message, 0, &reply); + assert_se(r >= 0); + + /* If the loopback device is down we won't get any results. */ + if (r > 0) { + assert_se(sd_netlink_message_read_in_addr(reply, IFA_LOCAL, &in_data) >= 0); + assert_se(sd_netlink_message_read_in_addr(reply, IFA_ADDRESS, &in_data) >= 0); + assert_se(sd_netlink_message_read_string(reply, IFA_LABEL, &label) >= 0); + assert_se(sd_netlink_message_read_cache_info(reply, IFA_CACHEINFO, &cache) == 0); + } } TEST(message_route) { diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ceff268..b0c4896 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -4264,6 +4264,7 @@ int manager_start_scope( const char *slice, const char *description, const char * const *requires, + const char * const *wants, const char * const *extra_after, const char *requires_mounts_for, sd_bus_message *more_properties, @@ -4313,6 +4314,16 @@ int manager_start_scope( return r; } + STRV_FOREACH(i, wants) { + r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i); + if (r < 0) + return r; + } + STRV_FOREACH(i, extra_after) { r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i); if (r < 0) @@ -4375,6 +4386,7 @@ int manager_start_scope( slice, description, requires, + wants, extra_after, requires_mounts_for, more_properties, diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h index 8459d04..d965ba9 100644 --- a/src/login/logind-dbus.h +++ b/src/login/logind-dbus.h @@ -32,6 +32,7 @@ int manager_start_scope( const char *slice, const char *description, const char * const *requires, + const char * const *wants, const char * const *extra_after, const char *requires_mounts_for, sd_bus_message *more_properties, diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 4713aa0..4758cb9 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -746,15 +746,15 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er s->user->slice, description, /* These should have been pulled in explicitly in user_start(). Just to be sure. */ - STRV_MAKE_CONST(s->user->runtime_dir_unit, - SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL), + /* requires = */ STRV_MAKE_CONST(s->user->runtime_dir_unit), + /* wants = */ STRV_MAKE_CONST(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL), /* We usually want to order session scopes after systemd-user-sessions.service * since the unit is used as login session barrier for unprivileged users. However * the barrier doesn't apply for root as sysadmin should always be able to log in * (and without waiting for any timeout to expire) in case something goes wrong * during the boot process. */ - STRV_MAKE_CONST("systemd-logind.service", - SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"), + /* extra_after = */ STRV_MAKE_CONST("systemd-logind.service", + SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"), user_record_home_directory(s->user->user_record), properties, error, diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index f48781e..cc22fbf 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -86,6 +86,36 @@ int link_lldp_tx_configure(Link *link) { return 0; } +int link_lldp_tx_update_capabilities(Link *link) { + int r; + + assert(link); + + if (!link->lldp_tx) + return 0; /* disabled, or not configured yet. */ + + r = sd_lldp_tx_set_capabilities(link->lldp_tx, + SD_LLDP_SYSTEM_CAPABILITIES_STATION | + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, + (link_get_ip_forwarding(link, AF_INET) > 0 || link_get_ip_forwarding(link, AF_INET6) > 0) ? + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION); + if (r < 0) + return r; + + if (sd_lldp_tx_is_running(link->lldp_tx)) { + r = sd_lldp_tx_stop(link->lldp_tx); + if (r < 0) + return r; + + r = sd_lldp_tx_start(link->lldp_tx); + if (r < 0) + return r; + } + + return 0; +} + static const char * const lldp_multicast_mode_table[_SD_LLDP_MULTICAST_MODE_MAX] = { [SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE] = "nearest-bridge", [SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = "non-tpmr-bridge", diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index 73757f1..346eb5c 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -6,5 +6,6 @@ typedef struct Link Link; int link_lldp_tx_configure(Link *link); +int link_lldp_tx_update_capabilities(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_lldp_multicast_mode); diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 68c23e0..a454322 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -7,7 +7,9 @@ #include "af-list.h" #include "missing_network.h" #include "networkd-link.h" +#include "networkd-lldp-tx.h" #include "networkd-manager.h" +#include "networkd-ndisc.h" #include "networkd-network.h" #include "networkd-sysctl.h" #include "socket-util.h" @@ -130,7 +132,7 @@ int link_get_ip_forwarding(Link *link, int family) { return link->manager->ip_forwarding[family == AF_INET6]; } -static int link_set_ip_forwarding(Link *link, int family) { +static int link_set_ip_forwarding_impl(Link *link, int family) { int r, t; assert(link); @@ -151,6 +153,65 @@ static int link_set_ip_forwarding(Link *link, int family) { return 0; } +static int link_reapply_ip_forwarding(Link *link, int family) { + int r, ret = 0; + + assert(link); + assert(IN_SET(family, AF_INET, AF_INET6)); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + (void) link_set_ip_forwarding_impl(link, family); + + r = link_lldp_tx_update_capabilities(link); + if (r < 0) + RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m")); + + if (family == AF_INET6 && !link_ndisc_enabled(link)) { + r = ndisc_stop(link); + if (r < 0) + RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery, ignoring: %m")); + + ndisc_flush(link); + } + + return ret; +} + +static int link_set_ip_forwarding(Link *link, int family) { + int r; + + assert(link); + assert(link->manager); + assert(link->network); + assert(IN_SET(family, AF_INET, AF_INET6)); + + if (!link_is_configured_for_family(link, family)) + return 0; + + /* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and + * re-apply per-link setting for all links. */ + if (FLAGS_SET(link->network->ip_masquerade, family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6) && + link->manager->ip_forwarding[family == AF_INET6] < 0) { + + link->manager->ip_forwarding[family == AF_INET6] = true; + manager_set_ip_forwarding(link->manager, family); + + Link *other; + HASHMAP_FOREACH(other, link->manager->links_by_index) { + r = link_reapply_ip_forwarding(other, family); + if (r < 0) + link_enter_failed(other); + } + + return 0; + } + + /* Otherwise, apply per-link setting for _this_ link. */ + return link_set_ip_forwarding_impl(link, family); +} + static int link_set_ipv4_rp_filter(Link *link) { assert(link); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 7d87116..c6ce36b 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2207,7 +2207,7 @@ static int copy_devnodes(const char *dest) { /* Explicitly warn the user when /dev is already populated. */ if (errno == EEXIST) log_notice("%s/dev/ is pre-mounted and pre-populated. If a pre-mounted /dev/ is provided it needs to be an unpopulated file system.", dest); - if (errno != EPERM) + if (!ERRNO_IS_PRIVILEGE(errno) || arg_uid_shift != 0) return log_error_errno(errno, "mknod(%s) failed: %m", to); /* Some systems abusively restrict mknod but allow bind mounts. */ @@ -2217,12 +2217,12 @@ static int copy_devnodes(const char *dest) { r = mount_nofollow_verbose(LOG_DEBUG, from, to, NULL, MS_BIND, NULL); if (r < 0) return log_error_errno(r, "Both mknod and bind mount (%s) failed: %m", to); + } else { + r = userns_lchown(to, 0, 0); + if (r < 0) + return log_error_errno(r, "chown() of device node %s failed: %m", to); } - r = userns_lchown(to, 0, 0); - if (r < 0) - return log_error_errno(r, "chown() of device node %s failed: %m", to); - dn = path_join("/dev", S_ISCHR(st.st_mode) ? "char" : "block"); if (!dn) return log_oom(); diff --git a/src/partition/repart.c b/src/partition/repart.c index 8a5ce7e..3b7c165 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4925,14 +4925,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { sfd, ".", pfd, fn, UID_INVALID, GID_INVALID, - COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE, + COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS, denylist, subvolumes_by_source_inode); } else r = copy_tree_at( sfd, ".", tfd, ".", UID_INVALID, GID_INVALID, - COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE, + COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS, denylist, subvolumes_by_source_inode); if (r < 0) return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m", @@ -8155,6 +8155,10 @@ static int run(int argc, char *argv[]) { if (!context) return log_oom(); + r = context_read_seed(context, arg_root); + if (r < 0) + return r; + r = context_copy_from(context); if (r < 0) return r; @@ -8232,10 +8236,6 @@ static int run(int argc, char *argv[]) { return r; } - r = context_read_seed(context, arg_root); - if (r < 0) - return r; - /* Make sure each partition has a unique UUID and unique label */ r = context_acquire_partition_uuids_and_labels(context); if (r < 0) diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 1b649af..a43571a 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -111,6 +111,7 @@ static inline uint8_t* DNS_PACKET_DATA(const DnsPacket *p) { #define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1) #define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1) +#define DNS_PACKET_FLAG_AD (UINT16_C(1) << 5) #define DNS_PACKET_FLAG_TC (UINT16_C(1) << 9) static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) { diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 23d4db9..c604a51 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -685,7 +685,8 @@ static int dns_stub_send_failure( static int dns_stub_patch_bypass_reply_packet( DnsPacket **ret, /* Where to place the patched packet */ DnsPacket *original, /* The packet to patch */ - DnsPacket *request) { /* The packet the patched packet shall look like a reply to */ + DnsPacket *request, /* The packet the patched packet shall look like a reply to */ + bool authenticated) { _cleanup_(dns_packet_unrefp) DnsPacket *c = NULL; int r; @@ -725,6 +726,10 @@ static int dns_stub_patch_bypass_reply_packet( DNS_PACKET_HEADER(c)->flags = htobe16(be16toh(DNS_PACKET_HEADER(c)->flags) | DNS_PACKET_FLAG_TC); } + /* Ensure we don't pass along an untrusted ad flag for bypass packets */ + if (!authenticated) + DNS_PACKET_HEADER(c)->flags = htobe16(be16toh(DNS_PACKET_HEADER(c)->flags) & ~DNS_PACKET_FLAG_AD); + *ret = TAKE_PTR(c); return 0; } @@ -745,7 +750,8 @@ static void dns_stub_query_complete(DnsQuery *query) { q->answer_full_packet->protocol == DNS_PROTOCOL_DNS) { _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; - r = dns_stub_patch_bypass_reply_packet(&reply, q->answer_full_packet, q->request_packet); + r = dns_stub_patch_bypass_reply_packet(&reply, q->answer_full_packet, q->request_packet, + FLAGS_SET(q->answer_query_flags, SD_RESOLVED_AUTHENTICATED)); if (r < 0) log_debug_errno(r, "Failed to patch bypass reply packet: %m"); else diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index bf79dc2..042c0ad 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -167,7 +167,16 @@ static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags if (r < 0) return r; - return retrieve_key(serial, ret); + _cleanup_strv_free_erase_ char **l = NULL; + r = retrieve_key(serial, &l); + if (r < 0) + return r; + + if (strv_isempty(l)) + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password from keyring."); + + *ret = TAKE_PTR(l); + return 0; } static int backspace_chars(int ttyfd, size_t p) { @@ -322,8 +331,8 @@ int ask_password_plymouth( return -ENOENT; } else if (IN_SET(buffer[0], 2, 9)) { + _cleanup_strv_free_erase_ char **l = NULL; uint32_t size; - char **l; /* One or more answers */ if (p < 5) @@ -341,15 +350,16 @@ int ask_password_plymouth( if (!l) return -ENOMEM; - *ret = l; - break; + if (strv_isempty(l)) + return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Received an empty password."); + + *ret = TAKE_PTR(l); + return 0; } else /* Unknown packet */ return -EIO; } - - return 0; } #define NO_ECHO "(no echo) " @@ -949,8 +959,8 @@ finish: static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) { _cleanup_(erase_and_freep) char *buffer = NULL; + _cleanup_strv_free_erase_ char **l = NULL; size_t size; - char **l; int r; assert(req); @@ -965,7 +975,10 @@ static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFla if (!l) return -ENOMEM; - *ret = l; + if (strv_isempty(l)) + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password in credential."); + + *ret = TAKE_PTR(l); return 0; } diff --git a/src/shared/copy.c b/src/shared/copy.c index 8389774..9b90afa 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -982,6 +982,7 @@ static int fd_copy_directory( _cleanup_close_ int fdf = -EBADF, fdt = -EBADF; _cleanup_closedir_ DIR *d = NULL; + struct stat dt_st; bool exists; int r; @@ -1026,6 +1027,9 @@ static int fd_copy_directory( if (fdt < 0) return fdt; + if (exists && FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS) && fstat(fdt, &dt_st) < 0) + return -errno; + r = 0; if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) { @@ -1125,7 +1129,9 @@ finish: (void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); - } + } else if (FLAGS_SET(copy_flags, COPY_RESTORE_DIRECTORY_TIMESTAMPS)) + /* If the directory already exists, make sure the timestamps stay the same as before. */ + (void) futimens(fdt, (struct timespec[]) { dt_st.st_atim, dt_st.st_mtim }); if (copy_flags & COPY_FSYNC_FULL) { if (fsync(fdt) < 0) diff --git a/src/shared/copy.h b/src/shared/copy.h index b8fb28a..db95738 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -12,25 +12,26 @@ #include "set.h" typedef enum CopyFlags { - COPY_REFLINK = 1 << 0, /* Try to reflink */ - COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */ - COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */ - COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */ - COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */ - COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */ - COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */ - COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */ - COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */ - COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */ - COPY_FSYNC = 1 << 10, /* fsync() after we are done */ - COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */ - COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */ - COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */ - COPY_HOLES = 1 << 14, /* Copy holes */ - COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */ - COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */ - COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */ - COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */ + COPY_REFLINK = 1 << 0, /* Try to reflink */ + COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */ + COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */ + COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */ + COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */ + COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */ + COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */ + COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */ + COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */ + COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */ + COPY_FSYNC = 1 << 10, /* fsync() after we are done */ + COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */ + COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */ + COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */ + COPY_HOLES = 1 << 14, /* Copy holes */ + COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */ + COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */ + COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */ + COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */ + COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */ } CopyFlags; typedef enum DenyType { diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index a9e211f..6a39010 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3077,6 +3077,7 @@ int dissected_image_decrypt_interactively( if (r < 0) return log_error_errno(r, "Failed to query for passphrase: %m"); + assert(!strv_isempty(z)); passphrase = z[0]; } } diff --git a/src/shared/tests.c b/src/shared/tests.c index 9169513..a919212 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -29,6 +29,7 @@ #include "strv.h" #include "tests.h" #include "tmpfile-util.h" +#include "uid-range.h" char* setup_fake_runtime_dir(void) { char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; @@ -166,6 +167,24 @@ bool have_namespaces(void) { assert_not_reached(); } +bool userns_has_single_user(void) { + _cleanup_(uid_range_freep) UIDRange *uidrange = NULL, *gidrange = NULL; + + /* Check if we're in a user namespace with only a single user mapped in. We special case this + * scenario in a few tests because it's the only kind of namespace that can be created unprivileged + * and as such happens more often than not, so we make sure to deal with it so that all tests pass + * in such environments. */ + + if (uid_range_load_userns(NULL, UID_RANGE_USERNS_INSIDE, &uidrange) < 0) + return false; + + if (uid_range_load_userns(NULL, GID_RANGE_USERNS_INSIDE, &gidrange) < 0) + return false; + + return uidrange->n_entries == 1 && uidrange->entries[0].nr == 1 && + gidrange->n_entries == 1 && gidrange->entries[0].nr == 1; +} + bool can_memlock(void) { /* Let's see if we can mlock() a larger blob of memory. BPF programs are charged against * RLIMIT_MEMLOCK, hence let's first make sure we can lock memory at all, and skip the test if we diff --git a/src/shared/tests.h b/src/shared/tests.h index 21f00db..f904c0d 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -76,6 +76,7 @@ void test_setup_logging(int level); int write_tmpfile(char *pattern, const char *contents); bool have_namespaces(void); +bool userns_has_single_user(void); /* We use the small but non-trivial limit here */ #define CAN_MEMLOCK_SIZE (512 * 1024U) @@ -217,6 +218,39 @@ static inline int run_test_table(void) { } \ }) +/* For funtions that return a boolean on success and a negative errno on failure. */ +#define ASSERT_OK_POSITIVE(expr) \ + ({ \ + typeof(expr) _result = (expr); \ + if (_result < 0) { \ + log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \ + PROJECT_FILE, __LINE__, #expr); \ + abort(); \ + } \ + if (_result == 0) { \ + log_error("%s:%i: Assertion failed: expected \"%s\" to be positive, but it is zero.", \ + PROJECT_FILE, __LINE__, #expr); \ + abort(); \ + } \ + }) + +#define ASSERT_OK_ZERO(expr) \ + ({ \ + typeof(expr) _result = (expr); \ + if (_result < 0) { \ + log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \ + PROJECT_FILE, __LINE__, #expr); \ + abort(); \ + } \ + if (_result != 0) { \ + char _sexpr[DECIMAL_STR_MAX(typeof(expr))]; \ + xsprintf(_sexpr, DECIMAL_STR_FMT(_result), _result); \ + log_error("%s:%i: Assertion failed: expected \"%s\" to be zero, but it is %s.", \ + PROJECT_FILE, __LINE__, #expr, _sexpr); \ + abort(); \ + } \ + }) + #define ASSERT_OK_ERRNO(expr) \ ({ \ typeof(expr) _result = (expr); \ diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c index 0cc9afc..daab75e 100644 --- a/src/test/test-acl-util.c +++ b/src/test/test-acl-util.c @@ -41,7 +41,7 @@ TEST_RET(add_acls_for_user) { cmd = strjoina("getfacl -p ", fn); assert_se(system(cmd) == 0); - if (getuid() == 0) { + if (getuid() == 0 && !userns_has_single_user()) { const char *nobody = NOBODY_USER_NAME; r = get_user_creds(&nobody, &uid, NULL, NULL, NULL, 0); if (r < 0) diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 34f3a91..51bd806 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -318,10 +318,13 @@ int main(int argc, char *argv[]) { show_capabilities(); - test_drop_privileges(); + if (!userns_has_single_user()) + test_drop_privileges(); + test_update_inherited_set(); - fork_test(test_have_effective_cap); + if (!userns_has_single_user()) + fork_test(test_have_effective_cap); if (run_ambient) fork_test(test_apply_ambient_caps); diff --git a/src/test/test-chase.c b/src/test/test-chase.c index 13ee702..c7ca3fd 100644 --- a/src/test/test-chase.c +++ b/src/test/test-chase.c @@ -183,7 +183,7 @@ TEST(chase) { /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */ - if (geteuid() == 0) { + if (geteuid() == 0 && !userns_has_single_user()) { p = strjoina(temp, "/user"); ASSERT_OK(mkdir(p, 0755)); ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY)); @@ -313,7 +313,7 @@ TEST(chase) { r = chase(p, NULL, 0, &result, NULL); assert_se(r == -ENOENT); - if (geteuid() == 0) { + if (geteuid() == 0 && !userns_has_single_user()) { p = strjoina(temp, "/priv1"); ASSERT_OK(mkdir(p, 0755)); diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c index 5d83f59..7558de7 100644 --- a/src/test/test-chown-rec.c +++ b/src/test/test-chown-rec.c @@ -153,8 +153,8 @@ TEST(chown_recursive) { } static int intro(void) { - if (geteuid() != 0) - return log_tests_skipped("not running as root"); + if (geteuid() != 0 || userns_has_single_user()) + return log_tests_skipped("not running as root or in userns with single user"); return EXIT_SUCCESS; } diff --git a/src/test/test-condition.c b/src/test/test-condition.c index be83690..76b2af9 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -1003,6 +1003,13 @@ TEST(condition_test_group) { condition_free(condition); free(gid); + /* In an unprivileged user namespace with the current user mapped to root, all the auxiliary groups + * of the user will be mapped to the nobody group, which means the user in the user namespace is in + * both the root and the nobody group, meaning the next test can't work, so let's skip it in that + * case. */ + if (in_group(NOBODY_GROUP_NAME) && in_group("root")) + return (void) log_tests_skipped("user is in both root and nobody group"); + groupname = (char*)(getegid() == 0 ? NOBODY_GROUP_NAME : "root"); condition = condition_new(CONDITION_GROUP, groupname, false, false); assert_se(condition); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index f2fa51f..09fd995 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -368,8 +368,8 @@ TEST(chmod_and_chown) { struct stat st; const char *p; - if (geteuid() != 0) - return; + if (geteuid() != 0 || userns_has_single_user()) + return (void) log_tests_skipped("not running as root or in userns with single user"); BLOCK_WITH_UMASK(0000); diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 3d5b0cf..b56f5b8 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -1117,6 +1117,18 @@ TEST(ASSERT) { ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT); ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT); + ASSERT_OK_POSITIVE(1); + ASSERT_OK_POSITIVE(255); + ASSERT_SIGNAL(ASSERT_OK_POSITIVE(0), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-1), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK_POSITIVE(-ENOANO), SIGABRT); + + ASSERT_OK_ZERO(0); + ASSERT_SIGNAL(ASSERT_OK_ZERO(1), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK_ZERO(255), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK_ZERO(-1), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK_ZERO(-ENOANO), SIGABRT); + ASSERT_OK_ERRNO(0 >= 0); ASSERT_OK_ERRNO(255 >= 0); ASSERT_OK_ERRNO(printf("Hello world\n")); diff --git a/src/test/test-rm-rf.c b/src/test/test-rm-rf.c index 4c69bd2..e4a4263 100644 --- a/src/test/test-rm-rf.c +++ b/src/test/test-rm-rf.c @@ -89,6 +89,9 @@ static void test_rm_rf_chmod_inner(void) { TEST(rm_rf_chmod) { int r; + if (getuid() == 0 && userns_has_single_user()) + return (void) log_tests_skipped("running as root or in userns with single user"); + if (getuid() == 0) { /* This test only works unpriv (as only then the access mask for the owning user matters), * hence drop privs here */ diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index e34aa10..967ba9d 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -170,7 +170,7 @@ TEST(getpeercred_getpeergroups) { struct ucred ucred; int pair[2] = EBADF_PAIR; - if (geteuid() == 0) { + if (geteuid() == 0 && !userns_has_single_user()) { test_uid = 1; test_gid = 2; test_gids = (gid_t*) gids; diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 31b284b..4e62b84 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -265,9 +265,7 @@ static int process_one_password_file(const char *filename) { return log_error_errno(r, "Failed to query password: %m"); } - if (strv_isempty(passwords)) - return -ECANCELED; - + assert(!strv_isempty(passwords)); r = send_passwords(socket_name, passwords); if (r < 0) return log_error_errno(r, "Failed to send: %m"); diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index 322f627..5815f2c 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -79,6 +79,7 @@ int builtin_main(int argc, char *argv[], void *userdata) { UdevBuiltinCommand cmd; int r; + log_set_max_level(LOG_DEBUG); log_setup(); r = parse_argv(argc, argv); diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index c8c23e8..58964b9 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -96,6 +96,7 @@ int test_main(int argc, char *argv[], void *userdata) { sigset_t mask, sigmask_orig; int r; + log_set_max_level(LOG_DEBUG); log_setup(); r = parse_argv(argc, argv); diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py index e3d49d4..bab0b24 100755 --- a/src/ukify/test/test_ukify.py +++ b/src/ukify/test/test_ukify.py @@ -45,6 +45,13 @@ except ImportError as e: sys.path.append(os.path.dirname(__file__) + '/..') import ukify +# Skip if we're running on an architecture that does not use UEFI. +try: + ukify.guess_efi_arch() +except ValueError as e: + print(str(e), file=sys.stderr) + sys.exit(77) + build_root = os.getenv('PROJECT_BUILD_ROOT') try: slow_tests = bool(int(os.getenv('SYSTEMD_SLOW_TESTS', '1'))) -- cgit v1.2.3