All of lore.kernel.org
 help / color / mirror / Atom feed
From: James Bottomley <James.Bottomley@HansenPartnership.com>
To: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Linux Containers <containers@lists.linux-foundation.org>,
	systemd-devel@lists.freedesktop.org, util-linux@vger.kernel.org
Subject: Re: Unprivileged containers and co-ordinating user namespaces
Date: Fri, 29 Apr 2016 15:18:30 -0700	[thread overview]
Message-ID: <1461968310.16292.20.camel@HansenPartnership.com> (raw)
In-Reply-To: <1461944328.2311.10.camel@HansenPartnership.com>

[-- Attachment #1: Type: text/plain, Size: 9250 bytes --]

On Fri, 2016-04-29 at 08:38 -0700, James Bottomley wrote:
> On Thu, 2016-04-28 at 16:00 -0700, W. Trevor King wrote:
> > On Thu, Apr 28, 2016 at 03:02:08PM -0700, James Bottomley wrote:
> > > /etc/usernamespaces
> > > 
> > > and the format be :::
> > > 
> > > …
> > > 
> > > If this sounds OK to people, I can code up a utility that does
> > > this,
> > > which should probably belong in util-linux.
> > 
> > This sounds a lot like shadow's newuidmap and newgidmap [1,2,3].
> > 
> > Cheers,
> > Trevor
> > 
> > [1]: https://github.com/shadow-maint/shadow/commit/673c2a6f9aa6c695
> > 88f4c1be08589b8d3475a520
> > [2]: http://man7.org/linux/man-pages/man1/newuidmap.1.html
> > [3]: http://man7.org/linux/man-pages/man5/subuid.5.html
> 
> I think that mostly works.  No-one's packaging it yet, which is why I
> didn't notice.  It also looks like the build dependencies have vastly
> expanded, so I can't get it to build in the build service yet.
> 
> It looks like the only addition it needs is the setgroups flag for
> newgidmap, which the security people will need, so I can patch that. 

How does this look for a patch?

James

---

diff --git a/lib/subordinateio.c b/lib/subordinateio.c
index 0d64a91..aa3c968 100644
--- a/lib/subordinateio.c
+++ b/lib/subordinateio.c
@@ -18,9 +18,10 @@ struct subordinate_range {
 	const char *owner;
 	unsigned long start;
 	unsigned long count;
+	const char *flags;
 };
 
-#define NFIELDS 3
+#define NFIELDS 4
 
 /*
  * subordinate_dup: create a duplicate range
@@ -44,6 +45,16 @@ static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent)
 		free(range);
 		return NULL;
 	}
+	if (rangeent->flags) {
+		range->flags = strdup (rangeent->flags);
+		if (NULL == range->flags) {
+			free((void *)range->owner);
+			free(range);
+			return NULL;
+		}
+	} else {
+		range->flags = NULL;
+	}
 	range->start = rangeent->start;
 	range->count = rangeent->count;
 
@@ -111,13 +122,17 @@ static void *subordinate_parse (const char *line)
 	 * There must be exactly NFIELDS colon separated fields or
 	 * the entry is invalid.  Also, fields must be non-blank.
 	 */
-	if (i != NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
+	if ((i != NFIELDS && i != NFIELDS -1) || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0')
 		return NULL;
 	range.owner = fields[0];
 	if (getulong (fields[1], &range.start) == 0)
 		return NULL;
 	if (getulong (fields[2], &range.count) == 0)
 		return NULL;
+	if (i == NFIELDS)
+		range.flags = fields[3];
+	else
+		range.flags = NULL;
 
 	return &range;
 }
@@ -134,10 +149,11 @@ static int subordinate_put (const void *ent, FILE * file)
 {
 	const struct subordinate_range *range = ent;
 
-	return fprintf(file, "%s:%lu:%lu\n",
+	return fprintf(file, "%s:%lu:%lu:%s\n",
 			       range->owner,
 			       range->start,
-			       range->count) < 0 ? -1  : 0;
+			       range->count,
+			       range->flags ? range->flags : "") < 0 ? -1  : 0;
 }
 
 static struct commonio_ops subordinate_ops = {
@@ -295,7 +311,8 @@ static const struct subordinate_range *find_range(struct commonio_db *db,
  * Returns true if @owner is authorized to use the range, false otherwise.
  */
 static bool have_range(struct commonio_db *db,
-		       const char *owner, unsigned long start, unsigned long count)
+		       const char *owner, unsigned long start, unsigned long count,
+		       const char **flags)
 {
 	const struct subordinate_range *range;
 	unsigned long end;
@@ -309,8 +326,11 @@ static bool have_range(struct commonio_db *db,
 		unsigned long last; 
 
 		last = range->start + range->count - 1;
-		if (last >= (start + count - 1))
+		if (last >= (start + count - 1)) {
+			if (flags)
+				*flags = range->flags;
 			return true;
+		}
 
 		count = end - last;
 		start = last + 1;
@@ -430,7 +450,7 @@ static int add_range(struct commonio_db *db,
 	range.count = count;
 
 	/* See if the range is already present */
-	if (have_range(db, owner, start, count))
+	if (have_range(db, owner, start, count, NULL))
 		return 1;
 
 	/* Otherwise append the range */
@@ -585,7 +605,7 @@ bool sub_uid_assigned(const char *owner)
 
 bool have_sub_uids(const char *owner, uid_t start, unsigned long count)
 {
-	return have_range (&subordinate_uid_db, owner, start, count);
+	return have_range (&subordinate_uid_db, owner, start, count, NULL);
 }
 
 int sub_uid_add (const char *owner, uid_t start, unsigned long count)
@@ -659,9 +679,9 @@ int sub_gid_open (int mode)
 	return commonio_open (&subordinate_gid_db, mode);
 }
 
-bool have_sub_gids(const char *owner, gid_t start, unsigned long count)
+bool have_sub_gids(const char *owner, gid_t start, unsigned long count, const char **flags)
 {
-	return have_range(&subordinate_gid_db, owner, start, count);
+	return have_range(&subordinate_gid_db, owner, start, count, flags);
 }
 
 bool sub_gid_assigned(const char *owner)
diff --git a/lib/subordinateio.h b/lib/subordinateio.h
index a21d72b..7e47659 100644
--- a/lib/subordinateio.h
+++ b/lib/subordinateio.h
@@ -25,7 +25,7 @@ extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count);
 extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count);
 
 extern int sub_gid_close(void);
-extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count);
+extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count, const char **flags);
 extern bool sub_gid_file_present (void);
 extern bool sub_gid_assigned(const char *owner);
 extern int sub_gid_lock (void);
diff --git a/libmisc/idmapping.c b/libmisc/idmapping.c
index 0dce634..7f7de88 100644
--- a/libmisc/idmapping.c
+++ b/libmisc/idmapping.c
@@ -106,7 +106,6 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings,
 	struct map_range *mapping;
 	size_t bufsize;
 	char *buf, *pos;
-	int fd;
 
 	bufsize = ranges * ((ULONG_DIGITS  + 1) * 3);
 	pos = buf = xmalloc(bufsize);
@@ -128,13 +127,20 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings,
 	}
 
 	/* Write the mapping to the maping file */
+	write_proc(proc_dir_fd, map_file, buf, pos - buf);
+}
+
+void write_proc(int proc_dir_fd, const char *map_file, void *buf, int len)
+{
+	int fd;
+
 	fd = openat(proc_dir_fd, map_file, O_WRONLY);
 	if (fd < 0) {
 		fprintf(stderr, _("%s: open of %s failed: %s\n"),
 			Prog, map_file, strerror(errno));
 		exit(EXIT_FAILURE);
 	}
-	if (write(fd, buf, pos - buf) != (pos - buf)) {
+	if (write(fd, buf, len) != len) {
 		fprintf(stderr, _("%s: write to %s failed: %s\n"),
 			Prog, map_file, strerror(errno));
 		exit(EXIT_FAILURE);
diff --git a/libmisc/idmapping.h b/libmisc/idmapping.h
index 58d000f..c2cec38 100644
--- a/libmisc/idmapping.h
+++ b/libmisc/idmapping.h
@@ -39,6 +39,7 @@ struct map_range {
 extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
 extern void write_mapping(int proc_dir_fd, int ranges,
 	struct map_range *mappings, const char *map_file);
+extern void write_proc(int proc_dir_fd, const char *map_file, void *buf, int len);
 
 #endif /* _ID_MAPPING_H_ */
 
diff --git a/src/newgidmap.c b/src/newgidmap.c
index 451c6a6..8d64e3b 100644
--- a/src/newgidmap.c
+++ b/src/newgidmap.c
@@ -46,14 +46,14 @@
  */
 const char *Prog;
 
-static bool verify_range(struct passwd *pw, struct map_range *range)
+static bool verify_range(struct passwd *pw, struct map_range *range, const char **flags)
 {
 	/* An empty range is invalid */
 	if (range->count == 0)
 		return false;
 
 	/* Test /etc/subgid */
-	if (have_sub_gids(pw->pw_name, range->lower, range->count))
+	if (have_sub_gids(pw->pw_name, range->lower, range->count, flags))
 		return true;
 
 	/* Allow a process to map it's own gid */
@@ -64,14 +64,14 @@ static bool verify_range(struct passwd *pw, struct map_range *range)
 }
 
 static void verify_ranges(struct passwd *pw, int ranges,
-	struct map_range *mappings)
+			  struct map_range *mappings, const char **flags)
 {
 	struct map_range *mapping;
 	int idx;
 
 	mapping = mappings;
 	for (idx = 0; idx < ranges; idx++, mapping++) {
-		if (!verify_range(pw, mapping)) {
+		if (!verify_range(pw, mapping, flags)) {
 			fprintf(stderr, _( "%s: gid range [%lu-%lu) -> [%lu-%lu) not allowed\n"),
 				Prog,
 				mapping->upper,
@@ -103,6 +103,7 @@ int main(int argc, char **argv)
 	struct stat st;
 	struct passwd *pw;
 	int written;
+	const char *flags;
 
 	Prog = Basename (argv[0]);
 
@@ -177,7 +178,18 @@ int main(int argc, char **argv)
 	if (!mappings)
 		usage();
 
-	verify_ranges(pw, ranges, mappings);
+	verify_ranges(pw, ranges, mappings, &flags);
+
+	if (flags && strlen(flags) != 0) {
+		/* only allowed flag is currently deny */
+		if (strcmp(flags, "deny") == 0) {
+			write_proc(proc_dir_fd, "setgroups", "deny", strlen("deny"));
+		} else {
+			fprintf(stderr, _("%s: illegal flag in map file: %s\n"),
+				Prog, flags);
+			exit(EXIT_FAILURE);
+		}
+	}	
 
 	write_mapping(proc_dir_fd, ranges, mappings, "gid_map");
 	sub_gid_close();

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

  parent reply	other threads:[~2016-04-29 22:18 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-28 22:02 Unprivileged containers and co-ordinating user namespaces James Bottomley
2016-04-28 22:02 ` James Bottomley
     [not found] ` <1461880928.2307.48.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-04-28 23:00   ` W. Trevor King
2016-04-28 23:00     ` W. Trevor King
     [not found]     ` <20160428230045.GS22888-q4NCUed9G3sTnwFZoN752g@public.gmane.org>
2016-04-29 15:38       ` James Bottomley
2016-04-29 15:38         ` James Bottomley
     [not found]         ` <1461944328.2311.10.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-04-29 15:53           ` Serge E. Hallyn
2016-04-29 22:18           ` James Bottomley
2016-05-04 15:02           ` Eric W. Biederman
2016-04-29 15:53         ` Serge E. Hallyn
     [not found]           ` <20160429155303.GA8900-7LNsyQBKDXoIagZqoN9o3w@public.gmane.org>
2016-04-29 18:34             ` W. Trevor King
2016-04-29 18:34               ` W. Trevor King
2016-04-29 21:50               ` Serge E. Hallyn
     [not found]               ` <20160429183404.GE22888-q4NCUed9G3sTnwFZoN752g@public.gmane.org>
2016-04-29 21:50                 ` Serge E. Hallyn
2016-04-29 22:18         ` James Bottomley [this message]
     [not found]           ` <1461968310.16292.20.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-05-01 16:37             ` Serge Hallyn
2016-05-01 16:37           ` Serge Hallyn
2016-05-01 23:29             ` James Bottomley
     [not found]               ` <1462145366.2337.18.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-05-02  4:13                 ` Serge E. Hallyn
2016-05-02  4:13                   ` Serge E. Hallyn
2016-05-01 23:29             ` James Bottomley
2016-05-04 15:02         ` Eric W. Biederman
2016-05-04 15:21           ` Phil Estes
2016-05-04 18:17           ` James Bottomley
2016-05-04 18:21             ` James Bottomley
     [not found]             ` <1462385876.14310.90.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-05-04 18:21               ` James Bottomley
     [not found]           ` <87wpn9988a.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
2016-05-04 15:21             ` Phil Estes
2016-05-04 18:17             ` James Bottomley

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=1461968310.16292.20.camel@HansenPartnership.com \
    --to=james.bottomley@hansenpartnership.com \
    --cc=containers@lists.linux-foundation.org \
    --cc=serge@hallyn.com \
    --cc=systemd-devel@lists.freedesktop.org \
    --cc=util-linux@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.