1
0
mirror of https://github.com/systemd/systemd synced 2026-04-24 07:55:12 +02:00

Compare commits

..

24 Commits

Author SHA1 Message Date
Lennart Poettering
bd4dea76de veritysetup: fix memory corruption
We must copy the option string, since in one case we are called with a
pointer into dynamic memory that will be freed by the caller.

As discussed here: https://github.com/systemd/systemd/pull/22908/files#r839394490

Follow-up for: #22908
2022-04-02 02:22:39 +09:00
Yu Watanabe
95cd21928f
Merge pull request #22939 from yuwata/tree-wide-space
tree-wide: add space after if, switch, for, and while
2022-04-02 01:32:26 +09:00
Yu Watanabe
b17a681be5 tree-wide: fix typo 2022-04-02 00:34:39 +09:00
Lennart Poettering
0d08db7f89 udev: add /dev/disk/by-diskseq symlink for block devices
This adds another symlink for block devices:

    /dev/disk/by-diskseq/<number>

where the number is the diskseq number as exposed by the kernel. It's
useful for apps because they can use it to open a device by diskseq, in
a way that is safe against device node reuse. I.e. if a device node path
like this is passed to an app it could open the device node via the
symlink and also parse the diskseq from the path. Once the device is
opened it could compare the parsed diskseq with the one returned by
BLKGETDISKSEQ on the open node, and if it matches they know they are
talking to the right device.

Fixes: #22906
2022-04-01 23:44:37 +09:00
Yu Watanabe
37ebfe49de
Merge pull request #22926 from bluca/analyze_offline_filter
analyze: fix offline checks for syscall filter and 'native' architecture
2022-04-01 23:43:18 +09:00
Lennart Poettering
356ad32dc2
Merge pull request #22921 from poettering/uid-range-tweaks
userns uid range tweaks: taint systemd if assigned userns uid range too short, and show userns uid range in userdbctl output
2022-04-01 15:58:55 +02:00
Yu Watanabe
798931160e tree-wide: add a space after if, switch, for, and while 2022-04-01 22:48:42 +09:00
Yu Watanabe
72dce85a60 test: add space between arguments 2022-04-01 22:28:13 +09:00
Lennart Poettering
fdf02a4f26 path-util: use STR_IN_SET() where appropriate 2022-04-01 15:23:43 +02:00
Lennart Poettering
6cdd6d1acc notify: remove spurious whitespace 2022-04-01 15:23:43 +02:00
Lennart Poettering
ca782b85fc
Merge pull request #22934 from poettering/tls-test-fix-root
tests: make test-resolved-stream suceed even when run as root with restrictive access mode on build tree dir
2022-04-01 15:22:43 +02:00
Lennart Poettering
aed3c5eca3 process-util: refactor APIs for reading /proc/self/xyz symlinks
The three functions for reading cwd, exe and root symlinks of processes
already share a common core: get_process_link_contents(). Let's refactor
that a bit, and move formatting of the /proc/self/ path into this helper
function instead of doing that in the caller, thus sharing more code.

While we are at it, make the return parameters optional, in case the
information if the links are readable is interesting, but the contents
is not. (This also means safe_getcwd() and readlinkat_malloc() are
updated to make the return parameter optional, as these are called by
the relevant three functions)
2022-04-01 15:22:27 +02:00
Luca Boccassi
dd51e725df analyze: fix offline check for syscal filter
The deny/allow list check was inverted, if we are deny listing and the
hashmap contains the syscall then that's good

Fixes https://github.com/systemd/systemd/issues/22914
2022-04-01 10:42:48 +01:00
Luca Boccassi
1449b0f8a9 analyze: fix offline check for 'native' syscall architecture
Enum values are stored in the set, not strings
2022-04-01 10:42:48 +01:00
Lennart Poettering
9cce7fb193 userdbctl: also show available UID range in current userns
Containers generally have a smaller UID range assigned than host
systems. Let's visualize this in the user/group tables. We insert
markers for unavailable regions. This way display is identical to status
quo ante on host systems, but in containers unavailable ranges will be
shown as that.

And while we are at it, also hide well-known UID ranges when they are
outside of userns uid_map range. This is mostly about the "container"
range. It's pointless showing the cotnainer range (i.e. a range UID >
65535) if that range isn#t available in the container anyway.
2022-04-01 11:21:44 +02:00
Lennart Poettering
49888d31b6 update TODO 2022-04-01 11:21:44 +02:00
Lennart Poettering
63e8df046b pid1: add taint flag if uid/gid userns range too small
This will taint systemd if invoked in containers that do not have the
full 16bit range of UIDs defined.

we pretty much need uid root…nobody to be defined for a variety of
purposes, hence let's add this taint flag. Of course taints are
graceful, but it at least communicates the mess in some way...
2022-04-01 11:21:44 +02:00
Lennart Poettering
556560495e uid-range: replace uid_range_contains() by more generalized uid_range_covers()
The former checks if one UID is inside the uid range set. The latter
checks if a full UID range is inside the uid range set. The former is
hence a special case of the latter.
2022-04-01 11:20:12 +02:00
Lennart Poettering
0a5c6a57c6 uid-range: add some overflow checks 2022-04-01 11:20:12 +02:00
Lennart Poettering
5674aa7a2c uid-range: add new uid_range_load_userns() for loading /proc/self/uid_map 2022-04-01 11:20:12 +02:00
Lennart Poettering
2e37ebdae9 test: port test-uid-range to tests.h 2022-04-01 11:20:12 +02:00
Lennart Poettering
09bbaa419b uid-range: use size_t for array size 2022-04-01 11:20:12 +02:00
Lennart Poettering
ed59b44309 test-resolved-stream: before entering user/network namespaces check if that's safe
I regularly run my tests also as root, since some of the tested code
uses privileged APIs. The test-resolved-stream so far tried to run its
tests in a user/network namespace if that can be allocated. This caused
the tests to fail on my system where once the user namespace is opened
access to the build tree in my $HOME is prohibited (due to restricted
access modes on my home dir). Let's add a check for that: before
actually isolating the test in a user/network namespace, let's see if
that would make it impossible for us to access the build tree (which we
need to do load the TLS certificates the test requires).

This should make the test pass when run as root from a build tree with
restrictive access mode.
2022-04-01 11:14:56 +02:00
Lennart Poettering
8419213d99 tests: modernize load_testdata_env() a bit
Let's add assert() around everyhing we don't expect to fail.

Port to path_extract_directory().

Log errrors from load_env_file_pairs() which we ignore.
2022-04-01 11:14:56 +02:00
74 changed files with 571 additions and 188 deletions

4
TODO
View File

@ -122,7 +122,7 @@ Features:
* maybe add a generator that reads /proc/cmdline, looks for
systemd.pull-raw-portable=, systemd-pull-raw-sysext= and similar switches
that take an URL as parameter. It then generates service units for
systemd-pull calls thta download these URLs if not installed yet. usecase:
systemd-pull calls that download these URLs if not installed yet. usecase:
invoke a VM or nspawn container in a way it automatically deploys/runs these
images as OS payloads. i.e. have a generic OS image you can point to any
payload you like, which is then downloaded, securely verified and run.
@ -761,8 +761,6 @@ Features:
systemd-journald writes to /var/log/journal, which could be useful when we
doing disk usage calculations and so on.
* taint systemd if there are fewer than 65536 users assigned (userns) to the system.
* deprecate RootDirectoryStartOnly= in favour of a new ExecStart= prefix char
* add a new RuntimeDirectoryPreserve= mode that defines a similar lifecycle for

View File

@ -943,7 +943,7 @@ Table=1234</programlisting></para>
<para>When <varname>Bond=</varname> is specified to a wireless interface, defaults to 3
seconds. When the DHCPv4 client is enabled and <varname>UseMTU=</varname> in the [DHCPv4]
sectionis enabled, defaults to 5 seconds. Otherwise, defaults to the value specified with
section enabled, defaults to 5 seconds. Otherwise, defaults to the value specified with
<varname>ConfigureWithoutCarrier=</varname>. When <varname>ActivationPolicy=</varname> is set
to <literal>always-up</literal>, this is forced to <literal>yes</literal>, and ignored any
user specified values.</para>

View File

@ -121,4 +121,9 @@ ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-i
ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
# by-diskseq link (if an app is told to open a path like this, they may parse
# the diskseq number from the path, then issue BLKGETDISKSEQ to verify they really got
# the right device, to access specific disks in a race-free fashion)
ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"
LABEL="persistent_storage_end"

View File

@ -530,6 +530,8 @@ static int assess_restrict_namespaces(
return 0;
}
#if HAVE_SECCOMP
static int assess_system_call_architectures(
const struct security_assessor *a,
const SecurityInfo *info,
@ -537,16 +539,19 @@ static int assess_system_call_architectures(
uint64_t *ret_badness,
char **ret_description) {
uint32_t native = 0;
char *d;
uint64_t b;
assert(ret_badness);
assert(ret_description);
assert_se(seccomp_arch_from_string("native", &native) >= 0);
if (set_isempty(info->system_call_architectures)) {
b = 10;
d = strdup("Service may execute system calls with all ABIs");
} else if (set_contains(info->system_call_architectures, "native") &&
} else if (set_contains(info->system_call_architectures, UINT32_TO_PTR(native + 1)) &&
set_size(info->system_call_architectures) == 1) {
b = 0;
d = strdup("Service may execute system calls only with native ABI");
@ -564,8 +569,6 @@ static int assess_system_call_architectures(
return 0;
}
#if HAVE_SECCOMP
static bool syscall_names_in_filter(Hashmap *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
const char *syscall;
@ -587,7 +590,7 @@ static bool syscall_names_in_filter(Hashmap *s, bool allow_list, const SyscallFi
if (id < 0)
continue;
if (hashmap_contains(s, syscall) == allow_list) {
if (hashmap_contains(s, syscall) != allow_list) {
log_debug("Offending syscall filter item: %s", syscall);
if (ret_offending_syscall)
*ret_offending_syscall = syscall;
@ -1476,6 +1479,7 @@ static const struct security_assessor security_assessor_table[] = {
.assess = assess_bool,
.offset = offsetof(SecurityInfo, restrict_address_family_other),
},
#if HAVE_SECCOMP
{
.id = "SystemCallArchitectures=",
.json_field = "SystemCallArchitectures",
@ -1484,7 +1488,6 @@ static const struct security_assessor security_assessor_table[] = {
.range = 10,
.assess = assess_system_call_architectures,
},
#if HAVE_SECCOMP
{
.id = "SystemCallFilter=~@swap",
.json_field = "SystemCallFilter_swap",

View File

@ -119,7 +119,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
size_t l = PATH_MAX;
assert(p);
assert(ret);
for (;;) {
_cleanup_free_ char *c = NULL;
@ -135,7 +134,10 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
if ((size_t) n < l) {
c[n] = 0;
if (ret)
*ret = TAKE_PTR(c);
return 0;
}

View File

@ -63,7 +63,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
}
int safe_getcwd(char **ret) {
char *cwd;
_cleanup_free_ char *cwd = NULL;
cwd = get_current_dir_name();
if (!cwd)
@ -71,12 +71,12 @@ int safe_getcwd(char **ret) {
/* Let's make sure the directory is really absolute, to protect us from the logic behind
* CVE-2018-1000001 */
if (cwd[0] != '/') {
free(cwd);
if (cwd[0] != '/')
return -ENOMEDIUM;
}
*ret = cwd;
if (ret)
*ret = TAKE_PTR(cwd);
return 0;
}
@ -1235,9 +1235,10 @@ bool hidden_or_backup_file(const char *filename) {
assert(filename);
if (filename[0] == '.' ||
streq(filename, "lost+found") ||
streq(filename, "aquota.user") ||
streq(filename, "aquota.group") ||
STR_IN_SET(filename,
"lost+found",
"aquota.user",
"aquota.group") ||
endswith(filename, "~"))
return true;

View File

@ -472,37 +472,33 @@ int get_process_capeff(pid_t pid, char **ret) {
return r;
}
static int get_process_link_contents(const char *proc_file, char **ret) {
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
const char *p;
int r;
assert(proc_file);
assert(ret);
r = readlink_malloc(proc_file, ret);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
p = procfs_file_alloca(pid, proc_file);
return 0;
r = readlink_malloc(p, ret);
return r == -ENOENT ? -ESRCH : r;
}
int get_process_exe(pid_t pid, char **ret) {
const char *p;
char *d;
int r;
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "exe");
r = get_process_link_contents(p, ret);
r = get_process_link_contents(pid, "exe", ret);
if (r < 0)
return r;
if (ret) {
d = endswith(*ret, " (deleted)");
if (d)
*d = '\0';
}
return 0;
}
@ -572,28 +568,17 @@ int get_process_gid(pid_t pid, gid_t *ret) {
}
int get_process_cwd(pid_t pid, char **ret) {
const char *p;
assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached())
return safe_getcwd(ret);
p = procfs_file_alloca(pid, "cwd");
return get_process_link_contents(p, ret);
return get_process_link_contents(pid, "cwd", ret);
}
int get_process_root(pid_t pid, char **ret) {
const char *p;
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "root");
return get_process_link_contents(p, ret);
return get_process_link_contents(pid, "root", ret);
}
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)

View File

@ -82,6 +82,7 @@
#include "terminal-util.h"
#include "time-util.h"
#include "transaction.h"
#include "uid-range.h"
#include "umask-util.h"
#include "unit-name.h"
#include "user-util.h"
@ -4350,16 +4351,34 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re
return 0;
}
static int short_uid_range(const char *path) {
_cleanup_free_ UidRange *p = NULL;
size_t n = 0;
int r;
assert(path);
/* Taint systemd if we the UID range assigned to this environment doesn't at least cover 0…65534,
* i.e. from root to nobody. */
r = uid_range_load_userns(&p, &n, path);
if (ERRNO_IS_NOT_SUPPORTED(r))
return false;
if (r < 0)
return log_debug_errno(r, "Failed to load %s: %m", path);
return !uid_range_covers(p, n, 0, 65535);
}
char *manager_taint_string(Manager *m) {
_cleanup_free_ char *destination = NULL, *overflowuid = NULL, *overflowgid = NULL;
struct utsname uts;
char *buf, *e;
int r;
/* Returns a "taint string", e.g. "local-hwclock:var-run-bad".
* Only things that are detected at runtime should be tagged
* here. For stuff that is set during compilation, emit a warning
* in the configuration phase. */
/* Returns a "taint string", e.g. "local-hwclock:var-run-bad". Only things that are detected at
* runtime should be tagged here. For stuff that is set during compilation, emit a warning in the
* configuration phase. */
assert(m);
@ -4370,7 +4389,9 @@ char *manager_taint_string(Manager *m) {
"var-run-bad:"
"overflowuid-not-65534:"
"overflowgid-not-65534:"
"old-kernel:"));
"old-kernel:"
"short-uid-range:"
"short-gid-range:"));
if (!buf)
return NULL;
@ -4396,7 +4417,6 @@ char *manager_taint_string(Manager *m) {
r = read_one_line_file("/proc/sys/kernel/overflowuid", &overflowuid);
if (r >= 0 && !streq(overflowuid, "65534"))
e = stpcpy(e, "overflowuid-not-65534:");
r = read_one_line_file("/proc/sys/kernel/overflowgid", &overflowgid);
if (r >= 0 && !streq(overflowgid, "65534"))
e = stpcpy(e, "overflowgid-not-65534:");
@ -4405,6 +4425,11 @@ char *manager_taint_string(Manager *m) {
if (strverscmp_improved(uts.release, KERNEL_BASELINE_VERSION) < 0)
e = stpcpy(e, "old-kernel:");
if (short_uid_range("/proc/self/uid_map") > 0)
e = stpcpy(e, "short-uid-range:");
if (short_uid_range("/proc/self/gid_map") > 0)
e = stpcpy(e, "short-gid-range:");
/* remove the last ':' */
if (e != buf)
e[-1] = 0;

View File

@ -1,11 +1,14 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
BEGIN{
print "const char *audit_type_to_string(int type) {\n\tswitch(type) {"
print "const char *audit_type_to_string(int type) {"
print " switch (type) {"
}
{
printf " case AUDIT_%s: return \"%s\";\n", $1, $1
}
END{
print " default: return NULL;\n\t}\n}\n"
print " default: return NULL;"
print " }"
print "}"
}

View File

@ -1,7 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
BEGIN{
print "const char *dns_type_to_string(int type) {\n\tswitch(type) {"
print "const char *dns_type_to_string(int type) {"
print " switch (type) {"
}
{
printf " case DNS_TYPE_%s: return ", $1;
@ -9,5 +10,7 @@ BEGIN{
printf "\"%s\";\n", $1
}
END{
print " default: return NULL;\n\t}\n}\n"
print " default: return NULL;"
print " }"
print "}"
}

View File

@ -16,6 +16,7 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
#include "path-util.h"
#include "process-util.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-question.h"
@ -330,12 +331,37 @@ static void test_dns_stream(bool tls) {
static void try_isolate_network(void) {
_cleanup_close_ int socket_fd = -1;
int r;
/* First test if CLONE_NEWUSER/CLONE_NEWNET can actually work for us, i.e. we can open the namespaces
* and then still access the build dir we are run from. We do that in a child process since it's
* nasty if we have to go back from the namespace once we entered it and realized it cannot work. */
r = safe_fork("(usernstest)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
if (r == 0) { /* child */
_cleanup_free_ char *rt = NULL, *d = NULL;
if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) {
log_warning("test-resolved-stream: Can't create user and network ns, running on host");
return;
log_warning_errno(errno, "test-resolved-stream: Can't create user and network ns, running on host: %m");
_exit(EXIT_FAILURE);
}
assert_se(get_process_exe(0, &rt) >= 0);
assert_se(path_extract_directory(rt, &d) >= 0);
if (access(d, F_OK) < 0) {
log_warning_errno(errno, "test-resolved-stream: Can't access /proc/self/exe from user/network ns, running on host: %m");
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
if (r == -EPROTO) /* EPROTO means nonzero exit code of child, i.e. the tests in the child failed */
return;
assert_se(r > 0);
/* Now that we know that the unshare() is safe, let's actually do it */
assert_se(unshare(CLONE_NEWUSER | CLONE_NEWNET) >= 0);
/* Bring up the loopback interfaceon the newly created network namespace */
struct ifreq req = { .ifr_ifindex = 1 };
assert_se((socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);

View File

@ -48,23 +48,26 @@ char* setup_fake_runtime_dir(void) {
static void load_testdata_env(void) {
static bool called = false;
_cleanup_free_ char *s = NULL;
_cleanup_free_ char *envpath = NULL;
_cleanup_free_ char *s = NULL, *d = NULL, *envpath = NULL;
_cleanup_strv_free_ char **pairs = NULL;
int r;
if (called)
return;
called = true;
assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0);
dirname(s);
assert_se(path_extract_directory(s, &d) >= 0);
assert_se(envpath = path_join(d, "systemd-runtest.env"));
envpath = path_join(s, "systemd-runtest.env");
if (load_env_file_pairs(NULL, envpath, &pairs) < 0)
r = load_env_file_pairs(NULL, envpath, &pairs);
if (r < 0) {
log_debug_errno(r, "Reading %s failed: %m", envpath);
return;
}
STRV_FOREACH_PAIR(k, v, pairs)
setenv(*k, *v, 0);
assert_se(setenv(*k, *v, 0) >= 0);
}
int get_testdata_dir(const char *suffix, char **ret) {

View File

@ -5,8 +5,13 @@
#include <string.h>
#include "alloc-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "macro.h"
#include "path-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "uid-range.h"
#include "user-util.h"
@ -17,12 +22,12 @@ static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
range->start + range->nr >= start;
}
static void uid_range_coalesce(UidRange **p, unsigned *n) {
static void uid_range_coalesce(UidRange **p, size_t *n) {
assert(p);
assert(n);
for (unsigned i = 0; i < *n; i++) {
for (unsigned j = i + 1; j < *n; j++) {
for (size_t i = 0; i < *n; i++) {
for (size_t j = i + 1; j < *n; j++) {
UidRange *x = (*p)+i, *y = (*p)+j;
if (uid_range_intersect(x, y->start, y->nr)) {
@ -54,7 +59,7 @@ static int uid_range_compare(const UidRange *a, const UidRange *b) {
return CMP(a->nr, b->nr);
}
int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) {
bool found = false;
UidRange *x;
@ -64,7 +69,10 @@ int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
if (nr <= 0)
return 0;
for (unsigned i = 0; i < *n; i++) {
if (start > UINT32_MAX - nr) /* overflow check */
return -ERANGE;
for (size_t i = 0; i < *n; i++) {
x = (*p) + i;
if (uid_range_intersect(x, start, nr)) {
found = true;
@ -100,7 +108,7 @@ int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
return *n;
}
int uid_range_add_str(UidRange **p, unsigned *n, const char *s) {
int uid_range_add_str(UidRange **p, size_t *n, const char *s) {
uid_t start, nr;
const char *t;
int r;
@ -138,15 +146,18 @@ int uid_range_add_str(UidRange **p, unsigned *n, const char *s) {
return uid_range_add(p, n, start, nr);
}
int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) {
int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) {
uid_t closest = UID_INVALID, candidate;
assert(p);
assert(uid);
if (*uid == 0)
return -EBUSY;
candidate = *uid - 1;
for (unsigned i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++) {
uid_t begin, end;
begin = p[i].start;
@ -168,13 +179,62 @@ int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) {
return 1;
}
bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) {
assert(p);
assert(uid);
bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr) {
assert(p || n == 0);
for (unsigned i = 0; i < n; i++)
if (uid >= p[i].start && uid < p[i].start + p[i].nr)
if (nr == 0) /* empty range? always covered... */
return true;
if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
return false;
for (size_t i = 0; i < n; i++)
if (start >= p[i].start && start + nr <= p[i].start + p[i].nr)
return true;
return false;
}
int uid_range_load_userns(UidRange **p, size_t *n, const char *path) {
_cleanup_fclose_ FILE *f = NULL;
int r;
/* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
* the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
* maps).
*
* To simplify things this will modify the passed array in case of later failure. */
if (!path)
path = "/proc/self/uid_map";
f = fopen(path, "re");
if (!f) {
r = -errno;
if (r == -ENOENT && path_startswith(path, "/proc/") && proc_mounted() > 0)
return -EOPNOTSUPP;
return r;
}
for (;;) {
uid_t uid_base, uid_shift, uid_range;
int k;
errno = 0;
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
if (k == EOF) {
if (ferror(f))
return errno_or_else(EIO);
return 0;
}
if (k != 3)
return -EBADMSG;
r = uid_range_add(p, n, uid_base, uid_range);
if (r < 0)
return r;
}
}

View File

@ -8,8 +8,14 @@ typedef struct UidRange {
uid_t start, nr;
} UidRange;
int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr);
int uid_range_add_str(UidRange **p, unsigned *n, const char *s);
int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr);
int uid_range_add_str(UidRange **p, size_t *n, const char *s);
int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid);
bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid);
int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid);
bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr);
static inline bool uid_range_contains(const UidRange *p, size_t n, uid_t uid) {
return uid_range_covers(p, n, uid, 1);
}
int uid_range_load_userns(UidRange **p, size_t *n, const char *path);

View File

@ -104,7 +104,7 @@ static Set *database_users = NULL, *database_groups = NULL;
static uid_t search_uid = UID_INVALID;
static UidRange *uid_range = NULL;
static unsigned n_uid_range = 0;
static size_t n_uid_range = 0;
static UGIDAllocationRange login_defs = {};
static bool login_defs_need_warning = false;

View File

@ -3,15 +3,26 @@
#include <stddef.h>
#include "alloc-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "tests.h"
#include "tmpfile-util.h"
#include "uid-range.h"
#include "user-util.h"
#include "util.h"
#include "virt.h"
int main(int argc, char *argv[]) {
TEST(uid_range) {
_cleanup_free_ UidRange *p = NULL;
unsigned n = 0;
size_t n = 0;
uid_t search;
assert_se(uid_range_covers(p, n, 0, 0));
assert_se(!uid_range_covers(p, n, 0, 1));
assert_se(!uid_range_covers(p, n, 100, UINT32_MAX));
assert_se(uid_range_add_str(&p, &n, "500-999") >= 0);
assert_se(n == 1);
assert_se(p[0].start == 500);
@ -22,6 +33,17 @@ int main(int argc, char *argv[]) {
assert_se(uid_range_contains(p, n, 999));
assert_se(!uid_range_contains(p, n, 1000));
assert_se(!uid_range_covers(p, n, 100, 150));
assert_se(!uid_range_covers(p, n, 400, 200));
assert_se(!uid_range_covers(p, n, 499, 1));
assert_se(uid_range_covers(p, n, 500, 1));
assert_se(uid_range_covers(p, n, 501, 10));
assert_se(uid_range_covers(p, n, 999, 1));
assert_se(!uid_range_covers(p, n, 999, 2));
assert_se(!uid_range_covers(p, n, 1000, 1));
assert_se(!uid_range_covers(p, n, 1000, 100));
assert_se(!uid_range_covers(p, n, 1001, 100));
search = UID_INVALID;
assert_se(uid_range_next_lower(p, n, &search));
assert_se(search == 999);
@ -69,6 +91,49 @@ int main(int argc, char *argv[]) {
assert_se(n == 1);
assert_se(p[0].start == 20);
assert_se(p[0].nr == 1983);
return 0;
}
TEST(load_userns) {
_cleanup_(unlink_and_freep) char *fn = NULL;
_cleanup_free_ UidRange *p = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t n = 0;
int r;
r = uid_range_load_userns(&p, &n, NULL);
if (ERRNO_IS_NOT_SUPPORTED(r))
return;
assert_se(r >= 0);
assert_se(uid_range_contains(p, n, getuid()));
r = running_in_userns();
if (r == 0) {
assert_se(n == 1);
assert_se(p[0].start == 0);
assert_se(p[0].nr == UINT32_MAX);
assert_se(uid_range_covers(p, n, 0, UINT32_MAX));
}
assert_se(fopen_temporary(NULL, &f, &fn) >= 0);
fputs("0 0 20\n"
"100 0 20\n", f);
assert_se(fflush_and_check(f) >= 0);
p = mfree(p);
n = 0;
assert_se(uid_range_load_userns(&p, &n, fn) >= 0);
assert_se(uid_range_contains(p, n, 0));
assert_se(uid_range_contains(p, n, 19));
assert_se(!uid_range_contains(p, n, 20));
assert_se(!uid_range_contains(p, n, 99));
assert_se(uid_range_contains(p, n, 100));
assert_se(uid_range_contains(p, n, 119));
assert_se(!uid_range_contains(p, n, 120));
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -17,6 +17,7 @@
#include "socket-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "uid-range.h"
#include "user-record-show.h"
#include "user-util.h"
#include "userdb.h"
@ -167,14 +168,22 @@ static const struct {
},
};
static int table_add_uid_boundaries(Table *table) {
static int table_add_uid_boundaries(
Table *table,
const UidRange *p,
size_t n) {
int r;
assert(table);
assert(p || n == 0);
for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
_cleanup_free_ char *name = NULL, *comment = NULL;
if (n > 0 &&
!uid_range_covers(p, n, uid_range_table[i].first, uid_range_table[i].last - uid_range_table[i].first + 1))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin ", uid_range_table[i].name, " users ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
@ -199,7 +208,7 @@ static int table_add_uid_boundaries(Table *table) {
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_INT, -1); /* sort before an other entry with the same UID */
TABLE_INT, -1); /* sort before any other entry with the same UID */
if (r < 0)
return table_log_add_error(r);
@ -229,7 +238,7 @@ static int table_add_uid_boundaries(Table *table) {
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_INT, 1); /* sort after an other entry with the same UID */
TABLE_INT, 1); /* sort after any other entry with the same UID */
if (r < 0)
return table_log_add_error(r);
}
@ -237,6 +246,104 @@ static int table_add_uid_boundaries(Table *table) {
return ELEMENTSOF(uid_range_table) * 2;
}
static int add_unavailable_uid(Table *table, uid_t start, uid_t end) {
_cleanup_free_ char *name = NULL;
int r;
assert(table);
assert(start <= end);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin unavailable users ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_UID, start,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_STRING, "First unavailable user",
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_INT, -1); /* sort before an other entry with the same UID */
if (r < 0)
return table_log_add_error(r);
free(name);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" end unavailable users ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_UID, end,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_STRING, "Last unavailable user",
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_INT, 1); /* sort after any other entry with the same UID */
if (r < 0)
return table_log_add_error(r);
return 2;
}
static int table_add_uid_map(
Table *table,
const UidRange *p,
size_t n,
int (*add_unavailable)(Table *t, uid_t start, uid_t end)) {
uid_t focus = 0;
int n_added = 0, r;
assert(table);
assert(p || n == 0);
assert(add_unavailable);
for (size_t i = 0; i < n; i++) {
if (focus < p[i].start) {
r = add_unavailable(table, focus, p[i].start-1);
if (r < 0)
return r;
n_added += r;
}
if (p[i].start > UINT32_MAX - p[i].nr) { /* overflow check */
focus = UINT32_MAX;
break;
}
focus = p[i].start + p[i].nr;
}
if (focus < UINT32_MAX-1) {
r = add_unavailable(table, focus, UINT32_MAX-1);
if (r < 0)
return r;
n_added += r;
}
return n_added;
}
static int display_user(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL;
bool draw_separator = false;
@ -322,12 +429,22 @@ static int display_user(int argc, char *argv[], void *userdata) {
}
if (table) {
int boundary_lines;
_cleanup_free_ UidRange *uid_range = NULL;
int boundary_lines, uid_map_lines;
size_t n_uid_range;
boundary_lines = table_add_uid_boundaries(table);
r = uid_range_load_userns(&uid_range, &n_uid_range, "/proc/self/uid_map");
if (r < 0)
log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m");
boundary_lines = table_add_uid_boundaries(table, uid_range, n_uid_range);
if (boundary_lines < 0)
return boundary_lines;
uid_map_lines = table_add_uid_map(table, uid_range, n_uid_range, add_unavailable_uid);
if (uid_map_lines < 0)
return uid_map_lines;
if (table_get_rows(table) > 1) {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
@ -335,8 +452,11 @@ static int display_user(int argc, char *argv[], void *userdata) {
}
if (arg_legend) {
if (table_get_rows(table) > 1)
printf("\n%zu users listed.\n", table_get_rows(table) - 1 - boundary_lines);
size_t k;
k = table_get_rows(table) - 1 - boundary_lines - uid_map_lines;
if (k > 0)
printf("\n%zu users listed.\n", k);
else
printf("No users.\n");
}
@ -411,14 +531,22 @@ static int show_group(GroupRecord *gr, Table *table) {
return 0;
}
static int table_add_gid_boundaries(Table *table) {
static int table_add_gid_boundaries(
Table *table,
const UidRange *p,
size_t n) {
int r;
assert(table);
assert(p || n == 0);
for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
_cleanup_free_ char *name = NULL, *comment = NULL;
if (n > 0 &&
!uid_range_covers(p, n, uid_range_table[i].first, uid_range_table[i].last))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin ", uid_range_table[i].name, " groups ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
@ -440,7 +568,7 @@ static int table_add_gid_boundaries(Table *table) {
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, comment,
TABLE_SET_COLOR, ansi_grey(),
TABLE_INT, -1); /* sort before an other entry with the same GID */
TABLE_INT, -1); /* sort before any other entry with the same GID */
if (r < 0)
return table_log_add_error(r);
@ -467,7 +595,7 @@ static int table_add_gid_boundaries(Table *table) {
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, comment,
TABLE_SET_COLOR, ansi_grey(),
TABLE_INT, 1); /* sort after an other entry with the same GID */
TABLE_INT, 1); /* sort after any other entry with the same GID */
if (r < 0)
return table_log_add_error(r);
}
@ -475,6 +603,57 @@ static int table_add_gid_boundaries(Table *table) {
return ELEMENTSOF(uid_range_table) * 2;
}
static int add_unavailable_gid(Table *table, uid_t start, uid_t end) {
_cleanup_free_ char *name = NULL;
int r;
assert(table);
assert(start <= end);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin unavailable groups ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_GID, start,
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, "First unavailable group",
TABLE_SET_COLOR, ansi_grey(),
TABLE_INT, -1); /* sort before any other entry with the same GID */
if (r < 0)
return table_log_add_error(r);
free(name);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" end unavailable groups ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_GID, end,
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, "Last unavailable group",
TABLE_SET_COLOR, ansi_grey(),
TABLE_INT, 1); /* sort after any other entry with the same GID */
if (r < 0)
return table_log_add_error(r);
return 2;
}
static int display_group(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL;
bool draw_separator = false;
@ -559,12 +738,22 @@ static int display_group(int argc, char *argv[], void *userdata) {
}
if (table) {
int boundary_lines;
_cleanup_free_ UidRange *gid_range = NULL;
int boundary_lines, gid_map_lines;
size_t n_gid_range;
boundary_lines = table_add_gid_boundaries(table);
r = uid_range_load_userns(&gid_range, &n_gid_range, "/proc/self/gid_map");
if (r < 0)
log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m");
boundary_lines = table_add_gid_boundaries(table, gid_range, n_gid_range);
if (boundary_lines < 0)
return boundary_lines;
gid_map_lines = table_add_uid_map(table, gid_range, n_gid_range, add_unavailable_gid);
if (gid_map_lines < 0)
return gid_map_lines;
if (table_get_rows(table) > 1) {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
@ -572,8 +761,11 @@ static int display_group(int argc, char *argv[], void *userdata) {
}
if (arg_legend) {
if (table_get_rows(table) > 1)
printf("\n%zu groups listed.\n", table_get_rows(table) - 1 - boundary_lines);
size_t k;
k = table_get_rows(table) - 1 - boundary_lines - gid_map_lines;
if (k > 0)
printf("\n%zu groups listed.\n", k);
else
printf("No groups.\n");
}

View File

@ -17,7 +17,9 @@
#include "terminal-util.h"
static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY;
static const char *arg_root_hash_signature = NULL;
static char *arg_root_hash_signature = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -39,13 +41,17 @@ static int help(void) {
}
static int save_roothashsig_option(const char *option, bool strict) {
int r;
if (path_is_absolute(option) || startswith(option, "base64:")) {
if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
arg_root_hash_signature = option;
r = free_and_strdup_warn(&arg_root_hash_signature, option);
if (r < 0)
return r;
return true;
}
@ -60,10 +66,10 @@ static int parse_options(const char *options) {
int r;
/* backward compatibility with the obsolete ROOTHASHSIG positional argument */
r = save_roothashsig_option(options, false);
r = save_roothashsig_option(options, /* strict= */ false);
if (r < 0)
return r;
if (r == 1) {
if (r > 0) {
log_warning("Usage of ROOTHASHSIG positional argument is deprecated. "
"Please use the option root-hash-signature=%s instead.", options);
return 0;
@ -99,7 +105,7 @@ static int parse_options(const char *options) {
arg_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION;
#endif
else if ((val = startswith(word, "root-hash-signature="))) {
r = save_roothashsig_option(val, true);
r = save_roothashsig_option(val, /* strict= */ true);
if (r < 0)
return r;

View File

@ -575,14 +575,14 @@ systemd-analyze security --threshold=90 --offline=true \
--root=/tmp/img/ testfile.service
# The strict profile adds a lot of sanboxing options
systemd-analyze security --threshold=20 --offline=true \
systemd-analyze security --threshold=25 --offline=true \
--security-policy=/tmp/testfile.json \
--profile=strict \
--root=/tmp/img/ testfile.service
set +e
# The trusted profile doesn't add any sanboxing options
systemd-analyze security --threshold=20 --offline=true \
systemd-analyze security --threshold=25 --offline=true \
--security-policy=/tmp/testfile.json \
--profile=/usr/lib/systemd/portable/profile/trusted/service.conf \
--root=/tmp/img/ testfile.service \