Skip to content

Commit

Permalink
newgidmap: add deny_setgroups flag to /etc/subgid
Browse files Browse the repository at this point in the history
Add a new deny_setgroups (and corresponding allow_setgroups) flag to
/etc/subgid. The purpose of this flag is to extend the security
protections against CVE-2018-7169, so that even group mapping configured
in /etc/subgid by an administrator can still disable setgroups.

However, rather than the fairly lenient semantics for self-mapping, the
semantics of /etc/subgid are stronger. If a mapping is encountered where
"deny_setgroups" is set, then no other mapping can "undo" this
restriction. The reason for this is that "deny_setgroups" indicates that
(according to the administrator) the mapping is unsafe to allow
setgroups in, and adding more mappings will not change this fact.
"allow_setgroups" is the default, and setting it is a noop. The logic
used when applying setgroups policies is unchanged (only denies are
written, and we don't write anything if it's already denied).

Signed-off-by: Aleksa Sarai <asarai@suse.de>
  • Loading branch information
cyphar committed Feb 18, 2018
1 parent 87b1360 commit f6f6335
Showing 1 changed file with 57 additions and 12 deletions.
69 changes: 57 additions & 12 deletions src/newgidmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,39 +46,83 @@
*/
const char *Prog;

static void parse_gid_flags(char **flags)
enum quadopt_t {
OPT_DEFAULT = 0,
OPT_YES,
OPT_NO,
};

static void parse_gid_flags(char **flags, enum quadopt_t *allow_setgroups)
{
int i;
enum quadopt_t new_allow_setgroups = *allow_setgroups;

if (NULL == flags)
return;

/* Iterate over each flag, and make sure it's in the valid flag set. */
for (i = 0; NULL != flags[i]; i++) {
char *flag = flags[i];
bool is_valid = false;

if (strlen(flag) < 1)
continue;

fprintf(stderr, _("%s: flag '%s' is not understood\n"),
Prog,
flag);
exit(EXIT_FAILURE);
/*
* Accumulate the "new" allow_setgroups value, so that
* "deny_setgroups,allow_setgroups" doesn't end up with unexpected
* results.
*/
else if (!strcmp(flag, "allow_setgroups"))
new_allow_setgroups = OPT_YES;
else if (!strcmp(flag, "deny_setgroups"))
new_allow_setgroups = OPT_NO;

/* Catch-all. */
else {
fprintf(stderr, _("%s: flag '%s' is not understood\n"),
Prog,
flag);
exit(EXIT_FAILURE);
}
}

/*
* We only enable in the case of "allow_setgroups" if we haven't changed
* from the default *allow_setgroups, but we *always* disable always
* disable when we see "deny_setgroups".
*/
switch (new_allow_setgroups) {
case OPT_YES:
*allow_setgroups = *allow_setgroups ?: OPT_YES;
break;
case OPT_NO:
*allow_setgroups = OPT_NO;
break;
case OPT_DEFAULT:
default:
break;
}
}

static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow_setgroups)
static bool verify_range(struct passwd *pw, struct map_range *range,
enum quadopt_t *allow_setgroups)
{
/* An empty range is invalid */
if (range->count == 0)
return false;

/* Test /etc/subgid. If the mapping is valid then we allow setgroups. */
/*
* Test /etc/subgid. We only allow setgroups if none of the mapping
* flags have deny_setgroups set.
*/
if (have_sub_gids(pw->pw_name, range->lower, range->count)) {
char **flags = sub_gid_flags(pw->pw_name, range->lower, range->count);

*allow_setgroups = true;
parse_gid_flags(flags);
parse_gid_flags(flags, allow_setgroups);
free_list(flags);

*allow_setgroups = *allow_setgroups ?: OPT_YES;
return true;
}

Expand All @@ -92,7 +136,8 @@ static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow
}

static void verify_ranges(struct passwd *pw, int ranges,
struct map_range *mappings, bool *allow_setgroups)
struct map_range *mappings,
enum quadopt_t *allow_setgroups)
{
struct map_range *mapping;
int idx;
Expand Down Expand Up @@ -195,7 +240,7 @@ int main(int argc, char **argv)
struct stat st;
struct passwd *pw;
int written;
bool allow_setgroups = false;
enum quadopt_t allow_setgroups = OPT_DEFAULT;

Prog = Basename (argv[0]);

Expand Down Expand Up @@ -272,7 +317,7 @@ int main(int argc, char **argv)

verify_ranges(pw, ranges, mappings, &allow_setgroups);

write_setgroups(proc_dir_fd, allow_setgroups);
write_setgroups(proc_dir_fd, allow_setgroups == OPT_YES);
write_mapping(proc_dir_fd, ranges, mappings, "gid_map");
sub_gid_close();

Expand Down

0 comments on commit f6f6335

Please sign in to comment.