#include "fileserv.h" #include #include #include #include static void addgid(gid_t **pgv, size_t *pgc, size_t *pgi, gid_t gid) { gid_t *gv = *pgv; size_t gc = *pgc; size_t gi = *pgi; if (gi == gc) { if (gc == 0) { gc = 16; gv = calloc(gc, sizeof(*gv)); } else if (gc <= SIZE_T_MAX / 2 / sizeof(*gv)) { gc *= 2; gv = realloc(gv, gc * sizeof(*gv)); } else xmalloc_fail(); if (!gv) xmalloc_fail(); } gv[gi++] = gid; *pgv = gv; *pgc = gc; *pgi = gi; } static int member(gid_t *gv, size_t gc, gid_t gid) { size_t i; for (i = 0; i < gc; i++) if (gv[i] == gid) return 1; return 0; } static size_t get_user_groups(const char *user, gid_t **pgv, size_t *pgc) { struct group *gr; size_t gi = 0; setgrent(); while ((gr = getgrent())) { char **p; for (p = gr->gr_mem; *p; p++) if (strcmp(*p, user) == 0) addgid(pgv, pgc, &gi, gr->gr_gid); } endgrent(); return gi; } void runas(char const *runas_user, char const *runas_group) { struct passwd *pw; struct group *gr; uid_t uid; gid_t gid; char const *user_name; gid_t *gv; size_t gc, gn; if (!(runas_user || runas_group)) return; if (getuid() != 0) { error("not root: can't switch to user privileges"); exit(1); } user_name = runas_user; if (!user_name) { pw = getpwuid(0); user_name = "root"; } else if (user_name[0] == '+') { char *end; unsigned long n; errno = 0; n = strtoul(user_name + 1, &end, 10); if (errno || *end) { error("invalid user name %s", user_name); exit(1); } pw = getpwuid(n); } else pw = getpwnam(user_name); if (!pw) { error("%s: no such user", runas_user); exit(1); } user_name = pw->pw_name; uid = pw->pw_uid; gid = pw->pw_gid; if (runas_group) { if (runas_group[0] == '+') { char *end; unsigned long n; errno = 0; n = strtoul(runas_group + 1, &end, 10); if (errno || *end) { error("invalid group name %s", runas_group); exit(1); } gr = getgrgid(n); } else gr = getgrnam(runas_group); if (!gr) { error("%s: no such group", runas_user); exit(1); } gid = gr->gr_gid; } gv = NULL; gc = 0; gn = get_user_groups(user_name, &gv, &gc); if (!member(gv, gn, gid)) addgid(&gv, &gc, &gn, gid); /* Reset group permissions */ if (setgroups(gn, gv)) { error("setgroups failed: %s", strerror(errno)); exit(1); } free(gv); if (gid) { /* Switch to the user's gid. */ if (setgid(gid)) { error("setgid(%lu) failed: %s", (unsigned long) gid, strerror(errno)); exit(1); } } /* Now reset uid */ if (uid) { if (setuid(uid)) { error("setuid(%lu) failed: %s", (unsigned long) uid, strerror(errno)); exit(1); } } }