/* 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 #include #include #include #include #include #include #include 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); }