basic user, group and capability housekeeping.
authorLunaixsky <lunaixsky@qq.com>
Tue, 18 Mar 2025 23:16:03 +0000 (23:16 +0000)
committerLunaixsky <lunaixsky@qq.com>
Tue, 1 Apr 2025 13:32:02 +0000 (14:32 +0100)
add usrscope for process-wise user management
implement {get,set}*{uid,euid,gid,egid,groups} syscalls
basic acl checking mechanism
move common syscall table to arch/generic.

15 files changed:
lunaix-os/arch/generic/includes/asm-generic/syscall_nr.inc [new file with mode: 0644]
lunaix-os/arch/x86/LBuild
lunaix-os/arch/x86/syscall32.S
lunaix-os/arch/x86/syscall64.S
lunaix-os/arch/x86/syscall_lut.S [new file with mode: 0644]
lunaix-os/arch/x86/syscall_nr.inc
lunaix-os/includes/lunaix/limits.h [new file with mode: 0644]
lunaix-os/includes/lunaix/process.h
lunaix-os/includes/lunaix/types.h
lunaix-os/includes/lunaix/usercaps.h [new file with mode: 0644]
lunaix-os/includes/lunaix/usrscope.h [new file with mode: 0644]
lunaix-os/includes/usr/lunaix/status.h
lunaix-os/kernel/LBuild
lunaix-os/kernel/process/process.c
lunaix-os/kernel/usrscope.c [new file with mode: 0644]

diff --git a/lunaix-os/arch/generic/includes/asm-generic/syscall_nr.inc b/lunaix-os/arch/generic/includes/asm-generic/syscall_nr.inc
new file mode 100644 (file)
index 0000000..7590c4b
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef SYSCALL
+#   error "SYSCALL missing pointer type"
+#endif
+
+SYSCALL 0
+SYSCALL __lxsys_fork          /* 1 */
+SYSCALL __lxsys_yield
+SYSCALL __lxsys_sbrk
+SYSCALL __lxsys_brk
+SYSCALL __lxsys_getpid        /* 5 */
+SYSCALL __lxsys_getppid
+SYSCALL __lxsys_sleep
+SYSCALL __lxsys_exit
+SYSCALL __lxsys_wait          
+SYSCALL __lxsys_waitpid       /* 10 */
+SYSCALL __lxsys_sigreturn
+SYSCALL __lxsys_sigprocmask
+SYSCALL __lxsys_sys_sigaction
+SYSCALL __lxsys_pause
+SYSCALL __lxsys_kill          /* 15 */
+SYSCALL __lxsys_alarm
+SYSCALL __lxsys_sigpending
+SYSCALL __lxsys_sigsuspend
+SYSCALL __lxsys_open
+SYSCALL __lxsys_close         /* 20 */
+SYSCALL __lxsys_read
+SYSCALL __lxsys_write
+SYSCALL __lxsys_sys_readdir
+SYSCALL __lxsys_mkdir
+SYSCALL __lxsys_lseek         /* 25 */
+SYSCALL __lxsys_geterrno
+SYSCALL __lxsys_readlink
+SYSCALL __lxsys_readlinkat
+SYSCALL __lxsys_rmdir
+SYSCALL __lxsys_unlink        /* 30 */
+SYSCALL __lxsys_unlinkat
+SYSCALL __lxsys_link
+SYSCALL __lxsys_fsync
+SYSCALL __lxsys_dup
+SYSCALL __lxsys_dup2          /* 35 */
+SYSCALL __lxsys_realpathat
+SYSCALL __lxsys_symlink
+SYSCALL __lxsys_chdir
+SYSCALL __lxsys_fchdir
+SYSCALL __lxsys_getcwd        /* 40 */
+SYSCALL __lxsys_rename
+SYSCALL __lxsys_mount
+SYSCALL __lxsys_unmount
+SYSCALL __lxsys_getxattr
+SYSCALL __lxsys_setxattr      /* 45 */
+SYSCALL __lxsys_fgetxattr
+SYSCALL __lxsys_fsetxattr
+SYSCALL __lxsys_ioctl
+SYSCALL __lxsys_getpgid
+SYSCALL __lxsys_setpgid       /* 50 */
+SYSCALL __lxsys_syslog
+SYSCALL __lxsys_sys_mmap
+SYSCALL __lxsys_munmap
+SYSCALL __lxsys_execve
+SYSCALL __lxsys_fstat         /* 55 */
+SYSCALL __lxsys_pollctl
+SYSCALL __lxsys_th_create
+SYSCALL __lxsys_th_self
+SYSCALL __lxsys_th_exit
+SYSCALL __lxsys_th_join       /* 60 */
+SYSCALL __lxsys_th_kill
+SYSCALL __lxsys_th_detach
+SYSCALL __lxsys_th_sigmask
+SYSCALL __lxsys_getuid
+SYSCALL __lxsys_getgid
+SYSCALL __lxsys_geteuid
+SYSCALL __lxsys_getegid
+SYSCALL __lxsys_getgroups
+SYSCALL __lxsys_setuid
+SYSCALL __lxsys_setgid
+SYSCALL __lxsys_seteuid
+SYSCALL __lxsys_setegid
+SYSCALL __lxsys_setgroups
\ No newline at end of file
index 941453b42c8eea2e2ddc29f7377b357d0087b323..ed4b4f8ac9e10f4396fbd1b7b9f0470f311ce447 100644 (file)
@@ -29,7 +29,8 @@ sources([
     "gdbstub.c",
     "trace.c",
     "hart.c",
     "gdbstub.c",
     "trace.c",
     "hart.c",
-    "failsafe.S"
+    "failsafe.S",
+    "syscall_lut.S"
 ])
 
 sources({
 ])
 
 sources({
index b8915ed8a0b165f5e1701c5b650a44d2dcf88d5a..3fb96b4f68ad4e44d394a2887ba6b7c650a811f5 100644 (file)
@@ -1,6 +1,5 @@
 #define __ASM__
 #include <lunaix/syscall.h>
 #define __ASM__
 #include <lunaix/syscall.h>
-#include "syscall_nr.inc"
 
 .section .text
     .type syscall_hndlr, @function
 
 .section .text
     .type syscall_hndlr, @function
@@ -16,7 +15,7 @@
         jae     2f
 
         shll    $2, %eax
         jae     2f
 
         shll    $2, %eax
-        addl    $syscall_table, %eax
+        addl    $__syscall_table, %eax
         cmpl    $0, (%eax)
         jne     1f
     2:    
         cmpl    $0, (%eax)
         jne     1f
     2:    
index 6125a84f5252d08d05306756febaa4cae0fe6710..cce528a937a69c82d4ab24a5ca2587874a33d6aa 100644 (file)
@@ -1,6 +1,5 @@
 #define __ASM__
 #include <lunaix/syscall.h>
 #define __ASM__
 #include <lunaix/syscall.h>
-#include "syscall_nr.inc"
 #include "asm/variants/interrupt64.S.inc"
 
 .section .text
 #include "asm/variants/interrupt64.S.inc"
 
 .section .text
@@ -18,7 +17,7 @@
         jae     2f
 
         shlq    $3, %rax               // %rax * 8
         jae     2f
 
         shlq    $3, %rax               // %rax * 8
-        movabsq $syscall_table, %r8
+        movabsq $__syscall_table, %r8
         addq    %r8, %rax
         cmpq    $0, (%rax)
         jne     1f
         addq    %r8, %rax
         cmpq    $0, (%rax)
         jne     1f
diff --git a/lunaix-os/arch/x86/syscall_lut.S b/lunaix-os/arch/x86/syscall_lut.S
new file mode 100644 (file)
index 0000000..4bee0b4
--- /dev/null
@@ -0,0 +1,20 @@
+#define __ASM__
+#include <lunaix/syscall.h>
+
+#ifdef CONFIG_ARCH_X86_64
+#   define SYSCALL     .8byte
+#   define SIZE    8
+#else
+#   define SIZE    4
+#   define SYSCALL     .4byte
+#endif
+
+.section .data
+    .global __syscall_table
+    __syscall_table:
+        1:
+        #include "syscall_nr.inc"
+        2:
+        .rept __SYSCALL_MAX - (2b - 1b) / SIZE
+            .long 0
+        .endr
\ No newline at end of file
index c30a720180d3019ffd0892e38c7c81ae453d090a..85762da739e14c82307136f994eaa94f1bad10bb 100644 (file)
@@ -1,79 +1,3 @@
-#ifdef CONFIG_ARCH_X86_64
-#   define PTR     .8byte
-#   define SIZE    8
-#else
-#   define PTR     .4byte
-#   define SIZE    4
-#endif
+#include <asm-generic/syscall_nr.inc>
 
 
-.section .data
-    syscall_table:
-        1:
-        PTR 0
-        PTR __lxsys_fork          /* 1 */
-        PTR __lxsys_yield
-        PTR __lxsys_sbrk
-        PTR __lxsys_brk
-        PTR __lxsys_getpid        /* 5 */
-        PTR __lxsys_getppid
-        PTR __lxsys_sleep
-        PTR __lxsys_exit
-        PTR __lxsys_wait          
-        PTR __lxsys_waitpid       /* 10 */
-        PTR __lxsys_sigreturn
-        PTR __lxsys_sigprocmask
-        PTR __lxsys_sys_sigaction
-        PTR __lxsys_pause
-        PTR __lxsys_kill          /* 15 */
-        PTR __lxsys_alarm
-        PTR __lxsys_sigpending
-        PTR __lxsys_sigsuspend
-        PTR __lxsys_open
-        PTR __lxsys_close         /* 20 */
-        PTR __lxsys_read
-        PTR __lxsys_write
-        PTR __lxsys_sys_readdir
-        PTR __lxsys_mkdir
-        PTR __lxsys_lseek         /* 25 */
-        PTR __lxsys_geterrno
-        PTR __lxsys_readlink
-        PTR __lxsys_readlinkat
-        PTR __lxsys_rmdir
-        PTR __lxsys_unlink        /* 30 */
-        PTR __lxsys_unlinkat
-        PTR __lxsys_link
-        PTR __lxsys_fsync
-        PTR __lxsys_dup
-        PTR __lxsys_dup2          /* 35 */
-        PTR __lxsys_realpathat
-        PTR __lxsys_symlink
-        PTR __lxsys_chdir
-        PTR __lxsys_fchdir
-        PTR __lxsys_getcwd        /* 40 */
-        PTR __lxsys_rename
-        PTR __lxsys_mount
-        PTR __lxsys_unmount
-        PTR __lxsys_getxattr
-        PTR __lxsys_setxattr      /* 45 */
-        PTR __lxsys_fgetxattr
-        PTR __lxsys_fsetxattr
-        PTR __lxsys_ioctl
-        PTR __lxsys_getpgid
-        PTR __lxsys_setpgid       /* 50 */
-        PTR __lxsys_syslog
-        PTR __lxsys_sys_mmap
-        PTR __lxsys_munmap
-        PTR __lxsys_execve
-        PTR __lxsys_fstat         /* 55 */
-        PTR __lxsys_pollctl
-        PTR __lxsys_th_create
-        PTR __lxsys_th_self
-        PTR __lxsys_th_exit
-        PTR __lxsys_th_join       /* 60 */
-        PTR __lxsys_th_kill
-        PTR __lxsys_th_detach
-        PTR __lxsys_th_sigmask
-        2:
-        .rept __SYSCALL_MAX - (2b - 1b) / SIZE
-            .long 0
-        .endr
\ No newline at end of file
+// arch specifics syscalls goes here
diff --git a/lunaix-os/includes/lunaix/limits.h b/lunaix-os/includes/lunaix/limits.h
new file mode 100644 (file)
index 0000000..9855801
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __LUNAIX_LIMITS_H
+#define __LUNAIX_LIMITS_H
+
+#define NGROUPS_MAX     128
+
+#endif /* __LUNAIX_LIMITS_H */
index 86e485e27a599d78c051d1d9879e3f4b8153f87c..8d4c70baf1230a6f09125d9e55f3d6a28db68033 100644 (file)
@@ -13,6 +13,8 @@
 #include <lunaix/types.h>
 #include <lunaix/spike.h>
 #include <lunaix/hart_state.h>
 #include <lunaix/types.h>
 #include <lunaix/spike.h>
 #include <lunaix/hart_state.h>
+#include <lunaix/usrscope.h>
+
 #include <stdint.h>
 
 
 #include <stdint.h>
 
 
@@ -113,7 +115,7 @@ struct thread
 {
     /*
         Any change to *critical section*, including layout, size
 {
     /*
         Any change to *critical section*, including layout, size
-        must be reflected in arch/x86/interrupt.S.inc to avoid
+        must be reflected in arch/<arch>/interrupt.S.inc to avoid
         disaster!
      */
     struct
         disaster!
      */
     struct
@@ -167,6 +169,13 @@ struct proc_info
         pid_t pgid;
         time_t created;
 
         pid_t pgid;
         time_t created;
 
+        uid_t euid;
+        uid_t suid;
+        gid_t egid;
+        gid_t sgid;
+
+        struct user_scope uscope;
+
         int state;
         int exit_code;
     };
         int state;
         int exit_code;
     };
@@ -463,5 +472,22 @@ thread_stats_user_elapse(struct thread* thread)
     return stats->last_entry - stats->last_leave;
 }
 
     return stats->last_entry - stats->last_leave;
 }
 
+static inline struct user_scope*
+current_user_scope()
+{
+    return &__current->uscope;
+}
+
+static inline uid_t must_inline
+current_euid()
+{
+    return __current->euid;
+}
+
+static inline gid_t must_inline
+current_egid()
+{
+    return __current->egid;
+}
 
 #endif /* __LUNAIX_PROCESS_H */
 
 #endif /* __LUNAIX_PROCESS_H */
index e065273ce93a20ef00d8a2e53aed9f30963711a0..fe06333f1939e18ac07f32aa8dd4ea7210c8653d 100644 (file)
@@ -2,6 +2,7 @@
 #define __LUNAIX_TYPES_H
 
 #include <lunaix/compiler.h>
 #define __LUNAIX_TYPES_H
 
 #include <lunaix/compiler.h>
+#include <lunaix/limits.h>
 #include <stdarg.h>
 #include <usr/lunaix/types.h>
 
 #include <stdarg.h>
 #include <usr/lunaix/types.h>
 
@@ -15,6 +16,9 @@ typedef unsigned int u32_t;
 typedef unsigned long ptr_t;
 typedef unsigned long reg_t;
 
 typedef unsigned long ptr_t;
 typedef unsigned long reg_t;
 
+typedef unsigned int uid_t;
+typedef unsigned int gid_t;
+
 #ifndef CONFIG_ARCH_BITS_64
 typedef unsigned long long u64_t;
 #else
 #ifndef CONFIG_ARCH_BITS_64
 typedef unsigned long long u64_t;
 #else
diff --git a/lunaix-os/includes/lunaix/usercaps.h b/lunaix-os/includes/lunaix/usercaps.h
new file mode 100644 (file)
index 0000000..b30aea9
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LUNAIX_USERCAPS_H
+#define __LUNAIX_USERCAPS_H
+
+typedef unsigned long caps_t;
+
+/*
+ *  Definition of user capabilities (withdrawn draft POSIX.1e) 
+ */
+
+#define CAP_SETUID          0
+#define CAP_SETGID          1
+#define CAP_CHOWN           2
+#define CAP_DAC_OVERRIDE    3
+#define CAP_FSETID          4
+#define CAP_KILL            5
+#define CAP_SYS_CHROOT      6
+#define CAP_SYS_TIM         7
+
+#endif /* __LUNAIX_USERCAPS_H */
diff --git a/lunaix-os/includes/lunaix/usrscope.h b/lunaix-os/includes/lunaix/usrscope.h
new file mode 100644 (file)
index 0000000..4d3c1f7
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef __LUNAIX_USRSCOPE_H
+#define __LUNAIX_USRSCOPE_H
+
+#include <lunaix/types.h>
+#include "usercaps.h"
+
+struct ugroup_obj
+{
+    gid_t* list;
+    unsigned int maxcap;
+    unsigned int refs;
+};
+
+struct user_scope
+{
+    uid_t ruid;
+    gid_t rgid;
+    caps_t capabilities;
+
+    struct ugroup_obj* grps;
+};
+
+/*
+    Process ACL check result. 
+    The encoding here is designed to be the mask for file ACL value
+*/
+enum acl_match
+{
+    ACL_MATCH_U  = 0b111000000, // (U)ser
+    ACL_MATCH_G  = 0b000111000, // (G)roup
+    ACL_NO_MATCH = 0b000000111, // (O)ther
+};
+
+#define user_groups(uscope)     ((uscope)->grps)
+
+#define grp_list_end       ((gid_t)-1)
+
+int 
+uscope_setgroups(struct user_scope* proc_usr, 
+                 const gid_t* grps, unsigned int len);
+
+void 
+uscope_copy(struct user_scope* from, struct user_scope* to);
+
+bool
+uscope_membership(struct user_scope* proc_usr, gid_t gid);
+
+enum acl_match
+check_current_acl(uid_t desired_u, gid_t desired_g);
+
+static inline bool
+uscope_with_capability(const struct user_scope* proc_usr, caps_t cap)
+{
+    return !!(proc_usr->capabilities & (1UL << cap));
+}
+
+#endif /* __LUNAIX_USRSCOPE_H */
index df37b6163e2901aee20adb5d44dc9bb322568ba6..ce0a57109fed0927584e2ca60b75671411d75367 100644 (file)
@@ -36,5 +36,6 @@
 #define EAGAIN -30
 #define EDEADLK -31
 #define EDQUOT -32
 #define EAGAIN -30
 #define EDEADLK -31
 #define EDQUOT -32
+#define EPERM -33
 
 #endif /* __LUNAIX_STATUS_H */
 
 #endif /* __LUNAIX_STATUS_H */
index 8e92773e54fba9df8055ca8b172a14a8b0348151..3e0a7be39e3a5e81495d66da3d00be896944f868 100644 (file)
@@ -17,6 +17,7 @@ sources([
     "bcache.c",
     "syscall.c",
     "changeling.c",
     "bcache.c",
     "syscall.c",
     "changeling.c",
+    "usrscope.c",
     "kprint/kp_records.c",
     "kprint/kprintf.c",
     "time/clock.c",
     "kprint/kp_records.c",
     "kprint/kprintf.c",
     "time/clock.c",
index bb5008df3dee688bf1de4d6c0a9eea511b18db02..e9fab24e0851148c5ff27e3009dbff81e8eb70eb 100644 (file)
 
 LOG_MODULE("PROC")
 
 
 LOG_MODULE("PROC")
 
-__DEFINE_LXSYSCALL(pid_t, getpid)
-{
-    return __current->pid;
-}
-
-__DEFINE_LXSYSCALL(pid_t, getppid)
-{
-    return __current->parent->pid;
-}
-
-__DEFINE_LXSYSCALL(pid_t, getpgid)
-{
-    return __current->pgid;
-}
-
-__DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
-{
-    struct proc_info* proc = pid ? get_process(pid) : __current;
-
-    if (!proc) {
-        syscall_result(EINVAL);
-        return -1;
-    }
-
-    pgid = pgid ? pgid : proc->pid;
-
-    struct proc_info* gruppenfuhrer = get_process(pgid);
-
-    if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
-        syscall_result(EINVAL);
-        return -1;
-    }
-
-    llist_delete(&proc->grp_member);
-    llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
-
-    proc->pgid = pgid;
-    return 0;
-}
-
 int
 spawn_process(struct thread** created, ptr_t entry, bool with_ustack) 
 {
 int
 spawn_process(struct thread** created, ptr_t entry, bool with_ustack) 
 {
@@ -130,4 +90,163 @@ fail:
 
 ptr_t proc_vmroot() {
     return __current->mm->vmroot;
 
 ptr_t proc_vmroot() {
     return __current->mm->vmroot;
+}
+
+__DEFINE_LXSYSCALL(pid_t, getpid)
+{
+    return __current->pid;
+}
+
+__DEFINE_LXSYSCALL(pid_t, getppid)
+{
+    return __current->parent->pid;
+}
+
+__DEFINE_LXSYSCALL(pid_t, getpgid)
+{
+    return __current->pgid;
+}
+
+__DEFINE_LXSYSCALL2(int, setpgid, pid_t, pid, pid_t, pgid)
+{
+    struct proc_info* proc = pid ? get_process(pid) : __current;
+
+    if (!proc) {
+        syscall_result(EINVAL);
+        return -1;
+    }
+
+    pgid = pgid ? pgid : proc->pid;
+
+    struct proc_info* gruppenfuhrer = get_process(pgid);
+
+    if (!gruppenfuhrer || proc->pgid == gruppenfuhrer->pid) {
+        syscall_result(EINVAL);
+        return -1;
+    }
+
+    llist_delete(&proc->grp_member);
+    llist_append(&gruppenfuhrer->grp_member, &proc->grp_member);
+
+    proc->pgid = pgid;
+    return 0;
+}
+
+static inline bool
+__can_change_real_id(const struct user_scope* procu, caps_t id_cap) {
+    if (uscope_with_capability(procu, id_cap)) {
+        return true;
+    }
+
+    if (check_current_acl(0, 0) != ACL_NO_MATCH) {
+        return true;
+    }
+
+    return false;
+}
+
+__DEFINE_LXSYSCALL1(int, setuid, uid_t, uid)
+{
+    struct user_scope* procu;
+
+    procu = current_user_scope();
+
+    if (__can_change_real_id(procu, CAP_SETUID)) 
+    {
+        procu->ruid = uid;
+    }
+
+    __current->suid = uid;
+    __current->euid = uid;
+
+    return 0;
+}
+
+__DEFINE_LXSYSCALL1(int, setgid, gid_t, gid)
+{
+    struct user_scope* procu;
+
+    procu = current_user_scope();
+
+    if (__can_change_real_id(procu, CAP_SETGID)) 
+    {
+        procu->rgid = gid;
+    }
+
+    __current->sgid = gid;
+    __current->egid = gid;
+
+    return 0;
+}
+
+__DEFINE_LXSYSCALL1(int, seteuid, uid_t, euid)
+{
+    __current->euid = euid;
+
+    return 0;
+}
+
+__DEFINE_LXSYSCALL1(int, setegid, gid_t, egid)
+{
+    __current->egid = egid;
+
+    return 0;
+}
+
+__DEFINE_LXSYSCALL2(int, setgroups, const gid_t*, gids, unsigned int, len)
+{
+    struct user_scope* procu;
+
+    procu = current_user_scope();
+
+    if (check_current_acl(0, 0) == ACL_NO_MATCH) {
+        return EPERM;
+    }
+
+    if (uscope_with_capability(procu, CAP_SETGID)) {
+        return EPERM;
+    }
+
+    return uscope_setgroups(procu, gids, len);
+}
+
+
+__DEFINE_LXSYSCALL(uid_t, getuid)
+{
+    return current_user_scope()->ruid;
+}
+
+__DEFINE_LXSYSCALL(gid_t, getgid)
+{
+    return current_user_scope()->rgid;
+}
+
+__DEFINE_LXSYSCALL(uid_t, geteuid)
+{
+    return __current->euid;
+}
+
+__DEFINE_LXSYSCALL(gid_t, getegid)
+{
+    return __current->egid;
+}
+
+__DEFINE_LXSYSCALL2(int, getgroups, gid_t*, out_buf, unsigned int, len)
+{
+    struct user_scope* procu;
+    struct ugroup_obj* gobj;
+
+    procu = current_user_scope();
+    gobj  = user_groups(procu);
+
+    assert(gobj);
+    len = MIN(gobj->maxcap, len);
+
+    unsigned i = 0;
+    for (; i < len && gobj->list[i] != grp_list_end; i++)
+    {
+        out_buf[i] = gobj->list[i];
+    }
+    
+    return i + 1;
 }
\ No newline at end of file
 }
\ No newline at end of file
diff --git a/lunaix-os/kernel/usrscope.c b/lunaix-os/kernel/usrscope.c
new file mode 100644 (file)
index 0000000..334cad7
--- /dev/null
@@ -0,0 +1,135 @@
+#include <lunaix/usrscope.h>
+#include <lunaix/mm/valloc.h>
+#include <lunaix/status.h>
+#include <lunaix/spike.h>
+#include <lunaix/process.h>
+
+#include <klibc/string.h>
+
+#define GLIST_INIT_LEN      8
+
+static struct ugroup_obj*
+__alloc_groups_obj(unsigned int len)
+{
+    unsigned int size;
+    struct ugroup_obj* ugo;
+
+    assert(len >= GLIST_INIT_LEN);
+    
+    ugo = valloc(sizeof(*ugo));
+    ugo->refs = 1;
+
+    size = len * sizeof(gid_t);
+    ugo->list = valloc(size);
+    ugo->maxcap = size;
+
+    memset(ugo->list, grp_list_end, size);
+    return ugo;
+}
+
+static inline void
+__ref_groups_obj(struct ugroup_obj* ugo)
+{
+    ugo->refs++;
+}
+
+static void
+__unref_groups_obj(struct ugroup_obj* ugo)
+{
+    ugo->refs--;
+    if (ugo->refs) {
+        return;
+    }
+
+    vfree_safe(ugo->list);
+    vfree(ugo);
+}
+
+static struct ugroup_obj*
+__modify_group_obj(struct user_scope* procu, unsigned int new_len)
+{
+    struct ugroup_obj* ugo;
+
+    ugo = procu->grps;
+    if (!ugo) {
+        return __alloc_groups_obj(GLIST_INIT_LEN);
+    }
+
+    __unref_groups_obj(ugo);
+
+    new_len = MAX(new_len, ugo->maxcap);
+    ugo = __alloc_groups_obj(new_len);
+    procu->grps = ugo;
+
+    return ugo;
+}
+
+int 
+uscope_setgroups(struct user_scope* proc_usr, 
+                 const gid_t* grps, unsigned int len)
+{
+    struct ugroup_obj* ugo;
+
+    if (len > NGROUPS_MAX) {
+        return E2BIG;
+    }
+
+    ugo = __modify_group_obj(proc_usr, len);
+    memcpy(ugo->list, grps, len * sizeof(gid_t));
+
+    return 0;
+}
+
+bool
+uscope_membership(struct user_scope* proc_usr, gid_t gid)
+{
+    struct ugroup_obj* ugo;
+    
+    ugo = proc_usr->grps;
+    if (unlikely(!ugo)) {
+        return false;
+    }
+
+    for (unsigned i = 0; i < ugo->maxcap; i++)
+    {
+        if (ugo->list[i] != grp_list_end) {
+            break;
+        }
+        
+        if (ugo->list[i] == gid) {
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+void 
+uscope_copy(struct user_scope* to, struct user_scope* from)
+{
+    __ref_groups_obj(from->grps);
+    memcpy(to, from, sizeof(*to));
+}
+
+
+enum acl_match
+check_current_acl(uid_t desired_u, gid_t desired_g)
+{
+    struct user_scope* uscope;
+
+    if (!__current->euid || __current->euid == desired_u) 
+    {
+        return ACL_MATCH_U;
+    }
+
+    if (__current->egid == desired_g) {
+        return ACL_MATCH_G;
+    }
+
+    uscope = current_user_scope();
+    if (uscope_membership(uscope, desired_g)) {
+        return ACL_MATCH_G;
+    }
+
+    return ACL_NO_MATCH;
+}
\ No newline at end of file