From: Mike Frysinger <vapier@gentoo.org>
To: containers@lists.linux-foundation.org
Cc: ebiederm@xmission.com, linux-kernel@vger.kernel.org
Subject: handling of supplemental groups with userns
Date: Tue, 22 Sep 2015 12:21:57 -0400 [thread overview]
Message-ID: <20150922162157.GB27286@vapier.lan> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 2069 bytes --]
is it possible to map in supplemental groups in a userns when the user
lacks setgid/etc... capabilities in the parent ns ? it doesn't seem
like it's currently possible, but is there a reason to not enable it ?
basically i have a build tool that i want to isolate a bit, but it
requires access to some of my supplemental groups. if i map just
my effective uid/gid, the build will fail when it tries to use the
chown/chgrp commands (gets back EINVAL).
my scenario boils down to:
- normal unprivileged user (uid=8282)
- member of multiple groups (gid=100, getgroups={100,16,250,...})
- create a new userns (to get access to other ns like mount/pid)
but still have access to existing groups where i'm root
- use various features that require caps (new pidns/mntns/etc...)
- create another userns and map back non-root users/groups
i.e. i switch from 8282 to 0, do what i need, then switch back to 8282.
i've attached a simple test program to show the issue. it can map the
current uid/gid fine, but fails to do so with a supplemental group.
my reading of kernel/user_namespace.c shows that it's not possible:
(1) if gid_map has any entries, map_write bails early with EPERM
(2) if gid_map is empty, then writing a single entry (e.g. "0 100 1")
works, but then a 2nd write runs into (1)
(3) if gid_map is empty, then writing multiple entries (e.g.
"0 100 1\n250 250 1\n") fails in new_idmap_permitted -- the first
check is skipped (since new_map->nr_extents is 2), and the user
does not have caps in the parent ns
i'm aware UID_GID_MAP_MAX_EXTENTS is low, so it's not even possible if i
had the caps to map all the existing supplemental groups, which is why i
only want to map the few critical groups. but assuming this use case is
one we want to support, maybe it makes sense to add a knob to map all of
the user's supplemental groups ?
in the mean time, a "quick" fix might be to change new_idmap_permitted
to walk all the extents, and if all the ranges are set to 1, check the
supplemental groups in addition to the current egid ?
-mike
[-- Attachment #1.2: test.c --]
[-- Type: text/x-c, Size: 2280 bytes --]
/* To compile:
* gcc test.c
* To run:
* ./a.out [1|2|3|4|5]
* ./a.out -1 "string to write to gid_map"
*/
#define _GNU_SOURCE
#include <assert.h>
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
int ret;
FILE *fp;
int uid = getuid();
int gid = getgid();
int nuid = 0;
int ngid = 0;
int suppgid;
gid_t *groups;
size_t i, gsize;
int group_mode;
switch (argc) {
case 0:
case 1:
group_mode = 1;
case 2:
group_mode = atoi(argv[1]);
case 3:
group_mode = -1;
}
/* Find one supplemental group */
gsize = getgroups(0, NULL);
assert(gsize != 0);
groups = malloc(sizeof(*groups) * gsize);
ret = getgroups(gsize, groups);
assert(ret == gsize);
for (i = 0; i < gsize; ++i)
if (groups[i] != gid) {
suppgid = groups[i];
break;
}
if (i == gsize)
errx(1, "could not find a supplemental group to test");
free(groups);
/* Create new userns */
assert(unshare(CLONE_NEWUSER) == 0);
/* Map the current uid */
fp = fopen("/proc/self/uid_map", "we");
assert(fp);
setbuf(fp, NULL);
fprintf(fp, "%i %i 1", nuid, uid);
fclose(fp);
/* Disable setgroups() */
fp = fopen("/proc/self/setgroups", "we");
assert(fp);
setbuf(fp, NULL);
fputs("deny", fp);
fclose(fp);
/* Map the various gids */
fp = fopen("/proc/self/gid_map", "we");
assert(fp);
setbuf(fp, NULL);
switch (group_mode) {
case 1: /* This works */
fprintf(fp, "%i %i 1\n", ngid, gid);
break;
case 2: /* This fails */
fprintf(fp, "%i %i 1\n", ngid, gid);
fprintf(fp, "%i %i 1\n", suppgid, suppgid);
break;
case 3: /* This fails */
fprintf(fp, "%i %i 1\n%i %i 1\n", ngid, gid, suppgid, suppgid);
break;
case 4: /* This fails */
fprintf(fp, "%i %i 1\n", suppgid, suppgid);
break;
case 5: /* This fails */
fprintf(fp, "0 0 10000\n");
break;
case -1:
fprintf(fp, argv[2]);
break;
}
fclose(fp);
/* Validate */
printf("uid:%i gid:%i\n", getuid(), getgid());
gsize = getgroups(0, NULL);
assert(gsize != 0);
groups = malloc(sizeof(*groups) * gsize);
ret = getgroups(gsize, groups);
assert(ret == gsize);
printf("groups: ");
for (i = 0; i < gsize; ++i)
printf("%i ", groups[i]);
printf("\n");
free(groups);
}
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
next reply other threads:[~2015-09-22 16:22 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-22 16:21 Mike Frysinger [this message]
[not found] ` <87d1xafdk8.fsf@x220.int.ebiederm.org>
2015-09-22 21:52 ` Mike Frysinger
2015-09-29 3:06 ` Mike Frysinger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150922162157.GB27286@vapier.lan \
--to=vapier@gentoo.org \
--cc=containers@lists.linux-foundation.org \
--cc=ebiederm@xmission.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®