This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 1014c49881b880bcbc60c6f6443f05ff9e0736b9
Author: Abhishek Mishra <[email protected]>
AuthorDate: Wed Aug 12 07:13:36 2026 +0000

    sched: add supplementary group IDs (setgroups/getgroups/initgroups)
    
    Track supplementary GIDs per task group, wire setgroups/getgroups
    syscalls when CONFIG_SCHED_NGROUPS > 0, and honor them in DAC checks
    via nxsched_has_gid().  When NGROUPS is 0, libc provides getgroups/
    setgroups stubs.  initgroups() fails instead of silently truncating
    when membership exceeds CONFIG_SCHED_NGROUPS.
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 binfmt/binfmt_checkexec.c                          |  9 ++--
 fs/inode/fs_inode.c                                |  8 +--
 include/limits.h                                   |  6 ++-
 include/nuttx/sched.h                              | 41 +++++++++++++++
 include/sys/syscall_lookup.h                       |  6 +++
 include/unistd.h                                   |  6 ++-
 libs/libc/grp/lib_find_grpfile.c                   | 11 ----
 libs/libc/grp/lib_initgroups.c                     | 53 +++++++++++++++----
 libs/libc/pwd/lib_find_pwdfile.c                   | 10 ----
 libs/libc/unistd/CMakeLists.txt                    |  9 +++-
 libs/libc/unistd/Make.defs                         |  9 +++-
 libs/libc/unistd/lib_getgroups.c                   | 33 ++++--------
 .../lib_initgroups.c => unistd/lib_setgroups.c}    | 34 ++++---------
 libs/libc/unistd/lib_sysconf.c                     |  4 ++
 sched/Kconfig                                      | 15 ++++++
 sched/group/CMakeLists.txt                         |  5 ++
 sched/group/Make.defs                              |  4 ++
 sched/group/group_create.c                         |  9 ++++
 .../group/group_getgroups.c                        | 59 ++++++++++++++--------
 sched/group/group_setegid.c                        | 11 ----
 sched/group/group_seteuid.c                        | 11 ----
 sched/group/group_setgid.c                         | 11 ----
 sched/group/{group_setegid.c => group_setgroups.c} | 51 +++++++++----------
 sched/group/group_setregid.c                       | 12 -----
 sched/group/group_setreuid.c                       | 12 -----
 sched/group/group_setuid.c                         | 11 ----
 syscall/syscall.csv                                |  4 ++
 27 files changed, 245 insertions(+), 209 deletions(-)

diff --git a/binfmt/binfmt_checkexec.c b/binfmt/binfmt_checkexec.c
index 1436298dc24..9d94d36300c 100644
--- a/binfmt/binfmt_checkexec.c
+++ b/binfmt/binfmt_checkexec.c
@@ -57,6 +57,7 @@
 int binfmt_checkexecperm(FAR struct binary_s *bin)
 {
   FAR struct tcb_s *rtcb;
+  FAR struct task_group_s *rgroup;
   mode_t xbits;
 
   rtcb = nxsched_self();
@@ -66,7 +67,9 @@ int binfmt_checkexecperm(FAR struct binary_s *bin)
       return OK;
     }
 
-  if (rtcb->group->tg_euid == 0)
+  rgroup = rtcb->group;
+
+  if (rgroup->tg_euid == 0)
     {
       /* Root can execute any file that has at least one execute bit set */
 
@@ -78,11 +81,11 @@ int binfmt_checkexecperm(FAR struct binary_s *bin)
       return OK;
     }
 
-  if (rtcb->group->tg_euid == bin->uid)
+  if (rgroup->tg_euid == bin->uid)
     {
       xbits = S_IXUSR;
     }
-  else if (rtcb->group->tg_egid == bin->gid)
+  else if (nxsched_has_gid(rtcb, bin->gid))
     {
       xbits = S_IXGRP;
     }
diff --git a/fs/inode/fs_inode.c b/fs/inode/fs_inode.c
index cc9b4115356..4b1838ed7aa 100644
--- a/fs/inode/fs_inode.c
+++ b/fs/inode/fs_inode.c
@@ -66,9 +66,9 @@ static rw_semaphore_t g_inode_lock = RWSEM_INITIALIZER;
 int fs_checkmode(uid_t owner, gid_t group, mode_t mode, int amode)
 {
   FAR struct tcb_s *rtcb;
+  FAR struct task_group_s *rgroup;
   mode_t perm;
   uid_t uid;
-  gid_t gid;
 
   rtcb = nxsched_self();
   if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
@@ -77,14 +77,14 @@ int fs_checkmode(uid_t owner, gid_t group, mode_t mode, int 
amode)
     }
 
   DEBUGASSERT(rtcb->group != NULL);
-  uid = rtcb->group->tg_euid;
-  gid = rtcb->group->tg_egid;
+  rgroup = rtcb->group;
+  uid = rgroup->tg_euid;
 
   if (uid == owner)
     {
       perm = (mode >> 6) & 7;
     }
-  else if (gid == group)
+  else if (nxsched_has_gid(rtcb, group))
     {
       perm = (mode >> 3) & 7;
     }
diff --git a/include/limits.h b/include/limits.h
index 047dc3d6b85..4e8bd5599e4 100644
--- a/include/limits.h
+++ b/include/limits.h
@@ -124,7 +124,11 @@
 #define _POSIX_MAX_CANON      255
 #define _POSIX_MAX_INPUT      255
 #define _POSIX_NAME_MAX       CONFIG_NAME_MAX
-#define _POSIX_NGROUPS_MAX    0
+#if defined(CONFIG_SCHED_NGROUPS) && CONFIG_SCHED_NGROUPS > 0
+#  define _POSIX_NGROUPS_MAX  CONFIG_SCHED_NGROUPS
+#else
+#  define _POSIX_NGROUPS_MAX  0
+#endif
 #define _POSIX_OPEN_MAX       16
 #define _POSIX_PATH_MAX       CONFIG_PATH_MAX
 #define _POSIX_PIPE_BUF       512
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index c2f6df13982..a0534906621 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -466,6 +466,10 @@ struct task_group_s
   gid_t   tg_egid;                  /* Effective group identity                
 */
   uid_t   tg_suid;                  /* Saved set-user identity                 
 */
   gid_t   tg_sgid;                  /* Saved set-group identity                
 */
+#  if CONFIG_SCHED_NGROUPS > 0
+  int     tg_ngroups;               /* Number of supplementary group IDs       
 */
+  gid_t   tg_groups[CONFIG_SCHED_NGROUPS];
+#  endif
 #endif
 
   /* Group membership *******************************************************/
@@ -861,6 +865,43 @@ EXTERN const struct tcbinfo_s g_tcbinfo;
  * Public Function Prototypes
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: nxsched_has_gid
+ *
+ * Description:
+ *   Return true if the task's group matches 'gid' via the effective GID or
+ *   any supplementary group ID.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_USER_IDENTITY
+static inline_function bool nxsched_has_gid(FAR struct tcb_s *tcb,
+                                            gid_t gid)
+{
+  FAR struct task_group_s *group = tcb->group;
+#if CONFIG_SCHED_NGROUPS > 0
+  int i;
+#endif
+
+  if (group->tg_egid == gid)
+    {
+      return true;
+    }
+
+#if CONFIG_SCHED_NGROUPS > 0
+  for (i = 0; i < group->tg_ngroups; i++)
+    {
+      if (group->tg_groups[i] == gid)
+        {
+          return true;
+        }
+    }
+#endif
+
+  return false;
+}
+#endif
+
 /****************************************************************************
  * Name: nxsched_self
  *
diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h
index 242366c8fd6..55a20f7f135 100644
--- a/include/sys/syscall_lookup.h
+++ b/include/sys/syscall_lookup.h
@@ -71,8 +71,14 @@ SYSCALL_LOOKUP(sethostname,                2)
   SYSCALL_LOOKUP(geteuid,                  0)
   SYSCALL_LOOKUP(setegid,                  1)
   SYSCALL_LOOKUP(getegid,                  0)
+#  if CONFIG_SCHED_NGROUPS > 0
+  SYSCALL_LOOKUP(setgroups,                2)
+  SYSCALL_LOOKUP(getgroups,                2)
+#  endif
   SYSCALL_LOOKUP(setreuid,                 2)
   SYSCALL_LOOKUP(setregid,                 2)
+  SYSCALL_LOOKUP(setresuid,                3)
+  SYSCALL_LOOKUP(setresgid,                3)
   SYSCALL_LOOKUP(getresuid,                3)
   SYSCALL_LOOKUP(getresgid,                3)
 #endif
diff --git a/include/unistd.h b/include/unistd.h
index 885bbed12a5..e46dda9d3cb 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -506,7 +506,11 @@ int     setregid(gid_t rgid, gid_t egid);
 int     getresuid(FAR uid_t *ruid, FAR uid_t *euid, FAR uid_t *suid);
 int     getresgid(FAR gid_t *rgid, FAR gid_t *egid, FAR gid_t *sgid);
 
-int     getgroups(int, gid_t[]);
+int     setresuid(uid_t ruid, uid_t euid, uid_t suid);
+int     setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+
+int     getgroups(int, FAR gid_t[]);
+int     setgroups(int, FAR const gid_t *);
 
 int     getentropy(FAR void *buffer, size_t length);
 
diff --git a/libs/libc/grp/lib_find_grpfile.c b/libs/libc/grp/lib_find_grpfile.c
index 98c4cc48150..999f082ad19 100644
--- a/libs/libc/grp/lib_find_grpfile.c
+++ b/libs/libc/grp/lib_find_grpfile.c
@@ -36,7 +36,6 @@
 #include <nuttx/lib/lib.h>
 
 #include "grp/lib_grp.h"
-
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -396,16 +395,6 @@ int grp_findby_name(FAR const char *gname, FAR struct 
group *entry,
 int grp_findby_gid(gid_t gid, FAR struct group *entry, FAR char *buffer,
                    size_t buflen)
 {
-  /* Verify that the GID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a GID_MAX or GID_MIN.  Instead we use a
-   * priori knowledge that gid_t is type int16_t.
-   */
-
-  if ((uint16_t)gid > INT16_MAX)
-    {
-      return -EINVAL;
-    }
-
   return grp_foreach(grp_match_gid, (uintptr_t)gid, entry, buffer, buflen);
 }
 
diff --git a/libs/libc/grp/lib_initgroups.c b/libs/libc/grp/lib_initgroups.c
index 5329ba96095..5ae27b62fdb 100644
--- a/libs/libc/grp/lib_initgroups.c
+++ b/libs/libc/grp/lib_initgroups.c
@@ -27,6 +27,11 @@
 #include <nuttx/config.h>
 
 #include <grp.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <nuttx/debug.h>
 
 /****************************************************************************
  * Public Functions
@@ -36,28 +41,56 @@
  * Name: initgroups
  *
  * Description:
- *   The group database /etc/group is read to determine all groups of which
- *   user is a member.  The additional group group is also added to this set,
- *   which is then used to set the supplementary group IDs of the calling
- *   process.
+ *   The group database is read to determine all groups of which user is a
+ *   member.  The additional group 'group' is also included.  The resulting
+ *   set is installed as the calling process's supplementary group IDs via
+ *   setgroups().
  *
  * Input Parameters:
- *   user  - Name of the user to query the /etc/group database for.
+ *   user  - Name of the user to query the group database for.
  *   group - Additional gid to add to the list of group IDs.
  *
  * Returned Value:
- *   The initgroups() function returns zero if successful, and -1 in case of
- *   failure, in which case errno is set appropriately.
+ *   Zero if successful, and -1 on failure with errno set.
  *
  ****************************************************************************/
 
 int initgroups(FAR const char *user, gid_t group)
 {
-  /* There currently is no support for supplementary group IDs in NuttX.
-   * Thus, just ignore this request silently and report success.
-   */
+#if defined(CONFIG_SCHED_NGROUPS) && CONFIG_SCHED_NGROUPS > 0
+  gid_t groups[NGROUPS_MAX];
+  int ngroups = NGROUPS_MAX;
+  int ret;
+
+  if (user == NULL)
+    {
+      set_errno(EINVAL);
+      return ERROR;
+    }
+
+  ret = getgrouplist(user, group, groups, &ngroups);
+  if (ret < 0)
+    {
+      /* Buffer too small or lookup failure — errno already set by
+       * getgrouplist when applicable.
+       */
+
+      if (ngroups > NGROUPS_MAX)
+        {
+          swarn("initgroups: user '%s' has %d groups, NGROUPS_MAX=%d\n",
+                user, ngroups, NGROUPS_MAX);
+          set_errno(EINVAL);
+        }
+
+      return ERROR;
+    }
+
+  return setgroups(ret, groups);
+#else
+  /* Without supplementary group storage, succeed silently. */
 
   UNUSED(user);
   UNUSED(group);
   return 0;
+#endif
 }
diff --git a/libs/libc/pwd/lib_find_pwdfile.c b/libs/libc/pwd/lib_find_pwdfile.c
index 88da93bbdac..bd520c998b7 100644
--- a/libs/libc/pwd/lib_find_pwdfile.c
+++ b/libs/libc/pwd/lib_find_pwdfile.c
@@ -382,16 +382,6 @@ int pwd_findby_name(FAR const char *uname, FAR struct 
passwd *entry,
 int pwd_findby_uid(uid_t uid, FAR struct passwd *entry, FAR char *buffer,
                    size_t buflen)
 {
-  /* Verify that the UID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a UID_MAX or UID_MIN.  Instead we use a
-   * priori knowledge that uid_t is type int16_t.
-   */
-
-  if ((uint16_t)uid > INT16_MAX)
-    {
-      return -EINVAL;
-    }
-
   return pwd_foreach(pwd_match_uid, (uintptr_t)uid, entry, buffer, buflen);
 }
 
diff --git a/libs/libc/unistd/CMakeLists.txt b/libs/libc/unistd/CMakeLists.txt
index 834d61355ff..b85e65e54d6 100644
--- a/libs/libc/unistd/CMakeLists.txt
+++ b/libs/libc/unistd/CMakeLists.txt
@@ -63,7 +63,6 @@ set(SRCS
     lib_getpgrp.c
     lib_getpgid.c
     lib_getsid.c
-    lib_getgroups.c
     lib_setpgid.c
     lib_setsid.c
     lib_lockf.c
@@ -90,7 +89,13 @@ if(NOT CONFIG_SCHED_USER_IDENTITY)
     lib_setregid.c
     lib_getresuid.c
     lib_getresgid.c
-    lib_issetugid.c)
+    lib_setresuid.c
+    lib_setresgid.c
+    lib_issetugid.c
+    lib_getgroups.c
+    lib_setgroups.c)
+elseif(NOT CONFIG_SCHED_NGROUPS)
+  list(APPEND SRCS lib_getgroups.c lib_setgroups.c)
 endif()
 
 if(NOT CONFIG_DISABLE_ENVIRON)
diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs
index d4fbc70879b..3e9c6dfeb21 100644
--- a/libs/libc/unistd/Make.defs
+++ b/libs/libc/unistd/Make.defs
@@ -32,7 +32,7 @@ CSRCS += lib_setrlimit.c lib_getrlimit.c lib_setpriority.c 
lib_getpriority.c
 CSRCS += lib_futimes.c lib_lutimes.c lib_gethostname.c lib_sethostname.c
 CSRCS += lib_fchownat.c lib_linkat.c lib_readlinkat.c lib_symlinkat.c
 CSRCS += lib_unlinkat.c lib_usleep.c lib_getpgrp.c lib_getpgid.c
-CSRCS += lib_getsid.c lib_getgroups.c lib_setpgid.c lib_setsid.c
+CSRCS += lib_getsid.c lib_setpgid.c lib_setsid.c
 CSRCS += lib_lockf.c lib_flock.c lib_getpass.c
 CSRCS += lib_chdir.c lib_fchdir.c lib_confstr.c lib_ulimit.c
 
@@ -40,7 +40,12 @@ ifneq ($(CONFIG_SCHED_USER_IDENTITY),y)
 CSRCS += lib_setuid.c lib_setgid.c lib_getuid.c lib_getgid.c
 CSRCS += lib_seteuid.c lib_setegid.c lib_geteuid.c lib_getegid.c
 CSRCS += lib_setreuid.c lib_setregid.c lib_getresuid.c lib_getresgid.c
-CSRCS += lib_issetugid.c
+CSRCS += lib_setresuid.c lib_setresgid.c
+CSRCS += lib_issetugid.c lib_getgroups.c lib_setgroups.c
+else
+ifeq ($(CONFIG_SCHED_NGROUPS),0)
+CSRCS += lib_getgroups.c lib_setgroups.c
+endif
 endif
 
 ifneq ($(CONFIG_DISABLE_ENVIRON),y)
diff --git a/libs/libc/unistd/lib_getgroups.c b/libs/libc/unistd/lib_getgroups.c
index 53cbc11ba82..f28704b2376 100644
--- a/libs/libc/unistd/lib_getgroups.c
+++ b/libs/libc/unistd/lib_getgroups.c
@@ -38,9 +38,10 @@
  *
  * Description:
  *   The getgroups() function returns the supplementary group IDs of the
- *   calling process in the array grouplist.  NuttX does not support
- *   supplementary group IDs, so the calling process is treated as belonging
- *   to a single group: its effective group ID.
+ *   calling process in the array grouplist.  Stub when
+ *   CONFIG_SCHED_USER_IDENTITY is disabled or CONFIG_SCHED_NGROUPS is 0:
+ *   there is no supplementary group list.  The effective group ID is not
+ *   synthesized; callers that need it should use getegid().
  *
  * Input Parameters:
  *   gidsetsize - The number of elements available in grouplist.
@@ -55,31 +56,15 @@
 
 int getgroups(int gidsetsize, gid_t grouplist[])
 {
+  UNUSED(grouplist);
+
   if (gidsetsize < 0)
     {
       set_errno(EINVAL);
-      return -1;
-    }
-
-  /* If gidsetsize is zero, return the number of group IDs without touching
-   * grouplist.
-   */
-
-  if (gidsetsize == 0)
-    {
-      return 1;
-    }
-
-  if (grouplist == NULL)
-    {
-      set_errno(EFAULT);
-      return -1;
+      return ERROR;
     }
 
-  /* NuttX has no notion of supplementary group IDs.  Report the single
-   * effective group ID of the calling process.
-   */
+  /* Empty supplementary list (do not synthesize egid). */
 
-  grouplist[0] = getegid();
-  return 1;
+  return 0;
 }
diff --git a/libs/libc/grp/lib_initgroups.c b/libs/libc/unistd/lib_setgroups.c
similarity index 64%
copy from libs/libc/grp/lib_initgroups.c
copy to libs/libc/unistd/lib_setgroups.c
index 5329ba96095..2ff3bb2ee2d 100644
--- a/libs/libc/grp/lib_initgroups.c
+++ b/libs/libc/unistd/lib_setgroups.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * libs/libc/grp/lib_initgroups.c
+ * libs/libc/unistd/lib_setgroups.c
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -26,38 +26,26 @@
 
 #include <nuttx/config.h>
 
-#include <grp.h>
+#include <unistd.h>
+#include <errno.h>
 
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: initgroups
+ * Name: setgroups
  *
  * Description:
- *   The group database /etc/group is read to determine all groups of which
- *   user is a member.  The additional group group is also added to this set,
- *   which is then used to set the supplementary group IDs of the calling
- *   process.
- *
- * Input Parameters:
- *   user  - Name of the user to query the /etc/group database for.
- *   group - Additional gid to add to the list of group IDs.
- *
- * Returned Value:
- *   The initgroups() function returns zero if successful, and -1 in case of
- *   failure, in which case errno is set appropriately.
+ *   Stub when CONFIG_SCHED_USER_IDENTITY is disabled or
+ *   CONFIG_SCHED_NGROUPS is 0.  Supplementary groups are not supported.
  *
  ****************************************************************************/
 
-int initgroups(FAR const char *user, gid_t group)
+int setgroups(int size, FAR const gid_t *list)
 {
-  /* There currently is no support for supplementary group IDs in NuttX.
-   * Thus, just ignore this request silently and report success.
-   */
-
-  UNUSED(user);
-  UNUSED(group);
-  return 0;
+  UNUSED(size);
+  UNUSED(list);
+  set_errno(ENOSYS);
+  return ERROR;
 }
diff --git a/libs/libc/unistd/lib_sysconf.c b/libs/libc/unistd/lib_sysconf.c
index dc6bfb8cba1..428482aea52 100644
--- a/libs/libc/unistd/lib_sysconf.c
+++ b/libs/libc/unistd/lib_sysconf.c
@@ -29,6 +29,7 @@
 #include <nuttx/atexit.h>
 
 #include <unistd.h>
+#include <limits.h>
 #include <sched.h>
 #include <errno.h>
 
@@ -241,6 +242,9 @@ long sysconf(int name)
       case _SC_OPEN_MAX:
         return OPEN_MAX;
 
+      case _SC_NGROUPS_MAX:
+        return NGROUPS_MAX;
+
       case _SC_ATEXIT_MAX:
         return ATEXIT_MAX;
 
diff --git a/sched/Kconfig b/sched/Kconfig
index 5532fc4114b..bac8e961aa2 100644
--- a/sched/Kconfig
+++ b/sched/Kconfig
@@ -803,6 +803,21 @@ config SCHED_USER_IDENTITY
                If this option is not selected, stub root-only versions of these
                interfaces are available instead.
 
+if SCHED_USER_IDENTITY
+
+config SCHED_NGROUPS
+       int "Maximum supplementary group IDs"
+       default 8
+       range 0 64
+       ---help---
+               Maximum number of supplementary group IDs per task group.  This
+               value becomes NGROUPS_MAX and sizes the getgroups()/setgroups()
+               list stored in each task group.  Set to 0 to disable 
supplementary
+               group storage (getgroups then always returns an empty list).
+               The effective GID is separate and available via getegid().
+
+endif # SCHED_USER_IDENTITY
+
 config SCHED_THREAD_LOCAL
        bool "Support __thread/thread_local keyword"
        default n
diff --git a/sched/group/CMakeLists.txt b/sched/group/CMakeLists.txt
index 42f21d504bc..5353552c849 100644
--- a/sched/group/CMakeLists.txt
+++ b/sched/group/CMakeLists.txt
@@ -57,7 +57,12 @@ if(CONFIG_SCHED_USER_IDENTITY)
     group_setregid.c
     group_getresuid.c
     group_getresgid.c
+    group_setresuid.c
+    group_setresgid.c
     group_issetugid.c)
+  if(CONFIG_SCHED_NGROUPS)
+    list(APPEND SRCS group_setgroups.c group_getgroups.c)
+  endif()
 endif()
 
 if(CONFIG_SIG_SIGSTOP_ACTION)
diff --git a/sched/group/Make.defs b/sched/group/Make.defs
index f505a0d80f5..b2605e53fe5 100644
--- a/sched/group/Make.defs
+++ b/sched/group/Make.defs
@@ -42,7 +42,11 @@ ifeq ($(CONFIG_SCHED_USER_IDENTITY),y)
 CSRCS += group_setuid.c group_setgid.c group_getuid.c group_getgid.c
 CSRCS += group_seteuid.c group_setegid.c group_geteuid.c group_getegid.c
 CSRCS += group_setreuid.c group_setregid.c group_getresuid.c group_getresgid.c
+CSRCS += group_setresuid.c group_setresgid.c
 CSRCS += group_issetugid.c
+ifneq ($(CONFIG_SCHED_NGROUPS),0)
+CSRCS += group_setgroups.c group_getgroups.c
+endif
 endif
 
 ifeq ($(CONFIG_SIG_SIGSTOP_ACTION),y)
diff --git a/sched/group/group_create.c b/sched/group/group_create.c
index c7b1af1fd46..1fb32bf7ff2 100644
--- a/sched/group/group_create.c
+++ b/sched/group/group_create.c
@@ -26,6 +26,7 @@
 
 #include <nuttx/config.h>
 
+#include <string.h>
 #include <sched.h>
 #include <assert.h>
 #include <errno.h>
@@ -84,6 +85,14 @@ static inline void group_inherit_identity(FAR struct 
task_group_s *group)
   group->tg_egid = rgroup->tg_egid;
   group->tg_suid = rgroup->tg_suid;
   group->tg_sgid = rgroup->tg_sgid;
+#if CONFIG_SCHED_NGROUPS > 0
+  group->tg_ngroups = rgroup->tg_ngroups;
+  if (rgroup->tg_ngroups > 0)
+    {
+      memcpy(group->tg_groups, rgroup->tg_groups,
+             rgroup->tg_ngroups * sizeof(gid_t));
+    }
+#endif
 }
 #else
 #  define group_inherit_identity(group)
diff --git a/libs/libc/unistd/lib_getgroups.c b/sched/group/group_getgroups.c
similarity index 62%
copy from libs/libc/unistd/lib_getgroups.c
copy to sched/group/group_getgroups.c
index 53cbc11ba82..90d9a8dd538 100644
--- a/libs/libc/unistd/lib_getgroups.c
+++ b/sched/group/group_getgroups.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * libs/libc/unistd/lib_getgroups.c
+ * sched/group/group_getgroups.c
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -27,8 +27,12 @@
 #include <nuttx/config.h>
 
 #include <unistd.h>
+#include <string.h>
+#include <assert.h>
 #include <errno.h>
 
+#include <sched/sched.h>
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -37,49 +41,60 @@
  * Name: getgroups
  *
  * Description:
- *   The getgroups() function returns the supplementary group IDs of the
- *   calling process in the array grouplist.  NuttX does not support
- *   supplementary group IDs, so the calling process is treated as belonging
- *   to a single group: its effective group ID.
+ *   getgroups() returns the supplementary group IDs of the calling
+ *   process.  The returned list is exactly the set installed by
+ *   setgroups()/initgroups() (may be empty).  The effective group ID is
+ *   not synthesized into an empty list; callers that need it should use
+ *   getegid().
  *
  * Input Parameters:
- *   gidsetsize - The number of elements available in grouplist.
- *   grouplist  - The buffer used to return the group IDs.
+ *   gidsetsize - Number of slots in grouplist, or 0 to query the count.
+ *   grouplist  - Buffer for group IDs (unused when gidsetsize is 0).
  *
  * Returned Value:
- *   On success, the number of group IDs is returned.  If gidsetsize is zero,
- *   the total number of group IDs is returned without modifying grouplist.
- *   On failure, -1 is returned and errno is set appropriately.
+ *   Number of group IDs on success; -1 on failure with errno set.
  *
  ****************************************************************************/
 
-int getgroups(int gidsetsize, gid_t grouplist[])
+int getgroups(int gidsetsize, FAR gid_t grouplist[])
 {
+  FAR struct tcb_s *rtcb;
+  FAR struct task_group_s *rgroup;
+  int count;
+
   if (gidsetsize < 0)
     {
       set_errno(EINVAL);
-      return -1;
+      return ERROR;
     }
 
-  /* If gidsetsize is zero, return the number of group IDs without touching
-   * grouplist.
-   */
+  rtcb   = this_task();
+  rgroup = rtcb->group;
+  DEBUGASSERT(rgroup != NULL);
+
+  count = rgroup->tg_ngroups;
 
   if (gidsetsize == 0)
     {
-      return 1;
+      return count;
     }
 
   if (grouplist == NULL)
     {
       set_errno(EFAULT);
-      return -1;
+      return ERROR;
     }
 
-  /* NuttX has no notion of supplementary group IDs.  Report the single
-   * effective group ID of the calling process.
-   */
+  if (gidsetsize < count)
+    {
+      set_errno(EINVAL);
+      return ERROR;
+    }
+
+  if (count > 0)
+    {
+      memcpy(grouplist, rgroup->tg_groups, count * sizeof(gid_t));
+    }
 
-  grouplist[0] = getegid();
-  return 1;
+  return count;
 }
diff --git a/sched/group/group_setegid.c b/sched/group/group_setegid.c
index 733f11f84dc..e80d503e395 100644
--- a/sched/group/group_setegid.c
+++ b/sched/group/group_setegid.c
@@ -62,17 +62,6 @@ int setegid(gid_t gid)
   FAR struct tcb_s *rtcb;
   FAR struct task_group_s *rgroup;
 
-  /* Verify that the GID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a GID_MAX or GID_MIN.  Instead we use a
-   * priori knowledge that gid_t is type int16_t.
-   */
-
-  if ((uint16_t)gid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   /* Get the currently executing thread's task group. */
 
   rtcb   = this_task();
diff --git a/sched/group/group_seteuid.c b/sched/group/group_seteuid.c
index 758a6355365..3fd4469b98e 100644
--- a/sched/group/group_seteuid.c
+++ b/sched/group/group_seteuid.c
@@ -64,17 +64,6 @@ int seteuid(uid_t uid)
   FAR struct tcb_s *rtcb;
   FAR struct task_group_s *rgroup;
 
-  /* Verify that the UID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a UID_MAX or UID_MIN.  Instead we use a
-   * priori knowledge that uid_t is type int16_t.
-   */
-
-  if ((uint16_t)uid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   /* Get the currently executing thread's task group. */
 
   rtcb   = this_task();
diff --git a/sched/group/group_setgid.c b/sched/group/group_setgid.c
index 72131e5ee5f..812958b2f9b 100644
--- a/sched/group/group_setgid.c
+++ b/sched/group/group_setgid.c
@@ -63,17 +63,6 @@ int setgid(gid_t gid)
   FAR struct tcb_s *rtcb;
   FAR struct task_group_s *rgroup;
 
-  /* Verify that the GID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a GID_MAX or GID_MIN.  Instead we use a
-   * priori knowledge that gid_t is type int16_t.
-   */
-
-  if ((uint16_t)gid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   /* Get the currently executing thread's task group. */
 
   rtcb   = this_task();
diff --git a/sched/group/group_setegid.c b/sched/group/group_setgroups.c
similarity index 63%
copy from sched/group/group_setegid.c
copy to sched/group/group_setgroups.c
index 733f11f84dc..7f6243dbb1c 100644
--- a/sched/group/group_setegid.c
+++ b/sched/group/group_setgroups.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * sched/group/group_setegid.c
+ * sched/group/group_setgroups.c
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -27,6 +27,7 @@
 #include <nuttx/config.h>
 
 #include <unistd.h>
+#include <string.h>
 #include <assert.h>
 #include <errno.h>
 
@@ -37,61 +38,55 @@
  ****************************************************************************/
 
 /****************************************************************************
- * Name: setegid
+ * Name: setgroups
  *
  * Description:
- *   The setegid() function sets the effective group ID of the calling
- *   process to gid, given appropriate privileges.
+ *   setgroups() sets the supplementary group IDs for the calling process.
+ *   Only a process with an effective user ID of 0 may change the list.
  *
  * Input Parameters:
- *   gid - Identity to set the various process's group ID attributes to.
+ *   size - Number of group IDs in list (0 to clear).
+ *   list - Array of supplementary group IDs, or NULL when size is 0.
  *
  * Returned Value:
- *   Zero if successful and -1 in case of failure, in which case errno is set
- *   to one of he following values:
- *
- *   EINVAL - The value of the uid argument is invalid and not supported by
- *            the implementation.
- *   EPERM  - The process does not have appropriate privileges and uid does
- *            not match the effective group ID or the saved set-group-ID.
+ *   Zero on success; -1 on failure with errno set.
  *
  ****************************************************************************/
 
-int setegid(gid_t gid)
+int setgroups(int size, FAR const gid_t *list)
 {
   FAR struct tcb_s *rtcb;
   FAR struct task_group_s *rgroup;
 
-  /* Verify that the GID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a GID_MAX or GID_MIN.  Instead we use a
-   * priori knowledge that gid_t is type int16_t.
-   */
-
-  if ((uint16_t)gid > INT16_MAX)
+  if (size < 0 || size > CONFIG_SCHED_NGROUPS)
     {
       set_errno(EINVAL);
       return ERROR;
     }
 
-  /* Get the currently executing thread's task group. */
+  if (size > 0 && list == NULL)
+    {
+      set_errno(EFAULT);
+      return ERROR;
+    }
 
   rtcb   = this_task();
   rgroup = rtcb->group;
-
   DEBUGASSERT(rgroup != NULL);
 
-  if (rgroup->tg_egid == 0 ||
-      gid == rgroup->tg_gid || gid == rgroup->tg_sgid)
-    {
-      /* Root may set any value; non-root may only set to real or saved. */
+  /* Only root (effective UID 0) may install a new supplementary set. */
 
-      rgroup->tg_egid = gid;
-    }
-  else
+  if (rgroup->tg_euid != 0)
     {
       set_errno(EPERM);
       return ERROR;
     }
 
+  if (size > 0)
+    {
+      memcpy(rgroup->tg_groups, list, size * sizeof(gid_t));
+    }
+
+  rgroup->tg_ngroups = size;
   return OK;
 }
diff --git a/sched/group/group_setregid.c b/sched/group/group_setregid.c
index 61360f939dd..5ec7676d8b0 100644
--- a/sched/group/group_setregid.c
+++ b/sched/group/group_setregid.c
@@ -63,18 +63,6 @@ int setregid(gid_t rgid, gid_t egid)
   gid_t old_egid;
   gid_t old_sgid;
 
-  if (rgid != (gid_t)-1 && (uint16_t)rgid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
-  if (egid != (gid_t)-1 && (uint16_t)egid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   if (rgid == (gid_t)-1 && egid == (gid_t)-1)
     {
       return OK;
diff --git a/sched/group/group_setreuid.c b/sched/group/group_setreuid.c
index 0610f9fdda6..1d37e070e2c 100644
--- a/sched/group/group_setreuid.c
+++ b/sched/group/group_setreuid.c
@@ -64,18 +64,6 @@ int setreuid(uid_t ruid, uid_t euid)
   uid_t old_euid;
   uid_t old_suid;
 
-  if (ruid != (uid_t)-1 && (uint16_t)ruid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
-  if (euid != (uid_t)-1 && (uint16_t)euid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   if (ruid == (uid_t)-1 && euid == (uid_t)-1)
     {
       return OK;
diff --git a/sched/group/group_setuid.c b/sched/group/group_setuid.c
index 16073ed5313..38424108594 100644
--- a/sched/group/group_setuid.c
+++ b/sched/group/group_setuid.c
@@ -64,17 +64,6 @@ int setuid(uid_t uid)
   FAR struct tcb_s *rtcb;
   FAR struct task_group_s *rgroup;
 
-  /* Verify that the UID is in the valid range of 0 through INT16_MAX.
-   * OpenGroup.org does not specify a UID_MAX or UID_MIN.  Instead we use a
-   * priori knowledge that uid_t is type int16_t.
-   */
-
-  if ((uint16_t)uid > INT16_MAX)
-    {
-      set_errno(EINVAL);
-      return ERROR;
-    }
-
   /* Get the currently executing thread's task group. */
 
   rtcb   = this_task();
diff --git a/syscall/syscall.csv b/syscall/syscall.csv
index f5675cea0a0..c488776b3c4 100644
--- a/syscall/syscall.csv
+++ b/syscall/syscall.csv
@@ -45,6 +45,7 @@
 "getenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","FAR char *","FAR const 
char *"
 "geteuid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","uid_t"
 "getgid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","gid_t"
+"getgroups","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY) && 
(CONFIG_SCHED_NGROUPS > 0)","int","int","FAR gid_t *"
 "gethostname","unistd.h","","int","FAR char *","size_t"
 
"getitimer","sys/time.h","!defined(CONFIG_DISABLE_POSIX_TIMERS)","int","int","FAR
 struct itimerval *"
 "getpeername","sys/socket.h","defined(CONFIG_NET)","int","int","FAR struct 
sockaddr *","FAR socklen_t *"
@@ -158,8 +159,11 @@
 "sendmsg","sys/socket.h","defined(CONFIG_NET)","ssize_t","int","FAR const 
struct msghdr *","int"
 "sendto","sys/socket.h","defined(CONFIG_NET)","ssize_t","int","FAR const void 
*","size_t","int","FAR const struct sockaddr *","socklen_t"
 "setegid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","gid_t"
+"setgroups","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY) && 
(CONFIG_SCHED_NGROUPS > 0)","int","int","FAR const gid_t *"
 
"setregid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","gid_t","gid_t"
 
"setreuid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","uid_t","uid_t"
+"setresgid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","gid_t","gid_t","gid_t"
+"setresuid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","uid_t","uid_t","uid_t"
 "setenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int","FAR const char 
*","FAR const char *","int"
 "seteuid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","uid_t"
 "setgid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","int","gid_t"

Reply via email to