setgroups and getgroups syscalls
MODIFY
bin/utest/cred.c
bin/utest/cred.c
#include <stdio.h>
#include <errno.h>
#include <assert.h>
+#include <limits.h>
int test_get_set_uid(void) {
uid_t ruid, euid, suid;
@@ -89,3 +90,66 @@ int test_get_set_gid(void) {
return 0;
}
+
+int test_get_set_groups(void) {
+ gid_t rgrp[NGROUPS_MAX], gidset[NGROUPS_MAX] = {0, 1, 2, 3, 4, 5};
+ int r, ngroups = 6;
+ uid_t euid;
+
+ /* check if we are a root at start */
+ getresuid(NULL, &euid, NULL);
+ assert(euid == 0);
+ r = getgroups(0, NULL);
+ assert(r == 0);
+
+ /* setting too many groups */
+ r = setgroups(NGROUPS_MAX + 2, gidset);
+ assert(r == -1 && errno == EINVAL);
+
+ /* setting groups (without fail now) */
+ r = setgroups(ngroups, gidset);
+ assert(r == 0);
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* first argument is too small */
+ r = getgroups(ngroups - 1, rgrp);
+ assert(r == -1 && errno == EINVAL);
+
+ /* dropping all supplementary groups */
+ r = setgroups(0, NULL);
+ assert(r == 0);
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r == 0);
+
+ r = setgroups(-ngroups, gidset);
+ assert(r < 0 && errno == EINVAL);
+ /* setting for further tests */
+ r = setgroups(ngroups, gidset);
+ assert(r == 0);
+
+ /* dropping privileges */
+ setresuid(1, 1, 1);
+
+ /* dropping shouldn't affect supplementary groups */
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* we can't change suplementary groups when we are not root user */
+ r = setgroups(ngroups, gidset);
+ assert(r == -1 && errno == EPERM);
+
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* first argument is too small */
+ r = getgroups(3, rgrp);
+ assert(r == -1 && errno == EINVAL);
+ return 0;
+}

MODIFY
bin/utest/main.c
bin/utest/main.c
@@ -85,6 +85,7 @@ int main(int argc, char **argv) {
CHECKRUN_TEST(get_set_uid);
CHECKRUN_TEST(get_set_gid);
+ CHECKRUN_TEST(get_set_groups);
printf("No user test \"%s\" available.\n", test_name);
return 1;

MODIFY
bin/utest/utest.h
bin/utest/utest.h
@@ -86,5 +86,6 @@ int test_killpg_other_group(void);
int test_get_set_uid(void);
int test_get_set_gid(void);
+int test_get_set_groups(void);
#endif /* __UTEST_H__ */

MODIFY
include/sys/cred.h
include/sys/cred.h
@@ -27,6 +27,7 @@ void do_getresuid(proc_t *p, uid_t *ruid, uid_t *euid, uid_t *suid);
void do_getresgid(proc_t *p, gid_t *rgid, gid_t *egid, gid_t *sgid);
int do_setresuid(proc_t *p, uid_t ruid, uid_t euid, uid_t suid);
int do_setresgid(proc_t *p, gid_t rgid, gid_t egid, gid_t sgid);
+int do_setgroups(proc_t *p, int ngroups, const gid_t *gidset);
void cred_fork(proc_t *to, proc_t *from);

MODIFY
include/sys/syscall.h
include/sys/syscall.h
#define SYS_sigsuspend 55
#define SYS_statvfs 56
#define SYS_fstatvfs 57
-#define SYS_MAXSYSCALL 58
+#define SYS_getgroups 58
+#define SYS_setgroups 59
+#define SYS_MAXSYSCALL 60
#define SYS_MAXSYSARGS 6

MODIFY
include/sys/syscallargs.h
include/sys/syscallargs.h
@@ -299,3 +299,13 @@ typedef struct {
int fd;
struct statvfs * buf;
} fstatvfs_args_t;
+
+typedef struct {
+ int ngroups;
+ gid_t * gidset;
+} getgroups_args_t;
+
+typedef struct {
+ int ngroups;
+ const gid_t * gidset;
+} setgroups_args_t;

MODIFY
include/unistd.h
include/unistd.h
@@ -37,6 +37,7 @@ gid_t getegid(void);
uid_t geteuid(void);
gid_t getgid(void);
int getgroups(int, gid_t[]);
+int setgroups(int, gid_t[]);
char *getlogin(void);
int getlogin_r(char *, size_t);
pid_t getpgrp(void);

MODIFY
lib/libc/sys/syscalls.S
lib/libc/sys/syscalls.S
@@ -56,3 +56,5 @@ SYSCALL(sched_yield, SYS_sched_yield)
SYSCALL(sigsuspend, SYS_sigsuspend)
SYSCALL(statvfs, SYS_statvfs)
SYSCALL(fstatvfs, SYS_fstatvfs)
+SYSCALL(getgroups, SYS_getgroups)
+SYSCALL(setgroups, SYS_setgroups)

MODIFY
sys/kern/cred_syscalls.c
sys/kern/cred_syscalls.c
@@ -120,3 +120,14 @@ end:
proc_unlock(p);
return error;
}
+
+int do_setgroups(proc_t *p, int ngroups, const gid_t *gidset) {
+ /* only root user can change supplementary groups */
+ if (p->p_cred.cr_euid != 0)
+ return EPERM;
+
+ p->p_cred.cr_ngroups = ngroups;
+ memcpy(p->p_cred.cr_groups, gidset, ngroups * sizeof(gid_t));
+
+ return 0;
+}

MODIFY
sys/kern/syscalls.c
sys/kern/syscalls.c
@@ -897,3 +897,47 @@ static int sys_fstatvfs(proc_t *p, fstatvfs_args_t *args, register_t *res) {
return error;
}
+
+static int sys_getgroups(proc_t *p, getgroups_args_t *args, register_t *res) {
+ int ngroups = args->ngroups;
+ gid_t *ugidset = args->gidset;
+ int pngroups = p->p_cred.cr_ngroups;
+
+ klog("getgroups(%d, %p)", ngroups, ugidset);
+
+ if (ngroups == 0) {
+ /* just return number of groups */
+ *res = pngroups;
+ return 0;
+ }
+
+ if (ngroups < pngroups)
+ return EINVAL;
+
+ *res = pngroups;
+ return copyout(p->p_cred.cr_groups, ugidset, pngroups * sizeof(gid_t));
+}
+
+static int sys_setgroups(proc_t *p, setgroups_args_t *args, register_t *res) {
+ int error = 0;
+ int ungroups = args->ngroups;
+ const gid_t *ugidset = args->gidset;
+
+ klog("setgroups(%d, %p)", ungroups, ugidset);
+
+ /* too many groups */
+ if (ungroups < 0 || ungroups > NGROUPS_MAX)
+ return EINVAL;
+
+ gid_t *gidset = kmalloc(M_TEMP, ungroups * sizeof(gid_t), M_NOWAIT);
+
+ /* if we set 0 groups kmalloc returns NULL, but it is not an error */
+ if (ungroups > 0 && gidset == NULL)
+ return ENOMEM;
+
+ if (!(error = copyin(ugidset, gidset, ungroups * sizeof(gid_t))))
+ error = do_setgroups(p, ungroups, gidset);
+
+ kfree(M_TEMP, gidset);
+ return error;
+}

MODIFY
sys/kern/syscalls.master
sys/kern/syscalls.master
55 { int sys_sigsuspend(sigset_t *sigmask); }
56 { int sys_statvfs(const char *path, struct statvfs *buf); }
57 { int sys_fstatvfs(int fd, struct statvfs *buf); }
+58 { int sys_getgroups(int ngroups, gid_t *gidset); }
+59 { int sys_setgroups(int ngroups, const gid_t *gidset); }
; vim: ts=4 sw=4 sts=4 et

MODIFY
sys/kern/sysent.h
sys/kern/sysent.h
@@ -63,6 +63,8 @@ static int sys_sched_yield(proc_t *, void *, register_t *);
static int sys_sigsuspend(proc_t *, sigsuspend_args_t *, register_t *);
static int sys_statvfs(proc_t *, statvfs_args_t *, register_t *);
static int sys_fstatvfs(proc_t *, fstatvfs_args_t *, register_t *);
+static int sys_getgroups(proc_t *, getgroups_args_t *, register_t *);
+static int sys_setgroups(proc_t *, setgroups_args_t *, register_t *);
struct sysent sysent[] = {
[SYS_syscall] = { .nargs = 1, .call = (syscall_t *)sys_syscall },
@@ -123,5 +125,7 @@ struct sysent sysent[] = {
[SYS_sigsuspend] = { .nargs = 1, .call = (syscall_t *)sys_sigsuspend },
[SYS_statvfs] = { .nargs = 2, .call = (syscall_t *)sys_statvfs },
[SYS_fstatvfs] = { .nargs = 2, .call = (syscall_t *)sys_fstatvfs },
+ [SYS_getgroups] = { .nargs = 2, .call = (syscall_t *)sys_getgroups },
+ [SYS_setgroups] = { .nargs = 2, .call = (syscall_t *)sys_setgroups },
};

MODIFY
sys/tests/utest.c
sys/tests/utest.c
@@ -141,3 +141,4 @@ UTEST_ADD_SIMPLE(killpg_other_group);
UTEST_ADD_SIMPLE(get_set_uid);
UTEST_ADD_SIMPLE(get_set_gid);
+UTEST_ADD_SIMPLE(get_set_groups);

setgroups and getgroups syscalls (#720)