From 9cce7fb193e5b12dee091ff7023669ef9034c1c0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 31 Mar 2022 15:22:33 +0200 Subject: 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. --- src/userdb/userdbctl.c | 220 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 206 insertions(+), 14 deletions(-) (limited to 'src/userdb') diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index c3d61f0059..b299bb8e11 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -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; + + 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); + 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; + + 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); + 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"); } -- cgit v1.2.1