Merge "Allow setting supplementary GIDs directly."
diff --git a/libminijail.c b/libminijail.c
index 9e2c24e..00a4344 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -87,6 +87,8 @@
 	struct {
 		int uid:1;
 		int gid:1;
+		int usergroups:1;
+		int suppl_gids:1;
 		int caps:1;
 		int vfs:1;
 		int enter_vfs:1;
@@ -97,7 +99,6 @@
 		int userns:1;
 		int seccomp:1;
 		int remount_proc_ro:1;
-		int usergroups:1;
 		int no_new_privs:1;
 		int seccomp_filter:1;
 		int log_seccomp_filter:1;
@@ -112,6 +113,8 @@
 	gid_t gid;
 	gid_t usergid;
 	char *user;
+	size_t suppl_gid_count;
+	gid_t *suppl_gid_list;
 	uint64_t caps;
 	pid_t initpid;
 	int mountns_fd;
@@ -188,6 +191,28 @@
 	j->flags.gid = 1;
 }
 
+int API minijail_set_supplementary_gids(struct minijail *j, size_t size,
+					const gid_t *list)
+{
+	if (j->flags.usergroups)
+		die("cannot inherit *and* set supplementary groups");
+
+	if (size == 0)
+		return -EINVAL;
+
+	/* Copy the gid_t array. */
+	j->suppl_gid_list = calloc(size, sizeof(gid_t));
+	if (!j->suppl_gid_list) {
+		return -ENOMEM;
+	}
+	for (size_t i = 0; i < size; i++) {
+		j->suppl_gid_list[i] = list[i];
+	}
+	j->suppl_gid_count = size;
+	j->flags.suppl_gids = 1;
+	return 0;
+}
+
 int API minijail_change_user(struct minijail *j, const char *user)
 {
 	char *buf = NULL;
@@ -962,12 +987,21 @@
 
 void drop_ugid(const struct minijail *j)
 {
+	if (j->flags.usergroups && j->flags.suppl_gids) {
+		die("tried to inherit *and* set supplementary groups;"
+		    " can only do one");
+	}
+
 	if (j->flags.usergroups) {
 		if (initgroups(j->user, j->usergid))
 			pdie("initgroups");
+	} else if (j->flags.suppl_gids) {
+		if (setgroups(j->suppl_gid_count, j->suppl_gid_list)) {
+			pdie("setgroups");
+		}
 	} else {
 		/*
-		 * Only attempt to clear supplemental groups if we are changing
+		 * Only attempt to clear supplementary groups if we are changing
 		 * users.
 		 */
 		if ((j->uid || j->gid) && setgroups(0, NULL))
diff --git a/libminijail.h b/libminijail.h
index aceb69b..f5c6ec7 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -40,6 +40,9 @@
  */
 void minijail_change_uid(struct minijail *j, uid_t uid);
 void minijail_change_gid(struct minijail *j, gid_t gid);
+/* Copies |list|. */
+int minijail_set_supplementary_gids(struct minijail *j, size_t size,
+				    const gid_t *list);
 /* Stores user to change to and copies |user| for internal consistency. */
 int minijail_change_user(struct minijail *j, const char *user);
 /* Does not take ownership of |group|. */
@@ -119,7 +122,7 @@
  * of minijail_mount() calls.
  */
 int minijail_mount(struct minijail *j, const char *src, const char *dest,
-		    const char *type, unsigned long flags);
+		   const char *type, unsigned long flags);
 
 /*
  * minijail_bind: bind-mounts @src into @j as @dest, optionally writeable