mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
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
This commit is contained in:
commit
356ad32dc2
2
TODO
2
TODO
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user