nuttx/Documentation/os/scheduling/user_identity.rst
Vinicius May f6ecf80ebb Documentation: brand new layout for NuttX documentation.
The documentation grew one page at a time, so the tree follows the
history of who wrote what and not the shape of NuttX. Scheduling is
spread over three places, a driver page can sit above the subsystem
that owns it, and the front page lists everything at the same level.
That is a lot to face when all you want to know is where the scheduler
lives.

This change files every page under the code it describes. It is a move,
not a rewrite: outside the ten pages named below, every page keeps the
text that is already in master, and no page's text is deleted.

What it does:

* Groups the table of contents into nine chapters.
* Moves the OS subsystems under os/: scheduling, memory, drivers,
  filesystem, networking, IPC, interrupts, libs, time.
* Renames the platform pages to the names the source tree uses, and
  derives their tags from the tree instead of by hand.
* Splits guides/ by subject.
* Adds Documentation/redirects.py, with a rule for every page that left
  its old path, so old URLs keep working. The redirect page also carries
  a link's #anchor across to the new page.

Ten pages have text that is new or rewritten. Nine of them are the
landing page of a chapter, which has to exist for the new structure:

    index                  the front page
    os/index               OS Design
    os/scheduling/index    Scheduling
    os/interrupts/index    Interrupts
    os/ipc/index           IPC
    os/time/index          Time and timers
    about/index            About
    developing/index       Developing NuttX
    ReleaseNotes/index     Release notes

The tenth is os/libs/libbuiltin, the only page here with technical
content: libs/libbuiltin/ had no page at all. Five SVG diagrams come
with these pages, hand-written XML with no editor metadata.

Nothing outside Documentation/ is touched.

How it was checked:

* Sphinx builds with -W: no warnings, and no document left outside a
  toctree.
* A script, offered in the PR, proves the narrow claim this rests on.
  For every page outside the ten named above it erases what a move
  touches -- link target, path, tag line, toctree block, table border --
  from the whole old text and the whole new text, and requires the two
  to be byte for byte identical. It also requires every sentence of a
  deleted page to turn up somewhere, and every page that left its old
  path to have a redirect, from a URL that existed, to where its content
  went. It exits non-zero and names the page if any of that is not true,
  and it tests added pages too, so forgetting to declare one cannot make
  it pass.
* An independent audit checked 133 factual claims on these ten pages
  against the tree, one shell command per claim: 130 confirmed, 1
  refuted and fixed here, 2 not checkable.
* tools/checkpatch.sh is clean over the range.

The diff is large because moving a page changes every link that points
to it. Most of it is pure renames, and board pages that gained one tag
line.

Assisted-by: Claude:claude-opus-5
2026-10-08 01:40:54 +08:00

189 lines
7.3 KiB
ReStructuredText

.. _user-identity:
=======================
User and Group Identity
=======================
When ``CONFIG_SCHED_USER_IDENTITY`` is enabled, each task group maintains POSIX
process credentials. All threads within a task group share the same credentials
(see :ref:`tasks-vs-threads`).
Credentials
===========
The full POSIX three-field credential model is stored in ``struct task_group_s``
(``include/nuttx/sched.h``):
* ``tg_uid`` / ``tg_gid`` — real user and group IDs.
* ``tg_euid`` / ``tg_egid`` — effective IDs used for permission checks.
* ``tg_suid`` / ``tg_sgid`` — saved set-IDs that allow a non-root process to
restore a previously held effective ID.
* ``tg_groups`` / ``tg_ngroups`` — supplementary group IDs (when
``CONFIG_SCHED_NGROUPS`` is greater than zero).
All six primary credential fields are zero-initialized at task creation, so
the initial task runs as root (UID/GID 0) unless explicitly changed. The
supplementary list starts empty.
Supplementary Groups
====================
When ``CONFIG_SCHED_NGROUPS`` is greater than zero:
* ``setgroups()`` replaces the calling task group's supplementary list
(requires effective UID 0).
* ``getgroups()`` returns that list as stored (may be empty after
``setgroups(0, NULL)``). The effective GID is not invented into an
empty list; use ``getegid()`` for the effective GID.
* ``initgroups()`` builds a membership list with ``getgrouplist()`` (from
``/etc/group`` when ``CONFIG_LIBC_GROUP_FILE`` is enabled) and installs it
with ``setgroups()``.
* ``NGROUPS_MAX`` equals ``CONFIG_SCHED_NGROUPS``.
Filesystem DAC (Discretionary Access Control -- ownership- and
mode-bit-based permission checks, as opposed to a mandatory policy
enforced independently of the file owner) is implemented by
``fs_checkmode()``, which grants the group-class mode bits when the
file's group matches ``tg_egid`` **or** any entry in ``tg_groups``.
Inheritance
===========
When a new task is created, ``group_inherit_identity()`` in
``sched/group/group_create.c`` copies all credential fields — including the
supplementary group list — from the parent task group to the child.
Privilege Transitions
=====================
``setuid()`` and ``setgid()``
-----------------------------
When the effective ID is zero (root):
* ``setuid(uid)`` sets ``tg_uid``, ``tg_euid``, and ``tg_suid`` to ``uid``.
* ``setgid(gid)`` sets ``tg_gid``, ``tg_egid``, and ``tg_sgid`` to ``gid``.
When the effective ID is non-zero:
* The caller may only set the effective ID to the current real or saved value.
* Any other value causes the function to return ``-1`` with ``errno`` set to
``EPERM``.
``seteuid()`` and ``setegid()``
-------------------------------
When the effective ID is zero, any value may be assigned as the new effective
ID.
When the effective ID is non-zero, the requested value must equal the real or
the saved ID. Otherwise the function returns ``-1`` with ``errno`` set to
``EPERM``.
This implements temporary privilege drop with ``seteuid()`` /
``setegid()`` and later restore from the saved ID.
``setreuid()`` and ``setregid()``
---------------------------------
These functions set the real and/or effective IDs in a single call. When the
effective ID is zero, any requested real and effective values may be assigned
and the saved set-ID is updated accordingly. When the effective ID is
non-zero, each requested value must equal the current effective ID, saved
set-ID, or (for the effective argument only) the real ID; otherwise the call
returns ``-1`` with ``errno`` set to ``EPERM``. When the real ID is changed,
or the effective ID is changed to a value not equal to the real ID, the saved
set-ID is set to the new effective ID.
``getresuid()`` and ``getresgid()``
-----------------------------------
These functions return the real, effective, and saved set-IDs for the calling
task group. Any output pointer may be ``NULL`` if that ID is not needed.
``setresuid()`` and ``setresgid()``
-----------------------------------
These functions set the real, effective, and saved set-IDs in one call.
Pass ``(uid_t)-1`` / ``(gid_t)-1`` to leave an ID unchanged. When the
effective UID is zero, any values may be assigned. When the effective
UID is non-zero, each new ID must equal the current real, effective, or
saved ID.
Soft drop (keep saved-root)::
setresgid(gid, gid, 0);
setresuid(uid, uid, 0);
Hard drop (clear saved-root)::
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
``setresgid()`` requires effective UID zero to assign arbitrary GIDs.
Change group IDs before dropping the effective UID.
Setuid-on-exec
================
When ``binfmt`` loads an executable with the set-user-ID bit set
(``S_ISUID`` in ``nx_mode``), the new task group's effective and saved
UIDs become the file owner's UID while the real UID remains the caller's.
This is the mechanism used by the setuid-root ``sudo`` helper
(``CONFIG_SYSTEM_SUDO``); see :ref:`cmdsudo`. A valid test logs in as
root, drops to an unprivileged sudoers account (``su user``), confirms
that account cannot exec a root-only binary, then runs that binary
through ``sudo``.
Configuration
=============
``CONFIG_SCHED_USER_IDENTITY``
Enables per-task-group credential tracking. Without this option, stub
root-only versions of all credential interfaces are provided.
``CONFIG_SCHED_NGROUPS``
Maximum supplementary group IDs per task group (default 8). Visible only
when ``CONFIG_SCHED_USER_IDENTITY`` is enabled. Becomes ``NGROUPS_MAX``.
``getgrouplist()`` / ``initgroups()`` return failure (they do **not**
silently truncate) when membership exceeds this limit; ``initgroups()``
also logs a warning. Increase ``CONFIG_SCHED_NGROUPS`` if needed.
``CONFIG_FS_PERMISSION``
Enables filesystem ownership and permission enforcement. Requires
``CONFIG_SCHED_USER_IDENTITY`` and ``CONFIG_PSEUDOFS_ATTRIBUTES``.
See :ref:`file-permission` for the VFS helpers, mount-crossing
traverse rules, and testing notes.
Flat Build Trust Boundary
=========================
This credential model is a DAC (Discretionary Access Control) layer for
cooperating tasks, not a process-isolation boundary. DAC here means
permission checks based on ownership and mode bits that the owner can
change (``chmod()``/``chown()``), rather than a mandatory policy
enforced independently of the object owner. On ``CONFIG_BUILD_FLAT``,
kernel and
application share one address space, so other code can write
``tg_euid`` / ``tg_egid`` (and other fields in ``task_group_s``)
directly and bypass the syscall checks. Protected and kernel builds
enforce the boundary via the syscall interface.
The same caveat applies to ``chroot()``'s ``euid == 0`` gate and
``tg_root``; see :ref:`chroot`.
Pseudo-Filesystem Ownership
===========================
When ``CONFIG_PSEUDOFS_ATTRIBUTES`` and ``CONFIG_SCHED_USER_IDENTITY`` are both
enabled, ``inode_alloc()`` assigns ``i_owner`` and ``i_group`` from the
caller's effective credentials. This covers
message queues (``mq_open()``), named semaphores (``sem_open()``), shared
memory objects (``shm_open()``), FIFOs (``mkfifo()``), and pseudo-files
created through the same inode reservation path.
Path resolution requires directory search permission (``X_OK``) on ancestors
via ``inode_checkpathperm()``. Open-time checks on the final node use
``inode_checkopenperm()`` (or ``inode_checkperm()`` for named IPC
objects). Full details, including mounts under private pseudoFS parents,
are in :ref:`file-permission`.